|  | // Copyright (c) 2015, 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. | 
|  |  | 
|  | #include "vm/os_thread.h" | 
|  |  | 
|  | #include "platform/address_sanitizer.h" | 
|  | #include "platform/atomic.h" | 
|  | #include "vm/lockers.h" | 
|  | #include "vm/log.h" | 
|  | #include "vm/thread_interrupter.h" | 
|  | #include "vm/timeline.h" | 
|  |  | 
|  | namespace dart { | 
|  |  | 
|  | // The single thread local key which stores all the thread local data | 
|  | // for a thread. | 
|  | ThreadLocalKey OSThread::thread_key_ = kUnsetThreadLocalKey; | 
|  | OSThread* OSThread::thread_list_head_ = nullptr; | 
|  | Mutex* OSThread::thread_list_lock_ = nullptr; | 
|  | bool OSThread::creation_enabled_ = false; | 
|  |  | 
|  | #if defined(SUPPORT_TIMELINE) | 
|  | inline void UpdateTimelineTrackMetadata(const OSThread& thread) { | 
|  | RecorderSynchronizationLockScope ls; | 
|  | if (!ls.IsActive()) { | 
|  | return; | 
|  | } | 
|  | TimelineEventRecorder* recorder = Timeline::recorder(); | 
|  | if (recorder != nullptr) { | 
|  | recorder->AddTrackMetadataBasedOnThread( | 
|  | OS::ProcessId(), OSThread::ThreadIdToIntPtr(thread.trace_id()), | 
|  | thread.name()); | 
|  | } | 
|  | } | 
|  | #endif  // defined(SUPPORT_TIMELINE) | 
|  |  | 
|  | OSThread::OSThread() | 
|  | : BaseThread(true), | 
|  | id_(OSThread::GetCurrentThreadId()), | 
|  | #ifdef SUPPORT_TIMELINE | 
|  | trace_id_(OSThread::GetCurrentThreadTraceId()), | 
|  | #endif | 
|  | name_(OSThread::GetCurrentThreadName()), | 
|  | timeline_block_lock_(), | 
|  | log_(new class Log()) { | 
|  | // Try to get accurate stack bounds from pthreads, etc. | 
|  | if (!GetCurrentStackBounds(&stack_limit_, &stack_base_)) { | 
|  | FATAL("Failed to retrieve stack bounds"); | 
|  | } | 
|  |  | 
|  | stack_headroom_ = CalculateHeadroom(stack_base_ - stack_limit_); | 
|  |  | 
|  | ASSERT(stack_base_ != 0); | 
|  | ASSERT(stack_limit_ != 0); | 
|  | ASSERT(stack_base_ > stack_limit_); | 
|  | ASSERT(stack_base_ > GetCurrentStackPointer()); | 
|  | ASSERT(stack_limit_ < GetCurrentStackPointer()); | 
|  | RELEASE_ASSERT(HasStackHeadroom()); | 
|  | } | 
|  |  | 
|  | OSThread* OSThread::CreateOSThread() { | 
|  | ASSERT(thread_list_lock_ != nullptr); | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | if (!creation_enabled_) { | 
|  | return nullptr; | 
|  | } | 
|  | OSThread* os_thread = new OSThread(); | 
|  | AddThreadToListLocked(os_thread); | 
|  | return os_thread; | 
|  | } | 
|  |  | 
|  | OSThread::~OSThread() { | 
|  | if (!is_os_thread()) { | 
|  | // If the embedder enters an isolate on this thread and does not exit the | 
|  | // isolate, the thread local at thread_key_, which we are destructing here, | 
|  | // will contain a dart::Thread instead of a dart::OSThread. | 
|  | FATAL("Thread exited without calling Dart_ExitIsolate"); | 
|  | } | 
|  | RemoveThreadFromList(this); | 
|  | delete log_; | 
|  | log_ = nullptr; | 
|  | #if defined(SUPPORT_TIMELINE) | 
|  | RecorderSynchronizationLockScope ls; | 
|  | TimelineEventRecorder* recorder = Timeline::recorder(); | 
|  | if (recorder != nullptr && !ls.IsShuttingDown()) { | 
|  | // Acquire the recorder's lock so that |timeline_block_| cannot be given to | 
|  | // another thread until the call to |TimelineEventRecorder::FinishBlock| is | 
|  | // complete. This is guarded by a check ensuring that the recorder has not | 
|  | // yet been deleted, and thus ensuring that the recorder's lock can be | 
|  | // acquired. It is fine to skip the call to | 
|  | // |TimelineEventRecorder::FinishBlock| if the recorder has already been | 
|  | // deleted, because only the recorder cares about whether blocks have been | 
|  | // marked as finished. | 
|  | MutexLocker recorder_lock_locker(&Timeline::recorder()->lock_); | 
|  | MutexLocker timeline_block_lock_locker(timeline_block_lock()); | 
|  | Timeline::recorder()->FinishBlock(timeline_block_); | 
|  | } | 
|  | #endif | 
|  | timeline_block_ = nullptr; | 
|  | free(name_); | 
|  | #if !defined(PRODUCT) | 
|  | if (prepared_for_interrupts_) { | 
|  | ThreadInterrupter::CleanupCurrentThreadState(thread_interrupter_state_); | 
|  | thread_interrupter_state_ = nullptr; | 
|  | prepared_for_interrupts_ = false; | 
|  | } | 
|  | #endif  // !defined(PRODUCT) | 
|  | } | 
|  |  | 
|  | void OSThread::SetName(const char* name) { | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | // Clear the old thread name. | 
|  | if (name_ != nullptr) { | 
|  | free(name_); | 
|  | name_ = nullptr; | 
|  | } | 
|  | ASSERT(OSThread::Current() == this); | 
|  | ASSERT(name != nullptr); | 
|  | name_ = Utils::StrDup(name); | 
|  |  | 
|  | #if defined(SUPPORT_TIMELINE) | 
|  | UpdateTimelineTrackMetadata(*this); | 
|  | #endif  // defined(SUPPORT_TIMELINE) | 
|  | } | 
|  |  | 
|  | DART_NOINLINE | 
|  | uword OSThread::GetCurrentStackPointer() { | 
|  | #ifdef _MSC_VER | 
|  | return reinterpret_cast<uword>(_AddressOfReturnAddress()); | 
|  | #elif __GNUC__ | 
|  | return reinterpret_cast<uword>(__builtin_frame_address(0)); | 
|  | #else | 
|  | #error Unimplemented | 
|  | #endif | 
|  | } | 
|  |  | 
|  | #if !defined(PRODUCT) | 
|  | void OSThread::DisableThreadInterrupts() { | 
|  | ASSERT(OSThread::Current() == this); | 
|  | thread_interrupt_disabled_.fetch_add(1u); | 
|  | } | 
|  |  | 
|  | void OSThread::EnableThreadInterrupts() { | 
|  | ASSERT(OSThread::Current() == this); | 
|  | uintptr_t old = thread_interrupt_disabled_.fetch_sub(1u); | 
|  | if (FLAG_profiler && (old == 1)) { | 
|  | // We just decremented from 1 to 0. | 
|  | // Make sure the thread interrupter is awake. | 
|  | if (!prepared_for_interrupts_) { | 
|  | thread_interrupter_state_ = ThreadInterrupter::PrepareCurrentThread(); | 
|  | prepared_for_interrupts_ = true; | 
|  | } | 
|  | ThreadInterrupter::WakeUp(); | 
|  | } | 
|  | if (old == 0) { | 
|  | // We just decremented from 0, this means we've got a mismatched pair | 
|  | // of calls to EnableThreadInterrupts and DisableThreadInterrupts. | 
|  | FATAL("Invalid call to OSThread::EnableThreadInterrupts()"); | 
|  | } | 
|  | } | 
|  |  | 
|  | bool OSThread::ThreadInterruptsEnabled() { | 
|  | return thread_interrupt_disabled_ == 0; | 
|  | } | 
|  | #endif  // !defined(PRODUCT) | 
|  |  | 
|  | static void DeleteThread(void* thread) { | 
|  | MSAN_UNPOISON(&thread, sizeof(thread)); | 
|  | delete reinterpret_cast<OSThread*>(thread); | 
|  | } | 
|  |  | 
|  | void OSThread::Init() { | 
|  | // Allocate the global OSThread lock. | 
|  | if (thread_list_lock_ == nullptr) { | 
|  | thread_list_lock_ = new Mutex(); | 
|  | } | 
|  | ASSERT(thread_list_lock_ != nullptr); | 
|  |  | 
|  | // Create the thread local key. | 
|  | if (thread_key_ == kUnsetThreadLocalKey) { | 
|  | thread_key_ = CreateThreadLocal(DeleteThread); | 
|  | } | 
|  | ASSERT(thread_key_ != kUnsetThreadLocalKey); | 
|  |  | 
|  | // Enable creation of OSThread structures in the VM. | 
|  | EnableOSThreadCreation(); | 
|  |  | 
|  | // Create a new OSThread structure and set it as the TLS. | 
|  | OSThread* os_thread = CreateOSThread(); | 
|  | ASSERT(os_thread != nullptr); | 
|  | OSThread::SetCurrent(os_thread); | 
|  | os_thread->SetName("Dart_Initialize"); | 
|  | } | 
|  |  | 
|  | void OSThread::Cleanup() { | 
|  | // We cannot delete the thread local key and thread list lock,  yet. | 
|  | // See the note on thread_list_lock_ in os_thread.h. | 
|  | #if 0 | 
|  | if (thread_list_lock_ != nullptr) { | 
|  | // Delete the thread local key. | 
|  | ASSERT(thread_key_ != kUnsetThreadLocalKey); | 
|  | DeleteThreadLocal(thread_key_); | 
|  | thread_key_ = kUnsetThreadLocalKey; | 
|  |  | 
|  | // Delete the global OSThread lock. | 
|  | ASSERT(thread_list_lock_ != nullptr); | 
|  | delete thread_list_lock_; | 
|  | thread_list_lock_ = nullptr; | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | OSThread* OSThread::CreateAndSetUnknownThread() { | 
|  | ASSERT(OSThread::GetCurrentTLS() == nullptr); | 
|  | OSThread* os_thread = CreateOSThread(); | 
|  | if (os_thread != nullptr) { | 
|  | OSThread::SetCurrent(os_thread); | 
|  | if (os_thread->name() == nullptr) { | 
|  | os_thread->SetName("Unknown"); | 
|  | } | 
|  | } | 
|  | return os_thread; | 
|  | } | 
|  |  | 
|  | bool OSThread::IsThreadInList(ThreadId id) { | 
|  | if (id == OSThread::kInvalidThreadId) { | 
|  | return false; | 
|  | } | 
|  | OSThreadIterator it; | 
|  | while (it.HasNext()) { | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | OSThread* t = it.Next(); | 
|  | // An address test is not sufficient because the allocator may recycle | 
|  | // the address for another Thread. Test against the thread's id. | 
|  | if (t->id() == id) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool OSThread::CanCreateOSThreads() { | 
|  | return creation_enabled_; | 
|  | } | 
|  |  | 
|  | void OSThread::DisableOSThreadCreation() { | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | creation_enabled_ = false; | 
|  | } | 
|  |  | 
|  | void OSThread::EnableOSThreadCreation() { | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | creation_enabled_ = true; | 
|  | } | 
|  |  | 
|  | OSThread* OSThread::GetOSThreadFromThread(ThreadState* thread) { | 
|  | ASSERT(thread->os_thread() != nullptr); | 
|  | return thread->os_thread(); | 
|  | } | 
|  |  | 
|  | void OSThread::AddThreadToListLocked(OSThread* thread) { | 
|  | ASSERT(thread != nullptr); | 
|  | ASSERT(thread_list_lock_ != nullptr); | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | ASSERT(creation_enabled_); | 
|  | ASSERT(thread->thread_list_next_ == nullptr); | 
|  |  | 
|  | #if defined(DEBUG) | 
|  | { | 
|  | // Ensure that we aren't already in the list. | 
|  | OSThread* current = thread_list_head_; | 
|  | while (current != nullptr) { | 
|  | ASSERT(current != thread); | 
|  | current = current->thread_list_next_; | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | // Insert at head of list. | 
|  | thread->thread_list_next_ = thread_list_head_; | 
|  | thread_list_head_ = thread; | 
|  | } | 
|  |  | 
|  | void OSThread::RemoveThreadFromList(OSThread* thread) { | 
|  | bool final_thread = false; | 
|  | { | 
|  | ASSERT(thread != nullptr); | 
|  | ASSERT(thread_list_lock_ != nullptr); | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | OSThread* current = thread_list_head_; | 
|  | OSThread* previous = nullptr; | 
|  |  | 
|  | // Scan across list and remove |thread|. | 
|  | while (current != nullptr) { | 
|  | if (current == thread) { | 
|  | // We found |thread|, remove from list. | 
|  | if (previous == nullptr) { | 
|  | thread_list_head_ = thread->thread_list_next_; | 
|  | } else { | 
|  | previous->thread_list_next_ = current->thread_list_next_; | 
|  | } | 
|  | thread->thread_list_next_ = nullptr; | 
|  | final_thread = !creation_enabled_ && (thread_list_head_ == nullptr); | 
|  | break; | 
|  | } | 
|  | previous = current; | 
|  | current = current->thread_list_next_; | 
|  | } | 
|  | } | 
|  | // Check if this is the last thread. The last thread does a cleanup | 
|  | // which removes the thread local key and the associated mutex. | 
|  | if (final_thread) { | 
|  | Cleanup(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // current_vm_thread_ is also accessed from signal handlers for the profiler. | 
|  | // When detaching the thread, it is important that the stores don't get | 
|  | // reordered, such as | 
|  | //     current_vm_thread_ = nullptr; | 
|  | //     thread->set_os_thread(nullptr); | 
|  | // otherwise the profiler may observe a non-null Thread::Current() with a null | 
|  | // thread->os_thread(). | 
|  | // Logically current_vm_thread_ should be volatile, but that is too restrictive | 
|  | // around loads. Instead, we block inlining of the much rarer stores. | 
|  | DART_NOINLINE | 
|  | void OSThread::SetCurrentTLS(BaseThread* value) { | 
|  | // Provides thread-local destructors. | 
|  | SetThreadLocal(thread_key_, reinterpret_cast<uword>(value)); | 
|  |  | 
|  | // Allows the C compiler more freedom to optimize. | 
|  | if ((value != nullptr) && !value->is_os_thread()) { | 
|  | current_vm_thread_ = static_cast<Thread*>(value); | 
|  | } else { | 
|  | current_vm_thread_ = nullptr; | 
|  | } | 
|  | } | 
|  |  | 
|  | void OSThread::Start(const char* name, | 
|  | ThreadStartFunction function, | 
|  | uword parameter) { | 
|  | int result = TryStart(name, function, parameter); | 
|  | if (result != 0) { | 
|  | const int kBufferSize = 1024; | 
|  | char error_buf[kBufferSize]; | 
|  | FATAL("Could not start thread %s: %d (%s)", name, result, | 
|  | Utils::StrError(result, error_buf, kBufferSize)); | 
|  | } | 
|  | } | 
|  |  | 
|  | OSThreadIterator::OSThreadIterator() { | 
|  | ASSERT(OSThread::thread_list_lock_ != nullptr); | 
|  | // Lock the thread list while iterating. | 
|  | OSThread::thread_list_lock_->Lock(); | 
|  | next_ = OSThread::thread_list_head_; | 
|  | } | 
|  |  | 
|  | OSThreadIterator::~OSThreadIterator() { | 
|  | ASSERT(OSThread::thread_list_lock_ != nullptr); | 
|  | // Unlock the thread list when done. | 
|  | OSThread::thread_list_lock_->Unlock(); | 
|  | } | 
|  |  | 
|  | bool OSThreadIterator::HasNext() const { | 
|  | ASSERT(OSThread::thread_list_lock_ != nullptr); | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | return next_ != nullptr; | 
|  | } | 
|  |  | 
|  | OSThread* OSThreadIterator::Next() { | 
|  | ASSERT(OSThread::thread_list_lock_ != nullptr); | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | OSThread* current = next_; | 
|  | next_ = next_->thread_list_next_; | 
|  | return current; | 
|  | } | 
|  |  | 
|  | }  // namespace dart |