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