/* * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef PANDA_RUNTIME_INCLUDE_MTMANAGED_THREAD_H_ #define PANDA_RUNTIME_INCLUDE_MTMANAGED_THREAD_H_ #include "managed_thread.h" // See issue 4100, js thread always true // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define ASSERT_MANAGED_CODE() ASSERT(::panda::MTManagedThread::GetCurrent()->IsManagedCode()) #define ASSERT_NATIVE_CODE() ASSERT(::panda::MTManagedThread::GetCurrent()->IsInNativeCode()) // NOLINT namespace panda { class MTManagedThread : public ManagedThread { public: enum ThreadState : uint8_t { NATIVE_CODE = 0, MANAGED_CODE = 1 }; ThreadId GetInternalId(); static MTManagedThread *Create(Runtime *runtime, PandaVM *vm); explicit MTManagedThread(ThreadId id, mem::InternalAllocatorPtr allocator, PandaVM *vm); ~MTManagedThread() override; std::unordered_set &GetMonitors(); void AddMonitor(Monitor *monitor); void RemoveMonitor(Monitor *monitor); void ReleaseMonitors(); void PushLocalObjectLocked(ObjectHeader *obj); void PopLocalObjectLocked(ObjectHeader *out); const PandaVector &GetLockedObjectInfos(); void VisitGCRoots(const ObjectVisitor &cb) override; void UpdateGCRoots() override; ThreadStatus GetWaitingMonitorOldStatus() const { return monitor_old_status_; } void SetWaitingMonitorOldStatus(ThreadStatus status) { monitor_old_status_ = status; } static bool IsManagedScope() { auto thread = GetCurrent(); return thread != nullptr && thread->is_managed_scope_; } void FreeInternalMemory() override; static bool Sleep(uint64_t ms); void SuspendImpl(bool internal_suspend = false); void ResumeImpl(bool internal_resume = false); Monitor *GetWaitingMonitor() const { return waiting_monitor_; } void SetWaitingMonitor(Monitor *monitor) { ASSERT(waiting_monitor_ == nullptr || monitor == nullptr); waiting_monitor_ = monitor; } virtual void StopDaemonThread(); bool IsDaemon() { return is_daemon_; } void SetDaemon(); virtual void Destroy(); static void Yield(); static void Interrupt(MTManagedThread *thread); [[nodiscard]] bool HasManagedCodeOnStack() const; [[nodiscard]] bool HasClearStack() const; /** * Transition to suspended and back to runnable, re-acquire share on mutator_lock_ */ void SuspendCheck(); bool IsUserSuspended() { return user_code_suspend_count_ > 0; } // Need to acquire the mutex before waiting to avoid scheduling between monitor release and clond_lock acquire os::memory::Mutex *GetWaitingMutex() RETURN_CAPABILITY(cond_lock_) { return &cond_lock_; } void Signal() { os::memory::LockHolder lock(cond_lock_); cond_var_.Signal(); } bool Interrupted(); bool IsInterrupted() const { os::memory::LockHolder lock(cond_lock_); return is_interrupted_; } bool IsInterruptedWithLockHeld() const REQUIRES(cond_lock_) { return is_interrupted_; } void ClearInterrupted() { os::memory::LockHolder lock(cond_lock_); is_interrupted_ = false; } void IncSuspended(bool is_internal) REQUIRES(suspend_lock_) { if (!is_internal) { user_code_suspend_count_++; } // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) auto old_count = suspend_count_++; if (old_count == 0) { SetFlag(SUSPEND_REQUEST); } } void DecSuspended(bool is_internal) REQUIRES(suspend_lock_) { if (!is_internal) { ASSERT(user_code_suspend_count_ != 0); user_code_suspend_count_--; } if (suspend_count_ > 0) { suspend_count_--; if (suspend_count_ == 0) { ClearFlag(SUSPEND_REQUEST); } } } static bool ThreadIsMTManagedThread(Thread *thread) { ASSERT(thread != nullptr); return thread->GetThreadType() == Thread::ThreadType::THREAD_TYPE_MT_MANAGED; } static MTManagedThread *CastFromThread(Thread *thread) { ASSERT(thread != nullptr); ASSERT(ThreadIsMTManagedThread(thread)); return static_cast(thread); } /** * @brief GetCurrentRaw Unsafe method to get current MTManagedThread. * It can be used in hotspots to get the best performance. * We can only use this method in places where the MTManagedThread exists. * @return pointer to MTManagedThread */ static MTManagedThread *GetCurrentRaw() { return CastFromThread(Thread::GetCurrent()); } /** * @brief GetCurrent Safe method to gets current MTManagedThread. * @return pointer to MTManagedThread or nullptr (if current thread is not a managed thread) */ static MTManagedThread *GetCurrent() { Thread *thread = Thread::GetCurrent(); ASSERT(thread != nullptr); if (ThreadIsMTManagedThread(thread)) { return CastFromThread(thread); } // no guarantee that we will return nullptr here in the future return nullptr; } void SafepointPoll(); /** * From NativeCode you can call ManagedCodeBegin. * From ManagedCode you can call NativeCodeBegin. * Call the same type is forbidden. */ virtual void NativeCodeBegin(); virtual void NativeCodeEnd(); [[nodiscard]] virtual bool IsInNativeCode() const; virtual void ManagedCodeBegin(); virtual void ManagedCodeEnd(); [[nodiscard]] virtual bool IsManagedCode() const; void WaitWithLockHeld(ThreadStatus wait_status) REQUIRES(cond_lock_) { ASSERT(wait_status == IS_WAITING); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) auto old_status = GetStatus(); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) UpdateStatus(wait_status); WaitWithLockHeldInternal(); // Unlock before setting status RUNNING to handle MutatorReadLock without inversed lock order. cond_lock_.Unlock(); UpdateStatus(old_status); cond_lock_.Lock(); } static void WaitForSuspension(ManagedThread *thread) { static constexpr uint32_t YIELD_ITERS = 500; uint32_t loop_iter = 0; while (thread->GetStatus() == RUNNING) { if (!thread->IsSuspended()) { LOG(WARNING, RUNTIME) << "No request for suspension, do not wait thread " << thread->GetId(); break; } loop_iter++; if (loop_iter < YIELD_ITERS) { MTManagedThread::Yield(); } else { // Use native sleep over ManagedThread::Sleep to prevent potentially time consuming // mutator_lock locking and unlocking static constexpr uint32_t SHORT_SLEEP_MS = 1; os::thread::NativeSleep(SHORT_SLEEP_MS); } } } void Wait(ThreadStatus wait_status) { ASSERT(wait_status == IS_WAITING); auto old_status = GetStatus(); { os::memory::LockHolder lock(cond_lock_); UpdateStatus(wait_status); WaitWithLockHeldInternal(); } UpdateStatus(old_status); } bool TimedWaitWithLockHeld(ThreadStatus wait_status, uint64_t timeout, uint64_t nanos, bool is_absolute = false) REQUIRES(cond_lock_) { ASSERT(wait_status == IS_TIMED_WAITING || wait_status == IS_SLEEPING || wait_status == IS_BLOCKED || wait_status == IS_SUSPENDED || wait_status == IS_COMPILER_WAITING || wait_status == IS_WAITING_INFLATION); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) auto old_status = GetStatus(); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) UpdateStatus(wait_status); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool res = TimedWaitWithLockHeldInternal(timeout, nanos, is_absolute); // Unlock before setting status RUNNING to handle MutatorReadLock without inversed lock order. cond_lock_.Unlock(); UpdateStatus(old_status); cond_lock_.Lock(); return res; } bool TimedWait(ThreadStatus wait_status, uint64_t timeout, uint64_t nanos = 0, bool is_absolute = false) { ASSERT(wait_status == IS_TIMED_WAITING || wait_status == IS_SLEEPING || wait_status == IS_BLOCKED || wait_status == IS_SUSPENDED || wait_status == IS_COMPILER_WAITING || wait_status == IS_WAITING_INFLATION); auto old_status = GetStatus(); bool res = false; { os::memory::LockHolder lock(cond_lock_); UpdateStatus(wait_status); res = TimedWaitWithLockHeldInternal(timeout, nanos, is_absolute); } UpdateStatus(old_status); return res; } void WaitSuspension() { constexpr int TIMEOUT = 100; auto old_status = GetStatus(); UpdateStatus(IS_SUSPENDED); { PrintSuspensionStackIfNeeded(); os::memory::LockHolder lock(suspend_lock_); while (suspend_count_ > 0) { suspend_var_.TimedWait(&suspend_lock_, TIMEOUT); // In case runtime is being terminated, we should abort suspension and release monitors if (UNLIKELY(IsRuntimeTerminated())) { suspend_lock_.Unlock(); TerminationLoop(); } } ASSERT(!IsSuspended()); } UpdateStatus(old_status); } void TerminationLoop() { ASSERT(IsRuntimeTerminated()); // Free all monitors first in case we are suspending in status IS_BLOCKED ReleaseMonitors(); UpdateStatus(IS_TERMINATED_LOOP); while (true) { static constexpr unsigned int LONG_SLEEP_MS = 1000000; os::thread::NativeSleep(LONG_SLEEP_MS); } } // NO_THREAD_SAFETY_ANALYSIS due to TSAN not being able to determine lock status void TransitionFromRunningToSuspended(enum ThreadStatus status) NO_THREAD_SAFETY_ANALYSIS { // Workaround: We masked the assert for 'ManagedThread::GetCurrent() == null' condition, // because JSThread updates status_ not from current thread. // (Remove it when issue 5183 is resolved) ASSERT(ManagedThread::GetCurrent() == this || ManagedThread::GetCurrent() == nullptr); Locks::mutator_lock->Unlock(); StoreStatus(status); } // NO_THREAD_SAFETY_ANALYSIS due to TSAN not being able to determine lock status void TransitionFromSuspendedToRunning(enum ThreadStatus status) NO_THREAD_SAFETY_ANALYSIS { // Workaround: We masked the assert for 'ManagedThread::GetCurrent() == null' condition, // because JSThread updates status_ not from current thread. // (Remove it when issue 5183 is resolved) ASSERT(ManagedThread::GetCurrent() == this || ManagedThread::GetCurrent() == nullptr); // NB! This thread is treated as suspended so when we transition from suspended state to // running we need to check suspension flag and counter so SafepointPoll has to be done before // acquiring mutator_lock. StoreStatusWithSafepoint(status); Locks::mutator_lock->ReadLock(); } void UpdateStatus(enum ThreadStatus status) { // Workaround: We masked the assert for 'ManagedThread::GetCurrent() == null' condition, // because JSThread updates status_ not from current thread. // (Remove it when issue 5183 is resolved) ASSERT(ManagedThread::GetCurrent() == this || ManagedThread::GetCurrent() == nullptr); ThreadStatus old_status = GetStatus(); if (old_status == RUNNING && status != RUNNING) { TransitionFromRunningToSuspended(status); } else if (old_status != RUNNING && status == RUNNING) { TransitionFromSuspendedToRunning(status); } else if (status == TERMINATING) { // Using Store with safepoint to be sure that main thread didn't suspend us while trying to update status StoreStatusWithSafepoint(status); } else { // NB! Status is not a simple bit, without atomics it can produce faulty GetStatus. StoreStatus(status); } } MTManagedThread *GetNextWait() const { return next_; } void SetWaitNext(MTManagedThread *next) { next_ = next; } mem::ReferenceStorage *GetPtReferenceStorage() const { return pt_reference_storage_.get(); } protected: virtual void ProcessCreatedThread(); virtual void StopDaemon0(); void StopSuspension() REQUIRES(suspend_lock_) { // Lock before this call. suspend_var_.Signal(); } os::memory::Mutex *GetSuspendMutex() RETURN_CAPABILITY(suspend_lock_) { return &suspend_lock_; } void WaitInternal() { os::memory::LockHolder lock(cond_lock_); WaitWithLockHeldInternal(); } void WaitWithLockHeldInternal() REQUIRES(cond_lock_) { ASSERT(this == ManagedThread::GetCurrent()); cond_var_.Wait(&cond_lock_); } bool TimedWaitInternal(uint64_t timeout, uint64_t nanos, bool is_absolute = false) { os::memory::LockHolder lock(cond_lock_); return TimedWaitWithLockHeldInternal(timeout, nanos, is_absolute); } bool TimedWaitWithLockHeldInternal(uint64_t timeout, uint64_t nanos, bool is_absolute = false) REQUIRES(cond_lock_) { ASSERT(this == ManagedThread::GetCurrent()); return cond_var_.TimedWait(&cond_lock_, timeout, nanos, is_absolute); } void SignalWithLockHeld() REQUIRES(cond_lock_) { cond_var_.Signal(); } void SetInterruptedWithLockHeld(bool interrupted) REQUIRES(cond_lock_) { is_interrupted_ = interrupted; } private: PandaString LogThreadStack(ThreadState new_state) const; void StoreStatusWithSafepoint(ThreadStatus status) { while (true) { SafepointPoll(); union FlagsAndThreadStatus old_fts { }; union FlagsAndThreadStatus new_fts { }; old_fts.as_int = ReadFlagsAndThreadStatusUnsafe(); // NOLINT(cppcoreguidelines-pro-type-union-access) new_fts.as_struct.flags = old_fts.as_struct.flags; // NOLINT(cppcoreguidelines-pro-type-union-access) new_fts.as_struct.status = status; // NOLINT(cppcoreguidelines-pro-type-union-access) bool no_flags = (old_fts.as_struct.flags == NO_FLAGS); // NOLINT(cppcoreguidelines-pro-type-union-access) // clang-format conflicts with CodeCheckAgent, so disable it here // clang-format off if (no_flags && stor_32_.fts_.as_atomic.compare_exchange_weak( // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) old_fts.as_nonvolatile_int, new_fts.as_nonvolatile_int, std::memory_order_release)) { // If CAS succeeded, we set new status and no request occurred here, safe to proceed. break; } // clang-format on } } // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) MTManagedThread *next_ {nullptr}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) ThreadId internal_id_ {0}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) PandaStack thread_frame_states_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) PandaVector local_objects_locked_; // Implementation of Wait/Notify os::memory::ConditionVariable cond_var_ GUARDED_BY(cond_lock_); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) mutable os::memory::Mutex cond_lock_; bool is_interrupted_ GUARDED_BY(cond_lock_) = false; os::memory::ConditionVariable suspend_var_ GUARDED_BY(suspend_lock_); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) os::memory::Mutex suspend_lock_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) uint32_t suspend_count_ GUARDED_BY(suspend_lock_) = 0; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) std::atomic_uint32_t user_code_suspend_count_ {0}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool is_daemon_ = false; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) Monitor *waiting_monitor_; // Monitor lock is required for multithreaded AddMonitor; RecursiveMutex to allow calling RemoveMonitor // in ReleaseMonitors // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) os::memory::RecursiveMutex monitor_lock_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) std::unordered_set entered_monitors_ GUARDED_BY(monitor_lock_); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) ThreadStatus monitor_old_status_ = FINISHED; // Boolean which is safe to access after runtime is destroyed // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool is_managed_scope_ {false}; PandaUniquePtr pt_reference_storage_ {nullptr}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) NO_COPY_SEMANTIC(MTManagedThread); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) NO_MOVE_SEMANTIC(MTManagedThread); }; } // namespace panda #endif // PANDA_RUNTIME_INCLUDE_MTMANAGED_THREAD_H_