4024151adb
This is a fixed version of c84f30741c90d040254767ff769a40d2cba3fb1a that resolves issues with comparing uint and intptr_t. Original Commit Message: Added tracking of memory usage inside of threads. In addition, the max memory usage is kept track of using a high watermark for both the threads and the isolates. Isolate high watermark information is updated when a thread exits the isolate. The isolate high watermark consists of the sum of all thread high watermarks (including the high watermark of the exiting thread). High watermark information for both threads and isolates is now visible in the isolate view in the Observatory. BUG= R=johnmccutchan@google.com Review-Url: https://codereview.chromium.org/2610253002 .
807 lines
28 KiB
C++
807 lines
28 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.
|
|
|
|
#ifndef RUNTIME_VM_THREAD_H_
|
|
#define RUNTIME_VM_THREAD_H_
|
|
|
|
#include "include/dart_api.h"
|
|
#include "platform/assert.h"
|
|
#include "vm/atomic.h"
|
|
#include "vm/bitfield.h"
|
|
#include "vm/globals.h"
|
|
#include "vm/handles.h"
|
|
#include "vm/os_thread.h"
|
|
#include "vm/store_buffer.h"
|
|
#include "vm/runtime_entry_list.h"
|
|
|
|
namespace dart {
|
|
|
|
class AbstractType;
|
|
class ApiLocalScope;
|
|
class Array;
|
|
class CHA;
|
|
class Class;
|
|
class Code;
|
|
class CompilerStats;
|
|
class Error;
|
|
class ExceptionHandlers;
|
|
class Field;
|
|
class Function;
|
|
class GrowableObjectArray;
|
|
class HandleScope;
|
|
class Heap;
|
|
class Instance;
|
|
class Isolate;
|
|
class Library;
|
|
class LongJumpScope;
|
|
class Object;
|
|
class OSThread;
|
|
class JSONObject;
|
|
class PcDescriptors;
|
|
class RawBool;
|
|
class RawObject;
|
|
class RawCode;
|
|
class RawError;
|
|
class RawGrowableObjectArray;
|
|
class RawString;
|
|
class RuntimeEntry;
|
|
class Smi;
|
|
class StackResource;
|
|
class String;
|
|
class TimelineStream;
|
|
class TypeArguments;
|
|
class TypeParameter;
|
|
class Zone;
|
|
|
|
#define REUSABLE_HANDLE_LIST(V) \
|
|
V(AbstractType) \
|
|
V(Array) \
|
|
V(Class) \
|
|
V(Code) \
|
|
V(Error) \
|
|
V(ExceptionHandlers) \
|
|
V(Field) \
|
|
V(Function) \
|
|
V(GrowableObjectArray) \
|
|
V(Instance) \
|
|
V(Library) \
|
|
V(Object) \
|
|
V(PcDescriptors) \
|
|
V(Smi) \
|
|
V(String) \
|
|
V(TypeArguments) \
|
|
V(TypeParameter)
|
|
|
|
|
|
#if defined(TARGET_ARCH_DBC)
|
|
#define CACHED_VM_STUBS_LIST(V)
|
|
#else
|
|
#define CACHED_VM_STUBS_LIST(V) \
|
|
V(RawCode*, update_store_buffer_code_, \
|
|
StubCode::UpdateStoreBuffer_entry()->code(), NULL) \
|
|
V(RawCode*, fix_callers_target_code_, \
|
|
StubCode::FixCallersTarget_entry()->code(), NULL) \
|
|
V(RawCode*, fix_allocation_stub_code_, \
|
|
StubCode::FixAllocationStubTarget_entry()->code(), NULL) \
|
|
V(RawCode*, invoke_dart_code_stub_, \
|
|
StubCode::InvokeDartCode_entry()->code(), NULL) \
|
|
V(RawCode*, call_to_runtime_stub_, StubCode::CallToRuntime_entry()->code(), \
|
|
NULL) \
|
|
V(RawCode*, monomorphic_miss_stub_, \
|
|
StubCode::MonomorphicMiss_entry()->code(), NULL) \
|
|
V(RawCode*, ic_lookup_through_code_stub_, \
|
|
StubCode::ICCallThroughCode_entry()->code(), NULL) \
|
|
V(RawCode*, lazy_deopt_from_return_stub_, \
|
|
StubCode::DeoptimizeLazyFromReturn_entry()->code(), NULL) \
|
|
V(RawCode*, lazy_deopt_from_throw_stub_, \
|
|
StubCode::DeoptimizeLazyFromThrow_entry()->code(), NULL)
|
|
|
|
#endif
|
|
|
|
// List of VM-global objects/addresses cached in each Thread object.
|
|
#define CACHED_VM_OBJECTS_LIST(V) \
|
|
V(RawObject*, object_null_, Object::null(), NULL) \
|
|
V(RawBool*, bool_true_, Object::bool_true().raw(), NULL) \
|
|
V(RawBool*, bool_false_, Object::bool_false().raw(), NULL) \
|
|
CACHED_VM_STUBS_LIST(V)
|
|
|
|
#if defined(TARGET_ARCH_DBC)
|
|
#define CACHED_VM_STUBS_ADDRESSES_LIST(V)
|
|
#else
|
|
#define CACHED_VM_STUBS_ADDRESSES_LIST(V) \
|
|
V(uword, update_store_buffer_entry_point_, \
|
|
StubCode::UpdateStoreBuffer_entry()->EntryPoint(), 0) \
|
|
V(uword, call_to_runtime_entry_point_, \
|
|
StubCode::CallToRuntime_entry()->EntryPoint(), 0) \
|
|
V(uword, megamorphic_call_checked_entry_, \
|
|
StubCode::MegamorphicCall_entry()->EntryPoint(), 0) \
|
|
V(uword, monomorphic_miss_entry_, \
|
|
StubCode::MonomorphicMiss_entry()->EntryPoint(), 0)
|
|
|
|
#endif
|
|
|
|
#define CACHED_ADDRESSES_LIST(V) \
|
|
CACHED_VM_STUBS_ADDRESSES_LIST(V) \
|
|
V(uword, native_call_wrapper_entry_point_, \
|
|
NativeEntry::NativeCallWrapperEntry(), 0) \
|
|
V(RawString**, predefined_symbols_address_, Symbols::PredefinedAddress(), \
|
|
NULL) \
|
|
V(uword, double_negate_address_, \
|
|
reinterpret_cast<uword>(&double_negate_constant), 0) \
|
|
V(uword, double_abs_address_, reinterpret_cast<uword>(&double_abs_constant), \
|
|
0) \
|
|
V(uword, float_not_address_, reinterpret_cast<uword>(&float_not_constant), \
|
|
0) \
|
|
V(uword, float_negate_address_, \
|
|
reinterpret_cast<uword>(&float_negate_constant), 0) \
|
|
V(uword, float_absolute_address_, \
|
|
reinterpret_cast<uword>(&float_absolute_constant), 0) \
|
|
V(uword, float_zerow_address_, \
|
|
reinterpret_cast<uword>(&float_zerow_constant), 0)
|
|
|
|
#define CACHED_CONSTANTS_LIST(V) \
|
|
CACHED_VM_OBJECTS_LIST(V) \
|
|
CACHED_ADDRESSES_LIST(V)
|
|
|
|
// A VM thread; may be executing Dart code or performing helper tasks like
|
|
// garbage collection or compilation. The Thread structure associated with
|
|
// a thread is allocated by EnsureInit before entering an isolate, and destroyed
|
|
// automatically when the underlying OS thread exits. NOTE: On Windows, CleanUp
|
|
// must currently be called manually (issue 23474).
|
|
class Thread : public BaseThread {
|
|
public:
|
|
// The kind of task this thread is performing. Sampled by the profiler.
|
|
enum TaskKind {
|
|
kUnknownTask = 0x0,
|
|
kMutatorTask = 0x1,
|
|
kCompilerTask = 0x2,
|
|
kSweeperTask = 0x4,
|
|
kMarkerTask = 0x8,
|
|
kFinalizerTask = 0x10,
|
|
};
|
|
// Converts a TaskKind to its corresponding C-String name.
|
|
static const char* TaskKindToCString(TaskKind kind);
|
|
|
|
~Thread();
|
|
|
|
// The currently executing thread, or NULL if not yet initialized.
|
|
static Thread* Current() {
|
|
BaseThread* thread = OSThread::GetCurrentTLS();
|
|
if (thread == NULL || thread->is_os_thread()) {
|
|
return NULL;
|
|
}
|
|
return reinterpret_cast<Thread*>(thread);
|
|
}
|
|
|
|
// Makes the current thread enter 'isolate'.
|
|
static bool EnterIsolate(Isolate* isolate);
|
|
// Makes the current thread exit its isolate.
|
|
static void ExitIsolate();
|
|
|
|
// A VM thread other than the main mutator thread can enter an isolate as a
|
|
// "helper" to gain limited concurrent access to the isolate. One example is
|
|
// SweeperTask (which uses the class table, which is copy-on-write).
|
|
// TODO(koda): Properly synchronize heap access to expand allowed operations.
|
|
static bool EnterIsolateAsHelper(Isolate* isolate,
|
|
TaskKind kind,
|
|
bool bypass_safepoint = false);
|
|
static void ExitIsolateAsHelper(bool bypass_safepoint = false);
|
|
|
|
// Empties the store buffer block into the isolate.
|
|
void PrepareForGC();
|
|
|
|
void SetStackLimit(uword value);
|
|
void SetStackLimitFromStackBase(uword stack_base);
|
|
void ClearStackLimit();
|
|
|
|
// Returns the current C++ stack pointer. Equivalent taking the address of a
|
|
// stack allocated local, but plays well with AddressSanitizer.
|
|
static uword GetCurrentStackPointer();
|
|
|
|
// Access to the current stack limit for generated code. This may be
|
|
// overwritten with a special value to trigger interrupts.
|
|
uword stack_limit_address() const {
|
|
return reinterpret_cast<uword>(&stack_limit_);
|
|
}
|
|
static intptr_t stack_limit_offset() {
|
|
return OFFSET_OF(Thread, stack_limit_);
|
|
}
|
|
|
|
// The true stack limit for this isolate.
|
|
uword saved_stack_limit() const { return saved_stack_limit_; }
|
|
|
|
#if defined(TARGET_ARCH_DBC)
|
|
// Access to the current stack limit for DBC interpreter.
|
|
uword stack_limit() const { return stack_limit_; }
|
|
#endif
|
|
|
|
// Stack overflow flags
|
|
enum {
|
|
kOsrRequest = 0x1, // Current stack overflow caused by OSR request.
|
|
};
|
|
|
|
uword stack_overflow_flags_address() const {
|
|
return reinterpret_cast<uword>(&stack_overflow_flags_);
|
|
}
|
|
static intptr_t stack_overflow_flags_offset() {
|
|
return OFFSET_OF(Thread, stack_overflow_flags_);
|
|
}
|
|
|
|
int32_t IncrementAndGetStackOverflowCount() {
|
|
return ++stack_overflow_count_;
|
|
}
|
|
|
|
TaskKind task_kind() const { return task_kind_; }
|
|
|
|
// Retrieves and clears the stack overflow flags. These are set by
|
|
// the generated code before the slow path runtime routine for a
|
|
// stack overflow is called.
|
|
uword GetAndClearStackOverflowFlags();
|
|
|
|
// Interrupt bits.
|
|
enum {
|
|
kVMInterrupt = 0x1, // Internal VM checks: safepoints, store buffers, etc.
|
|
kMessageInterrupt = 0x2, // An interrupt to process an out of band message.
|
|
|
|
kInterruptsMask = (kVMInterrupt | kMessageInterrupt),
|
|
};
|
|
|
|
void ScheduleInterrupts(uword interrupt_bits);
|
|
void ScheduleInterruptsLocked(uword interrupt_bits);
|
|
RawError* HandleInterrupts();
|
|
uword GetAndClearInterrupts();
|
|
|
|
// OSThread corresponding to this thread.
|
|
OSThread* os_thread() const { return os_thread_; }
|
|
void set_os_thread(OSThread* os_thread) { os_thread_ = os_thread; }
|
|
|
|
// Monitor corresponding to this thread.
|
|
Monitor* thread_lock() const { return thread_lock_; }
|
|
|
|
// The topmost zone used for allocation in this thread.
|
|
Zone* zone() const { return zone_; }
|
|
|
|
bool ZoneIsOwnedByThread(Zone* zone) const;
|
|
|
|
void IncrementMemoryUsage(uintptr_t value) {
|
|
current_thread_memory_ += value;
|
|
if (current_thread_memory_ > memory_high_watermark_) {
|
|
memory_high_watermark_ = current_thread_memory_;
|
|
}
|
|
}
|
|
|
|
void DecrementMemoryUsage(uintptr_t value) {
|
|
ASSERT(current_thread_memory_ >= value);
|
|
current_thread_memory_ -= value;
|
|
}
|
|
|
|
uintptr_t memory_high_watermark() const { return memory_high_watermark_; }
|
|
|
|
void ResetHighWatermark() { memory_high_watermark_ = current_thread_memory_; }
|
|
|
|
// The reusable api local scope for this thread.
|
|
ApiLocalScope* api_reusable_scope() const { return api_reusable_scope_; }
|
|
void set_api_reusable_scope(ApiLocalScope* value) {
|
|
ASSERT(value == NULL || api_reusable_scope_ == NULL);
|
|
api_reusable_scope_ = value;
|
|
}
|
|
|
|
// The api local scope for this thread, this where all local handles
|
|
// are allocated.
|
|
ApiLocalScope* api_top_scope() const { return api_top_scope_; }
|
|
void set_api_top_scope(ApiLocalScope* value) { api_top_scope_ = value; }
|
|
|
|
// The isolate that this thread is operating on, or NULL if none.
|
|
Isolate* isolate() const { return isolate_; }
|
|
static intptr_t isolate_offset() { return OFFSET_OF(Thread, isolate_); }
|
|
bool IsMutatorThread() const;
|
|
bool CanCollectGarbage() const;
|
|
|
|
// Offset of Dart TimelineStream object.
|
|
static intptr_t dart_stream_offset() {
|
|
return OFFSET_OF(Thread, dart_stream_);
|
|
}
|
|
|
|
// Is |this| executing Dart code?
|
|
bool IsExecutingDartCode() const;
|
|
|
|
// Has |this| exited Dart code?
|
|
bool HasExitedDartCode() const;
|
|
|
|
// The (topmost) CHA for the compilation in this thread.
|
|
CHA* cha() const {
|
|
ASSERT(isolate_ != NULL);
|
|
return cha_;
|
|
}
|
|
|
|
void set_cha(CHA* value) {
|
|
ASSERT(isolate_ != NULL);
|
|
cha_ = value;
|
|
}
|
|
|
|
int32_t no_callback_scope_depth() const { return no_callback_scope_depth_; }
|
|
|
|
void IncrementNoCallbackScopeDepth() {
|
|
ASSERT(no_callback_scope_depth_ < INT_MAX);
|
|
no_callback_scope_depth_ += 1;
|
|
}
|
|
|
|
void DecrementNoCallbackScopeDepth() {
|
|
ASSERT(no_callback_scope_depth_ > 0);
|
|
no_callback_scope_depth_ -= 1;
|
|
}
|
|
|
|
void StoreBufferAddObject(RawObject* obj);
|
|
void StoreBufferAddObjectGC(RawObject* obj);
|
|
#if defined(TESTING)
|
|
bool StoreBufferContains(RawObject* obj) const {
|
|
return store_buffer_block_->Contains(obj);
|
|
}
|
|
#endif
|
|
void StoreBufferBlockProcess(StoreBuffer::ThresholdPolicy policy);
|
|
static intptr_t store_buffer_block_offset() {
|
|
return OFFSET_OF(Thread, store_buffer_block_);
|
|
}
|
|
|
|
uword top_exit_frame_info() const { return top_exit_frame_info_; }
|
|
void set_top_exit_frame_info(uword top_exit_frame_info) {
|
|
top_exit_frame_info_ = top_exit_frame_info;
|
|
}
|
|
static intptr_t top_exit_frame_info_offset() {
|
|
return OFFSET_OF(Thread, top_exit_frame_info_);
|
|
}
|
|
|
|
StackResource* top_resource() const { return top_resource_; }
|
|
void set_top_resource(StackResource* value) { top_resource_ = value; }
|
|
static intptr_t top_resource_offset() {
|
|
return OFFSET_OF(Thread, top_resource_);
|
|
}
|
|
|
|
// Heap of the isolate that this thread is operating on.
|
|
Heap* heap() const { return heap_; }
|
|
static intptr_t heap_offset() { return OFFSET_OF(Thread, heap_); }
|
|
|
|
int32_t no_handle_scope_depth() const {
|
|
#if defined(DEBUG)
|
|
return no_handle_scope_depth_;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void IncrementNoHandleScopeDepth() {
|
|
#if defined(DEBUG)
|
|
ASSERT(no_handle_scope_depth_ < INT_MAX);
|
|
no_handle_scope_depth_ += 1;
|
|
#endif
|
|
}
|
|
|
|
void DecrementNoHandleScopeDepth() {
|
|
#if defined(DEBUG)
|
|
ASSERT(no_handle_scope_depth_ > 0);
|
|
no_handle_scope_depth_ -= 1;
|
|
#endif
|
|
}
|
|
|
|
HandleScope* top_handle_scope() const {
|
|
#if defined(DEBUG)
|
|
return top_handle_scope_;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void set_top_handle_scope(HandleScope* handle_scope) {
|
|
#if defined(DEBUG)
|
|
top_handle_scope_ = handle_scope;
|
|
#endif
|
|
}
|
|
|
|
int32_t no_safepoint_scope_depth() const {
|
|
#if defined(DEBUG)
|
|
return no_safepoint_scope_depth_;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
void IncrementNoSafepointScopeDepth() {
|
|
#if defined(DEBUG)
|
|
ASSERT(no_safepoint_scope_depth_ < INT_MAX);
|
|
no_safepoint_scope_depth_ += 1;
|
|
#endif
|
|
}
|
|
|
|
void DecrementNoSafepointScopeDepth() {
|
|
#if defined(DEBUG)
|
|
ASSERT(no_safepoint_scope_depth_ > 0);
|
|
no_safepoint_scope_depth_ -= 1;
|
|
#endif
|
|
}
|
|
|
|
#define DEFINE_OFFSET_METHOD(type_name, member_name, expr, default_init_value) \
|
|
static intptr_t member_name##offset() { \
|
|
return OFFSET_OF(Thread, member_name); \
|
|
}
|
|
CACHED_CONSTANTS_LIST(DEFINE_OFFSET_METHOD)
|
|
#undef DEFINE_OFFSET_METHOD
|
|
|
|
#define DEFINE_OFFSET_METHOD(name) \
|
|
static intptr_t name##_entry_point_offset() { \
|
|
return OFFSET_OF(Thread, name##_entry_point_); \
|
|
}
|
|
RUNTIME_ENTRY_LIST(DEFINE_OFFSET_METHOD)
|
|
#undef DEFINE_OFFSET_METHOD
|
|
|
|
#define DEFINE_OFFSET_METHOD(returntype, name, ...) \
|
|
static intptr_t name##_entry_point_offset() { \
|
|
return OFFSET_OF(Thread, name##_entry_point_); \
|
|
}
|
|
LEAF_RUNTIME_ENTRY_LIST(DEFINE_OFFSET_METHOD)
|
|
#undef DEFINE_OFFSET_METHOD
|
|
|
|
static bool CanLoadFromThread(const Object& object);
|
|
static intptr_t OffsetFromThread(const Object& object);
|
|
static bool ObjectAtOffset(intptr_t offset, Object* object);
|
|
static intptr_t OffsetFromThread(const RuntimeEntry* runtime_entry);
|
|
|
|
static const intptr_t kNoDeoptId = -1;
|
|
static const intptr_t kDeoptIdStep = 2;
|
|
static const intptr_t kDeoptIdBeforeOffset = 0;
|
|
static const intptr_t kDeoptIdAfterOffset = 1;
|
|
intptr_t deopt_id() const { return deopt_id_; }
|
|
void set_deopt_id(int value) {
|
|
ASSERT(value >= 0);
|
|
deopt_id_ = value;
|
|
}
|
|
intptr_t GetNextDeoptId() {
|
|
ASSERT(deopt_id_ != kNoDeoptId);
|
|
const intptr_t id = deopt_id_;
|
|
deopt_id_ += kDeoptIdStep;
|
|
return id;
|
|
}
|
|
|
|
static intptr_t ToDeoptAfter(intptr_t deopt_id) {
|
|
ASSERT(IsDeoptBefore(deopt_id));
|
|
return deopt_id + kDeoptIdAfterOffset;
|
|
}
|
|
|
|
static bool IsDeoptBefore(intptr_t deopt_id) {
|
|
return (deopt_id % kDeoptIdStep) == kDeoptIdBeforeOffset;
|
|
}
|
|
|
|
static bool IsDeoptAfter(intptr_t deopt_id) {
|
|
return (deopt_id % kDeoptIdStep) == kDeoptIdAfterOffset;
|
|
}
|
|
|
|
LongJumpScope* long_jump_base() const { return long_jump_base_; }
|
|
void set_long_jump_base(LongJumpScope* value) { long_jump_base_ = value; }
|
|
|
|
uword vm_tag() const { return vm_tag_; }
|
|
void set_vm_tag(uword tag) { vm_tag_ = tag; }
|
|
static intptr_t vm_tag_offset() { return OFFSET_OF(Thread, vm_tag_); }
|
|
|
|
RawGrowableObjectArray* pending_functions();
|
|
void clear_pending_functions();
|
|
|
|
RawObject* active_exception() const { return active_exception_; }
|
|
void set_active_exception(const Object& value);
|
|
static intptr_t active_exception_offset() {
|
|
return OFFSET_OF(Thread, active_exception_);
|
|
}
|
|
|
|
RawObject* active_stacktrace() const { return active_stacktrace_; }
|
|
void set_active_stacktrace(const Object& value);
|
|
static intptr_t active_stacktrace_offset() {
|
|
return OFFSET_OF(Thread, active_stacktrace_);
|
|
}
|
|
|
|
uword resume_pc() const { return resume_pc_; }
|
|
void set_resume_pc(uword value) { resume_pc_ = value; }
|
|
static uword resume_pc_offset() { return OFFSET_OF(Thread, resume_pc_); }
|
|
|
|
RawError* sticky_error() const;
|
|
void set_sticky_error(const Error& value);
|
|
void clear_sticky_error();
|
|
|
|
CompilerStats* compiler_stats() { return compiler_stats_; }
|
|
|
|
#if defined(DEBUG)
|
|
#define REUSABLE_HANDLE_SCOPE_ACCESSORS(object) \
|
|
void set_reusable_##object##_handle_scope_active(bool value) { \
|
|
reusable_##object##_handle_scope_active_ = value; \
|
|
} \
|
|
bool reusable_##object##_handle_scope_active() const { \
|
|
return reusable_##object##_handle_scope_active_; \
|
|
}
|
|
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_ACCESSORS)
|
|
#undef REUSABLE_HANDLE_SCOPE_ACCESSORS
|
|
|
|
bool IsAnyReusableHandleScopeActive() const {
|
|
#define IS_REUSABLE_HANDLE_SCOPE_ACTIVE(object) \
|
|
if (reusable_##object##_handle_scope_active_) { \
|
|
return true; \
|
|
}
|
|
REUSABLE_HANDLE_LIST(IS_REUSABLE_HANDLE_SCOPE_ACTIVE)
|
|
return false;
|
|
#undef IS_REUSABLE_HANDLE_SCOPE_ACTIVE
|
|
}
|
|
#endif // defined(DEBUG)
|
|
|
|
void ClearReusableHandles();
|
|
|
|
#define REUSABLE_HANDLE(object) \
|
|
object& object##Handle() const { return *object##_handle_; }
|
|
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE)
|
|
#undef REUSABLE_HANDLE
|
|
|
|
/*
|
|
* Fields used to support safepointing a thread.
|
|
*
|
|
* - Bit 0 of the safepoint_state_ field is used to indicate if the thread is
|
|
* already at a safepoint,
|
|
* - Bit 1 of the safepoint_state_ field is used to indicate if a safepoint
|
|
* operation is requested for this thread.
|
|
* - Bit 2 of the safepoint_state_ field is used to indicate that the thread
|
|
* is blocked for the safepoint operation to complete.
|
|
*
|
|
* The safepoint execution state (described above) for a thread is stored in
|
|
* in the execution_state_ field.
|
|
* Potential execution states a thread could be in:
|
|
* kThreadInGenerated - The thread is running jitted dart/stub code.
|
|
* kThreadInVM - The thread is running VM code.
|
|
* kThreadInNative - The thread is running native code.
|
|
* kThreadInBlockedState - The thread is blocked waiting for a resource.
|
|
*/
|
|
static intptr_t safepoint_state_offset() {
|
|
return OFFSET_OF(Thread, safepoint_state_);
|
|
}
|
|
static bool IsAtSafepoint(uint32_t state) {
|
|
return AtSafepointField::decode(state);
|
|
}
|
|
bool IsAtSafepoint() const {
|
|
return AtSafepointField::decode(safepoint_state_);
|
|
}
|
|
static uint32_t SetAtSafepoint(bool value, uint32_t state) {
|
|
return AtSafepointField::update(value, state);
|
|
}
|
|
void SetAtSafepoint(bool value) {
|
|
ASSERT(thread_lock()->IsOwnedByCurrentThread());
|
|
safepoint_state_ = AtSafepointField::update(value, safepoint_state_);
|
|
}
|
|
bool IsSafepointRequested() const {
|
|
return SafepointRequestedField::decode(safepoint_state_);
|
|
}
|
|
static uint32_t SetSafepointRequested(bool value, uint32_t state) {
|
|
return SafepointRequestedField::update(value, state);
|
|
}
|
|
uint32_t SetSafepointRequested(bool value) {
|
|
ASSERT(thread_lock()->IsOwnedByCurrentThread());
|
|
uint32_t old_state;
|
|
uint32_t new_state;
|
|
do {
|
|
old_state = safepoint_state_;
|
|
new_state = SafepointRequestedField::update(value, old_state);
|
|
} while (AtomicOperations::CompareAndSwapUint32(
|
|
&safepoint_state_, old_state, new_state) != old_state);
|
|
return old_state;
|
|
}
|
|
static bool IsBlockedForSafepoint(uint32_t state) {
|
|
return BlockedForSafepointField::decode(state);
|
|
}
|
|
bool IsBlockedForSafepoint() const {
|
|
return BlockedForSafepointField::decode(safepoint_state_);
|
|
}
|
|
void SetBlockedForSafepoint(bool value) {
|
|
ASSERT(thread_lock()->IsOwnedByCurrentThread());
|
|
safepoint_state_ =
|
|
BlockedForSafepointField::update(value, safepoint_state_);
|
|
}
|
|
|
|
enum ExecutionState {
|
|
kThreadInVM = 0,
|
|
kThreadInGenerated,
|
|
kThreadInNative,
|
|
kThreadInBlockedState
|
|
};
|
|
|
|
ExecutionState execution_state() const {
|
|
return static_cast<ExecutionState>(execution_state_);
|
|
}
|
|
void set_execution_state(ExecutionState state) {
|
|
execution_state_ = static_cast<uint32_t>(state);
|
|
}
|
|
static intptr_t execution_state_offset() {
|
|
return OFFSET_OF(Thread, execution_state_);
|
|
}
|
|
|
|
void EnterSafepoint() {
|
|
// First try a fast update of the thread state to indicate it is at a
|
|
// safepoint.
|
|
uint32_t new_state = SetAtSafepoint(true, 0);
|
|
uword addr = reinterpret_cast<uword>(this) + safepoint_state_offset();
|
|
if (AtomicOperations::CompareAndSwapUint32(
|
|
reinterpret_cast<uint32_t*>(addr), 0, new_state) != 0) {
|
|
// Fast update failed which means we could potentially be in the middle
|
|
// of a safepoint operation.
|
|
EnterSafepointUsingLock();
|
|
}
|
|
}
|
|
|
|
void ExitSafepoint() {
|
|
// First try a fast update of the thread state to indicate it is not at a
|
|
// safepoint anymore.
|
|
uint32_t old_state = SetAtSafepoint(true, 0);
|
|
uword addr = reinterpret_cast<uword>(this) + safepoint_state_offset();
|
|
if (AtomicOperations::CompareAndSwapUint32(
|
|
reinterpret_cast<uint32_t*>(addr), old_state, 0) != old_state) {
|
|
// Fast update failed which means we could potentially be in the middle
|
|
// of a safepoint operation.
|
|
ExitSafepointUsingLock();
|
|
}
|
|
}
|
|
|
|
void CheckForSafepoint() {
|
|
if (IsSafepointRequested()) {
|
|
BlockForSafepoint();
|
|
}
|
|
}
|
|
|
|
Thread* next() const { return next_; }
|
|
|
|
// Visit all object pointers.
|
|
void VisitObjectPointers(ObjectPointerVisitor* visitor, bool validate_frames);
|
|
|
|
bool IsValidLocalHandle(Dart_Handle object) const;
|
|
intptr_t CountLocalHandles() const;
|
|
intptr_t CountZoneHandles() const;
|
|
intptr_t CountScopedHandles() const;
|
|
int ZoneSizeInBytes() const;
|
|
void UnwindScopes(uword stack_marker);
|
|
|
|
void InitVMConstants();
|
|
|
|
#ifndef PRODUCT
|
|
void PrintJSON(JSONStream* stream) const;
|
|
#endif
|
|
|
|
private:
|
|
template <class T>
|
|
T* AllocateReusableHandle();
|
|
|
|
// Accessed from generated code:
|
|
uword stack_limit_;
|
|
uword stack_overflow_flags_;
|
|
Isolate* isolate_;
|
|
Heap* heap_;
|
|
uword top_exit_frame_info_;
|
|
StoreBufferBlock* store_buffer_block_;
|
|
uword vm_tag_;
|
|
TaskKind task_kind_;
|
|
// State that is cached in the TLS for fast access in generated code.
|
|
#define DECLARE_MEMBERS(type_name, member_name, expr, default_init_value) \
|
|
type_name member_name;
|
|
CACHED_CONSTANTS_LIST(DECLARE_MEMBERS)
|
|
#undef DECLARE_MEMBERS
|
|
|
|
#define DECLARE_MEMBERS(name) uword name##_entry_point_;
|
|
RUNTIME_ENTRY_LIST(DECLARE_MEMBERS)
|
|
#undef DECLARE_MEMBERS
|
|
|
|
#define DECLARE_MEMBERS(returntype, name, ...) uword name##_entry_point_;
|
|
LEAF_RUNTIME_ENTRY_LIST(DECLARE_MEMBERS)
|
|
#undef DECLARE_MEMBERS
|
|
|
|
TimelineStream* dart_stream_;
|
|
OSThread* os_thread_;
|
|
Monitor* thread_lock_;
|
|
Zone* zone_;
|
|
uintptr_t current_thread_memory_;
|
|
uintptr_t memory_high_watermark_;
|
|
ApiLocalScope* api_reusable_scope_;
|
|
ApiLocalScope* api_top_scope_;
|
|
StackResource* top_resource_;
|
|
LongJumpScope* long_jump_base_;
|
|
int32_t no_callback_scope_depth_;
|
|
#if defined(DEBUG)
|
|
HandleScope* top_handle_scope_;
|
|
int32_t no_handle_scope_depth_;
|
|
int32_t no_safepoint_scope_depth_;
|
|
#endif
|
|
VMHandles reusable_handles_;
|
|
uword saved_stack_limit_;
|
|
intptr_t defer_oob_messages_count_;
|
|
uint16_t deferred_interrupts_mask_;
|
|
uint16_t deferred_interrupts_;
|
|
int32_t stack_overflow_count_;
|
|
|
|
// Compiler state:
|
|
CHA* cha_;
|
|
intptr_t deopt_id_; // Compilation specific counter.
|
|
RawGrowableObjectArray* pending_functions_;
|
|
|
|
// JumpToExceptionHandler state:
|
|
RawObject* active_exception_;
|
|
RawObject* active_stacktrace_;
|
|
uword resume_pc_;
|
|
|
|
RawError* sticky_error_;
|
|
|
|
CompilerStats* compiler_stats_;
|
|
|
|
// Reusable handles support.
|
|
#define REUSABLE_HANDLE_FIELDS(object) object* object##_handle_;
|
|
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_FIELDS)
|
|
#undef REUSABLE_HANDLE_FIELDS
|
|
|
|
#if defined(DEBUG)
|
|
#define REUSABLE_HANDLE_SCOPE_VARIABLE(object) \
|
|
bool reusable_##object##_handle_scope_active_;
|
|
REUSABLE_HANDLE_LIST(REUSABLE_HANDLE_SCOPE_VARIABLE);
|
|
#undef REUSABLE_HANDLE_SCOPE_VARIABLE
|
|
#endif // defined(DEBUG)
|
|
|
|
class AtSafepointField : public BitField<uint32_t, bool, 0, 1> {};
|
|
class SafepointRequestedField : public BitField<uint32_t, bool, 1, 1> {};
|
|
class BlockedForSafepointField : public BitField<uint32_t, bool, 2, 1> {};
|
|
uint32_t safepoint_state_;
|
|
uint32_t execution_state_;
|
|
|
|
Thread* next_; // Used to chain the thread structures in an isolate.
|
|
|
|
explicit Thread(Isolate* isolate);
|
|
|
|
void StoreBufferRelease(
|
|
StoreBuffer::ThresholdPolicy policy = StoreBuffer::kCheckThreshold);
|
|
void StoreBufferAcquire();
|
|
|
|
void set_zone(Zone* zone) { zone_ = zone; }
|
|
|
|
void set_safepoint_state(uint32_t value) { safepoint_state_ = value; }
|
|
void EnterSafepointUsingLock();
|
|
void ExitSafepointUsingLock();
|
|
void BlockForSafepoint();
|
|
|
|
static void SetCurrent(Thread* current) {
|
|
OSThread::SetCurrentTLS(reinterpret_cast<uword>(current));
|
|
}
|
|
|
|
void DeferOOBMessageInterrupts();
|
|
void RestoreOOBMessageInterrupts();
|
|
|
|
#define REUSABLE_FRIEND_DECLARATION(name) \
|
|
friend class Reusable##name##HandleScope;
|
|
REUSABLE_HANDLE_LIST(REUSABLE_FRIEND_DECLARATION)
|
|
#undef REUSABLE_FRIEND_DECLARATION
|
|
|
|
friend class ApiZone;
|
|
friend class InterruptChecker;
|
|
friend class Isolate;
|
|
friend class IsolateTestHelper;
|
|
friend class NoOOBMessageScope;
|
|
friend class Simulator;
|
|
friend class StackZone;
|
|
friend class ThreadRegistry;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(Thread);
|
|
};
|
|
|
|
|
|
#if defined(TARGET_OS_WINDOWS)
|
|
// Clears the state of the current thread and frees the allocation.
|
|
void WindowsThreadCleanUp();
|
|
#endif
|
|
|
|
|
|
// Disable thread interrupts.
|
|
class DisableThreadInterruptsScope : public StackResource {
|
|
public:
|
|
explicit DisableThreadInterruptsScope(Thread* thread);
|
|
~DisableThreadInterruptsScope();
|
|
};
|
|
|
|
} // namespace dart
|
|
|
|
#endif // RUNTIME_VM_THREAD_H_
|