/* * 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_MANAGED_THREAD_H_ #define PANDA_RUNTIME_INCLUDE_MANAGED_THREAD_H_ #include "thread.h" namespace panda { enum ThreadFlag { NO_FLAGS = 0, GC_SAFEPOINT_REQUEST = 1, SUSPEND_REQUEST = 2, RUNTIME_TERMINATION_REQUEST = 4, }; /** * \brief Class represents managed thread * * When the thread is created it registers itself in the runtime, so * runtime knows about all managed threads at any given time. * * This class should be used to store thread specitic information that * is necessary to execute managed code: * - Frame * - Exception * - Interpreter cache * - etc. * * Now it's used by interpreter to store current frame only. */ class ManagedThread : public Thread { public: using ThreadId = uint32_t; using native_handle_type = os::thread::native_handle_type; static constexpr ThreadId NON_INITIALIZED_THREAD_ID = 0; static constexpr ThreadId MAX_INTERNAL_THREAD_ID = MarkWord::LIGHT_LOCK_THREADID_MAX_COUNT; void SetLanguageContext(LanguageContext ctx) { ctx_ = ctx; } LanguageContext GetLanguageContext() const { return ctx_; } void SetCurrentFrame(Frame *f) { stor_ptr_.frame_ = f; } tooling::PtThreadInfo *GetPtThreadInfo() const { return pt_thread_info_.get(); } Frame *GetCurrentFrame() const { return stor_ptr_.frame_; } void *GetFrame() const { void *fp = GetCurrentFrame(); if (IsCurrentFrameCompiled()) { return StackWalker::IsBoundaryFrame(fp) ? StackWalker::GetPrevFromBoundary(fp) : fp; } return fp; } bool IsCurrentFrameCompiled() const { return stor_32_.is_compiled_frame_; } void SetCurrentFrameIsCompiled(bool value) { stor_32_.is_compiled_frame_ = value; } void SetException(ObjectHeader *exception) { stor_ptr_.exception_ = exception; } ObjectHeader *GetException() const { return stor_ptr_.exception_; } bool HasPendingException() const { return stor_ptr_.exception_ != nullptr; } void ClearException() { stor_ptr_.exception_ = nullptr; } static bool ThreadIsManagedThread(Thread *thread) { ASSERT(thread != nullptr); Thread::ThreadType thread_type = thread->GetThreadType(); return thread_type == Thread::ThreadType::THREAD_TYPE_MANAGED || thread_type == Thread::ThreadType::THREAD_TYPE_MT_MANAGED; } static ManagedThread *CastFromThread(Thread *thread) { ASSERT(thread != nullptr); ASSERT(ThreadIsManagedThread(thread)); return static_cast(thread); } /** * @brief GetCurrentRaw Unsafe method to get current ManagedThread. * It can be used in hotspots to get the best performance. * We can only use this method in places where the ManagedThread exists. * @return pointer to ManagedThread */ static ManagedThread *GetCurrentRaw() { return CastFromThread(Thread::GetCurrent()); } /** * @brief GetCurrent Safe method to gets current ManagedThread. * @return pointer to ManagedThread or nullptr (if current thread is not a managed thread) */ static ManagedThread *GetCurrent() { Thread *thread = Thread::GetCurrent(); ASSERT(thread != nullptr); if (ThreadIsManagedThread(thread)) { return CastFromThread(thread); } return nullptr; } static bool Initialize(); static bool Shutdown(); bool IsThreadAlive() const { return GetStatus() != FINISHED; } enum ThreadStatus GetStatus() const { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) uint32_t res_int = stor_32_.fts_.as_atomic.load(std::memory_order_acquire); return static_cast(res_int >> THREAD_STATUS_OFFSET); } panda::mem::StackFrameAllocator *GetStackFrameAllocator() const { return stack_frame_allocator_; } panda::mem::InternalAllocator<>::LocalSmallObjectAllocator *GetLocalInternalAllocator() const { return internal_local_allocator_; } mem::TLAB *GetTLAB() const { ASSERT(stor_ptr_.tlab_ != nullptr); return stor_ptr_.tlab_; } void UpdateTLAB(mem::TLAB *tlab); void ClearTLAB(); void SetStringClassPtr(void *p) { stor_ptr_.string_class_ptr_ = p; } static ManagedThread *Create(Runtime *runtime, PandaVM *vm); ~ManagedThread() override; explicit ManagedThread(ThreadId id, mem::InternalAllocatorPtr allocator, PandaVM *vm, Thread::ThreadType thread_type); // Here methods which are just proxy or cache for runtime interface ALWAYS_INLINE mem::BarrierType GetPreBarrierType() const { return pre_barrier_type_; } ALWAYS_INLINE mem::BarrierType GetPostBarrierType() const { return post_barrier_type_; } // Methods to access thread local storage InterpreterCache *GetInterpreterCache() { return &interpreter_cache_; } uintptr_t GetNativePc() const { return stor_ptr_.native_pc_; } bool IsJavaThread() const { return is_java_thread_; } bool IsJSThread() const { return is_js_thread_; } LanguageContext GetLanguageContext(); inline bool IsSuspended() const { return ReadFlag(SUSPEND_REQUEST); } inline bool IsRuntimeTerminated() const { return ReadFlag(RUNTIME_TERMINATION_REQUEST); } inline void SetRuntimeTerminated() { SetFlag(RUNTIME_TERMINATION_REQUEST); } static constexpr size_t GetPtrStorageOffset(Arch arch, size_t offset) { return MEMBER_OFFSET(ManagedThread, stor_ptr_) + StoragePackedPtr::ConvertOffset(PointerSize(arch), offset); } static constexpr uint32_t GetFlagOffset() { return MEMBER_OFFSET(ManagedThread, stor_32_) + MEMBER_OFFSET(StoragePacked32, fts_); } static constexpr uint32_t GetNativePcOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, native_pc_)); } static constexpr uint32_t GetFrameKindOffset() { return MEMBER_OFFSET(ManagedThread, stor_32_) + MEMBER_OFFSET(StoragePacked32, is_compiled_frame_); } static constexpr uint32_t GetFrameOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, frame_)); } static constexpr uint32_t GetExceptionOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, exception_)); } static constexpr uint32_t GetTLABOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, tlab_)); } static constexpr uint32_t GetObjectOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, object_)); } static constexpr uint32_t GetTlsCardTableAddrOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, card_table_addr_)); } static constexpr uint32_t GetTlsCardTableMinAddrOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, card_table_min_addr_)); } static constexpr uint32_t GetTlsConcurrentMarkingAddrOffset(Arch arch) { return GetPtrStorageOffset(arch, MEMBER_OFFSET(StoragePackedPtr, concurrent_marking_addr_)); } virtual void VisitGCRoots(const ObjectVisitor &cb); virtual void UpdateGCRoots(); void PushLocalObject(ObjectHeader **object_header); void PopLocalObject(); void SetThreadPriority(int32_t prio); uint32_t GetThreadPriority() const; inline bool IsGcRequired() const { return ReadFlag(GC_SAFEPOINT_REQUEST); } // NO_THREAD_SANITIZE for invalid TSAN data race report NO_THREAD_SANITIZE bool ReadFlag(ThreadFlag flag) const { return (stor_32_.fts_.as_struct.flags & flag) != 0; // NOLINT(cppcoreguidelines-pro-type-union-access) } NO_THREAD_SANITIZE bool TestAllFlags() const { return (stor_32_.fts_.as_struct.flags) != NO_FLAGS; // NOLINT(cppcoreguidelines-pro-type-union-access) } void SetFlag(ThreadFlag flag) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) stor_32_.fts_.as_atomic.fetch_or(flag, std::memory_order_seq_cst); } void ClearFlag(ThreadFlag flag) { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) stor_32_.fts_.as_atomic.fetch_and(UINT32_MAX ^ flag, std::memory_order_seq_cst); } // Separate functions for NO_THREAD_SANITIZE to suppress TSAN data race report NO_THREAD_SANITIZE uint32_t ReadFlagsAndThreadStatusUnsafe() const { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) return stor_32_.fts_.as_int; } void StoreStatus(ThreadStatus status) { while (true) { 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) // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access) if (stor_32_.fts_.as_atomic.compare_exchange_weak(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; } } } bool IsManagedCodeAllowed() const { return is_managed_code_allowed_; } void SetManagedCodeAllowed(bool allowed) { is_managed_code_allowed_ = allowed; } // TaggedType has been specialized for js, Other types are empty implementation template inline HandleScope *PopHandleScope() { return nullptr; } // TaggedType has been specialized for js, Other types are empty implementation template inline void PushHandleScope([[maybe_unused]] HandleScope *handle_scope) { } // TaggedType has been specialized for js, Other types are empty implementation template inline HandleScope *GetTopScope() const { return nullptr; } // TaggedType has been specialized for js, Other types are empty implementation template inline HandleStorage *GetHandleStorage() const { return nullptr; } // TaggedType has been specialized for js, Other types are empty implementation template inline GlobalHandleStorage *GetGlobalHandleStorage() const { return nullptr; } CustomTLSData *GetCustomTLSData(const char *key); void SetCustomTLSData(const char *key, CustomTLSData *data); #if EVENT_METHOD_ENTER_ENABLED || EVENT_METHOD_EXIT_ENABLED uint32_t RecordMethodEnter() { return call_depth_++; } uint32_t RecordMethodExit() { return --call_depth_; } #endif bool IsAttached() const { return is_attached_.load(std::memory_order_relaxed); } void SetAttached() { is_attached_.store(true, std::memory_order_relaxed); } void SetDetached() { is_attached_.store(false, std::memory_order_relaxed); } bool IsVMThread() const { return is_vm_thread_; } void SetVMThread() { is_vm_thread_ = true; } bool IsThrowingOOM() const { return throwing_oom_count_ > 0; } void SetThrowingOOM(bool is_throwing_oom) { if (is_throwing_oom) { throwing_oom_count_++; return; } ASSERT(throwing_oom_count_ > 0); throwing_oom_count_--; } bool IsUsePreAllocObj() const { return use_prealloc_obj_; } void SetUsePreAllocObj(bool use_prealloc_obj) { use_prealloc_obj_ = use_prealloc_obj; } void PrintSuspensionStackIfNeeded(); ThreadId GetId() const { return id_.load(std::memory_order_relaxed); } virtual void FreeInternalMemory(); protected: static const int WAIT_INTERVAL = 10; void SetJavaThread() { is_java_thread_ = true; } void SetJSThread() { is_js_thread_ = true; } template T *GetAssociatedObject() const { return reinterpret_cast(stor_ptr_.object_); } template void SetAssociatedObject(T *object) { stor_ptr_.object_ = object; } virtual void InterruptPostImpl() {} void UpdateId(ThreadId id) { id_.store(id, std::memory_order_relaxed); } private: // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) static constexpr uint32_t THREAD_STATUS_OFFSET = 16; static_assert(sizeof(stor_32_.fts_) == sizeof(uint32_t), "Wrong fts_ size"); // Can cause data races if child thread's UpdateId is executed concurrently with GetNativeThreadId // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) std::atomic id_; static mem::TLAB *zero_tlab; static bool is_initialized; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) PandaVector local_objects_; // Something like custom TLS - it is faster to access via ManagedThread than via thread_local // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) InterpreterCache interpreter_cache_; PandaMap> custom_tls_cache_ GUARDED_BY(Locks::custom_tls_lock); // Keep these here to speed up interpreter // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) mem::BarrierType pre_barrier_type_ {mem::BarrierType::PRE_WRB_NONE}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) mem::BarrierType post_barrier_type_ {mem::BarrierType::POST_WRB_NONE}; // Thread local storages to avoid locks in heap manager // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) mem::StackFrameAllocator *stack_frame_allocator_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) mem::InternalAllocator<>::LocalSmallObjectAllocator *internal_local_allocator_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool is_java_thread_ = false; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) std::atomic_bool is_attached_ {false}; // Can be changed after thread is registered and can cause data race // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool is_vm_thread_ = false; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool is_js_thread_ = false; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool is_managed_code_allowed_ {true}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) size_t throwing_oom_count_ {0}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) bool use_prealloc_obj_ {false}; // remove ctx in thread later // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) LanguageContext ctx_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) PandaUniquePtr pt_thread_info_; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) PandaVector *> tagged_handle_scopes_ {}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) HandleStorage *tagged_handle_storage_ {nullptr}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) GlobalHandleStorage *tagged_global_handle_storage_ {nullptr}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) PandaVector *> object_header_handle_scopes_ {}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) HandleStorage *object_header_handle_storage_ {nullptr}; friend class panda::test::ThreadTest; friend class openjdkjvmti::TiThread; friend class openjdkjvmti::ScopedNoUserCodeSuspension; friend class Offsets_Thread_Test; friend class panda::ThreadManager; // Used in method events // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) uint32_t call_depth_ {0}; // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) NO_COPY_SEMANTIC(ManagedThread); // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_GLOBAL_VAR_AS_INTERFACE) NO_MOVE_SEMANTIC(ManagedThread); }; } // namespace panda #endif // PANDA_RUNTIME_INCLUDE_MANAGED_THREAD_H_