* Copyright (c) 2024 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 "ecmascript/daemon/daemon_thread.h"
#include "common_components/taskpool/taskpool.h"
#include "ecmascript/daemon/daemon_task-inl.h"
#include "ecmascript/runtime.h"
#ifdef ENABLE_QOS
#include "qos.h"
#endif
namespace panda::ecmascript {
DaemonThread *DaemonThread::instance_ = nullptr;
void DaemonThread::CreateNewInstance()
{
ASSERT(instance_ == nullptr);
instance_ = new DaemonThread();
instance_->StartRunning();
}
DaemonThread *DaemonThread::GetInstance()
{
ASSERT(instance_ != nullptr);
return instance_;
}
void DaemonThread::DestroyInstance()
{
ASSERT(instance_ != nullptr);
instance_->Stop();
delete instance_;
instance_ = nullptr;
}
void DaemonThread::StartRunning()
{
ASSERT(thread_ == nullptr);
ASSERT(!IsRunning());
ASSERT(HasRequestedTermination());
ASSERT(tasks_.empty());
ASSERT(GetThreadId() == 0);
terminationRequested_ = false;
std::atomic_thread_fence(std::memory_order_seq_cst);
thread_ = std::make_unique<std::thread>([this] {this->Run();});
common::Taskpool::GetCurrentTaskpool()->Initialize();
}
void DaemonThread::Stop()
{
[[maybe_unused]] PostTaskResult res = CheckAndPostTask(TerminateDaemonTask(nullptr));
ASSERT(res == PostTaskResult::SUCCESS);
thread_->join();
thread_.reset();
common::Taskpool::GetCurrentTaskpool()->Destroy(GetThreadId());
ASSERT(g_isEnableCMCGC || !IsInRunningState());
ASSERT(g_isEnableCMCGC || !IsRunning());
ASSERT(thread_ == nullptr);
ASSERT(tasks_.empty());
ResetThreadId();
}
bool DaemonThread::IsRunning() const
{
return running_;
}
void DaemonThread::Terminate()
{
ASSERT(JSThread::GetCurrent() == this);
running_ = false;
}
bool DaemonThread::HasRequestedTermination() const
{
return terminationRequested_;
}
void DaemonThread::RequestTermination()
{
ASSERT(!terminationRequested_);
terminationRequested_ = true;
}
DaemonThread::PostTaskResult DaemonThread::CheckAndPostTask(DaemonTask task)
{
LockHolder holder(mtx_);
if (UNLIKELY(HasRequestedTermination())) {
LOG_GC(ERROR) << "Try to post task to daemon, but daemon thread is not running.";
return PostTaskResult::DAEMON_THREAD_NOT_RUNNING;
}
if (AddTaskGroup(task.GetTaskGroup())) {
task.PostTaskPrologue();
tasks_.emplace_back(task);
cv_.Signal();
return PostTaskResult::SUCCESS;
}
return PostTaskResult::SAME_GROUP_TASK_ALREADY_POSTED;
}
void DaemonThread::Run()
{
ASSERT(!IsRunning());
running_ = true;
os::thread::native_handle_type thread = os::thread::GetNativeHandle();
os::thread::SetThreadName(thread, "OS_GC_Thread");
ASSERT(JSThread::GetCurrent() == nullptr);
if (g_isEnableCMCGC) {
glueData_.threadHolder_ = ToUintPtr(ThreadHolder::CreateAndRegisterNewThreadHolder(nullptr));
}
RegisterThread(this);
SetThreadId();
CaptureThreadName();
#ifdef ENABLE_QOS
OHOS::QOS::SetThreadQos(OHOS::QOS::QosLevel::QOS_USER_INITIATED);
#endif
while (IsRunning()) {
ASSERT(!IsInRunningState());
DaemonTask task = PopTask();
runningGroup_ = task.GetTaskGroup();
task.Run();
ASSERT(runningGroup_ == DaemonTaskGroup::NONE);
}
ASSERT(postedGroups_ == 0);
ASSERT(tasks_.empty());
UnregisterThread(this);
if (g_isEnableCMCGC) {
glueData_.threadHolder_ = 0;
}
}
bool DaemonThread::AddTaskGroup(DaemonTaskGroup taskGroup)
{
if ((postedGroups_ & static_cast<uint32_t>(taskGroup)) != 0) {
return false;
}
postedGroups_ |= static_cast<uint32_t>(taskGroup);
return true;
}
void DaemonThread::FinishRunningTask()
{
ASSERT(runningGroup_ != DaemonTaskGroup::NONE);
ASSERT((postedGroups_ & static_cast<uint32_t>(runningGroup_)) != 0);
postedGroups_ &= ~static_cast<uint32_t>(runningGroup_);
runningGroup_ = DaemonTaskGroup::NONE;
}
DaemonTask DaemonThread::PopTask()
{
LockHolder holder(mtx_);
while (true) {
if (!tasks_.empty()) {
DaemonTask task = tasks_.front();
tasks_.pop_front();
return task;
}
cv_.Wait(&mtx_);
}
}
void DaemonThread::SetSharedMarkStatus(SharedMarkStatus markStatus)
{
ASSERT(os::thread::GetCurrentThreadId() == GetThreadId());
markStatus_.store(markStatus, std::memory_order_release);
Runtime::GetInstance()->GCIterateThreadList([&](JSThread *thread) {
ASSERT(thread->IsSuspended() || thread->HasLaunchedSuspendAll());
thread->SetSharedMarkStatus(markStatus);
});
}
void DaemonThread::SetQosPriority(common::PriorityMode mode)
{
#ifdef ENABLE_QOS
switch (mode) {
case common::PriorityMode::STW: {
OHOS::QOS::SetQosForOtherThread(OHOS::QOS::QosLevel::QOS_USER_INTERACTIVE, GetThreadId());
OHOS::QOS::AddThreadToProcRtg(GetThreadId());
return;
}
case common::PriorityMode::FOREGROUND: {
OHOS::QOS::SetQosForOtherThread(OHOS::QOS::QosLevel::QOS_USER_INITIATED, GetThreadId());
OHOS::QOS::RemoveThreadFromProcRtg(GetThreadId());
return;
}
case common::PriorityMode::BACKGROUND: {
OHOS::QOS::ResetQosForOtherThread(GetThreadId());
OHOS::QOS::RemoveThreadFromProcRtg(GetThreadId());
return;
}
default:
UNREACHABLE();
break;
}
#endif
}
#ifndef NDEBUG
MutatorLock::MutatorLockState DaemonThread::GetMutatorLockState() const
{
return mutatorLockState_;
}
void DaemonThread::SetMutatorLockState(MutatorLock::MutatorLockState newState)
{
mutatorLockState_ = newState;
}
#endif
}