| // 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::~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<uword>(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 |