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 #include "ecmascript/ecma_vm.h"
17 #include "ecmascript/global_env.h"
18 #include "ecmascript/js_handle.h"
19 #include "ecmascript/js_runtime_options.h"
20 #include "ecmascript/js_thread.h"
21 #include "ecmascript/log_wrapper.h"
22 #include "ecmascript/tests/test_helper.h"
23 #include "ecmascript/checkpoint/thread_state_transition.h"
24
25 #include <csetjmp>
26 #include <csignal>
27 using namespace panda::ecmascript;
28
29 namespace panda::test {
30
31 class StateTransitioningTest : public BaseTestWithScope<false> {
32 public:
InitializeLogger()33 void InitializeLogger()
34 {
35 panda::ecmascript::JSRuntimeOptions runtimeOptions;
36 runtimeOptions.SetLogLevel("error");
37 common::Log::Initialize(runtimeOptions.GetLogOptions());
38 }
39
NewVMThreadEntry(EcmaVM * newVm,bool nativeState,std::atomic<bool> * changeToRunning,std::atomic<bool> * isTestEnded,std::atomic<size_t> * activeThreadCount)40 static void NewVMThreadEntry(EcmaVM *newVm,
41 bool nativeState,
42 std::atomic<bool> *changeToRunning,
43 std::atomic<bool> *isTestEnded,
44 std::atomic<size_t> *activeThreadCount)
45 {
46 panda::RuntimeOption postOption;
47 JSNApi::PostFork(newVm, postOption);
48 {
49 JSThread *thread = JSThread::GetCurrent();
50 ecmascript::ThreadManagedScope managedScope(thread);
51 if (nativeState) {
52 ecmascript::ThreadNativeScope nativeScope(thread);
53 activeThreadCount->fetch_add(1);
54 while (!isTestEnded->load() && !changeToRunning->load()) {}
55 ecmascript::ThreadManagedScope secondManagedScope(thread);
56 while (!isTestEnded->load()) {}
57 } else {
58 activeThreadCount->fetch_add(1);
59 while (!isTestEnded->load()) {
60 JSThread::GetCurrent()->CheckSafepoint();
61 }
62 }
63 }
64 JSNApi::DestroyJSVM(newVm);
65 activeThreadCount->fetch_sub(1);
66 }
67
CreateNewVMInSeparateThread(bool nativeState)68 void CreateNewVMInSeparateThread(bool nativeState)
69 {
70 std::thread t1([&]() {
71 RuntimeOption options;
72 EcmaVM *newVm = JSNApi::CreateJSVM(options);
73 vms.push_back(newVm);
74 {
75 ecmascript::ThreadManagedScope managedScope(newVm->GetJSThread());
76 JSNApi::PreFork(newVm);
77 }
78 size_t oldCount = activeThreadCount;
79 // This case isn't a really fork which causes JSThread::GetCurrentThread() equals nullptr in worker_thread.
80 // So reset the threadState as CREATED to skip the check.
81 newVm->GetAssociatedJSThread()->UpdateState(ecmascript::ThreadState::CREATED);
82 std::thread *worker_thread = new std::thread(StateTransitioningTest::NewVMThreadEntry, newVm, nativeState,
83 &changeToRunning, &isTestEnded, &activeThreadCount);
84 threads.push_back(worker_thread);
85 while (activeThreadCount == oldCount) {
86 }
87 });
88 {
89 ecmascript::ThreadSuspensionScope suspensionScope(thread);
90 t1.join();
91 }
92 }
93
ChangeAllThreadsToRunning()94 void ChangeAllThreadsToRunning()
95 {
96 changeToRunning.store(true);
97 }
98
CheckAllThreadsSuspended()99 bool CheckAllThreadsSuspended()
100 {
101 bool result = true;
102 for (auto i: vms) {
103 result &= i->GetAssociatedJSThread()->HasSuspendRequest();
104 }
105 return result;
106 }
107
CheckAllThreadsState(ecmascript::ThreadState expectedState)108 bool CheckAllThreadsState(ecmascript::ThreadState expectedState)
109 {
110 bool result = true;
111 for (auto i: vms) {
112 result &= (i->GetAssociatedJSThread()->GetState() == expectedState);
113 }
114 return result;
115 }
116
SuspendOrResumeAllThreads(bool toSuspend)117 void SuspendOrResumeAllThreads(bool toSuspend)
118 {
119 for (auto i: vms) {
120 if (toSuspend) {
121 i->GetAssociatedJSThread()->SuspendThread(false);
122 } else {
123 i->GetAssociatedJSThread()->ResumeThread(false);
124 }
125 }
126 }
127
DestroyAllVMs()128 void DestroyAllVMs()
129 {
130 isTestEnded = true;
131 while (activeThreadCount != 0) {}
132 for (auto i: threads) {
133 i->join();
134 delete(i);
135 }
136 }
137
138 std::list<EcmaVM *> vms;
139 std::list<std::thread *> threads;
140 std::atomic<size_t> activeThreadCount {0};
141 std::atomic<bool> isTestEnded {false};
142 std::atomic<bool> changeToRunning {false};
143 };
144
HWTEST_F_L0(StateTransitioningTest,ThreadStateTransitionScopeTest)145 HWTEST_F_L0(StateTransitioningTest, ThreadStateTransitionScopeTest)
146 {
147 ecmascript::ThreadState mainState = thread->GetState();
148 {
149 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::CREATED> scope(thread);
150 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::CREATED);
151 }
152 EXPECT_TRUE(thread->GetState() == mainState);
153 {
154 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::RUNNING> scope(thread);
155 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::RUNNING);
156 }
157 EXPECT_TRUE(thread->GetState() == mainState);
158 {
159 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::NATIVE> scope(thread);
160 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::NATIVE);
161 }
162 EXPECT_TRUE(thread->GetState() == mainState);
163 {
164 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::WAIT> scope(thread);
165 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::WAIT);
166 }
167 EXPECT_TRUE(thread->GetState() == mainState);
168 {
169 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::IS_SUSPENDED> scope(thread);
170 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::IS_SUSPENDED);
171 }
172 EXPECT_TRUE(thread->GetState() == mainState);
173 {
174 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::TERMINATED> scope(thread);
175 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::TERMINATED);
176 }
177 EXPECT_TRUE(thread->GetState() == mainState);
178 }
179
HWTEST_F_L0(StateTransitioningTest,ThreadManagedScopeTest)180 HWTEST_F_L0(StateTransitioningTest, ThreadManagedScopeTest)
181 {
182 ecmascript::ThreadState mainState = thread->GetState();
183 {
184 ecmascript::ThreadManagedScope scope(thread);
185 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::RUNNING);
186 }
187 if (mainState == ecmascript::ThreadState::RUNNING) {
188 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::WAIT> tempScope(thread);
189 {
190 ecmascript::ThreadManagedScope scope(thread);
191 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::RUNNING);
192 }
193 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::WAIT);
194 }
195 EXPECT_TRUE(thread->GetState() == mainState);
196 }
197
HWTEST_F_L0(StateTransitioningTest,ThreadNativeScopeTest)198 HWTEST_F_L0(StateTransitioningTest, ThreadNativeScopeTest)
199 {
200 ecmascript::ThreadState mainState = thread->GetState();
201 {
202 ecmascript::ThreadNativeScope scope(thread);
203 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::NATIVE);
204 }
205 if (mainState == ecmascript::ThreadState::NATIVE) {
206 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::WAIT> tempScope(thread);
207 {
208 ecmascript::ThreadNativeScope scope(thread);
209 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::NATIVE);
210 }
211 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::WAIT);
212 }
213 EXPECT_TRUE(thread->GetState() == mainState);
214 }
215
HWTEST_F_L0(StateTransitioningTest,ThreadSuspensionScopeTest)216 HWTEST_F_L0(StateTransitioningTest, ThreadSuspensionScopeTest)
217 {
218 ecmascript::ThreadState mainState = thread->GetState();
219 {
220 ecmascript::ThreadSuspensionScope scope(thread);
221 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::IS_SUSPENDED);
222 }
223 if (mainState == ecmascript::ThreadState::IS_SUSPENDED) {
224 ecmascript::ThreadStateTransitionScope<JSThread, ecmascript::ThreadState::WAIT> tempScope(thread);
225 {
226 ecmascript::ThreadSuspensionScope scope(thread);
227 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::IS_SUSPENDED);
228 }
229 EXPECT_TRUE(thread->GetState() == ecmascript::ThreadState::WAIT);
230 }
231 EXPECT_TRUE(thread->GetState() == mainState);
232 }
233
HWTEST_F_L0(StateTransitioningTest,IsInRunningStateTest)234 HWTEST_F_L0(StateTransitioningTest, IsInRunningStateTest)
235 {
236 {
237 ecmascript::ThreadNativeScope scope(thread);
238 EXPECT_TRUE(!thread->IsInRunningState());
239 }
240 {
241 ecmascript::ThreadManagedScope scope(thread);
242 EXPECT_TRUE(thread->IsInRunningState());
243 }
244 }
245
HWTEST_F_L0(StateTransitioningTest,ChangeStateTest)246 HWTEST_F_L0(StateTransitioningTest, ChangeStateTest)
247 {
248 {
249 ecmascript::ThreadNativeScope nativeScope(thread);
250 }
251 {
252 ecmascript::ThreadNativeScope nativeScope(thread);
253 {
254 ecmascript::ThreadManagedScope managedScope(thread);
255 }
256 }
257 }
258
HWTEST_F_L0(StateTransitioningTest,SuspendResumeRunningThreadVMTest)259 HWTEST_F_L0(StateTransitioningTest, SuspendResumeRunningThreadVMTest)
260 {
261 CreateNewVMInSeparateThread(false);
262 EXPECT_FALSE(CheckAllThreadsSuspended());
263 {
264 SuspendOrResumeAllThreads(true);
265 while (!CheckAllThreadsState(ecmascript::ThreadState::IS_SUSPENDED)) {}
266 EXPECT_TRUE(CheckAllThreadsSuspended());
267 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::IS_SUSPENDED));
268 }
269 SuspendOrResumeAllThreads(false);
270 while (CheckAllThreadsState(ecmascript::ThreadState::IS_SUSPENDED)) {}
271 EXPECT_FALSE(CheckAllThreadsSuspended());
272 EXPECT_FALSE(CheckAllThreadsState(ecmascript::ThreadState::IS_SUSPENDED));
273 DestroyAllVMs();
274 }
275
HWTEST_F_L0(StateTransitioningTest,SuspendAllManagedTest)276 HWTEST_F_L0(StateTransitioningTest, SuspendAllManagedTest)
277 {
278 CreateNewVMInSeparateThread(false);
279 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::RUNNING));
280 {
281 ecmascript::SuspendAllScope suspendScope(JSThread::GetCurrent());
282 EXPECT_TRUE(CheckAllThreadsSuspended());
283 }
284 while (CheckAllThreadsState(ecmascript::ThreadState::IS_SUSPENDED)) {}
285 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::RUNNING));
286 DestroyAllVMs();
287 }
288
HWTEST_F_L0(StateTransitioningTest,SuspendAllNativeTest)289 HWTEST_F_L0(StateTransitioningTest, SuspendAllNativeTest)
290 {
291 CreateNewVMInSeparateThread(true);
292 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::NATIVE));
293 {
294 ecmascript::SuspendAllScope suspendScope(JSThread::GetCurrent());
295 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::NATIVE));
296 }
297 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::NATIVE));
298 DestroyAllVMs();
299 }
300
HWTEST_F_L0(StateTransitioningTest,SuspendAllNativeTransferToRunningTest)301 HWTEST_F_L0(StateTransitioningTest, SuspendAllNativeTransferToRunningTest)
302 {
303 CreateNewVMInSeparateThread(true);
304 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::NATIVE));
305 {
306 ecmascript::SuspendAllScope suspendScope(JSThread::GetCurrent());
307 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::NATIVE));
308 ChangeAllThreadsToRunning();
309 while (!CheckAllThreadsState(ecmascript::ThreadState::NATIVE)) {}
310 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::NATIVE));
311 }
312 while (CheckAllThreadsState(ecmascript::ThreadState::IS_SUSPENDED)) {}
313 while (CheckAllThreadsState(ecmascript::ThreadState::NATIVE)) {}
314 EXPECT_TRUE(CheckAllThreadsState(ecmascript::ThreadState::RUNNING));
315 DestroyAllVMs();
316 }
317 } // namespace panda::test
318