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