blob: a982366024ebf80dba20bcac4d70e218b98b9dd6 [file] [log] [blame] [edit]
// 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