diff --git a/ecmascript/checkpoint/thread_state_transition.h b/ecmascript/checkpoint/thread_state_transition.h index 99e679ee930c4b16d56872d3fe8fc16d88870fd4..f0cfcebc8bdc3e2c3d53692d24e5b7314d7cee24 100644 --- a/ecmascript/checkpoint/thread_state_transition.h +++ b/ecmascript/checkpoint/thread_state_transition.h @@ -175,5 +175,28 @@ private: ThreadStateTransitionScope scope_; NO_COPY_SEMANTIC(SuspendAllScope); }; + +template +class SuspendOtherScope final { + static_assert(std::is_base_of_v); + static_assert(!std::is_same_v); +public: + explicit SuspendOtherScope(T* self, T* target) + : self_(self), target_(target), scope_(self) + { + ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "SuspendOther", ""); + Runtime::GetInstance()->SuspendOther(self_, target_); + } + ~SuspendOtherScope() + { + ECMA_BYTRACE_NAME(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_ARK, "ResumeOther", ""); + Runtime::GetInstance()->ResumeOther(self_, target_); + } +private: + T* self_; + T* target_; + ThreadStateTransitionScope scope_; + NO_COPY_SEMANTIC(SuspendOtherScope); +}; } // namespace panda::ecmascript #endif // ECMASCRIPT_CHECKPOINT_THREAD_STATE_TRANSITION_H diff --git a/ecmascript/napi/dfx_jsnapi.cpp b/ecmascript/napi/dfx_jsnapi.cpp index e377c9b54f91e19d3328f545d951bd7c1f3f9aa1..f661a89eaeae06d6aa77826e7eb779852fe2a353 100644 --- a/ecmascript/napi/dfx_jsnapi.cpp +++ b/ecmascript/napi/dfx_jsnapi.cpp @@ -1140,11 +1140,17 @@ void DFXJSNApi::GetMainThreadStackTrace(const EcmaVM *vm, std::string &stackTrac stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace( thread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX); } else { - ecmascript::ThreadManagedScope runningScope(thread); - ecmascript::SuspendAllScope suspendScope(thread); auto mainThread = ecmascript::Runtime::GetInstance()->GetMainThread(); - stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace( - mainThread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX); + ecmascript::ThreadManagedScope runningScope(thread); + if (g_isEnableCMCGC) { + ecmascript::SuspendAllScope suspendAllScope(thread); + stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace( + mainThread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX); + } else { + ecmascript::SuspendOtherScope suspendOtherScope(thread, mainThread); + stackTraceStr = ecmascript::JsStackInfo::BuildJsStackTrace( + mainThread, false, false, ecmascript::JS_STACK_TRACE_DEPTH_MAX); + } } auto sourceMapcb = vm->GetSourceMapCallback(); if (sourceMapcb != nullptr && !stackTraceStr.empty()) { diff --git a/ecmascript/runtime.cpp b/ecmascript/runtime.cpp index a3af7df74a0e1670df53be77ef0d9b7681934d69..6a469477d626a0264061cab6088579d9f2c7a295 100644 --- a/ecmascript/runtime.cpp +++ b/ecmascript/runtime.cpp @@ -362,6 +362,71 @@ void Runtime::ResumeAllThreadsImpl(JSThread *current) } } +void Runtime::SuspendOther(JSThread *current, JSThread *target) +{ + ASSERT(!g_isEnableCMCGC); + ASSERT(current != nullptr); + ASSERT(!current->IsInRunningState()); + SuspendOtherThreadImpl(current, target); +} + +void Runtime::ResumeOther(JSThread *current, JSThread *target) +{ + ASSERT(!g_isEnableCMCGC); + ASSERT(current != nullptr); + ASSERT(!current->IsInRunningState()); + ResumeOtherThreadImpl(current, target); +} + +void Runtime::SuspendOtherThreadImpl(JSThread *current, JSThread *target) +{ + SuspendBarrier barrier; + for (uint32_t iterCount = 1U;; ++iterCount) { + { + LockHolder lock(threadsLock_); + if (suspendNewCount_ == 0) { + suspendNewCount_++; + if (current == target || + std::find(threads_.begin(), threads_.end(), target) == threads_.end()) { + LOG_ECMA(FATAL) << "Invalid target thread!"; + UNREACHABLE(); + } + barrier.Initialize(1); + target->SuspendThread(+1, &barrier); + if (target->IsSuspended()) { + target->PassSuspendBarrier(); + } + return; + } + } + if (iterCount < MAX_SUSPEND_RETRIES) { + LockHolder lock(threadsLock_); + if (suspendNewCount_ != 0) { + threadSuspendCondVar_.Wait(&threadsLock_); + } + } else { + LOG_ECMA(FATAL) << "Too many SuspendOther retries!"; + UNREACHABLE(); + } + } + barrier.Wait(); +} + +void Runtime::ResumeOtherThreadImpl(JSThread *current, JSThread *target) +{ + LockHolder lock(threadsLock_); + if (suspendNewCount_ > 0) { + suspendNewCount_--; + } + if (suspendNewCount_ == 0) { + // Signal to waiting to suspend thread + threadSuspendCondVar_.Signal(); + } + if (target != current) { + target->ResumeThread(true); + } +} + void Runtime::IterateSharedRoot(RootVisitor &visitor) { IterateSerializeRoot(visitor); diff --git a/ecmascript/runtime.h b/ecmascript/runtime.h index f5064272ea40d0a80246607702f45464e505e671..8124b2ba213dbefaa84acf212ade47c1951aff20 100644 --- a/ecmascript/runtime.h +++ b/ecmascript/runtime.h @@ -51,6 +51,8 @@ public: void SuspendAll(JSThread *current); void ResumeAll(JSThread *current); + void SuspendOther(JSThread *current, JSThread *target); + void ResumeOther(JSThread *current, JSThread *target); void IterateSerializeRoot(RootVisitor &v); JSThread *GetMainThread() const @@ -315,6 +317,8 @@ private: ~Runtime(); void SuspendAllThreadsImpl(JSThread *current); void ResumeAllThreadsImpl(JSThread *current); + void SuspendOtherThreadImpl(JSThread *current, JSThread *target); + void ResumeOtherThreadImpl(JSThread *current, JSThread *target); void PreInitialization(const EcmaVM *vm); void PostInitialization(const EcmaVM *vm);