9e19d236ca
fields in a thread (i.e fields that are not Dart VM related) - Split the Thread structure to be a pure Dart per thread structure and add a pointer to os_thread which points to the OSThread structure - Change Schedule/UnSchedule to set the Dart Thread structure as the TLS of the thread when it is inside the Dart world and reset the TLS back to the OSThread strcuture when is exits the Dart World. - Moved the stack_base and few stack size related functions to OSThread from Isolate R=johnmccutchan@google.com, zra@google.com Review URL: https://codereview.chromium.org/1439483003 .
355 lines
11 KiB
C++
355 lines
11 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/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<object>();
|
|
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<class C>
|
|
C* Thread::AllocateReusableHandle() {
|
|
C* handle = reinterpret_cast<C*>(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<RawObject**>(&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
|