| // 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/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 Mutex { |
| public: |
| explicit Mutex(NOT_IN_PRODUCT(const char* name = "anonymous mutex")); |
| ~Mutex(); |
| |
| bool IsOwnedByCurrentThread() const; |
| |
| private: |
| void Lock(); |
| bool TryLock(); // Returns false if lock is busy and locking failed. |
| void Unlock(); |
| |
| MutexData data_; |
| NOT_IN_PRODUCT(const char* name_); |
| #if defined(DEBUG) |
| ThreadId owner_; |
| #endif // defined(DEBUG) |
| |
| friend class MallocLocker; |
| friend class MutexLocker; |
| friend class SafepointMutexLocker; |
| friend class OSThreadIterator; |
| friend class TimelineEventRecorder; |
| friend class PageSpace; |
| friend void Dart_TestMutex(); |
| DISALLOW_COPY_AND_ASSIGN(Mutex); |
| }; |
| |
| 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); |
| }; |
| |
| // Low-level operations on OS platform threads. |
| class OSThread : public BaseThread { |
| public: |
| // 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 NULL 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); |
| |
| void set_name(const char* name) { |
| ASSERT(OSThread::Current() == this); |
| ASSERT(name_ == NULL); |
| ASSERT(name != NULL); |
| name_ = Utils::StrDup(name); |
| } |
| |
| Mutex* timeline_block_lock() const { return &timeline_block_lock_; } |
| |
| // Only safe to access when holding |timeline_block_lock_|. |
| TimelineEventBlock* timeline_block() const { return timeline_block_; } |
| |
| // Only safe to access when holding |timeline_block_lock_|. |
| void set_timeline_block(TimelineEventBlock* block) { |
| 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); |
| } |
| |
| // 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 |
| |
| // Used to temporarily disable or enable thread interrupts. |
| void DisableThreadInterrupts(); |
| void EnableThreadInterrupts(); |
| bool ThreadInterruptsEnabled(); |
| |
| // The currently executing thread, or NULL if not yet initialized. |
| static OSThread* TryCurrent() { |
| BaseThread* thread = GetCurrentTLS(); |
| OSThread* os_thread = NULL; |
| if (thread != NULL) { |
| 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 == NULL) { |
| os_thread = CreateAndSetUnknownThread(); |
| } |
| return os_thread; |
| } |
| static void SetCurrent(OSThread* current) { SetCurrentTLS(current); } |
| |
| static ThreadState* CurrentVMThread() { return current_vm_thread_; } |
| |
| // 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 reinterpret_cast<BaseThread*>(OSThread::GetThreadLocal(thread_key_)); |
| } |
| 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 successfuly and a system specific error code if |
| // the thread failed to start. |
| static int Start(const char* name, |
| ThreadStartFunction function, |
| uword parameter); |
| |
| static ThreadLocalKey CreateThreadLocal(ThreadDestructor destructor = NULL); |
| static void DeleteThreadLocal(ThreadLocalKey key); |
| static uword GetThreadLocal(ThreadLocalKey key) { |
| return ThreadInlineImpl::GetThreadLocal(key); |
| } |
| static ThreadId GetCurrentThreadId(); |
| static void SetThreadLocal(ThreadLocalKey key, uword value); |
| static intptr_t GetMaxStackSize(); |
| static void Join(ThreadJoinId id); |
| static intptr_t ThreadIdToIntPtr(ThreadId id); |
| static ThreadId ThreadIdFromIntPtr(intptr_t id); |
| static bool Compare(ThreadId a, ThreadId b); |
| |
| // This function can be called only once per OSThread, and should only be |
| // called when the retunred 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 void EnableOSThreadCreation(); |
| |
| static const intptr_t kStackSizeBufferMax = (16 * KB * kWordSize); |
| static constexpr float kStackSizeBufferFraction = 0.5; |
| |
| static const ThreadId kInvalidThreadId; |
| static const ThreadJoinId kInvalidThreadJoinId; |
| |
| 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_; } |
| void set_thread(ThreadState* value) { thread_ = value; } |
| |
| static void Cleanup(); |
| #ifdef SUPPORT_TIMELINE |
| static ThreadId GetCurrentThreadTraceId(); |
| #endif // PRODUCT |
| 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; |
| } |
| |
| static ThreadLocalKey thread_key_; |
| |
| 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_; |
| #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_; |
| TimelineEventBlock* timeline_block_; |
| |
| // All |Thread|s are registered in the thread list. |
| OSThread* thread_list_next_; |
| |
| RelaxedAtomic<uintptr_t> thread_interrupt_disabled_; |
| Log* log_; |
| uword stack_base_; |
| uword stack_limit_; |
| uword stack_headroom_; |
| ThreadState* thread_; |
| // 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_; |
| |
| static thread_local ThreadState* current_vm_thread_; |
| |
| friend class IsolateGroup; // 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_; |
| }; |
| |
| class Monitor { |
| public: |
| enum WaitResult { kNotified, kTimedOut }; |
| |
| static const int64_t kNoTimeout = 0; |
| |
| Monitor(); |
| ~Monitor(); |
| |
| #if defined(DEBUG) |
| bool IsOwnedByCurrentThread() const { |
| return owner_ == OSThread::GetCurrentThreadId(); |
| } |
| #else |
| bool IsOwnedByCurrentThread() const { |
| UNREACHABLE(); |
| return false; |
| } |
| #endif |
| |
| private: |
| bool TryEnter(); // Returns false if lock is busy and locking failed. |
| void Enter(); |
| void Exit(); |
| |
| // Wait for notification or timeout. |
| WaitResult Wait(int64_t millis); |
| WaitResult WaitMicros(int64_t micros); |
| |
| // Notify waiting threads. |
| void Notify(); |
| void NotifyAll(); |
| |
| MonitorData data_; // OS-specific data. |
| #if defined(DEBUG) |
| ThreadId owner_; |
| #endif // defined(DEBUG) |
| |
| friend class MonitorLocker; |
| friend class SafepointMonitorLocker; |
| friend class SafepointRwLock; |
| friend void Dart_TestMonitor(); |
| DISALLOW_COPY_AND_ASSIGN(Monitor); |
| }; |
| |
| inline bool Mutex::IsOwnedByCurrentThread() const { |
| #if defined(DEBUG) |
| return owner_ == OSThread::GetCurrentThreadId(); |
| #else |
| UNREACHABLE(); |
| return false; |
| #endif |
| } |
| |
| } // namespace dart |
| |
| #endif // RUNTIME_VM_OS_THREAD_H_ |