| // 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()); | 
 |  | 
 | #if defined(SUPPORT_TIMELINE) | 
 |   UpdateTimelineTrackMetadata(*this); | 
 | #endif  // defined(SUPPORT_TIMELINE) | 
 | } | 
 |  | 
 | 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; | 
 | } | 
 |  | 
 | 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(); | 
 |   } | 
 | } | 
 |  | 
 | 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; | 
 |   } | 
 | } | 
 |  | 
 | 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 |