1 /**
2 * Copyright (c) 2021-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 "libpandabase/os/native_stack.h"
17 #include "libpandabase/os/thread.h"
18 #include "libpandabase/utils/logger.h"
19 #include "libpandabase/utils/utf.h"
20 #include "runtime/include/mem/allocator.h"
21 #include "runtime/include/panda_vm.h"
22 #include "runtime/include/runtime.h"
23 #include "runtime/include/thread-inl.h"
24 #include "runtime/include/thread_scopes.h"
25 #include "runtime/lock_order_graph.h"
26 #include "runtime/thread_manager.h"
27
28 namespace ark {
MTThreadManager(mem::InternalAllocatorPtr allocator)29 MTThreadManager::MTThreadManager(mem::InternalAllocatorPtr allocator) : threads_(allocator->Adapter())
30 {
31 lastId_ = 0;
32 pendingThreads_ = 0;
33 }
34
~MTThreadManager()35 MTThreadManager::~MTThreadManager()
36 {
37 threads_.clear();
38 }
39
GetInternalThreadId()40 uint32_t MTThreadManager::GetInternalThreadId()
41 {
42 os::memory::LockHolder lock(idsLock_);
43 for (size_t i = 0; i < internalThreadIds_.size(); i++) {
44 lastId_ = (lastId_ + 1) % internalThreadIds_.size();
45 if (!internalThreadIds_[lastId_]) {
46 internalThreadIds_.set(lastId_);
47 return lastId_ + 1; // 0 is reserved as uninitialized value.
48 }
49 }
50 LOG(FATAL, RUNTIME) << "Out of internal thread ids";
51 UNREACHABLE();
52 }
53
RemoveInternalThreadId(uint32_t id)54 void MTThreadManager::RemoveInternalThreadId(uint32_t id)
55 {
56 id--; // 0 is reserved as uninitialized value.
57 os::memory::LockHolder lock(idsLock_);
58 ASSERT(internalThreadIds_[id]);
59 internalThreadIds_.reset(id);
60 }
61
GetThreadByInternalThreadIdWithLockHeld(uint32_t threadId)62 MTManagedThread *MTThreadManager::GetThreadByInternalThreadIdWithLockHeld(uint32_t threadId)
63 {
64 // Do not optimize with std::find_if - sometimes there are problems with incorrect memory accesses
65 for (auto thread : threads_) {
66 if (thread->GetInternalId() == threadId) {
67 return thread;
68 }
69 }
70 return nullptr;
71 }
72
DeregisterSuspendedThreads()73 bool MTThreadManager::DeregisterSuspendedThreads()
74 {
75 auto current = MTManagedThread::GetCurrent();
76 auto i = threads_.begin();
77 bool isPotentiallyBlockedThreadPresent = false;
78 bool isNonblockedThreadPresent = false;
79 while (i != threads_.end()) {
80 MTManagedThread *thread = *i;
81 auto status = thread->GetStatus();
82 // Do not deregister current thread (which should be in status NATIVE) as HasNoActiveThreads
83 // assumes it stays registered.
84 if (thread == current) {
85 i++;
86 continue;
87 }
88 // Only threads in IS_TERMINATED_LOOP status can be deregistered.
89 if (CanDeregister(status)) {
90 DecreaseCountersForThread(thread);
91 i = threads_.erase(i);
92 continue;
93 }
94 if (status == ThreadStatus::NATIVE || status == ThreadStatus::IS_BLOCKED) {
95 // We have a blocked thread - there is a potential termination loop
96 isPotentiallyBlockedThreadPresent = true;
97 } else {
98 // We have at least one non-blocked thread - termination loop is impossible
99 isNonblockedThreadPresent = true;
100 }
101 LOG(DEBUG, RUNTIME) << "Daemon thread " << thread->GetId()
102 << " remains in DeregisterSuspendedThreads, status = "
103 << ManagedThread::ThreadStatusAsString(status);
104 i++;
105 }
106 if (isPotentiallyBlockedThreadPresent && !isNonblockedThreadPresent) {
107 // All threads except current are blocked (have BLOCKED or NATIVE status)
108 LOG(DEBUG, RUNTIME) << "Potential termination loop with daemon threads is detected";
109 return StopThreadsOnTerminationLoops(current);
110 }
111 // Sanity check, we should get at least current thread in that list.
112 ASSERT(!threads_.empty());
113 return threads_.size() == 1;
114 }
115
DecreaseCountersForThread(MTManagedThread * thread)116 void MTThreadManager::DecreaseCountersForThread(MTManagedThread *thread)
117 {
118 if (thread->IsDaemon()) {
119 daemonThreadsCount_--;
120 // Do not delete this thread structure as it may be used by suspended thread
121 daemonThreads_.push_back(thread);
122 }
123 threadsCount_--;
124 }
125
StopThreadsOnTerminationLoops(MTManagedThread * current)126 bool MTThreadManager::StopThreadsOnTerminationLoops(MTManagedThread *current)
127 {
128 if (!LockOrderGraph::CheckForTerminationLoops(threads_, daemonThreads_, current)) {
129 LOG(DEBUG, RUNTIME) << "Termination loop with daemon threads was not confirmed";
130 return false;
131 }
132
133 os::memory::Mutex::IgnoreChecksOnTerminationLoop();
134 auto i = threads_.begin();
135 while (i != threads_.end()) {
136 MTManagedThread *thread = *i;
137 if (thread != current) {
138 DecreaseCountersForThread(thread);
139 i = threads_.erase(i);
140 continue;
141 }
142 i++;
143 }
144 return true;
145 }
146
WaitForDeregistration()147 void MTThreadManager::WaitForDeregistration()
148 {
149 trace::ScopedTrace scopedTrace(__FUNCTION__);
150 {
151 os::memory::LockHolder lock(threadLock_);
152
153 // First wait for non-daemon threads to finish
154 while (!HasNoActiveThreads()) {
155 stopVar_.TimedWait(&threadLock_, WAIT_INTERVAL);
156 }
157
158 // Then stop daemon threads
159 StopDaemonThreads();
160
161 // Finally wait until all threads are suspended
162 while (true) {
163 if (pendingThreads_ != 0) {
164 // There are threads, which are not completely registered
165 // We can not destroy other threads, as they may use shared data (waiting mutexes)
166 stopVar_.TimedWait(&threadLock_, WAIT_INTERVAL);
167 continue;
168 }
169 if (DeregisterSuspendedThreads()) {
170 break;
171 }
172 stopVar_.TimedWait(&threadLock_, WAIT_INTERVAL);
173 }
174 }
175 for (const auto &thread : daemonThreads_) {
176 thread->FreeInternalMemory();
177 }
178 auto threshold = Runtime::GetOptions().GetIgnoreDaemonMemoryLeaksThreshold();
179 Runtime::GetCurrent()->SetDaemonMemoryLeakThreshold(daemonThreads_.size() * threshold);
180 Runtime::GetCurrent()->SetDaemonThreadsCount(daemonThreads_.size());
181 }
182
StopDaemonThreads()183 void MTThreadManager::StopDaemonThreads() REQUIRES(threadLock_)
184 {
185 trace::ScopedTrace scopedTrace(__FUNCTION__);
186 for (auto thread : threads_) {
187 if (thread->IsDaemon()) {
188 LOG(DEBUG, RUNTIME) << "Stopping daemon thread " << thread->GetId();
189 /* @sync 1
190 * @description The thread manager will request the daemon thread to go into the termination loop after this
191 * point.
192 * */
193 thread->StopDaemonThread();
194 }
195 }
196 // Suspend any future new threads
197 suspendNewCount_++;
198 }
199
GetThreadsCount()200 int MTThreadManager::GetThreadsCount()
201 {
202 return threadsCount_;
203 }
204
205 #ifndef NDEBUG
GetAllRegisteredThreadsCount()206 uint32_t MTThreadManager::GetAllRegisteredThreadsCount()
207 {
208 return registeredThreadsCount_;
209 }
210 #endif // NDEBUG
211
SuspendAllThreads()212 void MTThreadManager::SuspendAllThreads()
213 {
214 trace::ScopedTrace scopedTrace("Suspending mutator threads");
215 auto curThread = MTManagedThread::GetCurrent();
216 os::memory::LockHolder lock(threadLock_);
217 EnumerateThreadsWithLockheld([curThread](ManagedThread *thread) {
218 if (thread != curThread) {
219 thread->SuspendImpl(true);
220 }
221 return true;
222 });
223 suspendNewCount_++;
224 }
225
IsRunningThreadExist()226 bool MTThreadManager::IsRunningThreadExist()
227 {
228 auto curThread = MTManagedThread::GetCurrent();
229 os::memory::LockHolder lock(threadLock_);
230 bool isExists = false;
231 EnumerateThreadsWithLockheld([curThread, &isExists](ManagedThread *thread) {
232 if (thread != curThread) {
233 if (thread->GetStatus() == ThreadStatus::RUNNING) {
234 isExists = true;
235 return false;
236 };
237 }
238 return true;
239 });
240 return isExists;
241 }
242
ResumeAllThreads()243 void MTThreadManager::ResumeAllThreads()
244 {
245 trace::ScopedTrace scopedTrace("Resuming mutator threads");
246 auto curThread = MTManagedThread::GetCurrent();
247 os::memory::LockHolder lock(threadLock_);
248 if (suspendNewCount_ > 0) {
249 suspendNewCount_--;
250 }
251 EnumerateThreadsWithLockheld([curThread](ManagedThread *thread) {
252 if (thread != curThread) {
253 thread->ResumeImpl(true);
254 }
255 return true;
256 });
257 }
258
RegisterThread(MTManagedThread * thread)259 void MTThreadManager::RegisterThread(MTManagedThread *thread)
260 {
261 os::memory::LockHolder lock(threadLock_);
262 thread->GetVM()->GetGC()->OnThreadCreate(thread);
263 threadsCount_++;
264 #ifndef NDEBUG
265 registeredThreadsCount_++;
266 #endif // NDEBUG
267 threads_.emplace_back(thread);
268 for (uint32_t i = suspendNewCount_; i > 0; i--) {
269 thread->SuspendImpl(true);
270 }
271 }
272
UnregisterExitedThread(MTManagedThread * thread)273 bool MTThreadManager::UnregisterExitedThread(MTManagedThread *thread)
274 {
275 ASSERT(MTManagedThread::GetCurrent() == thread);
276 {
277 thread->NativeCodeEnd();
278
279 os::memory::LockHolder lock(threadLock_);
280 // While this thread is suspended, do not delete it as other thread can be accessing it.
281 // TestAllFlags is required because termination request can be sent while thread_lock_ is unlocked
282 while (thread->TestAllFlags()) {
283 threadLock_.Unlock();
284 thread->SafepointPoll();
285 threadLock_.Lock();
286 }
287
288 thread->CollectTLABMetrics();
289 thread->ClearTLAB();
290 thread->DestroyInternalResources();
291
292 LOG(DEBUG, RUNTIME) << "Stopping thread " << thread->GetId();
293 thread->UpdateStatus(ThreadStatus::FINISHED);
294 // Do not delete main thread, Runtime::GetMainThread is expected to always return valid object
295 if (thread == GetMainThread()) {
296 return false;
297 }
298
299 // This code should happen after thread has been resumed: Both WaitSuspension and ResumeImps requires locking
300 // suspend_lock_, so it acts as a memory barrier; flag clean should be visible in this thread after exit from
301 // WaitSuspenion
302 TSAN_ANNOTATE_HAPPENS_AFTER(&thread->fts_);
303
304 threads_.remove(thread);
305 if (thread->IsDaemon()) {
306 daemonThreadsCount_--;
307 }
308 threadsCount_--;
309
310 // If managed_thread, its nativePeer should be 0 before
311 delete thread;
312 stopVar_.Signal();
313 return true;
314 }
315 }
316
RegisterSensitiveThread() const317 void MTThreadManager::RegisterSensitiveThread() const
318 {
319 LOG(INFO, RUNTIME) << __func__ << " is an empty implementation now.";
320 }
321
DumpUnattachedThreads(std::ostream & os)322 void MTThreadManager::DumpUnattachedThreads(std::ostream &os)
323 {
324 os::native_stack::DumpUnattachedThread dump;
325 dump.InitKernelTidLists();
326 os::memory::LockHolder lock(threadLock_);
327 for (const auto &thread : threads_) {
328 dump.AddTid(static_cast<pid_t>(thread->GetId()));
329 }
330 dump.Dump(os, Runtime::GetCurrent()->IsDumpNativeCrash(), Runtime::GetCurrent()->GetUnwindStackFn());
331 }
332
SuspendAndWaitThreadByInternalThreadId(uint32_t threadId)333 MTManagedThread *MTThreadManager::SuspendAndWaitThreadByInternalThreadId(uint32_t threadId)
334 {
335 static constexpr uint32_t YIELD_ITERS = 500;
336 // NB! Expected to be called in registered thread, change implementation if this function used elsewhere
337 MTManagedThread *current = MTManagedThread::GetCurrent();
338 MTManagedThread *suspended = nullptr;
339 ASSERT(current->GetStatus() != ThreadStatus::RUNNING);
340
341 // Extract target thread
342 while (true) {
343 // If two threads call SuspendAndWaitThreadByInternalThreadId concurrently, one has to get suspended
344 // while other waits for thread to be suspended, so thread_lock_ is required to be held until
345 // SuspendImpl is called
346 current->SafepointPoll();
347 {
348 os::memory::LockHolder lock(threadLock_);
349
350 suspended = GetThreadByInternalThreadIdWithLockHeld(threadId);
351 if (UNLIKELY(suspended == nullptr)) {
352 // no thread found, exit
353 return nullptr;
354 }
355 ASSERT(current != suspended);
356 if (LIKELY(!current->IsSuspended())) {
357 suspended->SuspendImpl(true);
358 break;
359 }
360 // Unsafe to suspend as other thread may be waiting for this thread to suspend;
361 // Should get suspended on Safepoint()
362 }
363 }
364
365 // Now wait until target thread is really suspended
366 for (uint32_t loopIter = 0;; loopIter++) {
367 if (suspended->GetStatus() != ThreadStatus::RUNNING) {
368 // Thread is suspended now
369 return suspended;
370 }
371 if (loopIter < YIELD_ITERS) {
372 MTManagedThread::Yield();
373 } else {
374 static constexpr uint32_t SHORT_SLEEP_MS = 1;
375 os::thread::NativeSleep(SHORT_SLEEP_MS);
376 }
377 }
378 UNREACHABLE();
379 }
380
381 } // namespace ark
382