* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "thread/thread_holder-inl.h"
#include "common_components/common_runtime/hooks.h"
#include "common_components/mutator/mutator.h"
#include "common_components/mutator/thread_local.h"
#include "base_runtime.h"
#include "thread/base_thread.h"
#include "thread/thread_holder_manager.h"
#include "thread/thread_state_transition.h"
#ifdef PANDA_JS_ETS_HYBRID_MODE
namespace common {
InterOpCoroutineToNativeHookFunc interOpCoroutineToNativeHook = nullptr;
InterOpCoroutineToRunningHookFunc interOpCoroutineToRunningHook = nullptr;
bool InterOpCoroutineToNative([[maybe_unused]] ThreadHolder *current)
{
#ifndef CMC_LCOV_EXCL
if (interOpCoroutineToNativeHook == nullptr) {
return false;
}
return interOpCoroutineToNativeHook(current);
#else
return false;
#endif
}
bool InterOpCoroutineToRunning([[maybe_unused]] ThreadHolder *current)
{
#ifndef CMC_LCOV_EXCL
if (interOpCoroutineToRunningHook == nullptr) {
return false;
}
return interOpCoroutineToRunningHook(current);
#else
return false;
#endif
}
void RegisterInterOpCoroutineToNativeHook(InterOpCoroutineToNativeHookFunc func)
{
interOpCoroutineToNativeHook = func;
}
void RegisterInterOpCoroutineToRunningHook(InterOpCoroutineToRunningHookFunc func)
{
interOpCoroutineToRunningHook = func;
}
}
#endif
namespace common {
thread_local ThreadHolder *currentThreadHolder = nullptr;
ThreadHolder *ThreadHolder::CreateAndRegisterNewThreadHolder(void *vm)
{
if (ThreadLocal::IsArkProcessor()) {
LOG_COMMON(FATAL) << "CreateAndRegisterNewThreadHolder fail";
return nullptr;
}
Mutator* mutator = Mutator::NewMutator();
CHECK_CC(mutator != nullptr);
mutator->SetEcmaVMPtr(vm);
ThreadHolder *holder = mutator->GetThreadHolder();
BaseRuntime::GetInstance()->GetThreadHolderManager().RegisterThreadHolder(holder);
return holder;
}
void ThreadHolder::ReleaseAllocBuffer()
{
ThreadManagedScope scope(this);
if (allocBuffer_) {
auto buf = reinterpret_cast<AllocationBuffer*>(allocBuffer_);
if (buf->DecreaseRefCount()) {
buf->Unregister();
delete buf;
}
allocBuffer_ = nullptr;
}
}
void ThreadHolder::DestroyThreadHolder(ThreadHolder *holder)
{
holder->ReleaseAllocBuffer();
holder->TransferToNative();
BaseRuntime::GetInstance()->GetThreadHolderManager().UnregisterThreadHolder(holder);
}
ThreadHolder *ThreadHolder::GetCurrent()
{
return currentThreadHolder;
}
void ThreadHolder::SetCurrent(ThreadHolder *holder)
{
currentThreadHolder = holder;
}
void ThreadHolder::RegisterJSThread(JSThread *jsThread)
{
DCHECK_CC(!IsInRunningState());
TransferToRunning();
DCHECK_CC(jsThread_ == nullptr);
jsThread_ = jsThread;
mutatorBase_->RegisterJSThread(jsThread);
SynchronizeGCPhaseToJSThread(jsThread, mutatorBase_->GetMutatorPhase());
TransferToNative();
}
void ThreadHolder::UnregisterJSThread([[maybe_unused]] JSThread *jsThread)
{
#ifndef CMC_LCOV_EXCL
DCHECK_CC(!IsInRunningState());
TransferToRunning();
DCHECK_CC(jsThread_ == jsThread);
jsThread_ = nullptr;
mutatorBase_->UnregisterJSThread();
if (coroutines_.empty()) {
ThreadHolder::DestroyThreadHolder(this);
}
#endif
}
void ThreadHolder::RegisterCoroutine(Coroutine *coroutine)
{
ThreadManagedScope scope(this);
DCHECK_CC(coroutines_.find(coroutine) == coroutines_.end());
coroutines_.insert(coroutine);
}
void ThreadHolder::UnregisterCoroutine([[maybe_unused]] Coroutine *coroutine)
{
#ifndef CMC_LCOV_EXCL
DCHECK_CC(!IsInRunningState());
TransferToRunning();
DCHECK_CC(coroutines_.find(coroutine) != coroutines_.end());
coroutines_.erase(coroutine);
if (coroutines_.empty() && jsThread_ == nullptr) {
ThreadHolder::DestroyThreadHolder(this);
}
#endif
}
bool ThreadHolder::TryBindMutator()
{
if (ThreadLocal::IsArkProcessor()) {
return false;
}
BaseRuntime::GetInstance()->GetThreadHolderManager().BindMutator(this);
allocBuffer_ = ThreadLocal::GetAllocBuffer();
reinterpret_cast<AllocationBuffer*>(allocBuffer_)->IncreaseRefCount();
DCHECK_CC(allocBuffer_ != nullptr);
return true;
}
void ThreadHolder::BindMutator()
{
#ifndef CMC_LCOV_EXCL
if (!TryBindMutator()) {
LOG_COMMON(FATAL) << "BindMutator fail";
return;
}
#endif
}
void ThreadHolder::UnbindMutator()
{
allocBuffer_ = nullptr;
BaseRuntime::GetInstance()->GetThreadHolderManager().UnbindMutator(this);
}
void ThreadHolder::WaitSuspension()
{
mutatorBase_->HandleSuspensionRequest();
}
void ThreadHolder::VisitAllThreads(CommonRootVisitor visitor)
{
if (jsThread_ != nullptr) {
VisitJSThread(jsThread_, visitor);
}
for ([[maybe_unused]] auto *coroutine : coroutines_) {
}
}
ThreadHolder::TryBindMutatorScope::TryBindMutatorScope(ThreadHolder *holder) : holder_(nullptr)
{
if (holder->TryBindMutator()) {
holder_ = holder;
}
}
ThreadHolder::TryBindMutatorScope::~TryBindMutatorScope()
{
if (holder_ != nullptr) {
holder_->ReleaseAllocBuffer();
holder_->UnbindMutator();
holder_ = nullptr;
}
}
}