mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
f97e11f624
The safestack stack pointer is cached when invoking Dart code, and manually restored when jumping over C++ frames for Dart exceptions in Exceptions::JumpToFrame(). fixes #31356 Change-Id: I71c2e86d1d4f24571dd618a5db06fd1277339ebc Reviewed-on: https://dart-review.googlesource.com/23141 Commit-Queue: Zach Anderson <zra@google.com> Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
850 lines
26 KiB
C++
850 lines
26 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 "platform/address_sanitizer.h"
|
|
#include "platform/safe_stack.h"
|
|
#include "vm/compiler_stats.h"
|
|
#include "vm/dart_api_state.h"
|
|
#include "vm/growable_array.h"
|
|
#include "vm/isolate.h"
|
|
#include "vm/json_stream.h"
|
|
#include "vm/lockers.h"
|
|
#include "vm/log.h"
|
|
#include "vm/message_handler.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"
|
|
#include "vm/timeline.h"
|
|
#include "vm/zone.h"
|
|
|
|
namespace dart {
|
|
|
|
DECLARE_FLAG(bool, trace_service);
|
|
DECLARE_FLAG(bool, trace_service_verbose);
|
|
|
|
Thread::~Thread() {
|
|
// We should cleanly exit any isolate before destruction.
|
|
ASSERT(isolate_ == NULL);
|
|
if (compiler_stats_ != NULL) {
|
|
delete compiler_stats_;
|
|
compiler_stats_ = NULL;
|
|
}
|
|
// There should be no top api scopes at this point.
|
|
ASSERT(api_top_scope() == NULL);
|
|
// Delete the resusable api scope if there is one.
|
|
if (api_reusable_scope_) {
|
|
delete api_reusable_scope_;
|
|
api_reusable_scope_ = NULL;
|
|
}
|
|
delete thread_lock_;
|
|
thread_lock_ = 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),
|
|
stack_limit_(0),
|
|
stack_overflow_flags_(0),
|
|
isolate_(NULL),
|
|
heap_(NULL),
|
|
top_(0),
|
|
end_(0),
|
|
top_exit_frame_info_(0),
|
|
store_buffer_block_(NULL),
|
|
vm_tag_(0),
|
|
task_kind_(kUnknownTask),
|
|
async_stack_trace_(StackTrace::null()),
|
|
dart_stream_(NULL),
|
|
os_thread_(NULL),
|
|
thread_lock_(new Monitor()),
|
|
zone_(NULL),
|
|
current_zone_capacity_(0),
|
|
zone_high_watermark_(0),
|
|
api_reusable_scope_(NULL),
|
|
api_top_scope_(NULL),
|
|
top_resource_(NULL),
|
|
long_jump_base_(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_(),
|
|
saved_stack_limit_(0),
|
|
defer_oob_messages_count_(0),
|
|
deferred_interrupts_mask_(0),
|
|
deferred_interrupts_(0),
|
|
stack_overflow_count_(0),
|
|
cha_(NULL),
|
|
deopt_id_(0),
|
|
pending_functions_(GrowableObjectArray::null()),
|
|
active_exception_(Object::null()),
|
|
active_stacktrace_(Object::null()),
|
|
resume_pc_(0),
|
|
sticky_error_(Error::null()),
|
|
compiler_stats_(NULL),
|
|
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_INITIALIZERS)
|
|
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_INIT) safepoint_state_(0),
|
|
execution_state_(kThreadInNative),
|
|
#if defined(USING_SAFE_STACK)
|
|
saved_safestack_limit_(0),
|
|
#endif
|
|
next_(NULL) {
|
|
#if !defined(PRODUCT)
|
|
dart_stream_ = Timeline::GetDartStream();
|
|
ASSERT(dart_stream_ != NULL);
|
|
#endif
|
|
#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();
|
|
}
|
|
|
|
if (FLAG_support_compiler_stats) {
|
|
compiler_stats_ = new CompilerStats(isolate);
|
|
if (FLAG_compiler_benchmark) {
|
|
compiler_stats_->EnableBenchmark();
|
|
}
|
|
}
|
|
// This thread should not yet own any zones. If it does, we need to make sure
|
|
// we've accounted for any memory it has already allocated.
|
|
if (zone_ == NULL) {
|
|
ASSERT(current_zone_capacity_ == 0);
|
|
} else {
|
|
Zone* current = zone_;
|
|
uintptr_t total_zone_capacity = 0;
|
|
while (current != NULL) {
|
|
total_zone_capacity += current->CapacityInBytes();
|
|
current = current->previous();
|
|
}
|
|
ASSERT(current_zone_capacity_ == total_zone_capacity);
|
|
}
|
|
}
|
|
|
|
static const struct ALIGN16 {
|
|
uint64_t a;
|
|
uint64_t b;
|
|
} double_negate_constant = {0x8000000000000000LL, 0x8000000000000000LL};
|
|
|
|
static const struct ALIGN16 {
|
|
uint64_t a;
|
|
uint64_t b;
|
|
} double_abs_constant = {0x7FFFFFFFFFFFFFFFLL, 0x7FFFFFFFFFFFFFFFLL};
|
|
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} float_not_constant = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF};
|
|
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} float_negate_constant = {0x80000000, 0x80000000, 0x80000000, 0x80000000};
|
|
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} float_absolute_constant = {0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF};
|
|
|
|
static const struct ALIGN16 {
|
|
uint32_t a;
|
|
uint32_t b;
|
|
uint32_t c;
|
|
uint32_t d;
|
|
} float_zerow_constant = {0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000};
|
|
|
|
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
|
|
}
|
|
|
|
#ifndef PRODUCT
|
|
// Collect information about each individual zone associated with this thread.
|
|
void Thread::PrintJSON(JSONStream* stream) const {
|
|
JSONObject jsobj(stream);
|
|
jsobj.AddProperty("type", "_Thread");
|
|
jsobj.AddPropertyF("id", "threads/%" Pd "",
|
|
OSThread::ThreadIdToIntPtr(os_thread()->trace_id()));
|
|
jsobj.AddProperty("kind", TaskKindToCString(task_kind()));
|
|
jsobj.AddPropertyF("_zoneHighWatermark", "%" Pu "", zone_high_watermark_);
|
|
jsobj.AddPropertyF("_zoneCapacity", "%" Pu "", current_zone_capacity_);
|
|
}
|
|
#endif
|
|
|
|
RawGrowableObjectArray* Thread::pending_functions() {
|
|
if (pending_functions_ == GrowableObjectArray::null()) {
|
|
pending_functions_ = GrowableObjectArray::New(Heap::kOld);
|
|
}
|
|
return pending_functions_;
|
|
}
|
|
|
|
void Thread::clear_pending_functions() {
|
|
pending_functions_ = GrowableObjectArray::null();
|
|
}
|
|
|
|
void Thread::set_active_exception(const Object& value) {
|
|
ASSERT(!value.IsNull());
|
|
active_exception_ = value.raw();
|
|
}
|
|
|
|
void Thread::set_active_stacktrace(const Object& value) {
|
|
active_stacktrace_ = value.raw();
|
|
}
|
|
|
|
RawError* Thread::sticky_error() const {
|
|
return sticky_error_;
|
|
}
|
|
|
|
void Thread::set_sticky_error(const Error& value) {
|
|
ASSERT(!value.IsNull());
|
|
sticky_error_ = value.raw();
|
|
}
|
|
|
|
void Thread::clear_sticky_error() {
|
|
sticky_error_ = Error::null();
|
|
}
|
|
|
|
RawError* Thread::get_and_clear_sticky_error() {
|
|
NoSafepointScope nss;
|
|
RawError* return_value = sticky_error_;
|
|
sticky_error_ = Error::null();
|
|
return return_value;
|
|
}
|
|
|
|
const char* Thread::TaskKindToCString(TaskKind kind) {
|
|
switch (kind) {
|
|
case kUnknownTask:
|
|
return "kUnknownTask";
|
|
case kMutatorTask:
|
|
return "kMutatorTask";
|
|
case kCompilerTask:
|
|
return "kCompilerTask";
|
|
case kSweeperTask:
|
|
return "kSweeperTask";
|
|
case kMarkerTask:
|
|
return "kMarkerTask";
|
|
default:
|
|
UNREACHABLE();
|
|
return "";
|
|
}
|
|
}
|
|
|
|
RawStackTrace* Thread::async_stack_trace() const {
|
|
return async_stack_trace_;
|
|
}
|
|
|
|
void Thread::set_async_stack_trace(const StackTrace& stack_trace) {
|
|
ASSERT(!stack_trace.IsNull());
|
|
async_stack_trace_ = stack_trace.raw();
|
|
}
|
|
|
|
void Thread::set_raw_async_stack_trace(RawStackTrace* raw_stack_trace) {
|
|
async_stack_trace_ = raw_stack_trace;
|
|
}
|
|
|
|
void Thread::clear_async_stack_trace() {
|
|
async_stack_trace_ = StackTrace::null();
|
|
}
|
|
|
|
bool Thread::EnterIsolate(Isolate* isolate) {
|
|
const bool kIsMutatorThread = true;
|
|
Thread* thread = isolate->ScheduleThread(kIsMutatorThread);
|
|
if (thread != NULL) {
|
|
ASSERT(thread->store_buffer_block_ == NULL);
|
|
thread->task_kind_ = kMutatorTask;
|
|
thread->StoreBufferAcquire();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Thread::ExitIsolate() {
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread != NULL && thread->IsMutatorThread());
|
|
DEBUG_ASSERT(!thread->IsAnyReusableHandleScopeActive());
|
|
thread->task_kind_ = kUnknownTask;
|
|
Isolate* isolate = thread->isolate();
|
|
ASSERT(isolate != NULL);
|
|
ASSERT(thread->execution_state() == Thread::kThreadInVM);
|
|
// Clear since GC will not visit the thread once it is unscheduled.
|
|
thread->ClearReusableHandles();
|
|
thread->StoreBufferRelease();
|
|
if (isolate->is_runnable()) {
|
|
thread->set_vm_tag(VMTag::kIdleTagId);
|
|
} else {
|
|
thread->set_vm_tag(VMTag::kLoadWaitTagId);
|
|
}
|
|
const bool kIsMutatorThread = true;
|
|
isolate->UnscheduleThread(thread, kIsMutatorThread);
|
|
}
|
|
|
|
bool Thread::EnterIsolateAsHelper(Isolate* isolate,
|
|
TaskKind kind,
|
|
bool bypass_safepoint) {
|
|
ASSERT(kind != kMutatorTask);
|
|
const bool kIsNotMutatorThread = false;
|
|
Thread* thread =
|
|
isolate->ScheduleThread(kIsNotMutatorThread, bypass_safepoint);
|
|
if (thread != NULL) {
|
|
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.
|
|
thread->task_kind_ = kind;
|
|
ASSERT(!thread->IsMutatorThread());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Thread::ExitIsolateAsHelper(bool bypass_safepoint) {
|
|
Thread* thread = Thread::Current();
|
|
ASSERT(thread != NULL);
|
|
ASSERT(!thread->IsMutatorThread());
|
|
ASSERT(thread->execution_state() == Thread::kThreadInVM);
|
|
thread->task_kind_ = kUnknownTask;
|
|
// Clear since GC will not visit the thread once it is unscheduled.
|
|
thread->ClearReusableHandles();
|
|
thread->StoreBufferRelease();
|
|
Isolate* isolate = thread->isolate();
|
|
ASSERT(isolate != NULL);
|
|
const bool kIsNotMutatorThread = false;
|
|
isolate->UnscheduleThread(thread, kIsNotMutatorThread, bypass_safepoint);
|
|
}
|
|
|
|
void Thread::PrepareForGC() {
|
|
ASSERT(IsAtSafepoint());
|
|
// Prevent scheduling another GC by ignoring the threshold.
|
|
ASSERT(store_buffer_block_ != NULL);
|
|
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.
|
|
store_buffer_block_ = isolate()->store_buffer()->PopEmptyBlock();
|
|
}
|
|
|
|
void Thread::SetStackLimitFromStackBase(uword stack_base) {
|
|
#if defined(USING_SIMULATOR)
|
|
SetStackLimit(Simulator::Current()->stack_limit());
|
|
#else
|
|
SetStackLimit(OSThread::Current()->stack_limit_with_headroom());
|
|
#endif
|
|
}
|
|
|
|
void Thread::SetStackLimit(uword limit) {
|
|
// The thread setting the stack limit is not necessarily the thread which
|
|
// the stack limit is being set on.
|
|
MonitorLocker ml(thread_lock_);
|
|
if (stack_limit_ == saved_stack_limit_) {
|
|
// No interrupt pending, set stack_limit_ too.
|
|
stack_limit_ = limit;
|
|
}
|
|
saved_stack_limit_ = limit;
|
|
}
|
|
|
|
void Thread::ClearStackLimit() {
|
|
SetStackLimit(~static_cast<uword>(0));
|
|
}
|
|
|
|
// Disable AdressSanitizer and SafeStack transformation on this function. In
|
|
// particular, taking the address of a local gives an address on the stack
|
|
// instead of an address in the shadow memory (AddressSanitizer) or the safe
|
|
// stack (SafeStack).
|
|
NO_SANITIZE_ADDRESS
|
|
NO_SANITIZE_SAFE_STACK
|
|
uword Thread::GetCurrentStackPointer() {
|
|
uword stack_allocated_local = reinterpret_cast<uword>(&stack_allocated_local);
|
|
return stack_allocated_local;
|
|
}
|
|
|
|
void Thread::ScheduleInterrupts(uword interrupt_bits) {
|
|
MonitorLocker ml(thread_lock_);
|
|
ScheduleInterruptsLocked(interrupt_bits);
|
|
}
|
|
|
|
void Thread::ScheduleInterruptsLocked(uword interrupt_bits) {
|
|
ASSERT(thread_lock_->IsOwnedByCurrentThread());
|
|
ASSERT((interrupt_bits & ~kInterruptsMask) == 0); // Must fit in mask.
|
|
|
|
// Check to see if any of the requested interrupts should be deferred.
|
|
uword defer_bits = interrupt_bits & deferred_interrupts_mask_;
|
|
if (defer_bits != 0) {
|
|
deferred_interrupts_ |= defer_bits;
|
|
interrupt_bits &= ~deferred_interrupts_mask_;
|
|
if (interrupt_bits == 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (stack_limit_ == saved_stack_limit_) {
|
|
stack_limit_ = kInterruptStackLimit & ~kInterruptsMask;
|
|
}
|
|
stack_limit_ |= interrupt_bits;
|
|
}
|
|
|
|
uword Thread::GetAndClearInterrupts() {
|
|
MonitorLocker ml(thread_lock_);
|
|
if (stack_limit_ == saved_stack_limit_) {
|
|
return 0; // No interrupt was requested.
|
|
}
|
|
uword interrupt_bits = stack_limit_ & kInterruptsMask;
|
|
stack_limit_ = saved_stack_limit_;
|
|
return interrupt_bits;
|
|
}
|
|
|
|
bool Thread::ZoneIsOwnedByThread(Zone* zone) const {
|
|
ASSERT(zone != NULL);
|
|
Zone* current = zone_;
|
|
while (current != NULL) {
|
|
if (current == zone) {
|
|
return true;
|
|
}
|
|
current = current->previous();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Thread::DeferOOBMessageInterrupts() {
|
|
MonitorLocker ml(thread_lock_);
|
|
defer_oob_messages_count_++;
|
|
if (defer_oob_messages_count_ > 1) {
|
|
// OOB message interrupts are already deferred.
|
|
return;
|
|
}
|
|
ASSERT(deferred_interrupts_mask_ == 0);
|
|
deferred_interrupts_mask_ = kMessageInterrupt;
|
|
|
|
if (stack_limit_ != saved_stack_limit_) {
|
|
// Defer any interrupts which are currently pending.
|
|
deferred_interrupts_ = stack_limit_ & deferred_interrupts_mask_;
|
|
|
|
// Clear deferrable interrupts, if present.
|
|
stack_limit_ &= ~deferred_interrupts_mask_;
|
|
|
|
if ((stack_limit_ & kInterruptsMask) == 0) {
|
|
// No other pending interrupts. Restore normal stack limit.
|
|
stack_limit_ = saved_stack_limit_;
|
|
}
|
|
}
|
|
if (FLAG_trace_service && FLAG_trace_service_verbose) {
|
|
OS::PrintErr("[+%" Pd64 "ms] Isolate %s deferring OOB interrupts\n",
|
|
Dart::UptimeMillis(), isolate()->name());
|
|
}
|
|
}
|
|
|
|
void Thread::RestoreOOBMessageInterrupts() {
|
|
MonitorLocker ml(thread_lock_);
|
|
defer_oob_messages_count_--;
|
|
if (defer_oob_messages_count_ > 0) {
|
|
return;
|
|
}
|
|
ASSERT(defer_oob_messages_count_ == 0);
|
|
ASSERT(deferred_interrupts_mask_ == kMessageInterrupt);
|
|
deferred_interrupts_mask_ = 0;
|
|
if (deferred_interrupts_ != 0) {
|
|
if (stack_limit_ == saved_stack_limit_) {
|
|
stack_limit_ = kInterruptStackLimit & ~kInterruptsMask;
|
|
}
|
|
stack_limit_ |= deferred_interrupts_;
|
|
deferred_interrupts_ = 0;
|
|
}
|
|
if (FLAG_trace_service && FLAG_trace_service_verbose) {
|
|
OS::PrintErr("[+%" Pd64 "ms] Isolate %s restoring OOB interrupts\n",
|
|
Dart::UptimeMillis(), isolate()->name());
|
|
}
|
|
}
|
|
|
|
RawError* Thread::HandleInterrupts() {
|
|
uword interrupt_bits = GetAndClearInterrupts();
|
|
if ((interrupt_bits & kVMInterrupt) != 0) {
|
|
if (isolate()->store_buffer()->Overflowed()) {
|
|
if (FLAG_verbose_gc) {
|
|
OS::PrintErr("Scavenge scheduled by store buffer overflow.\n");
|
|
}
|
|
heap()->CollectGarbage(Heap::kNew);
|
|
}
|
|
}
|
|
if ((interrupt_bits & kMessageInterrupt) != 0) {
|
|
MessageHandler::MessageStatus status =
|
|
isolate()->message_handler()->HandleOOBMessages();
|
|
if (status != MessageHandler::kOK) {
|
|
// False result from HandleOOBMessages signals that the isolate should
|
|
// be terminating.
|
|
if (FLAG_trace_isolates) {
|
|
OS::Print(
|
|
"[!] Terminating isolate due to OOB message:\n"
|
|
"\tisolate: %s\n",
|
|
isolate()->name());
|
|
}
|
|
Thread* thread = Thread::Current();
|
|
const Error& error = Error::Handle(thread->sticky_error());
|
|
ASSERT(!error.IsNull() && error.IsUnwindError());
|
|
thread->clear_sticky_error();
|
|
return error.raw();
|
|
}
|
|
}
|
|
return Error::null();
|
|
}
|
|
|
|
uword Thread::GetAndClearStackOverflowFlags() {
|
|
uword stack_overflow_flags = stack_overflow_flags_;
|
|
stack_overflow_flags_ = 0;
|
|
return stack_overflow_flags;
|
|
}
|
|
|
|
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::CanCollectGarbage() const {
|
|
// We grow the heap instead of triggering a garbage collection when a
|
|
// thread is at a safepoint in the following situations :
|
|
// - background compiler thread finalizing and installing code
|
|
// - disassembly of the generated code is done after compilation
|
|
// So essentially we state that garbage collection is possible only
|
|
// when we are not at a safepoint.
|
|
return !IsAtSafepoint();
|
|
}
|
|
|
|
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,
|
|
bool validate_frames) {
|
|
ASSERT(visitor != NULL);
|
|
|
|
if (zone_ != NULL) {
|
|
zone_->VisitObjectPointers(visitor);
|
|
}
|
|
|
|
// Visit objects in thread specific handles area.
|
|
reusable_handles_.VisitObjectPointers(visitor);
|
|
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&pending_functions_));
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&active_exception_));
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&active_stacktrace_));
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&sticky_error_));
|
|
visitor->VisitPointer(reinterpret_cast<RawObject**>(&async_stack_trace_));
|
|
|
|
// Visit the api local scope as it has all the api local handles.
|
|
ApiLocalScope* scope = api_top_scope_;
|
|
while (scope != NULL) {
|
|
scope->local_handles()->VisitObjectPointers(visitor);
|
|
scope = scope->previous();
|
|
}
|
|
|
|
// The MarkTask, which calls this method, can run on a different thread. We
|
|
// therefore assume the mutator is at a safepoint and we can iterate it's
|
|
// stack.
|
|
// TODO(vm-team): It would be beneficial to be able to ask the mutator thread
|
|
// whether it is in fact blocked at the moment (at a "safepoint") so we can
|
|
// safely iterate it's stack.
|
|
//
|
|
// Unfortunately we cannot use `this->IsAtSafepoint()` here because that will
|
|
// return `false` even though the mutator thread is waiting for mark tasks
|
|
// (which iterate it's stack) to finish.
|
|
const StackFrameIterator::CrossThreadPolicy cross_thread_policy =
|
|
StackFrameIterator::kAllowCrossThreadIteration;
|
|
|
|
const StackFrameIterator::ValidationPolicy validation_policy =
|
|
validate_frames ? StackFrameIterator::kValidateFrames
|
|
: StackFrameIterator::kDontValidateFrames;
|
|
|
|
// Iterate over all the stack frames and visit objects on the stack.
|
|
StackFrameIterator frames_iterator(top_exit_frame_info(), validation_policy,
|
|
this, cross_thread_policy);
|
|
StackFrame* frame = frames_iterator.NextFrame();
|
|
while (frame != NULL) {
|
|
frame->VisitObjectPointers(visitor);
|
|
frame = frames_iterator.NextFrame();
|
|
}
|
|
}
|
|
|
|
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) {
|
|
if (Isolate::Current() == Dart::vm_isolate()) {
|
|
// --disassemble-stubs runs before all the references through
|
|
// thread have targets
|
|
return false;
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
bool Thread::IsValidHandle(Dart_Handle object) const {
|
|
return IsValidLocalHandle(object) || IsValidZoneHandle(object) ||
|
|
IsValidScopedHandle(object);
|
|
}
|
|
|
|
bool Thread::IsValidLocalHandle(Dart_Handle object) const {
|
|
ApiLocalScope* scope = api_top_scope_;
|
|
while (scope != NULL) {
|
|
if (scope->local_handles()->IsValidHandle(object)) {
|
|
return true;
|
|
}
|
|
scope = scope->previous();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
intptr_t Thread::CountLocalHandles() const {
|
|
intptr_t total = 0;
|
|
ApiLocalScope* scope = api_top_scope_;
|
|
while (scope != NULL) {
|
|
total += scope->local_handles()->CountHandles();
|
|
scope = scope->previous();
|
|
}
|
|
return total;
|
|
}
|
|
|
|
bool Thread::IsValidZoneHandle(Dart_Handle object) const {
|
|
Zone* zone = zone_;
|
|
while (zone != NULL) {
|
|
if (zone->handles()->IsValidZoneHandle(reinterpret_cast<uword>(object))) {
|
|
return true;
|
|
}
|
|
zone = zone->previous();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
intptr_t Thread::CountZoneHandles() const {
|
|
intptr_t count = 0;
|
|
Zone* zone = zone_;
|
|
while (zone != NULL) {
|
|
count += zone->handles()->CountZoneHandles();
|
|
zone = zone->previous();
|
|
}
|
|
ASSERT(count >= 0);
|
|
return count;
|
|
}
|
|
|
|
bool Thread::IsValidScopedHandle(Dart_Handle object) const {
|
|
Zone* zone = zone_;
|
|
while (zone != NULL) {
|
|
if (zone->handles()->IsValidScopedHandle(reinterpret_cast<uword>(object))) {
|
|
return true;
|
|
}
|
|
zone = zone->previous();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
intptr_t Thread::CountScopedHandles() const {
|
|
intptr_t count = 0;
|
|
Zone* zone = zone_;
|
|
while (zone != NULL) {
|
|
count += zone->handles()->CountScopedHandles();
|
|
zone = zone->previous();
|
|
}
|
|
ASSERT(count >= 0);
|
|
return count;
|
|
}
|
|
|
|
int Thread::ZoneSizeInBytes() const {
|
|
int total = 0;
|
|
ApiLocalScope* scope = api_top_scope_;
|
|
while (scope != NULL) {
|
|
total += scope->zone()->SizeInBytes();
|
|
scope = scope->previous();
|
|
}
|
|
return total;
|
|
}
|
|
|
|
void Thread::UnwindScopes(uword stack_marker) {
|
|
// Unwind all scopes using the same stack_marker, i.e. all scopes allocated
|
|
// under the same top_exit_frame_info.
|
|
ApiLocalScope* scope = api_top_scope_;
|
|
while (scope != NULL && scope->stack_marker() != 0 &&
|
|
scope->stack_marker() == stack_marker) {
|
|
api_top_scope_ = scope->previous();
|
|
delete scope;
|
|
scope = api_top_scope_;
|
|
}
|
|
}
|
|
|
|
void Thread::EnterSafepointUsingLock() {
|
|
isolate()->safepoint_handler()->EnterSafepointUsingLock(this);
|
|
}
|
|
|
|
void Thread::ExitSafepointUsingLock() {
|
|
isolate()->safepoint_handler()->ExitSafepointUsingLock(this);
|
|
}
|
|
|
|
void Thread::BlockForSafepoint() {
|
|
isolate()->safepoint_handler()->BlockForSafepoint(this);
|
|
}
|
|
|
|
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
|