3b8cbf8bfa
They can be loaded via the THR register. In particular, do this for null, true, false, and the entry point of the UpdateStoreBufferStub. This will help supporting precompilation and reduce memory usage from constant pools (assuming there are more constant pools than threads). R=srdjan@google.com Review URL: https://codereview.chromium.org//1225933002.
226 lines
6.2 KiB
C++
226 lines
6.2 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/isolate.h"
|
|
#include "vm/object.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/profiler.h"
|
|
#include "vm/stub_code.h"
|
|
#include "vm/thread_interrupter.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);
|
|
}
|
|
|
|
|
|
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) {
|
|
#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 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::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();
|
|
}
|
|
|
|
|
|
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();
|
|
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);
|
|
}
|
|
|
|
|
|
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->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) \
|
|
if (object.raw() == expr) return Thread::member_name##offset();
|
|
CACHED_VM_OBJECTS_LIST(COMPUTE_OFFSET)
|
|
#undef COMPUTE_OFFSET
|
|
UNREACHABLE();
|
|
return -1;
|
|
}
|
|
|
|
} // namespace dart
|