// 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/thread.h" #include "vm/growable_array.h" #include "vm/isolate.h" #include "vm/lockers.h" #include "vm/object.h" #include "vm/os_thread.h" #include "vm/profiler.h" #include "vm/stub_code.h" #include "vm/thread_interrupter.h" #include "vm/thread_registry.h" namespace dart { // The single thread local key which stores all the thread local data // for a thread. // TODO(koda): Can we merge this with ThreadInterrupter::thread_state_key_? ThreadLocalKey Thread::thread_key_ = OSThread::kUnsetThreadLocalKey; static void DeleteThread(void* thread) { delete reinterpret_cast(thread); } Thread::~Thread() { // We should cleanly exit any isolate before destruction. ASSERT(isolate_ == NULL); } void Thread::InitOnceBeforeIsolate() { ASSERT(thread_key_ == OSThread::kUnsetThreadLocalKey); thread_key_ = OSThread::CreateThreadLocal(DeleteThread); ASSERT(thread_key_ != OSThread::kUnsetThreadLocalKey); ASSERT(Thread::Current() == NULL); // Postpone initialization of VM constants for this first thread. SetCurrent(new Thread(false)); } void Thread::InitOnceAfterObjectAndStubCode() { Thread* thread = Thread::Current(); ASSERT(thread != NULL); ASSERT(thread->isolate() == Dart::vm_isolate()); thread->InitVMConstants(); } void Thread::SetCurrent(Thread* current) { OSThread::SetThreadLocal(thread_key_, reinterpret_cast(current)); } void Thread::EnsureInit() { if (Thread::Current() == NULL) { SetCurrent(new Thread()); } } #if defined(TARGET_OS_WINDOWS) void Thread::CleanUp() { Thread* current = Current(); if (current != NULL) { delete current; } SetCurrent(NULL); } #endif Thread::Thread(bool init_vm_constants) : isolate_(NULL), store_buffer_block_(NULL) { ClearState(); #define DEFAULT_INIT(type_name, member_name, init_expr, default_init_value) \ member_name = default_init_value; CACHED_CONSTANTS_LIST(DEFAULT_INIT) #undef DEFAULT_INIT if (init_vm_constants) { InitVMConstants(); } } void Thread::InitVMConstants() { #define ASSERT_VM_HEAP(type_name, member_name, init_expr, default_init_value) \ ASSERT((init_expr)->IsOldObject()); CACHED_VM_OBJECTS_LIST(ASSERT_VM_HEAP) #undef ASSERT_VM_HEAP #define INIT_VALUE(type_name, member_name, init_expr, default_init_value) \ ASSERT(member_name == default_init_value); \ member_name = (init_expr); CACHED_CONSTANTS_LIST(INIT_VALUE) #undef INIT_VALUE } void Thread::Schedule(Isolate* isolate) { State st; if (isolate->thread_registry()->RestoreStateTo(this, &st)) { ASSERT(isolate->thread_registry()->Contains(this)); state_ = st; } } void Thread::Unschedule() { ThreadRegistry* reg = isolate_->thread_registry(); ASSERT(reg->Contains(this)); reg->SaveStateFrom(this, state_); ClearState(); } void Thread::EnterIsolate(Isolate* isolate) { Thread* thread = Thread::Current(); ASSERT(thread != NULL); ASSERT(thread->isolate() == NULL); ASSERT(!isolate->HasMutatorThread()); thread->isolate_ = isolate; isolate->MakeCurrentThreadMutator(thread); // TODO(koda): Migrate thread_state_ and profile_data_ to Thread, to allow // helper threads concurrent with mutator. ASSERT(isolate->thread_state() == NULL); InterruptableThreadState* thread_state = ThreadInterrupter::GetCurrentThreadState(); #if defined(DEBUG) Isolate::CheckForDuplicateThreadState(thread_state); #endif ASSERT(thread_state != NULL); Profiler::BeginExecution(isolate); isolate->set_thread_state(thread_state); isolate->set_vm_tag(VMTag::kVMTagId); ASSERT(thread->store_buffer_block_ == NULL); thread->store_buffer_block_ = isolate->store_buffer()->PopBlock(); ASSERT(isolate->heap() != NULL); thread->heap_ = isolate->heap(); thread->Schedule(isolate); } void Thread::ExitIsolate() { Thread* thread = Thread::Current(); // TODO(koda): Audit callers; they should know whether they're in an isolate. if (thread == NULL || thread->isolate() == NULL) return; Isolate* isolate = thread->isolate(); thread->Unschedule(); StoreBufferBlock* block = thread->store_buffer_block_; thread->store_buffer_block_ = NULL; isolate->store_buffer()->PushBlock(block); if (isolate->is_runnable()) { isolate->set_vm_tag(VMTag::kIdleTagId); } else { isolate->set_vm_tag(VMTag::kLoadWaitTagId); } isolate->set_thread_state(NULL); Profiler::EndExecution(isolate); isolate->ClearMutatorThread(); thread->isolate_ = NULL; ASSERT(Isolate::Current() == NULL); thread->heap_ = NULL; } void Thread::EnterIsolateAsHelper(Isolate* isolate) { Thread* thread = Thread::Current(); ASSERT(thread != NULL); ASSERT(thread->isolate() == NULL); thread->isolate_ = isolate; ASSERT(isolate->heap() != NULL); thread->heap_ = isolate->heap(); // Do not update isolate->mutator_thread, but perform sanity check: // this thread should not be both the main mutator and helper. ASSERT(!isolate->MutatorThreadIsCurrentThread()); thread->Schedule(isolate); } void Thread::ExitIsolateAsHelper() { Thread* thread = Thread::Current(); // If the helper thread chose to use the store buffer, check that it has // already been flushed manually. ASSERT(thread->store_buffer_block_ == NULL); Isolate* isolate = thread->isolate(); ASSERT(isolate != NULL); thread->Unschedule(); thread->isolate_ = NULL; thread->heap_ = NULL; ASSERT(!isolate->MutatorThreadIsCurrentThread()); } void Thread::PrepareForGC() { Thread* thread = Thread::Current(); StoreBuffer* sb = thread->isolate()->store_buffer(); StoreBufferBlock* block = thread->store_buffer_block_; thread->store_buffer_block_ = NULL; const bool kCheckThreshold = false; // Prevent scheduling another GC. sb->PushBlock(block, kCheckThreshold); thread->store_buffer_block_ = sb->PopEmptyBlock(); } void Thread::StoreBufferBlockProcess(bool check_threshold) { StoreBuffer* sb = isolate()->store_buffer(); StoreBufferBlock* block = store_buffer_block_; store_buffer_block_ = NULL; sb->PushBlock(block, check_threshold); store_buffer_block_ = sb->PopBlock(); } void Thread::StoreBufferAddObject(RawObject* obj) { store_buffer_block_->Push(obj); if (store_buffer_block_->IsFull()) { StoreBufferBlockProcess(true); } } void Thread::StoreBufferAddObjectGC(RawObject* obj) { store_buffer_block_->Push(obj); if (store_buffer_block_->IsFull()) { StoreBufferBlockProcess(false); } } CHA* Thread::cha() const { ASSERT(isolate_ != NULL); return isolate_->cha_; } void Thread::set_cha(CHA* value) { ASSERT(isolate_ != NULL); isolate_->cha_ = value; } bool Thread::CanLoadFromThread(const Object& object) { #define CHECK_OBJECT(type_name, member_name, expr, default_init_value) \ if (object.raw() == expr) return true; CACHED_VM_OBJECTS_LIST(CHECK_OBJECT) #undef CHECK_OBJECT return false; } intptr_t Thread::OffsetFromThread(const Object& object) { #define COMPUTE_OFFSET(type_name, member_name, expr, default_init_value) \ ASSERT((expr)->IsVMHeapObject()); \ if (object.raw() == expr) return Thread::member_name##offset(); CACHED_VM_OBJECTS_LIST(COMPUTE_OFFSET) #undef COMPUTE_OFFSET UNREACHABLE(); return -1; } } // namespace dart