1 /*
2 * Copyright (c) 2025 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 #include "plugins/ets/runtime/interop_js/sts_vm_interface_impl.h"
17 #include "plugins/ets/runtime/interop_js/xgc/xgc.h"
18
19 namespace ark::ets::interop::js {
20
21 thread_local STSVMInterfaceImpl::XGCSyncState STSVMInterfaceImpl::xgcSyncState_ =
22 STSVMInterfaceImpl::XGCSyncState::NONE;
23
MarkFromObject(void * obj)24 void STSVMInterfaceImpl::MarkFromObject(void *obj)
25 {
26 XGC::GetInstance()->MarkFromObject(obj);
27 }
28
STSVMInterfaceImpl()29 STSVMInterfaceImpl::STSVMInterfaceImpl() : xgcBarrier_(STSVMInterface::DEFAULT_XGC_EXECUTORS_COUNT) {}
30
OnVMAttach()31 void STSVMInterfaceImpl::OnVMAttach()
32 {
33 xgcBarrier_.Increment();
34 }
35
OnVMDetach()36 void STSVMInterfaceImpl::OnVMDetach()
37 {
38 xgcBarrier_.Decrement();
39 }
40
StartXGCBarrier(const NoWorkPred & func)41 bool STSVMInterfaceImpl::StartXGCBarrier(const NoWorkPred &func)
42 {
43 ASSERT(xgcSyncState_ == XGCSyncState::NONE);
44 auto res = xgcBarrier_.InitialWait(func);
45 if (res) {
46 xgcSyncState_ = XGCSyncState::CONCURRENT_PHASE;
47 }
48 return res;
49 }
50
WaitForConcurrentMark(const NoWorkPred & func)51 bool STSVMInterfaceImpl::WaitForConcurrentMark(const NoWorkPred &func)
52 {
53 ASSERT(xgcSyncState_ == XGCSyncState::CONCURRENT_PHASE);
54 auto res = xgcBarrier_.Wait(func);
55 if (res) {
56 xgcSyncState_ = XGCSyncState::CONCURRENT_FINISHED;
57 }
58 return res;
59 }
60
RemarkStartBarrier()61 void STSVMInterfaceImpl::RemarkStartBarrier()
62 {
63 ASSERT(xgcSyncState_ == XGCSyncState::CONCURRENT_FINISHED);
64 xgcBarrier_.Wait();
65 xgcSyncState_ = XGCSyncState::REMARK_PHASE;
66 }
67
WaitForRemark(const NoWorkPred & func)68 bool STSVMInterfaceImpl::WaitForRemark(const NoWorkPred &func)
69 {
70 ASSERT(xgcSyncState_ == XGCSyncState::REMARK_PHASE);
71 auto res = xgcBarrier_.Wait(func);
72 if (res) {
73 xgcSyncState_ = XGCSyncState::NONE;
74 }
75 return res;
76 }
77
FinishXGCBarrier()78 void STSVMInterfaceImpl::FinishXGCBarrier()
79 {
80 xgcBarrier_.Wait();
81 xgcSyncState_ = XGCSyncState::NONE;
82 }
83
NotifyWaiters()84 void STSVMInterfaceImpl::NotifyWaiters()
85 {
86 xgcBarrier_.Signal();
87 }
88
VMBarrier(size_t vmsCount)89 STSVMInterfaceImpl::VMBarrier::VMBarrier(size_t vmsCount)
90 {
91 os::memory::LockHolder lh(barrierMutex_);
92 vmsCount_ = vmsCount;
93 currentVMsCount_ = 0U;
94 epochCount_ = 0U;
95 currentWaitersCount_ = 0U;
96 weakCount_ = 0U;
97 }
98
Increment()99 void STSVMInterfaceImpl::VMBarrier::Increment()
100 {
101 os::memory::LockHolder lh(barrierMutex_);
102 vmsCount_++;
103 }
104
Decrement()105 void STSVMInterfaceImpl::VMBarrier::Decrement()
106 {
107 os::memory::LockHolder lh(barrierMutex_);
108 ASSERT(vmsCount_ > 1); // after this Decrement, barrier is broken and will not wait correctly
109 vmsCount_--;
110 Wake();
111 }
112
InitialWait(const NoWorkPred & noWorkPred)113 bool STSVMInterfaceImpl::VMBarrier::InitialWait(const NoWorkPred &noWorkPred)
114 {
115 return Wait(noWorkPred, true);
116 }
117
Wait(const NoWorkPred & noWorkPred)118 bool STSVMInterfaceImpl::VMBarrier::Wait(const NoWorkPred &noWorkPred)
119 {
120 return Wait(noWorkPred, false);
121 }
122
Wait(const NoWorkPred & noWorkPred,bool isInitial)123 bool STSVMInterfaceImpl::VMBarrier::Wait(const NoWorkPred &noWorkPred, bool isInitial)
124 {
125 os::memory::LockHolder lh(barrierMutex_);
126 // Need check if noWorkPred exist and if yes, check if it's true. This prevent situation with lost Signal.
127 if (CheckNoWorkPred(noWorkPred)) {
128 return false;
129 }
130 size_t epochCount = 0;
131 do {
132 // Save current epoch to look at it in future.
133 epochCount = epochCount_;
134 // No we can try to pass the barrier. First we increment count of waiters.
135 auto currentWaitersCount = IncrementCurrentWaitersCount();
136 // For Inintial barrier we use variable vmsCount_, otherwise we use currentVMsCount_ that can not be checked
137 // between 2 Initial barriers.
138 size_t waitersCount = 0;
139 if (isInitial) {
140 waitersCount = vmsCount_;
141 } else {
142 waitersCount = currentVMsCount_;
143 }
144 // Next we check if this waiter is the last, if it true, we increase epoch and go out
145 if (currentWaitersCount == waitersCount) {
146 // for initial barrier we also should set new currentVMsCount_;
147 if (isInitial) {
148 currentVMsCount_ = vmsCount_;
149 }
150 epochCount_++;
151 Wake();
152 return true;
153 }
154 // We go to wait, it will return true if noWorkPred() returns true
155 if (WaitNextEpochOrSignal(noWorkPred)) {
156 return false;
157 }
158 } while (epochCount == epochCount_);
159 return true;
160 }
161
Signal()162 void STSVMInterfaceImpl::VMBarrier::Signal()
163 {
164 os::memory::LockHolder lh(barrierMutex_);
165 Wake();
166 }
167
IncrementCurrentWaitersCount()168 size_t STSVMInterfaceImpl::VMBarrier::IncrementCurrentWaitersCount()
169 {
170 return ++currentWaitersCount_;
171 }
172
WaitNextEpochOrSignal(const NoWorkPred & noWorkPred)173 bool STSVMInterfaceImpl::VMBarrier::WaitNextEpochOrSignal(const NoWorkPred &noWorkPred)
174 {
175 size_t weakCount = weakCount_;
176 do {
177 condVar_.Wait(&barrierMutex_);
178 // This while is needed only to avoid problems with spurious wakeups
179 } while (weakCount == weakCount_);
180 return CheckNoWorkPred(noWorkPred);
181 }
182
Wake()183 void STSVMInterfaceImpl::VMBarrier::Wake()
184 {
185 currentWaitersCount_ = 0;
186 weakCount_++;
187 condVar_.SignalAll();
188 }
189
190 } // namespace ark::ets::interop::js
191