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