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/daemon/daemon_task-inl.h"
17 #include "ecmascript/daemon/daemon_thread.h"
18 #include "ecmascript/runtime.h"
19
20 #ifdef ENABLE_QOS
21 #include "qos.h"
22 #endif
23
24 namespace panda::ecmascript {
25
26 DaemonThread *DaemonThread::instance_ = nullptr;
27
CreateNewInstance()28 void DaemonThread::CreateNewInstance()
29 {
30 ASSERT(instance_ == nullptr);
31 instance_ = new DaemonThread();
32 instance_->StartRunning();
33 }
34
GetInstance()35 DaemonThread *DaemonThread::GetInstance()
36 {
37 ASSERT(instance_ != nullptr);
38 return instance_;
39 }
40
DestroyInstance()41 void DaemonThread::DestroyInstance()
42 {
43 ASSERT(instance_ != nullptr);
44 instance_->WaitFinished();
45 delete instance_;
46 instance_ = nullptr;
47 }
48
StartRunning()49 void DaemonThread::StartRunning()
50 {
51 ASSERT(thread_ == nullptr);
52 ASSERT(!IsRunning());
53 ASSERT(tasks_.empty());
54 Taskpool::GetCurrentTaskpool()->Initialize();
55 ASSERT(GetThreadId() == 0);
56 thread_ = std::make_unique<std::thread>([this] {this->Run();});
57 // Wait until daemon thread is running.
58 while (!IsRunning());
59 #ifdef ENABLE_QOS
60 OHOS::QOS::SetQosForOtherThread(OHOS::QOS::QosLevel::QOS_USER_INITIATED, GetThreadId());
61 #endif
62 ASSERT(GetThreadId() != 0);
63 }
64
IsRunning() const65 bool DaemonThread::IsRunning() const
66 {
67 return running_.load(std::memory_order_acquire);
68 }
69
MarkTerminate()70 void DaemonThread::MarkTerminate()
71 {
72 running_.store(false, std::memory_order_release);
73 }
74
WaitFinished()75 void DaemonThread::WaitFinished()
76 {
77 if (IsRunning()) {
78 CheckAndPostTask(TerminateDaemonTask(nullptr));
79 thread_->join();
80 thread_.reset();
81 Taskpool::GetCurrentTaskpool()->Destroy(GetThreadId());
82 }
83 ASSERT(!IsInRunningState());
84 ASSERT(!IsRunning());
85 ASSERT(thread_ == nullptr);
86 ASSERT(tasks_.empty());
87 ResetThreadId();
88 }
89
CheckAndPostTask(DaemonTask task)90 bool DaemonThread::CheckAndPostTask(DaemonTask task)
91 {
92 if (UNLIKELY(!IsRunning())) {
93 LOG_GC(FATAL) << "Try to post task to terminated daemon thread, taskType = "
94 << static_cast<uint32_t>(task.GetTaskType());
95 UNREACHABLE();
96 }
97 LockHolder holder(mtx_);
98 if (AddTaskGroup(task.GetTaskGroup())) {
99 tasks_.emplace_back(task);
100 cv_.Signal();
101 return true;
102 }
103 return false;
104 }
105
Run()106 void DaemonThread::Run()
107 {
108 ASSERT(!IsRunning());
109 os::thread::native_handle_type thread = os::thread::GetNativeHandle();
110 os::thread::SetThreadName(thread, "OS_GC_Thread");
111 ASSERT(JSThread::GetCurrent() == nullptr);
112 RegisterThread(this);
113 SetThreadId();
114 running_.store(true, std::memory_order_release);
115 ASSERT(JSThread::GetCurrent() == this);
116 // Load running_ here do not need atomic, because only daemon thread will set it to false
117 while (running_.load(std::memory_order_acquire)) {
118 ASSERT(!IsInRunningState());
119 DaemonTask task = PopTask();
120 runningGroup_ = task.GetTaskGroup();
121 task.Run();
122 ASSERT(runningGroup_ == DaemonTaskGroup::NONE);
123 }
124 ASSERT(postedGroups_ == 0);
125 ASSERT(tasks_.empty());
126 UnregisterThread(this);
127 }
128
AddTaskGroup(DaemonTaskGroup taskGroup)129 bool DaemonThread::AddTaskGroup(DaemonTaskGroup taskGroup)
130 {
131 if ((postedGroups_ & static_cast<uint32_t>(taskGroup)) != 0) {
132 return false;
133 }
134 postedGroups_ |= static_cast<uint32_t>(taskGroup);
135 return true;
136 }
137
FinishRunningTask()138 void DaemonThread::FinishRunningTask()
139 {
140 ASSERT(runningGroup_ != DaemonTaskGroup::NONE);
141 ASSERT((postedGroups_ & static_cast<uint32_t>(runningGroup_)) != 0);
142 // Update to postedGroups_ is in DaemeanSuspendAll, and protected by the Runtime::mutatorLock_,
143 // so do not need lock; the runningGroup_ is only used in daemon thread, so do not need lock too.
144 postedGroups_ &= ~static_cast<uint32_t>(runningGroup_);
145 runningGroup_ = DaemonTaskGroup::NONE;
146 }
147
PopTask()148 DaemonTask DaemonThread::PopTask()
149 {
150 LockHolder holder(mtx_);
151 while (true) {
152 if (!tasks_.empty()) {
153 DaemonTask task = tasks_.front();
154 tasks_.pop_front();
155 return task;
156 }
157 cv_.Wait(&mtx_);
158 }
159 }
160
SetSharedMarkStatus(SharedMarkStatus markStatus)161 void DaemonThread::SetSharedMarkStatus(SharedMarkStatus markStatus)
162 {
163 ASSERT(os::thread::GetCurrentThreadId() == GetThreadId());
164 markStatus_.store(markStatus, std::memory_order_release);
165 Runtime::GetInstance()->GCIterateThreadList([&](JSThread *thread) {
166 ASSERT(!thread->IsInRunningState());
167 thread->SetSharedMarkStatus(markStatus);
168 });
169 }
170
171 #ifndef NDEBUG
GetMutatorLockState() const172 MutatorLock::MutatorLockState DaemonThread::GetMutatorLockState() const
173 {
174 return mutatorLockState_;
175 }
176
SetMutatorLockState(MutatorLock::MutatorLockState newState)177 void DaemonThread::SetMutatorLockState(MutatorLock::MutatorLockState newState)
178 {
179 mutatorLockState_ = newState;
180 }
181 #endif
182 } // namespace panda::ecmascript