// 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/log.h" #include "vm/native_entry.h" #include "vm/object.h" #include "vm/os_thread.h" #include "vm/profiler.h" #include "vm/runtime_entry.h" #include "vm/stub_code.h" #include "vm/symbols.h" #include "vm/thread_interrupter.h" #include "vm/thread_registry.h" namespace dart { Thread::~Thread() { // We should cleanly exit any isolate before destruction. ASSERT(isolate_ == NULL); } #if defined(DEBUG) #define REUSABLE_HANDLE_SCOPE_INIT(object) \ reusable_##object##_handle_scope_active_(false), #else #define REUSABLE_HANDLE_SCOPE_INIT(object) #endif // defined(DEBUG) #define REUSABLE_HANDLE_INITIALIZERS(object) \ object##_handle_(NULL), Thread::Thread(Isolate* isolate) : BaseThread(false), os_thread_(NULL), isolate_(NULL), heap_(NULL), zone_(NULL), top_exit_frame_info_(0), top_resource_(NULL), long_jump_base_(NULL), store_buffer_block_(NULL), no_callback_scope_depth_(0), #if defined(DEBUG) top_handle_scope_(NULL), no_handle_scope_depth_(0), no_safepoint_scope_depth_(0), #endif reusable_handles_(), cha_(NULL), deopt_id_(0), vm_tag_(0), pending_functions_(GrowableObjectArray::null()), REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS) REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_INIT) next_(NULL) { #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 #define DEFAULT_INIT(name) \ name##_entry_point_ = 0; RUNTIME_ENTRY_LIST(DEFAULT_INIT) #undef DEFAULT_INIT #define DEFAULT_INIT(returntype, name, ...) \ name##_entry_point_ = 0; LEAF_RUNTIME_ENTRY_LIST(DEFAULT_INIT) #undef DEFAULT_INIT // We cannot initialize the VM constants here for the vm isolate thread // due to boot strapping issues. if ((Dart::vm_isolate() != NULL) && (isolate != Dart::vm_isolate())) { 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 #define INIT_VALUE(name) \ ASSERT(name##_entry_point_ == 0); \ name##_entry_point_ = k##name##RuntimeEntry.GetEntryPoint(); RUNTIME_ENTRY_LIST(INIT_VALUE) #undef INIT_VALUE #define INIT_VALUE(returntype, name, ...) \ ASSERT(name##_entry_point_ == 0); \ name##_entry_point_ = k##name##RuntimeEntry.GetEntryPoint(); LEAF_RUNTIME_ENTRY_LIST(INIT_VALUE) #undef INIT_VALUE // Setup the thread specific reusable handles. #define REUSABLE_HANDLE_ALLOCATION(object) \ this->object##_handle_ = this->AllocateReusableHandle(); REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_ALLOCATION) #undef REUSABLE_HANDLE_ALLOCATION } RawGrowableObjectArray* Thread::pending_functions() { if (pending_functions_ == GrowableObjectArray::null()) { pending_functions_ = GrowableObjectArray::New(Heap::kOld); } return pending_functions_; } void Thread::EnterIsolate(Isolate* isolate) { const bool kIsMutatorThread = true; const bool kDontBypassSafepoints = false; ThreadRegistry* tr = isolate->thread_registry(); Thread* thread = tr->Schedule( isolate, kIsMutatorThread, kDontBypassSafepoints); isolate->MakeCurrentThreadMutator(thread); thread->set_vm_tag(VMTag::kVMTagId); ASSERT(thread->store_buffer_block_ == NULL); thread->StoreBufferAcquire(); } void Thread::ExitIsolate() { Thread* thread = Thread::Current(); ASSERT(thread != NULL); ASSERT(thread->IsMutatorThread()); #if defined(DEBUG) ASSERT(!thread->IsAnyReusableHandleScopeActive()); #endif // DEBUG // Clear since GC will not visit the thread once it is unscheduled. thread->ClearReusableHandles(); thread->StoreBufferRelease(); Isolate* isolate = thread->isolate(); ASSERT(isolate != NULL); if (isolate->is_runnable()) { thread->set_vm_tag(VMTag::kIdleTagId); } else { thread->set_vm_tag(VMTag::kLoadWaitTagId); } const bool kIsMutatorThread = true; const bool kDontBypassSafepoints = false; ThreadRegistry* tr = isolate->thread_registry(); tr->Unschedule(thread, kIsMutatorThread, kDontBypassSafepoints); isolate->ClearMutatorThread(); } void Thread::EnterIsolateAsHelper(Isolate* isolate, bool bypass_safepoint) { const bool kIsNotMutatorThread = false; ThreadRegistry* tr = isolate->thread_registry(); Thread* thread = tr->Schedule(isolate, kIsNotMutatorThread, bypass_safepoint); ASSERT(thread->store_buffer_block_ == NULL); // TODO(koda): Use StoreBufferAcquire once we properly flush before Scavenge. thread->store_buffer_block_ = thread->isolate()->store_buffer()->PopEmptyBlock(); // This thread should not be the main mutator. ASSERT(!thread->IsMutatorThread()); } void Thread::ExitIsolateAsHelper(bool bypass_safepoint) { Thread* thread = Thread::Current(); ASSERT(thread != NULL); ASSERT(!thread->IsMutatorThread()); thread->StoreBufferRelease(); Isolate* isolate = thread->isolate(); ASSERT(isolate != NULL); const bool kIsNotMutatorThread = false; ThreadRegistry* tr = isolate->thread_registry(); tr->Unschedule(thread, kIsNotMutatorThread, bypass_safepoint); } // TODO(koda): Make non-static and invoke in SafepointThreads. void Thread::PrepareForGC() { Thread* thread = Thread::Current(); // Prevent scheduling another GC. thread->StoreBufferRelease(StoreBuffer::kIgnoreThreshold); // Make sure to get an *empty* block; the isolate needs all entries // at GC time. // TODO(koda): Replace with an epilogue (PrepareAfterGC) that acquires. thread->store_buffer_block_ = thread->isolate()->store_buffer()->PopEmptyBlock(); } void Thread::StoreBufferBlockProcess(StoreBuffer::ThresholdPolicy policy) { StoreBufferRelease(policy); StoreBufferAcquire(); } void Thread::StoreBufferAddObject(RawObject* obj) { store_buffer_block_->Push(obj); if (store_buffer_block_->IsFull()) { StoreBufferBlockProcess(StoreBuffer::kCheckThreshold); } } void Thread::StoreBufferAddObjectGC(RawObject* obj) { store_buffer_block_->Push(obj); if (store_buffer_block_->IsFull()) { StoreBufferBlockProcess(StoreBuffer::kIgnoreThreshold); } } void Thread::StoreBufferRelease(StoreBuffer::ThresholdPolicy policy) { StoreBufferBlock* block = store_buffer_block_; store_buffer_block_ = NULL; isolate_->store_buffer()->PushBlock(block, policy); } void Thread::StoreBufferAcquire() { store_buffer_block_ = isolate()->store_buffer()->PopNonFullBlock(); } bool Thread::IsMutatorThread() const { return ((isolate_ != NULL) && (isolate_->mutator_thread() == this)); } bool Thread::IsExecutingDartCode() const { return (top_exit_frame_info() == 0) && (vm_tag() == VMTag::kDartTagId); } bool Thread::HasExitedDartCode() const { return (top_exit_frame_info() != 0) && (vm_tag() != VMTag::kDartTagId); } template C* Thread::AllocateReusableHandle() { C* handle = reinterpret_cast(reusable_handles_.AllocateScopedHandle()); C::initializeHandle(handle, C::null()); return handle; } void Thread::ClearReusableHandles() { #define CLEAR_REUSABLE_HANDLE(object) \ *object##_handle_ = object::null(); REUSABLE_HANDLE_LIST(CLEAR_REUSABLE_HANDLE) #undef CLEAR_REUSABLE_HANDLE } void Thread::VisitObjectPointers(ObjectPointerVisitor* visitor) { ASSERT(visitor != NULL); // Visit objects in thread specific handles area. reusable_handles_.VisitObjectPointers(visitor); if (pending_functions_ != GrowableObjectArray::null()) { visitor->VisitPointer( reinterpret_cast(&pending_functions_)); } } 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; } bool Thread::ObjectAtOffset(intptr_t offset, Object* object) { #define COMPUTE_OFFSET(type_name, member_name, expr, default_init_value) \ if (Thread::member_name##offset() == offset) { \ *object = expr; \ return true; \ } CACHED_VM_OBJECTS_LIST(COMPUTE_OFFSET) #undef COMPUTE_OFFSET return false; } intptr_t Thread::OffsetFromThread(const RuntimeEntry* runtime_entry) { #define COMPUTE_OFFSET(name) \ if (runtime_entry->function() == k##name##RuntimeEntry.function()) { \ return Thread::name##_entry_point_offset(); \ } RUNTIME_ENTRY_LIST(COMPUTE_OFFSET) #undef COMPUTE_OFFSET #define COMPUTE_OFFSET(returntype, name, ...) \ if (runtime_entry->function() == k##name##RuntimeEntry.function()) { \ return Thread::name##_entry_point_offset(); \ } LEAF_RUNTIME_ENTRY_LIST(COMPUTE_OFFSET) #undef COMPUTE_OFFSET UNREACHABLE(); return -1; } DisableThreadInterruptsScope::DisableThreadInterruptsScope(Thread* thread) : StackResource(thread) { if (thread != NULL) { OSThread* os_thread = thread->os_thread(); ASSERT(os_thread != NULL); os_thread->DisableThreadInterrupts(); } } DisableThreadInterruptsScope::~DisableThreadInterruptsScope() { if (thread() != NULL) { OSThread* os_thread = thread()->os_thread(); ASSERT(os_thread != NULL); os_thread->EnableThreadInterrupts(); } } } // namespace dart