• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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