|  | // 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_ = NULL; | 
|  | Mutex* OSThread::thread_list_lock_ = NULL; | 
|  | bool OSThread::creation_enabled_ = false; | 
|  |  | 
|  | thread_local ThreadState* OSThread::current_vm_thread_ = NULL; | 
|  |  | 
|  | OSThread::OSThread() | 
|  | : BaseThread(true), | 
|  | id_(OSThread::GetCurrentThreadId()), | 
|  | #if defined(DEBUG) | 
|  | join_id_(kInvalidThreadJoinId), | 
|  | #endif | 
|  | #ifdef SUPPORT_TIMELINE | 
|  | trace_id_(OSThread::GetCurrentThreadTraceId()), | 
|  | #endif | 
|  | name_(NULL), | 
|  | timeline_block_lock_(), | 
|  | timeline_block_(NULL), | 
|  | thread_list_next_(NULL), | 
|  | thread_interrupt_disabled_(1),  // Thread interrupts disabled by default. | 
|  | log_(new class Log()), | 
|  | stack_base_(0), | 
|  | stack_limit_(0), | 
|  | stack_headroom_(0), | 
|  | thread_(NULL) { | 
|  | // 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_ != NULL); | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | if (!creation_enabled_) { | 
|  | return NULL; | 
|  | } | 
|  | 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_ = NULL; | 
|  | #if defined(SUPPORT_TIMELINE) | 
|  | if (Timeline::recorder() != NULL) { | 
|  | Timeline::recorder()->FinishBlock(timeline_block_); | 
|  | } | 
|  | #endif | 
|  | timeline_block_ = NULL; | 
|  | free(name_); | 
|  | } | 
|  |  | 
|  | void OSThread::SetName(const char* name) { | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | // Clear the old thread name. | 
|  | if (name_ != NULL) { | 
|  | free(name_); | 
|  | name_ = NULL; | 
|  | } | 
|  | set_name(name); | 
|  | } | 
|  |  | 
|  | // Disable AdressSanitizer and SafeStack transformation on this function. In | 
|  | // particular, taking the address of a local gives an address on the stack | 
|  | // instead of an address in the shadow memory (AddressSanitizer) or the safe | 
|  | // stack (SafeStack). | 
|  | NO_SANITIZE_ADDRESS | 
|  | NO_SANITIZE_SAFE_STACK | 
|  | DART_NOINLINE | 
|  | uword OSThread::GetCurrentStackPointer() { | 
|  | uword stack_allocated_local = reinterpret_cast<uword>(&stack_allocated_local); | 
|  | return stack_allocated_local; | 
|  | } | 
|  |  | 
|  | 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. | 
|  | 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; | 
|  | } | 
|  |  | 
|  | 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_ == NULL) { | 
|  | thread_list_lock_ = new Mutex(); | 
|  | } | 
|  | ASSERT(thread_list_lock_ != NULL); | 
|  |  | 
|  | // 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 strcture and set it as the TLS. | 
|  | OSThread* os_thread = CreateOSThread(); | 
|  | ASSERT(os_thread != NULL); | 
|  | OSThread::SetCurrent(os_thread); | 
|  | os_thread->set_name("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_ != NULL) { | 
|  | // Delete the thread local key. | 
|  | ASSERT(thread_key_ != kUnsetThreadLocalKey); | 
|  | DeleteThreadLocal(thread_key_); | 
|  | thread_key_ = kUnsetThreadLocalKey; | 
|  |  | 
|  | // Delete the global OSThread lock. | 
|  | ASSERT(thread_list_lock_ != NULL); | 
|  | delete thread_list_lock_; | 
|  | thread_list_lock_ = NULL; | 
|  | } | 
|  | #endif | 
|  | } | 
|  |  | 
|  | OSThread* OSThread::CreateAndSetUnknownThread() { | 
|  | ASSERT(OSThread::GetCurrentTLS() == NULL); | 
|  | OSThread* os_thread = CreateOSThread(); | 
|  | if (os_thread != NULL) { | 
|  | OSThread::SetCurrent(os_thread); | 
|  | os_thread->set_name("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() != NULL); | 
|  | return thread->os_thread(); | 
|  | } | 
|  |  | 
|  | void OSThread::AddThreadToListLocked(OSThread* thread) { | 
|  | ASSERT(thread != NULL); | 
|  | ASSERT(thread_list_lock_ != NULL); | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | ASSERT(creation_enabled_); | 
|  | ASSERT(thread->thread_list_next_ == NULL); | 
|  |  | 
|  | #if defined(DEBUG) | 
|  | { | 
|  | // Ensure that we aren't already in the list. | 
|  | OSThread* current = thread_list_head_; | 
|  | while (current != NULL) { | 
|  | 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 != NULL); | 
|  | ASSERT(thread_list_lock_ != NULL); | 
|  | MutexLocker ml(thread_list_lock_); | 
|  | OSThread* current = thread_list_head_; | 
|  | OSThread* previous = NULL; | 
|  |  | 
|  | // Scan across list and remove |thread|. | 
|  | while (current != NULL) { | 
|  | if (current == thread) { | 
|  | // We found |thread|, remove from list. | 
|  | if (previous == NULL) { | 
|  | thread_list_head_ = thread->thread_list_next_; | 
|  | } else { | 
|  | previous->thread_list_next_ = current->thread_list_next_; | 
|  | } | 
|  | thread->thread_list_next_ = NULL; | 
|  | final_thread = !creation_enabled_ && (thread_list_head_ == NULL); | 
|  | 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 != NULL) && !value->is_os_thread()) { | 
|  | current_vm_thread_ = static_cast<Thread*>(value); | 
|  | } else { | 
|  | current_vm_thread_ = NULL; | 
|  | } | 
|  | } | 
|  |  | 
|  | OSThreadIterator::OSThreadIterator() { | 
|  | ASSERT(OSThread::thread_list_lock_ != NULL); | 
|  | // Lock the thread list while iterating. | 
|  | OSThread::thread_list_lock_->Lock(); | 
|  | next_ = OSThread::thread_list_head_; | 
|  | } | 
|  |  | 
|  | OSThreadIterator::~OSThreadIterator() { | 
|  | ASSERT(OSThread::thread_list_lock_ != NULL); | 
|  | // Unlock the thread list when done. | 
|  | OSThread::thread_list_lock_->Unlock(); | 
|  | } | 
|  |  | 
|  | bool OSThreadIterator::HasNext() const { | 
|  | ASSERT(OSThread::thread_list_lock_ != NULL); | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | return next_ != NULL; | 
|  | } | 
|  |  | 
|  | OSThread* OSThreadIterator::Next() { | 
|  | ASSERT(OSThread::thread_list_lock_ != NULL); | 
|  | ASSERT(OSThread::thread_list_lock_->IsOwnedByCurrentThread()); | 
|  | OSThread* current = next_; | 
|  | next_ = next_->thread_list_next_; | 
|  | return current; | 
|  | } | 
|  |  | 
|  | }  // namespace dart |