• 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 #ifndef PANDA_RUNTIME_THREAD_MANAGER_H_
16 #define PANDA_RUNTIME_THREAD_MANAGER_H_
17 
18 #include <bitset>
19 
20 #include "libpandabase/os/mutex.h"
21 #include "libpandabase/utils/time.h"
22 #include "libpandabase/os/time.h"
23 #include "runtime/include/coretypes/array-inl.h"
24 #include "runtime/include/mem/panda_containers.h"
25 #include "runtime/include/mem/panda_smart_pointers.h"
26 #include "runtime/include/mtmanaged_thread.h"
27 #include "runtime/include/thread_status.h"
28 #include "runtime/include/locks.h"
29 
30 namespace ark {
31 
32 // This interval is required for waiting for threads to stop.
33 static const int WAIT_INTERVAL = 10;
34 static constexpr int64_t K_MAX_DUMP_TIME_NS = UINT64_C(6 * 1000 * 1000 * 1000);   // 6s
35 static constexpr int64_t K_MAX_SINGLE_DUMP_TIME_NS = UINT64_C(50 * 1000 * 1000);  // 50ms
36 
37 enum class EnumerationFlag {
38     NONE = 0,                 // Nothing
39     NON_CORE_THREAD = 1,      // Plugin type thread
40     MANAGED_CODE_THREAD = 2,  // Thread which can execute managed code
41     VM_THREAD = 4,            // Includes VM threads
42     ALL = 8,                  // See the comment in the function SatisfyTheMask below
43 };
44 
45 class ThreadManager {
46 public:
47     NO_COPY_SEMANTIC(ThreadManager);
48     NO_MOVE_SEMANTIC(ThreadManager);
49 
50     using Callback = std::function<bool(ManagedThread *)>;
51 
52     explicit ThreadManager() = default;
53     virtual ~ThreadManager() = default;
54 
55     /**
56      * @brief thread enumeration and applying @param cb to them
57      * @return true if for every thread @param cb was successful (returned true) and false otherwise
58      */
59     bool EnumerateThreads(const Callback &cb, unsigned int incMask = static_cast<unsigned int>(EnumerationFlag::ALL),
60                           unsigned int xorMask = static_cast<unsigned int>(EnumerationFlag::NONE)) const
61     {
62         return EnumerateThreadsImpl(cb, incMask, xorMask);
63     }
64 
65     virtual void WaitForDeregistration() = 0;
66 
67     virtual void SuspendAllThreads() = 0;
68     virtual void ResumeAllThreads() = 0;
69 
70     virtual bool IsRunningThreadExist() = 0;
71 
GetMainThread()72     ManagedThread *GetMainThread() const
73     {
74         return mainThread_;
75     }
76 
SetMainThread(ManagedThread * thread)77     void SetMainThread(ManagedThread *thread)
78     {
79         mainThread_ = thread;
80     }
81 
82 protected:
SatisfyTheMask(ManagedThread * t,unsigned int mask)83     bool SatisfyTheMask(ManagedThread *t, unsigned int mask) const
84     {
85         if ((mask & static_cast<unsigned int>(EnumerationFlag::ALL)) != 0) {
86             // Some uninitialized threads may not have attached flag,
87             // So, they are not included as MANAGED_CODE_THREAD.
88             // Newly created threads are using flag suspend new count.
89             // The case leads to deadlocks, when the thread can not be resumed.
90             // To deal with it, just add a specific ALL case
91             return true;
92         }
93 
94         // For NONE mask
95         bool target = false;
96         if ((mask & static_cast<unsigned int>(EnumerationFlag::MANAGED_CODE_THREAD)) != 0) {
97             target = t->IsAttached();
98             if ((mask & static_cast<unsigned int>(EnumerationFlag::NON_CORE_THREAD)) != 0) {
99                 // Due to hyerarhical structure, we need to conjunct types
100                 bool nonCoreThread = t->GetThreadLang() != ark::panda_file::SourceLang::PANDA_ASSEMBLY;
101                 target = target && nonCoreThread;
102             }
103         }
104 
105         if ((mask & static_cast<unsigned int>(EnumerationFlag::VM_THREAD)) != 0) {
106             target = target || t->IsVMThread();
107         }
108 
109         return target;
110     }
111 
ApplyCallbackToThread(const Callback & cb,ManagedThread * t,unsigned int incMask,unsigned int xorMask)112     bool ApplyCallbackToThread(const Callback &cb, ManagedThread *t, unsigned int incMask, unsigned int xorMask) const
113     {
114         bool incTarget = SatisfyTheMask(t, incMask);
115         bool xorTarget = SatisfyTheMask(t, xorMask);
116         if (incTarget != xorTarget) {
117             if (!cb(t)) {
118                 return false;
119             }
120         }
121         return true;
122     }
123 
124     virtual bool EnumerateThreadsImpl(const Callback &cb, unsigned int incMask, unsigned int xorMask) const = 0;
125 
126 private:
127     ManagedThread *mainThread_ {nullptr};
128 };
129 
130 class MTThreadManager : public ThreadManager {
131 public:
132     NO_COPY_SEMANTIC(MTThreadManager);
133     NO_MOVE_SEMANTIC(MTThreadManager);
134 
135     // For performance reasons don't exceed specified amount of bits.
136     static constexpr size_t MAX_INTERNAL_THREAD_ID = std::min(0xffffU, ManagedThread::MAX_INTERNAL_THREAD_ID);
137 
138     explicit MTThreadManager(mem::InternalAllocatorPtr allocator);
139 
140     ~MTThreadManager() override;
141 
142     bool EnumerateThreadsWithLockheld(const Callback &cb,
143                                       unsigned int incMask = static_cast<unsigned int>(EnumerationFlag::ALL),
144                                       unsigned int xorMask = static_cast<unsigned int>(EnumerationFlag::NONE)) const
145     // REQUIRES(*GetThreadsLock())
146     // Cannot enable the annotation, as the function is also called with thread_lock directly
147     {
148         for (auto t : GetThreadsList()) {
149             if (!ApplyCallbackToThread(cb, t, incMask, xorMask)) {
150                 return false;
151             }
152         }
153         return true;
154     }
155 
156     template <class Callback>
EnumerateThreadsForDump(const Callback & cb,std::ostream & os)157     void EnumerateThreadsForDump(const Callback &cb, std::ostream &os)
158     {
159         // NOTE(00510180 & 00537420) can not get WriteLock() when other thread run code "while {}"
160         // issue #3085
161         SuspendAllThreads();
162         MTManagedThread *self = MTManagedThread::GetCurrent();
163         self->GetMutatorLock()->WriteLock();
164         {
165             os << "ARK THREADS (" << threadsCount_ << "):\n";
166         }
167         if (self != nullptr) {
168             os::memory::LockHolder lock(threadLock_);
169             int64_t start = ark::os::time::GetClockTimeInThreadCpuTime();
170             int64_t end;
171             int64_t lastTime = start;
172             cb(self, os);
173             for (const auto &thread : threads_) {
174                 if (thread == self) {
175                     continue;
176                 }
177                 cb(thread, os);
178                 end = ark::os::time::GetClockTimeInThreadCpuTime();
179                 if ((end - lastTime) > K_MAX_SINGLE_DUMP_TIME_NS) {
180                     LOG(ERROR, RUNTIME) << "signal catcher: thread_list_dump thread : " << thread->GetId()
181                                         << "timeout : " << (end - lastTime);
182                 }
183                 lastTime = end;
184                 if ((end - start) > K_MAX_DUMP_TIME_NS) {
185                     LOG(ERROR, RUNTIME) << "signal catcher: thread_list_dump timeout : " << end - start << "\n";
186                     break;
187                 }
188             }
189         }
190         DumpUnattachedThreads(os);
191         self->GetMutatorLock()->Unlock();
192         ResumeAllThreads();
193     }
194 
195     void DumpUnattachedThreads(std::ostream &os);
196 
197     void RegisterThread(MTManagedThread *thread);
198 
IncPendingThreads()199     void IncPendingThreads()
200     {
201         os::memory::LockHolder lock(threadLock_);
202         pendingThreads_++;
203     }
204 
DecPendingThreads()205     void DecPendingThreads()
206     {
207         os::memory::LockHolder lock(threadLock_);
208         pendingThreads_--;
209     }
210 
AddDaemonThread()211     void AddDaemonThread()
212     {
213         daemonThreadsCount_++;
214     }
215 
216     int GetThreadsCount();
217 
218 #ifndef NDEBUG
219     uint32_t GetAllRegisteredThreadsCount();
220 #endif  // NDEBUG
221 
222     void WaitForDeregistration() override;
223 
224     void SuspendAllThreads() override;
225     void ResumeAllThreads() override;
226 
227     uint32_t GetInternalThreadId();
228 
229     void RemoveInternalThreadId(uint32_t id);
230 
231     bool IsRunningThreadExist() override;
232 
233     // Returns true if unregistration succeeded; for now it can fail when we are trying to unregister main thread
234     bool UnregisterExitedThread(MTManagedThread *thread);
235 
236     MTManagedThread *SuspendAndWaitThreadByInternalThreadId(uint32_t threadId);
237 
238     void RegisterSensitiveThread() const;
239 
GetThreadsLock()240     os::memory::Mutex *GetThreadsLock()
241     {
242         return &threadLock_;
243     }
244 
245 protected:
EnumerateThreadsImpl(const Callback & cb,unsigned int incMask,unsigned int xorMask)246     bool EnumerateThreadsImpl(const Callback &cb, unsigned int incMask, unsigned int xorMask) const override
247     {
248         os::memory::LockHolder lock(*GetThreadsLock());
249         return EnumerateThreadsWithLockheld(cb, incMask, xorMask);
250     }
251 
252     // The methods are used only in EnumerateThreads in mt mode
GetThreadsList()253     const PandaList<MTManagedThread *> &GetThreadsList() const
254     {
255         return threads_;
256     }
GetThreadsLock()257     os::memory::Mutex *GetThreadsLock() const
258     {
259         return &threadLock_;
260     }
261 
262 private:
HasNoActiveThreads()263     bool HasNoActiveThreads() const REQUIRES(threadLock_)
264     {
265         ASSERT(threadsCount_ >= daemonThreadsCount_);
266         auto thread = static_cast<uint32_t>(threadsCount_ - daemonThreadsCount_);
267         return thread < 2 && pendingThreads_ == 0;
268     }
269 
270     bool StopThreadsOnTerminationLoops(MTManagedThread *current) REQUIRES(threadLock_);
271 
272     /**
273      * Tries to stop all daemon threads in case there are no active basic threads
274      * returns false if we need to wait
275      */
276     void StopDaemonThreads() REQUIRES(threadLock_);
277 
278     /**
279      * Deregister all suspended threads including daemon threads.
280      * Returns true on success and false otherwise.
281      */
282     bool DeregisterSuspendedThreads() REQUIRES(threadLock_);
283 
284     void DecreaseCountersForThread(MTManagedThread *thread) REQUIRES(threadLock_);
285 
286     MTManagedThread *GetThreadByInternalThreadIdWithLockHeld(uint32_t threadId) REQUIRES(threadLock_);
287 
CanDeregister(enum ThreadStatus status)288     bool CanDeregister(enum ThreadStatus status)
289     {
290         // Deregister thread only for IS_TERMINATED_LOOP.
291         // In all other statuses we should wait:
292         // * CREATED - wait until threads finish initializing which requires communication with ThreadManager;
293         // * BLOCKED - it means we are trying to acquire lock in Monitor, which was created in internalAllocator;
294         // * TERMINATING - threads which requires communication with Runtime;
295         // * FINISHED threads should be deleted itself;
296         // * NATIVE threads are either go to FINISHED status or considered a part of a deadlock;
297         // * other statuses - should eventually go to IS_TERMINATED_LOOP or FINISHED status.
298         return status == ThreadStatus::IS_TERMINATED_LOOP;
299     }
300 
301     mutable os::memory::Mutex threadLock_;
302     // Counter used to suspend newly created threads after SuspendAllThreads/SuspendDaemonThreads
303     uint32_t suspendNewCount_ GUARDED_BY(threadLock_) = 0;
304     // We should delete only finished thread structures, so call delete explicitly on finished threads
305     // and don't touch other pointers
306     PandaList<MTManagedThread *> threads_ GUARDED_BY(threadLock_);
307     os::memory::Mutex idsLock_;
308     std::bitset<MAX_INTERNAL_THREAD_ID> internalThreadIds_ GUARDED_BY(idsLock_);
309     uint32_t lastId_ GUARDED_BY(idsLock_);
310     PandaList<MTManagedThread *> daemonThreads_;
311 
312     os::memory::ConditionVariable stopVar_;
313     std::atomic_uint32_t threadsCount_ = 0;
314 #ifndef NDEBUG
315     // This field is required for counting all registered threads (including finished daemons)
316     // in AttachThreadTest. It is not needed in production mode.
317     std::atomic_uint32_t registeredThreadsCount_ = 0;
318 #endif  // NDEBUG
319     std::atomic_uint32_t daemonThreadsCount_ = 0;
320     // A specific counter of threads, which are not completely created
321     // When the counter != 0, operations with thread set are permitted to avoid destruction of shared data (mutexes)
322     // Synchronized with lock (not atomic) for mutual exclusion with thread operations
323     int pendingThreads_ GUARDED_BY(threadLock_);
324 };
325 
326 }  // namespace ark
327 
328 #endif  // PANDA_RUNTIME_THREAD_MANAGER_H_
329