• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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