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.h" 22#include "ecmascript/tests/test_helper.h" 23#include "ecmascript/checkpoint/thread_state_transition.h" 24 25#include <csetjmp> 26#include <csignal> 27using namespace panda::ecmascript; 28 29namespace panda::test { 30 31class StateTransitioningTest : public BaseTestWithScope<false> { 32public: 33 void InitializeLogger() 34 { 35 panda::ecmascript::JSRuntimeOptions runtimeOptions; 36 runtimeOptions.SetLogLevel("error"); 37 ecmascript::Log::Initialize(runtimeOptions); 38 } 39 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 ThreadManagedScope managedScope(thread); 51 if (nativeState) { 52 ThreadNativeScope nativeScope(thread); 53 activeThreadCount->fetch_add(1); 54 while (!isTestEnded->load() && !changeToRunning->load()) {} 55 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 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 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(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 ThreadSuspensionScope suspensionScope(thread); 90 t1.join(); 91 } 92 } 93 94 void ChangeAllThreadsToRunning() 95 { 96 changeToRunning.store(true); 97 } 98 99 bool CheckAllThreadsSuspended() 100 { 101 bool result = true; 102 for (auto i: vms) { 103 result &= i->GetAssociatedJSThread()->HasSuspendRequest(); 104 } 105 return result; 106 } 107 108 bool CheckAllThreadsState(ThreadState expectedState) 109 { 110 bool result = true; 111 for (auto i: vms) { 112 result &= (i->GetAssociatedJSThread()->GetState() == expectedState); 113 } 114 return result; 115 } 116 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 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 145HWTEST_F_L0(StateTransitioningTest, ThreadStateTransitionScopeTest) 146{ 147 ThreadState mainState = thread->GetState(); 148 { 149 ThreadStateTransitionScope<JSThread, ThreadState::CREATED> scope(thread); 150 EXPECT_TRUE(thread->GetState() == ThreadState::CREATED); 151 } 152 EXPECT_TRUE(thread->GetState() == mainState); 153 { 154 ThreadStateTransitionScope<JSThread, ThreadState::RUNNING> scope(thread); 155 EXPECT_TRUE(thread->GetState() == ThreadState::RUNNING); 156 } 157 EXPECT_TRUE(thread->GetState() == mainState); 158 { 159 ThreadStateTransitionScope<JSThread, ThreadState::NATIVE> scope(thread); 160 EXPECT_TRUE(thread->GetState() == ThreadState::NATIVE); 161 } 162 EXPECT_TRUE(thread->GetState() == mainState); 163 { 164 ThreadStateTransitionScope<JSThread, ThreadState::WAIT> scope(thread); 165 EXPECT_TRUE(thread->GetState() == ThreadState::WAIT); 166 } 167 EXPECT_TRUE(thread->GetState() == mainState); 168 { 169 ThreadStateTransitionScope<JSThread, ThreadState::IS_SUSPENDED> scope(thread); 170 EXPECT_TRUE(thread->GetState() == ThreadState::IS_SUSPENDED); 171 } 172 EXPECT_TRUE(thread->GetState() == mainState); 173 { 174 ThreadStateTransitionScope<JSThread, ThreadState::TERMINATED> scope(thread); 175 EXPECT_TRUE(thread->GetState() == ThreadState::TERMINATED); 176 } 177 EXPECT_TRUE(thread->GetState() == mainState); 178} 179 180HWTEST_F_L0(StateTransitioningTest, ThreadManagedScopeTest) 181{ 182 ThreadState mainState = thread->GetState(); 183 { 184 ThreadManagedScope scope(thread); 185 EXPECT_TRUE(thread->GetState() == ThreadState::RUNNING); 186 } 187 if (mainState == ThreadState::RUNNING) { 188 ThreadStateTransitionScope<JSThread, ThreadState::WAIT> tempScope(thread); 189 { 190 ThreadManagedScope scope(thread); 191 EXPECT_TRUE(thread->GetState() == ThreadState::RUNNING); 192 } 193 EXPECT_TRUE(thread->GetState() == ThreadState::WAIT); 194 } 195 EXPECT_TRUE(thread->GetState() == mainState); 196} 197 198HWTEST_F_L0(StateTransitioningTest, ThreadNativeScopeTest) 199{ 200 ThreadState mainState = thread->GetState(); 201 { 202 ThreadNativeScope scope(thread); 203 EXPECT_TRUE(thread->GetState() == ThreadState::NATIVE); 204 } 205 if (mainState == ThreadState::NATIVE) { 206 ThreadStateTransitionScope<JSThread, ThreadState::WAIT> tempScope(thread); 207 { 208 ThreadNativeScope scope(thread); 209 EXPECT_TRUE(thread->GetState() == ThreadState::NATIVE); 210 } 211 EXPECT_TRUE(thread->GetState() == ThreadState::WAIT); 212 } 213 EXPECT_TRUE(thread->GetState() == mainState); 214} 215 216HWTEST_F_L0(StateTransitioningTest, ThreadSuspensionScopeTest) 217{ 218 ThreadState mainState = thread->GetState(); 219 { 220 ThreadSuspensionScope scope(thread); 221 EXPECT_TRUE(thread->GetState() == ThreadState::IS_SUSPENDED); 222 } 223 if (mainState == ThreadState::IS_SUSPENDED) { 224 ThreadStateTransitionScope<JSThread, ThreadState::WAIT> tempScope(thread); 225 { 226 ThreadSuspensionScope scope(thread); 227 EXPECT_TRUE(thread->GetState() == ThreadState::IS_SUSPENDED); 228 } 229 EXPECT_TRUE(thread->GetState() == ThreadState::WAIT); 230 } 231 EXPECT_TRUE(thread->GetState() == mainState); 232} 233 234HWTEST_F_L0(StateTransitioningTest, IsInRunningStateTest) 235{ 236 { 237 ThreadNativeScope scope(thread); 238 EXPECT_TRUE(!thread->IsInRunningState()); 239 } 240 { 241 ThreadManagedScope scope(thread); 242 EXPECT_TRUE(thread->IsInRunningState()); 243 } 244} 245 246HWTEST_F_L0(StateTransitioningTest, ChangeStateTest) 247{ 248 { 249 ThreadNativeScope nativeScope(thread); 250 } 251 { 252 ThreadNativeScope nativeScope(thread); 253 { 254 ThreadManagedScope managedScope(thread); 255 } 256 } 257} 258 259HWTEST_F_L0(StateTransitioningTest, SuspendResumeRunningThreadVMTest) 260{ 261 CreateNewVMInSeparateThread(false); 262 EXPECT_FALSE(CheckAllThreadsSuspended()); 263 { 264 SuspendOrResumeAllThreads(true); 265 while (!CheckAllThreadsState(ThreadState::IS_SUSPENDED)) {} 266 EXPECT_TRUE(CheckAllThreadsSuspended()); 267 EXPECT_TRUE(CheckAllThreadsState(ThreadState::IS_SUSPENDED)); 268 } 269 SuspendOrResumeAllThreads(false); 270 while (CheckAllThreadsState(ThreadState::IS_SUSPENDED)) {} 271 EXPECT_FALSE(CheckAllThreadsSuspended()); 272 EXPECT_FALSE(CheckAllThreadsState(ThreadState::IS_SUSPENDED)); 273 DestroyAllVMs(); 274} 275 276HWTEST_F_L0(StateTransitioningTest, SuspendAllManagedTest) 277{ 278 CreateNewVMInSeparateThread(false); 279 EXPECT_TRUE(CheckAllThreadsState(ThreadState::RUNNING)); 280 { 281 SuspendAllScope suspendScope(JSThread::GetCurrent()); 282 EXPECT_TRUE(CheckAllThreadsSuspended()); 283 } 284 while (CheckAllThreadsState(ThreadState::IS_SUSPENDED)) {} 285 EXPECT_TRUE(CheckAllThreadsState(ThreadState::RUNNING)); 286 DestroyAllVMs(); 287} 288 289HWTEST_F_L0(StateTransitioningTest, SuspendAllNativeTest) 290{ 291 CreateNewVMInSeparateThread(true); 292 EXPECT_TRUE(CheckAllThreadsState(ThreadState::NATIVE)); 293 { 294 SuspendAllScope suspendScope(JSThread::GetCurrent()); 295 EXPECT_TRUE(CheckAllThreadsState(ThreadState::NATIVE)); 296 } 297 EXPECT_TRUE(CheckAllThreadsState(ThreadState::NATIVE)); 298 DestroyAllVMs(); 299} 300 301HWTEST_F_L0(StateTransitioningTest, SuspendAllNativeTransferToRunningTest) 302{ 303 CreateNewVMInSeparateThread(true); 304 EXPECT_TRUE(CheckAllThreadsState(ThreadState::NATIVE)); 305 { 306 SuspendAllScope suspendScope(JSThread::GetCurrent()); 307 EXPECT_TRUE(CheckAllThreadsState(ThreadState::NATIVE)); 308 ChangeAllThreadsToRunning(); 309 while (!CheckAllThreadsState(ThreadState::NATIVE)) {} 310 EXPECT_TRUE(CheckAllThreadsState(ThreadState::NATIVE)); 311 } 312 while (CheckAllThreadsState(ThreadState::IS_SUSPENDED)) {} 313 while (CheckAllThreadsState(ThreadState::NATIVE)) {} 314 EXPECT_TRUE(CheckAllThreadsState(ThreadState::RUNNING)); 315 DestroyAllVMs(); 316} 317} // namespace panda::test 318