1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef ECMASCRIPT_MEM_RSET_WORKLIST_HANDLER_INL_H
17 #define ECMASCRIPT_MEM_RSET_WORKLIST_HANDLER_INL_H
18
19 #include "ecmascript/mem/rset_worklist_handler.h"
20
21 #include "ecmascript/ecma_vm.h"
22 #include "ecmascript/js_thread.h"
23 #include "ecmascript/mem/heap.h"
24
25 namespace panda::ecmascript {
RSetWorkListHandler(Heap *heap)26 inline RSetWorkListHandler::RSetWorkListHandler(Heap *heap) : heap_(heap)
27 {
28 CollectRSetItemsInHeap(heap);
29 }
30
31 template<class Visitor>
Process([[maybe_unused]] const Visitor &visitor)32 inline void RSetItem::Process([[maybe_unused]] const Visitor &visitor)
33 {
34 visitor(region_, rSet_);
35 }
36
MergeBack()37 inline void RSetItem::MergeBack()
38 {
39 region_->MergeLocalToShareRSetForCM(rSet_);
40 }
41
EnumerateRegions(const Heap *heap)42 inline void RSetWorkListHandler::EnumerateRegions(const Heap *heap)
43 {
44 heap->EnumerateRegions([this](Region *region) {
45 RememberedSet *rset = region->ExtractLocalToShareRSet();
46 if (rset != nullptr) {
47 items_.emplace_back(region, rset);
48 }
49 });
50 }
51
CollectRSetItemsInHeap(const Heap *heap)52 inline void RSetWorkListHandler::CollectRSetItemsInHeap(const Heap *heap)
53 {
54 ASSERT(items_.empty());
55 ASSERT(items_.capacity() == 0);
56 items_.reserve(heap->GetRegionCount());
57 EnumerateRegions(heap);
58 ASSERT(items_.size() <= heap->GetRegionCount());
59 Initialize();
60 }
61
Initialize()62 ARK_INLINE void RSetWorkListHandler::Initialize()
63 {
64 ASSERT(!initialized_);
65 // Maybe less than -1, when js thread called MergeBackAndReset first, and then daemon thread called
66 // ProcessAll, which will make it less than -1.
67 ASSERT(nextItemIndex_ < 0);
68 ASSERT(remainItems_ == 0);
69 initialized_ = true;
70 int num = static_cast<int>(items_.size());
71 nextItemIndex_.store(num - 1, std::memory_order_relaxed);
72 remainItems_ = num;
73 }
74
75 template<class Visitor>
76 ARK_INLINE bool RSetWorkListHandler::ProcessNext(const Visitor &visitor)
77 {
78 // At this time, the items may be already merged back and cleared by other threads,
79 // but the idx will get a negative value, so it's ok.
80 int idx = nextItemIndex_.fetch_sub(1, std::memory_order_relaxed);
81 if (idx < 0) {
82 return false;
83 }
84 items_[idx].Process(visitor);
85 return true;
86 }
87
88 template<class Visitor>
89 inline void RSetWorkListHandler::ProcessAllVisitor(const Visitor &visitor, int done)
90 {
91 while (ProcessNext(visitor)) {
92 ++done;
93 }
94 if (done > 0) {
95 LockHolder lock(mutex_);
96 remainItems_ -= done;
97 if (remainItems_ == 0) {
98 cv_.SignalAll();
99 }
100 }
101 }
102
103 template<class Visitor>
104 inline void RSetWorkListHandler::ProcessAll(const Visitor &visitor)
105 {
106 ASSERT(JSThread::GetCurrent()->IsDaemonThread() ||
107 (JSThread::GetCurrent() == heap_->GetEcmaVM()->GetJSThread() && JSThread::GetCurrent()->IsInRunningState()));
108 // At this time, the items may be already cleared, but the ProcessNext will do nothing and return false,
109 // it just means that there is nothing to work.
110 int done = 0;
111 ProcessAllVisitor(visitor, done);
112 }
113
114 // This should only be called from js thread in RUNNING state, see comments for initialized_.
WaitFinishedThenMergeBack()115 inline void RSetWorkListHandler::WaitFinishedThenMergeBack()
116 {
117 ASSERT(JSThread::GetCurrent() == heap_->GetEcmaVM()->GetJSThread());
118 ASSERT(JSThread::GetCurrent()->IsInRunningState());
119 ASSERT(initialized_);
120 LockHolder lock(mutex_);
121 while (remainItems_ != 0) {
122 cv_.Wait(&mutex_);
123 }
124 ASSERT(remainItems_ == 0);
125 [[maybe_unused]] bool res = MergeBack();
126 ASSERT(res);
127 }
128
TryMergeBack()129 inline bool RSetWorkListHandler::TryMergeBack()
130 {
131 return reinterpret_cast<std::atomic<bool>*>(&initialized_)->exchange(false, std::memory_order_relaxed) == true;
132 }
133
MergeBackForAllItem()134 inline void RSetWorkListHandler::MergeBackForAllItem()
135 {
136 ASSERT(nextItemIndex_ < 0);
137 ASSERT(remainItems_ == 0);
138 for (RSetItem &item : items_) {
139 item.MergeBack();
140 }
141 }
142
143 inline bool RSetWorkListHandler::MergeBack()
144 {
145 ASSERT((JSThread::GetCurrent()->IsJSThread() && JSThread::GetCurrent()->IsInRunningState()) ||
146 (JSThread::GetCurrent()->IsDaemonThread() && JSThread::GetCurrent()->IsInSuspendedState()));
147 if (!TryMergeBack()) {
148 // Is merged back by bound js thread.
149 ASSERT(JSThread::GetCurrent()->IsDaemonThread());
150 return false;
151 }
152 MergeBackForAllItem();
153 // This in only called in the bound js thread, or daemon thread when SuspendAll, so this set without atomic
154 // is safety.
155 heap_->SetRSetWorkListHandler(nullptr);
156 return true;
157 }
158
GetHeap()159 inline Heap *RSetWorkListHandler::GetHeap()
160 {
161 return heap_;
162 }
163 } // namespace panda::ecmascript
164
165 #endif // ECMASCRIPT_MEM_RSET_WORKLIST_HANDLER_INL_H
166