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