Files
sdk/runtime/vm/thread.cc
T
Daniel Andersson 868d2c6c3e The sweeper must not be running during isolate shutdown.
In release mode, there seems to be nothing to prevent this.
In debug mode, the "Verify" call waits for the sweeper, but there is still a race between the task count update and the ExitIsolateAsHelper call, which could cause problems.

Fix both of these, and add more assertions and verbose error messages.

- make sweeper task cleanly exit isolate *before* notifying
- wait for sweeper before shutting down isolate
- verbose pthread failures

BUG=
R=asiva@google.com

Review URL: https://codereview.chromium.org//1233563004 .
2015-07-13 17:49:49 -07:00

262 lines
7.1 KiB
C++

// 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->mutator_thread() == NULL);
thread->isolate_ = isolate;
isolate->set_mutator_thread(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();
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->set_mutator_thread(NULL);
thread->isolate_ = NULL;
ASSERT(Isolate::Current() == NULL);
}
void Thread::EnterIsolateAsHelper(Isolate* isolate) {
Thread* thread = Thread::Current();
ASSERT(thread != NULL);
ASSERT(thread->isolate() == NULL);
thread->isolate_ = isolate;
// Do not update isolate->mutator_thread, but perform sanity check:
// this thread should not be both the main mutator and helper.
ASSERT(isolate->mutator_thread() != thread);
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;
ASSERT(isolate->mutator_thread() != thread);
}
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