| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| #ifndef RUNTIME_VM_OS_THREAD_H_ | |
| #define RUNTIME_VM_OS_THREAD_H_ | |
| #include "platform/atomic.h" | |
| #include "platform/globals.h" | |
| #include "platform/safe_stack.h" | |
| #include "platform/synchronization.h" | |
| #include "platform/thread_sanitizer.h" | |
| #include "platform/threads.h" | |
| #include "platform/utils.h" | |
| #include "vm/allocation.h" | |
| #include "vm/globals.h" | |
| // Declare the OS-specific types ahead of defining the generic classes. | |
| #if defined(DART_USE_ABSL) | |
| #include "vm/os_thread_absl.h" | |
| #elif defined(DART_HOST_OS_ANDROID) | |
| #include "vm/os_thread_android.h" | |
| #elif defined(DART_HOST_OS_FUCHSIA) | |
| #include "vm/os_thread_fuchsia.h" | |
| #elif defined(DART_HOST_OS_LINUX) | |
| #include "vm/os_thread_linux.h" | |
| #elif defined(DART_HOST_OS_MACOS) | |
| #include "vm/os_thread_macos.h" | |
| #elif defined(DART_HOST_OS_WINDOWS) | |
| #include "vm/os_thread_win.h" | |
| #else | |
| #error Unknown target os. | |
| #endif | |
| namespace dart { | |
| // Forward declarations. | |
| class Log; | |
| class Mutex; | |
| class ThreadState; | |
| class TimelineEventBlock; | |
| class BaseThread { | |
| public: | |
| bool is_os_thread() const { return is_os_thread_; } | |
| private: | |
| explicit BaseThread(bool is_os_thread) : is_os_thread_(is_os_thread) {} | |
| virtual ~BaseThread() {} | |
| bool is_os_thread_; | |
| friend class ThreadState; | |
| friend class OSThread; | |
| DISALLOW_IMPLICIT_CONSTRUCTORS(BaseThread); | |
| }; | |
| using ThreadId = platform::ThreadId; | |
| class OSThread; | |
| // Wrapper to get TLS destructor. | |
| class OSThreadPtr { | |
| public: | |
| constexpr OSThreadPtr() {} | |
| ~OSThreadPtr(); | |
| OSThread* get() const { return thread_; } | |
| void set(OSThread* value) { thread_ = value; } | |
| private: | |
| OSThread* thread_ = nullptr; | |
| }; | |
| // Low-level operations on OS platform threads. | |
| class OSThread : public BaseThread { | |
| public: | |
| static const uword kInvalidStackLimit = ~static_cast<uword>(0); | |
| // The constructor of OSThread is never called directly, instead we call | |
| // this factory style method 'CreateOSThread' to create OSThread structures. | |
| // The method can return a nullptr if the Dart VM is in shutdown mode. | |
| static OSThread* CreateOSThread(); | |
| ~OSThread(); | |
| ThreadId id() const { | |
| ASSERT(id_ != OSThread::kInvalidThreadId); | |
| return id_; | |
| } | |
| #ifdef SUPPORT_TIMELINE | |
| ThreadId trace_id() const { | |
| ASSERT(trace_id_ != OSThread::kInvalidThreadId); | |
| return trace_id_; | |
| } | |
| #endif | |
| const char* name() const { return name_; } | |
| void SetName(const char* name); | |
| Mutex* timeline_block_lock() const { return &timeline_block_lock_; } | |
| // Only safe to access when holding |timeline_block_lock_|. | |
| TimelineEventBlock* TimelineBlockLocked() const { | |
| ASSERT(timeline_block_lock()->IsOwnedByCurrentThread()); | |
| return timeline_block_; | |
| } | |
| // Only safe to access when holding |timeline_block_lock_|. | |
| void SetTimelineBlockLocked(TimelineEventBlock* block) { | |
| ASSERT(timeline_block_lock()->IsOwnedByCurrentThread()); | |
| timeline_block_ = block; | |
| } | |
| Log* log() const { return log_; } | |
| uword stack_base() const { return stack_base_; } | |
| uword stack_limit() const { return stack_limit_; } | |
| uword overflow_stack_limit() const { return stack_limit_ + stack_headroom_; } | |
| bool HasStackHeadroom() { return HasStackHeadroom(stack_headroom_); } | |
| bool HasStackHeadroom(intptr_t headroom) { | |
| return GetCurrentStackPointer() > (stack_limit_ + headroom); | |
| } | |
| #ifdef SUPPORT_TIMELINE | |
| static ThreadId GetCurrentThreadTraceId(); | |
| #endif // SUPPORT_TIMELINE | |
| // May fail for the main thread on Linux if resources are low. | |
| static bool GetCurrentStackBounds(uword* lower, uword* upper); | |
| // Returns the current C++ stack pointer. Equivalent taking the address of a | |
| // stack allocated local, but plays well with AddressSanitizer and SafeStack. | |
| // Accurate enough for stack overflow checks but not accurate enough for | |
| // alignment checks. | |
| static uword GetCurrentStackPointer(); | |
| #if defined(USING_SAFE_STACK) | |
| static uword GetCurrentSafestackPointer(); | |
| static void SetCurrentSafestackPointer(uword ssp); | |
| #endif | |
| #if defined(DART_INCLUDE_PROFILER) | |
| // Used to temporarily disable or enable thread interrupts. | |
| void DisableThreadInterrupts(); | |
| void EnableThreadInterrupts(); | |
| bool ThreadInterruptsEnabled(); | |
| #else | |
| // Used to temporarily disable or enable thread interrupts. | |
| void DisableThreadInterrupts() {} | |
| void EnableThreadInterrupts() {} | |
| bool ThreadInterruptsEnabled() { return false; } | |
| #endif // defined(DART_INCLUDE_PROFILER) | |
| // The currently executing thread, or nullptr if not yet initialized. | |
| static OSThread* TryCurrent() { | |
| BaseThread* thread = GetCurrentTLS(); | |
| OSThread* os_thread = nullptr; | |
| if (thread != nullptr) { | |
| if (thread->is_os_thread()) { | |
| os_thread = reinterpret_cast<OSThread*>(thread); | |
| } else { | |
| ThreadState* vm_thread = reinterpret_cast<ThreadState*>(thread); | |
| os_thread = GetOSThreadFromThread(vm_thread); | |
| } | |
| } | |
| return os_thread; | |
| } | |
| // The currently executing thread. If there is no currently executing thread, | |
| // a new OSThread is created and returned. | |
| static OSThread* Current() { | |
| OSThread* os_thread = TryCurrent(); | |
| if (os_thread == nullptr) { | |
| os_thread = CreateAndSetUnknownThread(); | |
| } | |
| return os_thread; | |
| } | |
| static void SetCurrent(OSThread* current) { SetCurrentTLS(current); } | |
| static ThreadState* CurrentVMThread() { return current_vm_thread_; } | |
| #if defined(DEBUG) | |
| static void SetCurrentVMThread(ThreadState* thread) { | |
| current_vm_thread_ = thread; | |
| } | |
| #endif | |
| // TODO(5411455): Use flag to override default value and Validate the | |
| // stack size by querying OS. | |
| static uword GetSpecifiedStackSize() { | |
| intptr_t headroom = | |
| OSThread::CalculateHeadroom(OSThread::GetMaxStackSize()); | |
| ASSERT(headroom < OSThread::GetMaxStackSize()); | |
| uword stack_size = OSThread::GetMaxStackSize() - headroom; | |
| return stack_size; | |
| } | |
| static BaseThread* GetCurrentTLS() { return current_os_thread_.get(); } | |
| static void SetCurrentTLS(BaseThread* value); | |
| typedef void (*ThreadStartFunction)(uword parameter); | |
| typedef void (*ThreadDestructor)(void* parameter); | |
| // Start a thread running the specified function. Returns 0 if the | |
| // thread started successfully and a system specific error code if | |
| // the thread failed to start. | |
| DART_WARN_UNUSED_RESULT static int TryStart(const char* name, | |
| ThreadStartFunction function, | |
| uword parameter); | |
| // Start a thread running the specified function. If the thread fails to | |
| // start, then exit with an descriptive error message. | |
| static void Start(const char* name, | |
| ThreadStartFunction function, | |
| uword parameter); | |
| static intptr_t GetMaxStackSize(); | |
| static void Join(ThreadJoinId id); | |
| static void Detach(ThreadJoinId id); | |
| static intptr_t ThreadIdToIntPtr(ThreadId id); | |
| static ThreadId ThreadIdFromIntPtr(intptr_t id); | |
| // This function can be called only once per OSThread, and should only be | |
| // called when the returned id will eventually be passed to OSThread::Join(). | |
| static ThreadJoinId GetCurrentThreadJoinId(OSThread* thread); | |
| // Called at VM startup and shutdown. | |
| static void Init(); | |
| static bool IsThreadInList(ThreadId id); | |
| static void DisableOSThreadCreation(); | |
| static bool CanCreateOSThreads(); | |
| static void EnableOSThreadCreation(); | |
| static constexpr intptr_t kStackSizeBufferMax = (16 * KB * kWordSize); | |
| static constexpr float kStackSizeBufferFraction = 0.5; | |
| static const ThreadJoinId kInvalidThreadJoinId; | |
| static constexpr ThreadId kInvalidThreadId = platform::kInvalidThreadId; | |
| static ThreadId GetCurrentThreadId() { | |
| return platform::GetCurrentThreadId(); | |
| } | |
| static bool Compare(ThreadId a, ThreadId b) { | |
| return platform::AreSameThreads(a, b); | |
| } | |
| private: | |
| // The constructor is private as CreateOSThread should be used | |
| // to create a new OSThread structure. | |
| OSThread(); | |
| // These methods should not be used in a generic way and hence | |
| // are private, they have been added to solve the problem of | |
| // accessing the VM thread structure from an OSThread object | |
| // in the windows thread interrupter which is used for profiling. | |
| // We could eliminate this requirement if the windows thread interrupter | |
| // is implemented differently. | |
| ThreadState* thread() const { return thread_; } | |
| NO_SANITIZE_THREAD | |
| ThreadState* thread_ignore_race() const { return thread_; } | |
| void set_thread(ThreadState* value) { thread_ = value; } | |
| static void Cleanup(); | |
| // Retrieves the name given to the current thread at the OS level and returns | |
| // it as a heap-allocated string that must eventually be freed by the caller | |
| // using free. Returns |nullptr| when the name cannot be retrieved. | |
| static char* GetCurrentThreadName(); | |
| static OSThread* GetOSThreadFromThread(ThreadState* thread); | |
| static void AddThreadToListLocked(OSThread* thread); | |
| static void RemoveThreadFromList(OSThread* thread); | |
| static OSThread* CreateAndSetUnknownThread(); | |
| static uword CalculateHeadroom(uword stack_size) { | |
| uword headroom = kStackSizeBufferFraction * stack_size; | |
| return (headroom > kStackSizeBufferMax) ? kStackSizeBufferMax : headroom; | |
| } | |
| const ThreadId id_; | |
| #if defined(DEBUG) | |
| // In DEBUG mode we use this field to ensure that GetCurrentThreadJoinId is | |
| // only called once per OSThread. | |
| ThreadJoinId join_id_ = kInvalidThreadJoinId; | |
| #endif | |
| #ifdef SUPPORT_TIMELINE | |
| const ThreadId trace_id_; // Used to interface with tracing tools. | |
| #endif | |
| char* name_; // A name for this thread. | |
| mutable Mutex timeline_block_lock_; | |
| // The block that the timeline recorder has permitted this thread to write | |
| // events to. | |
| TimelineEventBlock* timeline_block_ = nullptr; | |
| // All |Thread|s are registered in the thread list. | |
| OSThread* thread_list_next_ = nullptr; | |
| #if defined(DART_INCLUDE_PROFILER) | |
| // Thread interrupts disabled by default. | |
| RelaxedAtomic<uintptr_t> thread_interrupt_disabled_ = {1}; | |
| #endif // defined(DART_INCLUDE_PROFILER) | |
| Log* log_; | |
| uword stack_base_ = 0; | |
| uword stack_limit_ = 0; | |
| uword stack_headroom_ = 0; | |
| ThreadState* thread_ = nullptr; | |
| // The ThreadPool::Worker which owns this OSThread. If this OSThread was not | |
| // started by a ThreadPool it will be nullptr. This TLS value is not | |
| // protected and should only be read/written by the OSThread itself. | |
| void* owning_thread_pool_worker_ = nullptr; | |
| // thread_list_lock_ cannot have a static lifetime because the order in which | |
| // destructors run is undefined. At the moment this lock cannot be deleted | |
| // either since otherwise, if a thread only begins to run after we have | |
| // started to run TLS destructors for a call to exit(), there will be a race | |
| // on its deletion in CreateOSThread(). | |
| static Mutex* thread_list_lock_; | |
| static OSThread* thread_list_head_; | |
| static bool creation_enabled_; | |
| // Inline initialization is important for avoiding unnecessary TLS | |
| // initialization checks at each use. | |
| static inline thread_local ThreadState* current_vm_thread_ = nullptr; | |
| // Inline initialization results in duplicate definition link errors on Mac. | |
| // But this TLS is rarely accessed, so the guard doesn't matter much. | |
| static thread_local OSThreadPtr current_os_thread_; | |
| friend class Thread; // to access set_thread(Thread*). | |
| friend class OSThreadIterator; | |
| friend class ThreadInterrupterFuchsia; | |
| friend class ThreadInterrupterMacOS; | |
| friend class ThreadInterrupterWin; | |
| friend class ThreadPool; // to access owning_thread_pool_worker_ | |
| }; | |
| // Note that this takes the thread list lock, prohibiting threads from coming | |
| // on- or off-line. | |
| class OSThreadIterator : public ValueObject { | |
| public: | |
| OSThreadIterator(); | |
| ~OSThreadIterator(); | |
| // Returns false when there are no more threads left. | |
| bool HasNext() const; | |
| // Returns the current thread and moves forward. | |
| OSThread* Next(); | |
| private: | |
| OSThread* next_; | |
| }; | |
| // Mark when we are running in a signal handler (Linux, Android) or with a | |
| // suspended thread (Windows, Mac, Fuchia). During this time, we cannot take | |
| // locks, access Thread/Isolate::Current(), or use malloc. | |
| class ThreadInterruptScope : public ValueObject { | |
| #if defined(DEBUG) | |
| public: | |
| ThreadInterruptScope() { | |
| // Poison attempts to use Thread::Current. This is much cheaper than adding | |
| // an assert in Thread::Current itself. | |
| saved_current_vm_thread_ = OSThread::CurrentVMThread(); | |
| OSThread::SetCurrentVMThread(reinterpret_cast<ThreadState*>(0xabababab)); | |
| } | |
| ~ThreadInterruptScope() { | |
| OSThread::SetCurrentVMThread(saved_current_vm_thread_); | |
| } | |
| private: | |
| DisallowMutexLockingScope disallow_locks_; | |
| ThreadState* saved_current_vm_thread_; | |
| #endif // DEBUG | |
| }; | |
| } // namespace dart | |
| #endif // RUNTIME_VM_OS_THREAD_H_ |