// 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_ #if defined(SHOULD_NOT_INCLUDE_RUNTIME) #error "Should not include runtime" #endif #include #include #include #include "include/dart_api.h" #include "platform/assert.h" #include "platform/atomic.h" #include "platform/safe_stack.h" #include "platform/thread_sanitizer.h" #include "vm/bitfield.h" #include "vm/compiler/runtime_api.h" #include "vm/constants.h" #include "vm/globals.h" #include "vm/handles.h" #include "vm/heap/pointer_block.h" #include "vm/heap/sampler.h" #include "vm/intrusive_dlist.h" #include "vm/os_thread.h" #include "vm/pending_deopts.h" #include "vm/random.h" #include "vm/runtime_entry_list.h" #include "vm/tags.h" #include "vm/thread_stack_resource.h" #include "vm/thread_state.h" #include "vm/virtual_memory.h" namespace dart { class AbstractType; class ApiLocalScope; class Array; class Bytecode; class CompilerState; class CompilerTimings; class Class; class Code; class DeoptContext; class Error; class ExceptionHandlers; class Field; class FieldTable; class Function; class GrowableObjectArray; class HandleScope; class Heap; class HierarchyInfo; class Instance; class Interpreter; class Isolate; class IsolateGroup; class Library; class LocalHandle; class Object; class OSThread; class JSONObject; class NoActiveIsolateScope; class PcDescriptors; class RuntimeEntry; class Smi; class StackResource; class StackTrace; class StreamInfo; class String; class TimelineStream; class TypeArguments; class TypeParameter; class TypeUsageInfo; class WeakTable; class Zone; namespace bytecode { class BytecodeLoader; } namespace compiler { namespace target { class Thread; } // namespace target } // namespace compiler #define REUSABLE_HANDLE_LIST(V) \ V(AbstractType) \ V(Array) \ V(Class) \ V(Code) \ V(Bytecode) \ V(Error) \ V(ExceptionHandlers) \ V(Field) \ V(Function) \ V(GrowableObjectArray) \ V(Instance) \ V(Library) \ V(LoadingUnit) \ V(Object) \ V(PcDescriptors) \ V(Smi) \ V(String) \ V(TypeParameters) \ V(TypeArguments) \ V(TypeParameter) \ V(WeakArray) #define CACHED_VM_STUBS_LIST(V) \ V(CodePtr, fix_callers_target_code_, StubCode::FixCallersTarget().ptr(), \ nullptr) \ V(CodePtr, fix_allocation_stub_code_, \ StubCode::FixAllocationStubTarget().ptr(), nullptr) \ V(CodePtr, invoke_dart_code_stub_, StubCode::InvokeDartCode().ptr(), \ nullptr) \ V(CodePtr, invoke_dart_code_from_bytecode_stub_, \ StubCode::InvokeDartCodeFromBytecode().ptr(), nullptr) \ V(CodePtr, call_to_runtime_stub_, StubCode::CallToRuntime().ptr(), nullptr) \ V(CodePtr, late_initialization_error_shared_without_fpu_regs_stub_, \ StubCode::LateInitializationErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, late_initialization_error_shared_with_fpu_regs_stub_, \ StubCode::LateInitializationErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, null_error_shared_without_fpu_regs_stub_, \ StubCode::NullErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, null_error_shared_with_fpu_regs_stub_, \ StubCode::NullErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, null_arg_error_shared_without_fpu_regs_stub_, \ StubCode::NullArgErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, null_arg_error_shared_with_fpu_regs_stub_, \ StubCode::NullArgErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, null_cast_error_shared_without_fpu_regs_stub_, \ StubCode::NullCastErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, null_cast_error_shared_with_fpu_regs_stub_, \ StubCode::NullCastErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, range_error_shared_without_fpu_regs_stub_, \ StubCode::RangeErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, range_error_shared_with_fpu_regs_stub_, \ StubCode::RangeErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, write_error_shared_without_fpu_regs_stub_, \ StubCode::WriteErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, write_error_shared_with_fpu_regs_stub_, \ StubCode::WriteErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, field_access_error_shared_without_fpu_regs_stub_, \ StubCode::FieldAccessErrorSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, field_access_error_shared_with_fpu_regs_stub_, \ StubCode::FieldAccessErrorSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, allocate_mint_with_fpu_regs_stub_, \ StubCode::AllocateMintSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, allocate_mint_without_fpu_regs_stub_, \ StubCode::AllocateMintSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, async_exception_handler_stub_, \ StubCode::AsyncExceptionHandler().ptr(), nullptr) \ V(CodePtr, resume_stub_, StubCode::Resume().ptr(), nullptr) \ V(CodePtr, return_async_stub_, StubCode::ReturnAsync().ptr(), nullptr) \ V(CodePtr, return_async_not_future_stub_, \ StubCode::ReturnAsyncNotFuture().ptr(), nullptr) \ V(CodePtr, return_async_star_stub_, StubCode::ReturnAsyncStar().ptr(), \ nullptr) \ V(CodePtr, stack_overflow_shared_without_fpu_regs_stub_, \ StubCode::StackOverflowSharedWithoutFPURegs().ptr(), nullptr) \ V(CodePtr, stack_overflow_shared_with_fpu_regs_stub_, \ StubCode::StackOverflowSharedWithFPURegs().ptr(), nullptr) \ V(CodePtr, switchable_call_miss_stub_, StubCode::SwitchableCallMiss().ptr(), \ nullptr) \ V(CodePtr, throw_stub_, StubCode::Throw().ptr(), nullptr) \ V(CodePtr, re_throw_stub_, StubCode::Throw().ptr(), nullptr) \ V(CodePtr, optimize_stub_, StubCode::OptimizeFunction().ptr(), nullptr) \ V(CodePtr, deoptimize_stub_, StubCode::Deoptimize().ptr(), nullptr) \ V(CodePtr, lazy_deopt_from_return_stub_, \ StubCode::DeoptimizeLazyFromReturn().ptr(), nullptr) \ V(CodePtr, lazy_deopt_from_throw_stub_, \ StubCode::DeoptimizeLazyFromThrow().ptr(), nullptr) \ V(CodePtr, slow_type_test_stub_, StubCode::SlowTypeTest().ptr(), nullptr) \ V(CodePtr, lazy_specialize_type_test_stub_, \ StubCode::LazySpecializeTypeTest().ptr(), nullptr) \ V(CodePtr, enter_safepoint_stub_, StubCode::EnterSafepoint().ptr(), nullptr) \ V(CodePtr, exit_safepoint_stub_, StubCode::ExitSafepoint().ptr(), nullptr) \ V(CodePtr, call_native_through_safepoint_stub_, \ StubCode::CallNativeThroughSafepoint().ptr(), nullptr) #define CACHED_NON_VM_STUB_LIST(V) \ V(ObjectPtr, object_null_, Object::null(), nullptr) \ V(SentinelPtr, object_sentinel_, Object::sentinel().ptr(), nullptr) \ V(BoolPtr, bool_true_, Object::bool_true().ptr(), nullptr) \ V(BoolPtr, bool_false_, Object::bool_false().ptr(), nullptr) \ V(ArrayPtr, empty_array_, Object::empty_array().ptr(), nullptr) \ V(TypeArgumentsPtr, empty_type_arguments_, \ Object::empty_type_arguments().ptr(), nullptr) \ V(TypePtr, dynamic_type_, Type::dynamic_type().ptr(), nullptr) // List of VM-global objects/addresses cached in each Thread object. // Important: constant false must immediately follow constant true. #define CACHED_VM_OBJECTS_LIST(V) \ CACHED_NON_VM_STUB_LIST(V) \ CACHED_VM_STUBS_LIST(V) #define CACHED_FUNCTION_ENTRY_POINTS_LIST(V) \ V(suspend_state_init_async) \ V(suspend_state_await) \ V(suspend_state_await_with_type_check) \ V(suspend_state_return_async) \ V(suspend_state_return_async_not_future) \ V(suspend_state_init_async_star) \ V(suspend_state_yield_async_star) \ V(suspend_state_return_async_star) \ V(suspend_state_init_sync_star) \ V(suspend_state_suspend_sync_star_at_start) \ V(suspend_state_handle_exception) // This assertion marks places which assume that boolean false immediate // follows bool true in the CACHED_VM_OBJECTS_LIST #define ASSERT_BOOL_FALSE_FOLLOWS_BOOL_TRUE() \ ASSERT((Thread::bool_true_offset() + kWordSize) == \ Thread::bool_false_offset()); #define CACHED_VM_STUBS_ADDRESSES_LIST(V) \ V(uword, write_barrier_entry_point_, StubCode::WriteBarrier().EntryPoint(), \ 0) \ V(uword, array_write_barrier_entry_point_, \ StubCode::ArrayWriteBarrier().EntryPoint(), 0) \ V(uword, call_to_runtime_entry_point_, \ StubCode::CallToRuntime().EntryPoint(), 0) \ V(uword, allocate_mint_with_fpu_regs_entry_point_, \ StubCode::AllocateMintSharedWithFPURegs().EntryPoint(), 0) \ V(uword, allocate_mint_without_fpu_regs_entry_point_, \ StubCode::AllocateMintSharedWithoutFPURegs().EntryPoint(), 0) \ V(uword, allocate_object_entry_point_, \ StubCode::AllocateObject().EntryPoint(), 0) \ V(uword, allocate_object_parameterized_entry_point_, \ StubCode::AllocateObjectParameterized().EntryPoint(), 0) \ V(uword, allocate_object_slow_entry_point_, \ StubCode::AllocateObjectSlow().EntryPoint(), 0) \ V(uword, stack_overflow_shared_without_fpu_regs_entry_point_, \ StubCode::StackOverflowSharedWithoutFPURegs().EntryPoint(), 0) \ V(uword, stack_overflow_shared_with_fpu_regs_entry_point_, \ StubCode::StackOverflowSharedWithFPURegs().EntryPoint(), 0) \ V(uword, megamorphic_call_checked_entry_, \ StubCode::MegamorphicCall().EntryPoint(), 0) \ V(uword, switchable_call_miss_entry_, \ StubCode::SwitchableCallMiss().EntryPoint(), 0) \ V(uword, optimize_entry_, StubCode::OptimizeFunction().EntryPoint(), 0) \ V(uword, deoptimize_entry_, StubCode::Deoptimize().EntryPoint(), 0) \ V(uword, call_native_through_safepoint_entry_point_, \ StubCode::CallNativeThroughSafepoint().EntryPoint(), 0) \ V(uword, jump_to_frame_entry_point_, StubCode::JumpToFrame().EntryPoint(), \ 0) \ V(uword, slow_type_test_entry_point_, StubCode::SlowTypeTest().EntryPoint(), \ 0) \ V(uword, resume_interpreter_adjusted_entry_point_, \ StubCode::ResumeInterpreter().EntryPoint() + \ SuspendStubABI::kResumePcDistance, \ 0) #define CACHED_ADDRESSES_LIST(V) \ CACHED_VM_STUBS_ADDRESSES_LIST(V) \ V(uword, bootstrap_native_wrapper_entry_point_, \ NativeEntry::BootstrapNativeCallWrapperEntry(), 0) \ V(uword, no_scope_native_wrapper_entry_point_, \ NativeEntry::NoScopeNativeCallWrapperEntry(), 0) \ V(uword, auto_scope_native_wrapper_entry_point_, \ NativeEntry::AutoScopeNativeCallWrapperEntry(), 0) \ V(uword, interpret_call_entry_point_, RuntimeEntry::InterpretCallEntry(), 0) \ V(StringPtr*, predefined_symbols_address_, Symbols::PredefinedAddress(), \ nullptr) \ V(uword, double_nan_address_, reinterpret_cast(&double_nan_constant), \ 0) \ V(uword, double_negate_address_, \ reinterpret_cast(&double_negate_constant), 0) \ V(uword, double_abs_address_, reinterpret_cast(&double_abs_constant), \ 0) \ V(uword, float_not_address_, reinterpret_cast(&float_not_constant), \ 0) \ V(uword, float_negate_address_, \ reinterpret_cast(&float_negate_constant), 0) \ V(uword, float_absolute_address_, \ reinterpret_cast(&float_absolute_constant), 0) \ V(uword, float_zerow_address_, \ reinterpret_cast(&float_zerow_constant), 0) #define CACHED_CONSTANTS_LIST(V) \ CACHED_VM_OBJECTS_LIST(V) \ CACHED_ADDRESSES_LIST(V) enum class ValidationPolicy { kValidateFrames = 0, kDontValidateFrames = 1, }; enum class RuntimeCallDeoptAbility { // There was no leaf call or a leaf call that can cause deoptimization // after-call. kCanLazyDeopt, // There was a leaf call and the VM cannot cause deoptimize after-call. kCannotLazyDeopt, }; // The safepoint level a thread is on or a safepoint operation is requested for // // The higher the number the stronger the guarantees: // * the time-to-safepoint latency increases with level // * the frequency of hitting possible safe points decreases with level enum SafepointLevel { // Safe to GC kGC, // Safe to GC as well as Deopt. kGCAndDeopt, // Safe to GC, Deopt as well as Reload. kGCAndDeoptAndReload, // Number of levels. kNumLevels, // No safepoint. kNoSafepoint, }; // Accessed from generated code. struct TsanUtils { // Used to allow unwinding runtime C frames using longjmp() when throwing // exceptions. This allows triggering the normal TSAN shadow stack unwinding // implementation. // -> See https://dartbug.com/47472#issuecomment-948235479 for details. #if defined(USING_THREAD_SANITIZER) void* setjmp_function = reinterpret_cast(&DART_SETJMP); #else // MSVC (on Windows) is not happy with getting address of purely intrinsic. void* setjmp_function = nullptr; #endif jmp_buf* setjmp_buffer = nullptr; uword exception_pc = 0; uword exception_sp = 0; uword exception_fp = 0; static intptr_t setjmp_function_offset() { return OFFSET_OF(TsanUtils, setjmp_function); } static intptr_t setjmp_buffer_offset() { return OFFSET_OF(TsanUtils, setjmp_buffer); } static intptr_t exception_pc_offset() { return OFFSET_OF(TsanUtils, exception_pc); } static intptr_t exception_sp_offset() { return OFFSET_OF(TsanUtils, exception_sp); } static intptr_t exception_fp_offset() { return OFFSET_OF(TsanUtils, exception_fp); } }; // 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 ThreadRegistry::GetFromFreelistLocked either // before entering an isolate or entering an isolate group, and destroyed // automatically when the underlying OS thread exits. class Thread : public ThreadState, public IntrusiveDListEntry { public: // The kind of task this thread is performing. Sampled by the profiler. enum TaskKind { kUnknownTask = 0, kMutatorTask, kCompilerTask, kMarkerTask, kSweeperTask, kCompactorTask, kScavengerTask, kSampleBlockTask, kIncrementalCompactorTask, kSpawnTask, kIsolateGroupBoundCallbackTask, }; ~Thread(); // The currently executing thread, or nullptr if not yet initialized. static Thread* Current() { return static_cast(OSThread::CurrentVMThread()); } // Whether there's any active state on the [thread] that needs to be preserved // across `Thread::ExitIsolate()` and `Thread::EnterIsolate()`. bool HasActiveState(); void AssertNonMutatorInvariants(); void AssertDartMutatorInvariants(); void AssertNonDartMutatorInvariants(); void AssertEmptyStackInvariants(); void AssertEmptyThreadInvariants(); // Makes the current thread enter 'isolate'. static void EnterIsolate(Isolate* isolate); // Makes the current thread exit its isolate. static void ExitIsolate(bool isolate_shutdown = false); static void EnterIsolateGroupAsHelper(IsolateGroup* isolate_group, TaskKind kind, bool bypass_safepoint); static void ExitIsolateGroupAsHelper(bool bypass_safepoint); static void EnterIsolateGroupAsNonMutator(IsolateGroup* isolate_group, TaskKind kind); static void ExitIsolateGroupAsNonMutator(); static void EnterIsolateGroupAsMutator(IsolateGroup* isolate_group, bool bypass_safepoint, Thread* suspended_thread = nullptr); static void ExitIsolateGroupAsMutator(bool bypass_safepoint); // Empties the store buffer block into the isolate. void ReleaseStoreBuffer(); void AcquireMarkingStack(); void ReleaseMarkingStack(); void SetStackLimit(uword value); void ClearStackLimit(); // The stack limit used by stack overflow checks in generated code. Either the // true OS thread's stack limit minus some headroom, or a special value to // trigger interrupts. uword stack_limit_address() const { return reinterpret_cast(&stack_limit_); } static intptr_t stack_limit_offset() { return OFFSET_OF(Thread, stack_limit_); } // The true stack limit for this OS thread minus some headroom. Used on ARM64 // to keep CSP/R31 signal-handler safe while Dart uses R15 as its stack // pointer. static intptr_t saved_stack_limit_offset() { return OFFSET_OF(Thread, saved_stack_limit_); } uword saved_stack_limit() const { return saved_stack_limit_; } #if defined(USING_SAFE_STACK) // The SafeStack pointer during the top-most DartEntry. Needs to be restored // on Dart throw like longjmp would. uword saved_safestack() const { return saved_safestack_; } void set_saved_safestack(uword ssp) { saved_safestack_ = ssp; } #endif // The ShadowCallStack pointer during the top-most entry frame. Needs to be // restored on Dart throw like longjmp would. uword saved_shadow_call_stack() const { return saved_shadow_call_stack_; } void set_saved_shadow_call_stack(uword ssp) { saved_shadow_call_stack_ = ssp; } static uword saved_shadow_call_stack_offset() { return OFFSET_OF(Thread, saved_shadow_call_stack_); } // Stack overflow flags enum { kOsrRequest = 0x1, // Current stack overflow caused by OSR request. }; uword write_barrier_mask() const { return write_barrier_mask_; } uword heap_base() const { #if defined(DART_COMPRESSED_POINTERS) return heap_base_; #else return 0; #endif } static intptr_t write_barrier_mask_offset() { return OFFSET_OF(Thread, write_barrier_mask_); } #if defined(DART_COMPRESSED_POINTERS) static intptr_t heap_base_offset() { return OFFSET_OF(Thread, heap_base_); } #endif static intptr_t stack_overflow_flags_offset() { return OFFSET_OF(Thread, stack_overflow_flags_); } uint32_t IncrementAndGetStackOverflowCount() { return ++stack_overflow_count_; } uint32_t IncrementAndGetRuntimeCallCount() { return ++runtime_call_count_; } static uword stack_overflow_shared_stub_entry_point_offset(bool fpu_regs) { return fpu_regs ? stack_overflow_shared_with_fpu_regs_entry_point_offset() : stack_overflow_shared_without_fpu_regs_entry_point_offset(); } static intptr_t safepoint_state_offset() { return OFFSET_OF(Thread, safepoint_state_); } // Tag state is maintained on transitions. enum { // Always true in generated state. kDidNotExit = 0, // The VM exited the generated state through FFI. // This can be true in both native and VM state. kExitThroughFfi = 1, // The VM exited the generated state through a runtime call. // This can be true in both native and VM state. kExitThroughRuntimeCall = 2, }; uword exit_through_ffi() { return exit_through_ffi_; } static intptr_t exit_through_ffi_offset() { return OFFSET_OF(Thread, exit_through_ffi_); } TaskKind task_kind() const { return task_kind_.load(std::memory_order_acquire); } void set_task_kind(TaskKind kind) { task_kind_.store(kind, std::memory_order_release); } // 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); ErrorPtr HandleInterrupts(); ErrorPtr HandleInterrupts(uword interrupt_bits); uword GetAndClearInterrupts(); bool HasScheduledInterrupts() const { return (stack_limit_.load() & kInterruptsMask) != 0; } // Monitor corresponding to this thread. Monitor* thread_lock() const { return &thread_lock_; } // 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 == nullptr || api_reusable_scope_ == nullptr); 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; } static intptr_t api_top_scope_offset() { return OFFSET_OF(Thread, api_top_scope_); } void EnterApiScope(); void ExitApiScope(); static intptr_t double_truncate_round_supported_offset() { return OFFSET_OF(Thread, double_truncate_round_supported_); } static intptr_t tsan_utils_offset() { return OFFSET_OF(Thread, tsan_utils_); } #if defined(USING_THREAD_SANITIZER) uword exit_through_ffi() const { return exit_through_ffi_; } TsanUtils* tsan_utils() const { return tsan_utils_; } #endif // defined(USING_THREAD_SANITIZER) // The isolate that this thread is operating on, or nullptr if none. Isolate* isolate() const { return isolate_; } static intptr_t isolate_offset() { return OFFSET_OF(Thread, isolate_); } static intptr_t isolate_group_offset() { return OFFSET_OF(Thread, isolate_group_); } // The isolate group that this thread is operating on, or nullptr if none. IsolateGroup* isolate_group() const { return isolate_group_; } static intptr_t field_table_values_offset() { return OFFSET_OF(Thread, field_table_values_); } static intptr_t shared_field_table_values_offset() { return OFFSET_OF(Thread, shared_field_table_values_); } bool IsDartMutatorThread() const { return task_kind_ == kMutatorTask; } bool HasDartMutatorStack() const { // The thread with dart mutator task might be temporarily // occupied by a gc task. return IsDartMutatorThread() // mutator thread with isolate || scheduled_dart_mutator_isolate_ != nullptr // mutator thread without isolate || top_exit_frame_info_ != 0; } // Returns the dart mutator [Isolate] this thread belongs to or nullptr. // // `isolate()` in comparison can return // - `nullptr` for dart mutators (e.g. if the mutator runs under // [NoActiveIsolateScope]) // - an incorrect isolate (e.g. if [ActiveIsolateScope] is used to seemingly // enter another isolate) Isolate* scheduled_dart_mutator_isolate() const { return scheduled_dart_mutator_isolate_; } #if defined(DEBUG) bool IsInsideCompiler() const { return inside_compiler_; } #endif // Offset of Dart TimelineStream object. static intptr_t dart_stream_offset() { return OFFSET_OF(Thread, dart_stream_); } // Offset of the Dart VM Service Extension StreamInfo object. static intptr_t service_extension_stream_offset() { return OFFSET_OF(Thread, service_extension_stream_); } // Is |this| executing Dart code? bool IsExecutingDartCode() const; // Has |this| exited Dart code? bool HasExitedDartCode() const; bool HasCompilerState() const { return compiler_state_ != nullptr; } CompilerState& compiler_state() { ASSERT(HasCompilerState()); return *compiler_state_; } HierarchyInfo* hierarchy_info() const { ASSERT(isolate_group_ != nullptr); return hierarchy_info_; } void set_hierarchy_info(HierarchyInfo* value) { ASSERT(isolate_group_ != nullptr); ASSERT((hierarchy_info_ == nullptr && value != nullptr) || (hierarchy_info_ != nullptr && value == nullptr)); hierarchy_info_ = value; } TypeUsageInfo* type_usage_info() const { ASSERT(isolate_group_ != nullptr); return type_usage_info_; } void set_type_usage_info(TypeUsageInfo* value) { ASSERT(isolate_group_ != nullptr); ASSERT((type_usage_info_ == nullptr && value != nullptr) || (type_usage_info_ != nullptr && value == nullptr)); type_usage_info_ = value; } CompilerTimings* compiler_timings() const { return compiler_timings_; } void set_compiler_timings(CompilerTimings* stats) { compiler_timings_ = stats; } 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; } bool force_growth() const { return force_growth_scope_depth_ != 0; } void IncrementForceGrowthScopeDepth() { ASSERT(force_growth_scope_depth_ < INT_MAX); force_growth_scope_depth_ += 1; } void DecrementForceGrowthScopeDepth() { ASSERT(force_growth_scope_depth_ > 0); force_growth_scope_depth_ -= 1; } bool is_unwind_in_progress() const { return is_unwind_in_progress_; } void StartUnwindError() { is_unwind_in_progress_ = true; SetUnwindErrorInProgress(true); } #if defined(DEBUG) void EnterCompiler() { ASSERT(!IsInsideCompiler()); inside_compiler_ = true; } void LeaveCompiler() { ASSERT(IsInsideCompiler()); inside_compiler_ = false; } #endif void StoreBufferAddObject(ObjectPtr obj); void StoreBufferAddObjectGC(ObjectPtr obj); #if defined(TESTING) bool StoreBufferContains(ObjectPtr obj) const { return store_buffer_block_->Contains(obj); } #endif void StoreBufferBlockProcess(StoreBuffer::ThresholdPolicy policy); void StoreBufferReleaseGC(); void StoreBufferAcquireGC(); static intptr_t store_buffer_block_offset() { return OFFSET_OF(Thread, store_buffer_block_); } bool is_marking() const { return old_marking_stack_block_ != nullptr; } void MarkingStackAddObject(ObjectPtr obj); void OldMarkingStackAddObject(ObjectPtr obj); void NewMarkingStackAddObject(ObjectPtr obj); void DeferredMarkingStackAddObject(ObjectPtr obj); void OldMarkingStackBlockProcess(); void NewMarkingStackBlockProcess(); void DeferredMarkingStackBlockProcess(); static intptr_t old_marking_stack_block_offset() { return OFFSET_OF(Thread, old_marking_stack_block_); } static intptr_t new_marking_stack_block_offset() { return OFFSET_OF(Thread, new_marking_stack_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_); } Heap* heap() const; // The TLAB memory boundaries. // // When the heap sampling profiler is enabled, we use the TLAB boundary to // trigger slow path allocations so we can take a sample. This means that // true_end() >= end(), where true_end() is the actual end address of the // TLAB and end() is the chosen sampling boundary for the thread. // // When the heap sampling profiler is disabled, true_end() == end(). uword top() const { return top_.load(std::memory_order_relaxed); } uword end() const { return end_; } uword true_end() const { return true_end_; } void set_top(uword top) { top_.store(top, std::memory_order_relaxed); } void set_end(uword end) { end_ = end; } void set_true_end(uword true_end) { true_end_ = true_end; } static intptr_t top_offset() { return OFFSET_OF(Thread, top_); } static intptr_t end_offset() { return OFFSET_OF(Thread, end_); } 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 } bool IsInNoReloadScope() const { return no_reload_scope_depth_ > 0; } bool IsInStoppedMutatorsScope() const { return stopped_mutators_scope_depth_ > 0; } #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 static intptr_t write_barrier_wrappers_thread_offset(Register reg) { ASSERT((kDartAvailableCpuRegs & (1 << reg)) != 0); intptr_t index = 0; for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) { if ((kDartAvailableCpuRegs & (1 << i)) == 0) continue; if (i == reg) break; ++index; } return OFFSET_OF(Thread, write_barrier_wrappers_entry_points_) + index * sizeof(uword); } static intptr_t WriteBarrierWrappersOffsetForRegister(Register reg) { intptr_t index = 0; for (intptr_t i = 0; i < kNumberOfCpuRegisters; ++i) { if ((kDartAvailableCpuRegs & (1 << i)) == 0) continue; if (i == reg) { return index * kStoreBufferWrapperSize; } ++index; } UNREACHABLE(); return 0; } #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 ObjectPoolPtr global_object_pool() const { return global_object_pool_; } void set_global_object_pool(ObjectPoolPtr raw_value) { global_object_pool_ = raw_value; } const uword* dispatch_table_array() const { return dispatch_table_array_; } void set_dispatch_table_array(const uword* array) { dispatch_table_array_ = array; } 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); #define DEFINE_OFFSET_METHOD(name) \ static intptr_t name##_entry_point_offset() { \ return OFFSET_OF(Thread, name##_entry_point_); \ } CACHED_FUNCTION_ENTRY_POINTS_LIST(DEFINE_OFFSET_METHOD) #undef DEFINE_OFFSET_METHOD #if defined(DEBUG) // For asserts only. Has false positives when running with a simulator or // SafeStack. bool TopErrorHandlerIsSetJump() const; bool TopErrorHandlerIsExitFrame() const; #endif 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_); } int64_t unboxed_int64_runtime_arg() const { return unboxed_runtime_arg_.int64_storage[0]; } void set_unboxed_int64_runtime_arg(int64_t value) { unboxed_runtime_arg_.int64_storage[0] = value; } int64_t unboxed_int64_runtime_second_arg() const { return unboxed_runtime_arg_.int64_storage[1]; } void set_unboxed_int64_runtime_second_arg(int64_t value) { unboxed_runtime_arg_.int64_storage[1] = value; } double unboxed_double_runtime_arg() const { return unboxed_runtime_arg_.double_storage[0]; } void set_unboxed_double_runtime_arg(double value) { unboxed_runtime_arg_.double_storage[0] = value; } simd128_value_t unboxed_simd128_runtime_arg() const { return unboxed_runtime_arg_; } void set_unboxed_simd128_runtime_arg(simd128_value_t value) { unboxed_runtime_arg_ = value; } static intptr_t unboxed_runtime_arg_offset() { return OFFSET_OF(Thread, unboxed_runtime_arg_); } static intptr_t global_object_pool_offset() { return OFFSET_OF(Thread, global_object_pool_); } static intptr_t dispatch_table_array_offset() { return OFFSET_OF(Thread, dispatch_table_array_); } ObjectPtr active_exception() const { return active_exception_; } void set_active_exception(const Object& value); void set_active_exception(LocalHandle* value); static intptr_t active_exception_offset() { return OFFSET_OF(Thread, active_exception_); } ObjectPtr 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_); } ErrorPtr sticky_error() const; void set_sticky_error(const Error& value); void ClearStickyError(); DART_WARN_UNUSED_RESULT ErrorPtr StealStickyError(); #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 { \ ASSERT(object##_handle_ != nullptr); \ return *object##_handle_; \ } REUSABLE_HANDLE_LIST(REUSABLE_HANDLE) #undef REUSABLE_HANDLE static bool IsAtSafepoint(SafepointLevel level, uword state) { const uword mask = AtSafepointBits(level); return (state & mask) == mask; } // Whether the current thread is owning any safepoint level. bool IsAtSafepoint() const { // Owning a higher level safepoint implies owning the lower levels as well. return IsAtSafepoint(SafepointLevel::kGC); } bool IsAtSafepoint(SafepointLevel level) const { return IsAtSafepoint(level, safepoint_state_.load()); } void SetAtSafepoint(bool value, SafepointLevel level) { ASSERT(thread_lock()->IsOwnedByCurrentThread()); ASSERT(level <= current_safepoint_level()); if (value) { safepoint_state_ |= AtSafepointBits(level); } else { safepoint_state_ &= ~AtSafepointBits(level); } } bool IsSafepointRequestedLocked(SafepointLevel level) const { ASSERT(thread_lock()->IsOwnedByCurrentThread()); return IsSafepointRequested(level); } bool IsSafepointRequested() const { return IsSafepointRequested(current_safepoint_level()); } bool IsSafepointRequested(SafepointLevel level) const { const uword state = safepoint_state_.load(); for (intptr_t i = level; i >= 0; --i) { if (IsSafepointLevelRequested(state, static_cast(i))) return true; } return false; } bool IsSafepointLevelRequestedLocked(SafepointLevel level) const { ASSERT(thread_lock()->IsOwnedByCurrentThread()); if (level > current_safepoint_level()) return false; const uword state = safepoint_state_.load(); return IsSafepointLevelRequested(state, level); } static bool IsSafepointLevelRequested(uword state, SafepointLevel level) { switch (level) { case SafepointLevel::kGC: return SafepointRequestedField::decode(state); case SafepointLevel::kGCAndDeopt: return DeoptSafepointRequestedField::decode(state); case SafepointLevel::kGCAndDeoptAndReload: return ReloadSafepointRequestedField::decode(state); default: UNREACHABLE(); } } void BlockForSafepoint(); uword SetSafepointRequested(SafepointLevel level, bool value) { ASSERT(thread_lock()->IsOwnedByCurrentThread()); uword mask = 0; switch (level) { case SafepointLevel::kGC: mask = SafepointRequestedField::mask_in_place(); break; case SafepointLevel::kGCAndDeopt: mask = DeoptSafepointRequestedField::mask_in_place(); break; case SafepointLevel::kGCAndDeoptAndReload: mask = ReloadSafepointRequestedField::mask_in_place(); break; default: UNREACHABLE(); } if (value) { // acquire pulls from the release in TryEnterSafepoint. return safepoint_state_.fetch_or(mask, std::memory_order_acquire); } else { // release pushes to the acquire in TryExitSafepoint. return safepoint_state_.fetch_and(~mask, std::memory_order_release); } } static bool IsBlockedForSafepoint(uword state) { return BlockedForSafepointField::decode(state); } bool IsBlockedForSafepoint() const { return BlockedForSafepointField::decode(safepoint_state_); } // Normally execution state is only accessed for the current thread. NO_SANITIZE_THREAD bool IsBlockedForSafepointCrossThreadForTesting() const { return BlockedForSafepointField::decode(safepoint_state_); } void SetBlockedForSafepoint(bool value) { ASSERT(thread_lock()->IsOwnedByCurrentThread()); const uword mask = BlockedForSafepointField::mask_in_place(); if (value) { safepoint_state_.fetch_or(mask); } else { safepoint_state_.fetch_and(~mask); } } bool BypassSafepoints() const { return BypassSafepointsField::decode(safepoint_state_); } static uword SetBypassSafepoints(bool value, uword state) { return BypassSafepointsField::update(value, state); } bool UnwindErrorInProgress() const { return UnwindErrorInProgressField::decode(safepoint_state_); } void SetUnwindErrorInProgress(bool value) { const uword mask = UnwindErrorInProgressField::mask_in_place(); if (value) { safepoint_state_.fetch_or(mask); } else { safepoint_state_.fetch_and(~mask); } } void SetNoReloadScope(bool value) { const uword mask = NoReloadScopeField::mask_in_place(); if (value) { safepoint_state_.fetch_or(mask); } else { safepoint_state_.fetch_and(~mask); } } bool OwnsGCSafepoint() const; bool OwnsReloadSafepoint() const; bool OwnsDeoptSafepoint() const; bool OwnsSafepoint() const; bool CanAcquireSafepointLocks() const; uword safepoint_state() { return safepoint_state_; } enum ExecutionState { kThreadInVM = 0, kThreadInGenerated, kThreadInNative, kThreadInBlockedState, kThreadInReloadableBlockedState }; ExecutionState execution_state() const { return static_cast(execution_state_); } void set_execution_state(ExecutionState state) { execution_state_ = static_cast(state); } static intptr_t execution_state_offset() { return OFFSET_OF(Thread, execution_state_); } virtual bool MayAllocateHandles() { return (execution_state() == kThreadInVM) || (execution_state() == kThreadInGenerated); } static uword native_safepoint_state_unacquired() { return AtSafepointField::encode(false) | AtDeoptSafepointField::encode(false) | #if !defined(PRODUCT) AtReloadSafepointField::encode(false) | #endif ActiveMutatorStealableField::encode(false) | NoReloadScopeField::encode(false); } static uword native_safepoint_state_acquired() { return AtSafepointField::encode(true) | AtDeoptSafepointField::encode(true) | #if !defined(PRODUCT) AtReloadSafepointField::encode(true) | #endif ActiveMutatorStealableField::encode(true) | NoReloadScopeField::encode(false); } bool TryStealActiveMutator() { uword old_state = safepoint_state_.load(); if (!ActiveMutatorStealableField::decode(old_state)) return false; uword new_state = old_state; new_state = ActiveMutatorStealableField::update(false, new_state); new_state = ActiveMutatorStolenField::update(true, new_state); return safepoint_state_.compare_exchange_strong(old_state, new_state, std::memory_order_relaxed); } bool TryEnterSafepointToNative() { uword old_state = native_safepoint_state_unacquired(); uword new_state = native_safepoint_state_acquired(); return safepoint_state_.compare_exchange_strong(old_state, new_state, std::memory_order_release); } void EnterSafepointToNative() { ASSERT(no_safepoint_scope_depth() == 0); // First try a fast update of the thread state to indicate it is at a // safepoint. if (!TryEnterSafepointToNative()) { // Fast update failed which means we could potentially be in the middle // of a safepoint operation. EnterSafepointUsingLock(); if (!NoReloadScopeField::decode(safepoint_state_)) { safepoint_state_.fetch_or(ActiveMutatorStealableField::encode(true)); } } } bool TryEnterSafepoint() { uword old_state = 0; uword new_state = AtSafepointBits(current_safepoint_level()); return safepoint_state_.compare_exchange_strong(old_state, new_state, std::memory_order_release); } void EnterSafepoint() { ASSERT(no_safepoint_scope_depth() == 0); // First try a fast update of the thread state to indicate it is at a // safepoint. if (!TryEnterSafepoint()) { // Fast update failed which means we could potentially be in the middle // of a safepoint operation. EnterSafepointUsingLock(); } } bool TryExitSafepoint() { uword old_state = AtSafepointBits(current_safepoint_level()); uword new_state = 0; return safepoint_state_.compare_exchange_strong(old_state, new_state, std::memory_order_acquire); } void ExitSafepoint() { ASSERT(!ActiveMutatorStealableField::decode(safepoint_state_)); ASSERT(!ActiveMutatorStolenField::decode(safepoint_state_)); // First try a fast update of the thread state to indicate it is not at a // safepoint anymore. if (!TryExitSafepoint()) { // Fast update failed which means we could potentially be in the middle // of a safepoint operation. ExitSafepointUsingLock(); } #ifndef PRODUCT // Exit frame must have remained valid for the whole duration of the // safepoint. Do some quick checks to validate that. if (FLAG_use_slow_path) { ValidateExitFrameState(); } #endif } bool TryExitSafepointFromNative() { uword old_state = native_safepoint_state_acquired(); uword new_state = native_safepoint_state_unacquired(); return safepoint_state_.compare_exchange_strong(old_state, new_state, std::memory_order_acquire); } void ExitSafepointFromNative() { if (!TryExitSafepointFromNative()) { ExitSafepointUsingLock(); uword old_state = safepoint_state_.fetch_and( ~(ActiveMutatorStealableField::encode(true) | ActiveMutatorStolenField::encode(true))); if (ActiveMutatorStolenField::decode(old_state)) { set_execution_state(Thread::kThreadInVM); allow_reload_scope_depth_++; HandleStolen(); allow_reload_scope_depth_--; } } ASSERT(!ActiveMutatorStealableField::decode(safepoint_state_)); ASSERT(!ActiveMutatorStolenField::decode(safepoint_state_)); #ifndef PRODUCT // Exit frame must have remained valid for the whole duration of the // safepoint. Do some quick checks to validate that. if (FLAG_use_slow_path) { ValidateExitFrameState(); } #endif } void HandleStolen(); #ifndef PRODUCT void ValidateExitFrameState(); #endif void CheckForSafepoint() { // If we are in a runtime call that doesn't support lazy deopt, we will only // respond to gc safepointing requests. ASSERT(no_safepoint_scope_depth() == 0); if (IsSafepointRequested()) { bool stolen = ActiveMutatorStolenField::decode(safepoint_state_.load()); ASSERT(!stolen); BlockForSafepoint(); } } Thread* next() const { return next_; } // Visit all object pointers. void VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validate_frames); void RememberLiveTemporaries(); void DeferredMarkLiveTemporaries(); bool IsValidHandle(Dart_Handle object) const; bool IsValidLocalHandle(Dart_Handle object) const; intptr_t CountLocalHandles() const; int ZoneSizeInBytes() const; void UnwindScopes(uword stack_marker); void InitVMConstants(); void FixInitiallyNullFields(); int64_t GetNextTaskId() { return next_task_id_++; } static intptr_t next_task_id_offset() { return OFFSET_OF(Thread, next_task_id_); } Random* random() { return &random_; } static intptr_t random_offset() { return OFFSET_OF(Thread, random_); } #ifndef PRODUCT void PrintJSON(JSONStream* stream) const; #endif #if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER) HeapProfileSampler& heap_sampler() { return heap_sampler_; } #endif PendingDeopts& pending_deopts() { return pending_deopts_; } SafepointLevel current_safepoint_level() const { if (runtime_call_deopt_ability_ == RuntimeCallDeoptAbility::kCannotLazyDeopt) { return SafepointLevel::kGC; } #if defined(PRODUCT) return SafepointLevel::kGCAndDeopt; #else if (no_reload_scope_depth_ > 0) { return SafepointLevel::kGCAndDeopt; } if (execution_state_ == kThreadInNative || execution_state_ == kThreadInReloadableBlockedState) { return SafepointLevel::kGCAndDeoptAndReload; } if (allow_reload_scope_depth_ <= 0) { return SafepointLevel::kGCAndDeopt; } return SafepointLevel::kGCAndDeoptAndReload; #endif } #if defined(DART_BYTECODE_INTERPRETER) Interpreter* interpreter() const { return interpreter_; } void set_interpreter(Interpreter* value) { interpreter_ = value; } bytecode::BytecodeLoader* bytecode_loader() const { return bytecode_loader_; } void set_bytecode_loader(bytecode::BytecodeLoader* value) { bytecode_loader_ = value; } #endif void set_single_step(bool value) { single_step_ = value; } bool single_step() const { return single_step_; } static intptr_t single_step_offset() { return OFFSET_OF(Thread, single_step_); } bool IsDeoptimizing() const { return deopt_context_ != nullptr; } DeoptContext* deopt_context() const { return deopt_context_; } void set_deopt_context(DeoptContext* value) { ASSERT(value == nullptr || deopt_context_ == nullptr); deopt_context_ = value; } // The weak table used in the snapshot writer for the purpose of fast message // sending. WeakTable* forward_table_new() { return forward_table_new_.get(); } void set_forward_table_new(WeakTable* table); WeakTable* forward_table_old() { return forward_table_old_.get(); } void set_forward_table_old(WeakTable* table); MallocGrowableArray* pointers_to_verify_at_exit() { return &pointers_to_verify_at_exit_; } uword user_tag() const { return user_tag_; } static intptr_t user_tag_offset() { return OFFSET_OF(Thread, user_tag_); } static intptr_t current_tag_offset() { return OFFSET_OF(Thread, current_tag_); } static intptr_t default_tag_offset() { return OFFSET_OF(Thread, default_tag_); } UserTagPtr current_tag() const { return current_tag_; } void set_current_tag(const UserTag& tag); UserTagPtr default_tag() const { return default_tag_; } void set_default_tag(const UserTag& tag); void set_user_tag(uword tag) { user_tag_ = tag; } ArrayPtr thread_locals() const { return thread_locals_; } void set_thread_locals(const Array& thread_locals); static intptr_t thread_locals_offset() { return OFFSET_OF(Thread, thread_locals_); } private: template T* AllocateReusableHandle(); enum class RestoreWriteBarrierInvariantOp { kAddToRememberedSet, kAddToDeferredMarkingStack }; friend class RestoreWriteBarrierInvariantVisitor; void RestoreWriteBarrierInvariant(RestoreWriteBarrierInvariantOp op); // Set the current compiler state and return the previous compiler state. CompilerState* SetCompilerState(CompilerState* state) { CompilerState* previous = compiler_state_; compiler_state_ = state; return previous; } // Accessed from generated code. // ** This block of fields must come first! ** // For AOT cross-compilation, we rely on these members having the same offsets // in SIMARM(IA32) and ARM, and the same offsets in SIMARM64(X64) and ARM64. // We use only word-sized fields to avoid differences in struct packing on the // different architectures. See also CheckOffsets in dart.cc. volatile RelaxedAtomic stack_limit_ = 0; uword write_barrier_mask_; #if defined(DART_COMPRESSED_POINTERS) uword heap_base_ = 0; #endif std::atomic top_ = 0; uword end_ = 0; const uword* dispatch_table_array_ = nullptr; ObjectPtr* field_table_values_ = nullptr; ObjectPtr* shared_field_table_values_ = nullptr; // Offsets up to this point can all fit in a byte on X64. All of the above // fields are very abundantly accessed from code. Thus, keeping them first // is important for code size (although code size on X64 is not a priority). // 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 uword write_barrier_wrappers_entry_points_[kNumberOfDartAvailableCpuRegs]; #define DECLARE_MEMBERS(name) uword name##_entry_point_ = 0; CACHED_FUNCTION_ENTRY_POINTS_LIST(DECLARE_MEMBERS) #undef DECLARE_MEMBERS Isolate* isolate_ = nullptr; IsolateGroup* isolate_group_ = nullptr; uword saved_stack_limit_ = OSThread::kInvalidStackLimit; // The mutator uses this to indicate it wants to OSR (by // setting [Thread::kOsrRequest]) before going to runtime which will see this // bit. uword stack_overflow_flags_ = 0; uword volatile top_exit_frame_info_ = 0; StoreBufferBlock* store_buffer_block_ = nullptr; MarkingStackBlock* old_marking_stack_block_ = nullptr; MarkingStackBlock* new_marking_stack_block_ = nullptr; MarkingStackBlock* deferred_marking_stack_block_ = nullptr; uword volatile vm_tag_ = 0; // JumpToExceptionHandler state: ObjectPtr active_exception_; ObjectPtr active_stacktrace_; ObjectPoolPtr global_object_pool_; uword resume_pc_ = 0; uword saved_shadow_call_stack_ = 0; /* * The execution state for a thread. * * 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. * * Warning: Execution state doesn't imply the safepoint state. It's possible * to be in [kThreadInNative] and still not be at-safepoint (e.g. due to a * pending Dart_TypedDataAcquire() that increases no-callback-scope) */ uword execution_state_ = kThreadInNative; /* * Stores * * - whether the thread is at a safepoint (current thread sets these) * [AtSafepointField] * [AtDeoptSafepointField] * [AtReloadSafepointField] * * - whether the thread is requested to safepoint (other thread sets these) * [SafepointRequestedField] * [DeoptSafepointRequestedField] * [ReloadSafepointRequestedField] * * - whether the thread is blocked due to safepoint request and needs to * be resumed after safepoint is done (current thread sets this) * [BlockedForSafepointField] * * - whether the thread should be ignored for safepointing purposes * [BypassSafepointsField] * * - whether the isolate running this thread has triggered an unwind error, * which requires enforced exit on a transition from native back to * generated. * [UnwindErrorInProgressField] * * - whether a NoReloadScope is active (current thread sets these), this * causes transitions to native/FFI to take the slow path instead of * entering a reload safepoint * [NoReloadScopeField] */ std::atomic safepoint_state_ = 0; uword exit_through_ffi_ = 0; #define DECLARE_MEMBERS(returntype, name, ...) uword name##_entry_point_; LEAF_RUNTIME_ENTRY_LIST(DECLARE_MEMBERS) #undef DECLARE_MEMBERS ApiLocalScope* api_top_scope_ = nullptr; uint8_t double_truncate_round_supported_; // Memory locations dedicated for passing unboxed int64 and double // values from generated code to runtime. // TODO(dartbug.com/33549): Clean this up when unboxed values // could be passed as arguments. ALIGN8 simd128_value_t unboxed_runtime_arg_; ALIGN8 int64_t next_task_id_; ALIGN8 Random random_; TsanUtils* tsan_utils_ = nullptr; bool single_step_ = false; uword user_tag_ = 0; UserTagPtr current_tag_; UserTagPtr default_tag_; TimelineStream* const dart_stream_; StreamInfo* const service_extension_stream_; ArrayPtr thread_locals_ = nullptr; // ---- End accessed from generated code. ---- // The layout of Thread object up to this point should not depend // on DART_PRECOMPILED_RUNTIME, as it is accessed from generated code. // The code is generated without DART_PRECOMPILED_RUNTIME, but used with // DART_PRECOMPILED_RUNTIME. uword true_end_ = 0; mutable Monitor thread_lock_; ApiLocalScope* api_reusable_scope_ = nullptr; std::atomic task_kind_ = kUnknownTask; int32_t no_callback_scope_depth_ = 0; int32_t force_growth_scope_depth_ = 0; int32_t no_reload_scope_depth_ = 0; int32_t allow_reload_scope_depth_ = 0; int32_t stopped_mutators_scope_depth_ = 0; #if defined(DEBUG) int32_t no_safepoint_scope_depth_ = 0; #endif uint32_t stack_overflow_count_ = 0; uint32_t runtime_call_count_ = 0; VMHandles reusable_handles_; // Deoptimization of stack frames. RuntimeCallDeoptAbility runtime_call_deopt_ability_ = RuntimeCallDeoptAbility::kCanLazyDeopt; PendingDeopts pending_deopts_; // Compiler state: CompilerState* compiler_state_ = nullptr; HierarchyInfo* hierarchy_info_ = nullptr; TypeUsageInfo* type_usage_info_ = nullptr; NoActiveIsolateScope* no_active_isolate_scope_ = nullptr; CompilerTimings* compiler_timings_ = nullptr; ErrorPtr sticky_error_; ObjectPtr* field_table_values() const { return field_table_values_; } ObjectPtr* shared_field_table_values() const { return shared_field_table_values_; } // 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) using AtSafepointField = BitField; using SafepointRequestedField = BitField; using AtDeoptSafepointField = BitField; using DeoptSafepointRequestedField = BitField; using AtReloadSafepointField = BitField; using ReloadSafepointRequestedField = BitField; using ActiveMutatorStealableField = BitField; using ActiveMutatorStolenField = BitField; using BlockedForSafepointField = BitField; using BypassSafepointsField = BitField; using UnwindErrorInProgressField = BitField; using NoReloadScopeField = BitField; static uword AtSafepointBits(SafepointLevel level) { switch (level) { case SafepointLevel::kGC: return AtSafepointField::encode(true); case SafepointLevel::kGCAndDeopt: return AtSafepointField::encode(true) | AtDeoptSafepointField::encode(true); case SafepointLevel::kGCAndDeoptAndReload: return AtSafepointField::encode(true) | AtDeoptSafepointField::encode(true) | AtReloadSafepointField::encode(true); default: UNREACHABLE(); } } #if defined(USING_SAFE_STACK) uword saved_safestack_ = 0; #endif Thread* next_; // Used to chain the thread structures in an isolate. Isolate* scheduled_dart_mutator_isolate_ = nullptr; bool is_unwind_in_progress_ = false; #if defined(DEBUG) bool inside_compiler_ = false; #endif #if !defined(PRODUCT) || defined(FORCE_INCLUDE_SAMPLING_HEAP_PROFILER) HeapProfileSampler heap_sampler_; #endif #if defined(DART_BYTECODE_INTERPRETER) Interpreter* interpreter_ = nullptr; bytecode::BytecodeLoader* bytecode_loader_ = nullptr; #endif DeoptContext* deopt_context_ = nullptr; // Used during message sending of messages between isolates. std::unique_ptr forward_table_new_; std::unique_ptr forward_table_old_; MallocGrowableArray pointers_to_verify_at_exit_; explicit Thread(bool is_bootstrapping); void StoreBufferRelease( StoreBuffer::ThresholdPolicy policy = StoreBuffer::kCheckThreshold); void StoreBufferAcquire(); void OldMarkingStackRelease(); void OldMarkingStackAcquire(); void NewMarkingStackRelease(); void NewMarkingStackAcquire(); void DeferredMarkingStackRelease(); void DeferredMarkingStackAcquire(); void AcquireMarkingStacks(); void ReleaseMarkingStacks(); void FlushMarkingStacks(); void set_safepoint_state(uint32_t value) { safepoint_state_ = value; } void EnterSafepointUsingLock(); void ExitSafepointUsingLock(); void SetupStateLocked(TaskKind kind); void ResetStateLocked(); void SetupMutatorState(); void ResetMutatorState(); void SetupDartMutatorState(Isolate* isolate); void SetupDartMutatorStateDependingOnSnapshot(IsolateGroup* group); void ResetDartMutatorState(); static void SuspendDartMutatorThreadInternal(Thread* thread, VMTag::VMTagId tag); static void ResumeDartMutatorThreadInternal(Thread* thread); static void SuspendThreadInternal(Thread* thread, VMTag::VMTagId tag); static void ResumeThreadInternal(Thread* thread); // Adds a new active thread to thread registry while associating it // with the given isolate (group). // // All existing safepoint operations are waited for before adding the thread // to the thread registry. // // => Anyone who iterates the active threads will first have to get us to // safepoint (but can access `Thread::isolate()`). static Thread* AddActiveThread(IsolateGroup* group, Isolate* isolate, TaskKind task_kind, bool bypass_safepoint); // Releases an active thread from the thread registry. // // Thread needs to be at-safepoint. static void FreeActiveThread(Thread* thread, Isolate* isolate, bool bypass_safepoint); static void SetCurrent(Thread* current) { OSThread::SetCurrentTLS(current); } #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 ActiveIsolateScope; friend class Interpreter; friend class InterruptChecker; friend class Isolate; friend class IsolateGroup; friend class NoActiveIsolateScope; friend class NoReloadScope; friend class RawReloadParticipationScope; friend class Simulator; friend class StackZone; friend class StoppedMutatorsScope; friend class ThreadRegistry; friend class CompilerState; friend class compiler::target::Thread; friend class FieldTable; friend class RuntimeCallDeoptScope; friend class Dart; // Calls SetupCachedEntryPoints after snapshot reading friend class TransitionGeneratedToVM; // IsSafepointRequested/BlockForSafepoint friend class TransitionVMToGenerated; // IsSafepointRequested/BlockForSafepoint friend class MonitorLocker; // ExitSafepointUsingLock friend Isolate* CreateWithinExistingIsolateGroup(IsolateGroup*, const char*, char**); DISALLOW_COPY_AND_ASSIGN(Thread); }; class RuntimeCallDeoptScope : public StackResource { public: RuntimeCallDeoptScope(Thread* thread, RuntimeCallDeoptAbility kind) : StackResource(thread) { // We cannot have nested calls into the VM without deopt support. ASSERT(thread->runtime_call_deopt_ability_ == RuntimeCallDeoptAbility::kCanLazyDeopt); thread->runtime_call_deopt_ability_ = kind; } virtual ~RuntimeCallDeoptScope() { thread()->runtime_call_deopt_ability_ = RuntimeCallDeoptAbility::kCanLazyDeopt; } private: Thread* thread() { return reinterpret_cast(StackResource::thread()); } }; #if defined(DART_HOST_OS_WINDOWS) // Clears the state of the current thread and frees the allocation. void WindowsThreadCleanUp(); #endif #if defined(DART_INCLUDE_PROFILER) // Disable thread interrupts. class DisableThreadInterruptsScope : public StackResource { public: explicit DisableThreadInterruptsScope(Thread* thread); ~DisableThreadInterruptsScope(); }; #else class DisableThreadInterruptsScope : public StackResource { public: explicit DisableThreadInterruptsScope(Thread* thread) : StackResource(thread) {} ~DisableThreadInterruptsScope() {} }; #endif // defined(DART_INCLUDE_PROFILER) // Within a NoSafepointScope, the thread must not reach any safepoint. Used // around code that manipulates raw object pointers directly without handles. #if defined(DEBUG) class NoSafepointScope : public ThreadStackResource { public: explicit NoSafepointScope(Thread* thread = nullptr) : ThreadStackResource(thread != nullptr ? thread : Thread::Current()) { this->thread()->IncrementNoSafepointScopeDepth(); } ~NoSafepointScope() { thread()->DecrementNoSafepointScopeDepth(); } private: DISALLOW_COPY_AND_ASSIGN(NoSafepointScope); }; #else // defined(DEBUG) class NoSafepointScope : public ValueObject { public: explicit NoSafepointScope(Thread* thread = nullptr) {} private: DISALLOW_COPY_AND_ASSIGN(NoSafepointScope); }; #endif // defined(DEBUG) // Disables initiating a reload operation as well as participating in another // threads reload operation. // // Reload triggered by a mutator thread happens by sending all other mutator // threads (that are running) OOB messages to check into a safepoint. The thread // initiating the reload operation will block until all mutators are at a reload // safepoint. // // When running under this scope, the processing of those OOB messages will // ignore reload safepoint checkin requests. Yet we'll have to ensure that the // dropped message is still acted upon. // // => To solve this we make the [~NoReloadScope] destructor resend a new reload // OOB request to itself (the [~NoReloadScope] destructor is not necessarily at // well-defined place where reload can happen - those places will explicitly // opt-in via [ReloadParticipationScope]). // class NoReloadScope : public ThreadStackResource { public: explicit NoReloadScope(Thread* thread); ~NoReloadScope(); private: DISALLOW_COPY_AND_ASSIGN(NoReloadScope); }; // Allows triggering reload safepoint operations as well as participating in // reload operations (at safepoint checks). // // By-default safepoint checkins will not participate in reload operations, as // reload has to happen at very well-defined places. This scope is intended // for those places where we explicitly want to allow safepoint checkins to // participate in reload operations (triggered by other threads). // // If there is any [NoReloadScope] active we will still disable the safepoint // checkins to participate in reload. // // We also require the thread inititating a reload operation to explicitly // opt-in via this scope. class RawReloadParticipationScope { public: explicit RawReloadParticipationScope(Thread* thread) : thread_(thread) { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) thread->allow_reload_scope_depth_++; ASSERT(thread->allow_reload_scope_depth_ >= 0); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) } ~RawReloadParticipationScope() { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) thread_->allow_reload_scope_depth_ -= 1; ASSERT(thread_->allow_reload_scope_depth_ >= 0); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) } private: Thread* thread_; DISALLOW_COPY_AND_ASSIGN(RawReloadParticipationScope); }; using ReloadParticipationScope = AsThreadStackResource; class StoppedMutatorsScope : public ThreadStackResource { public: explicit StoppedMutatorsScope(Thread* thread) : ThreadStackResource(thread) { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) thread->stopped_mutators_scope_depth_++; ASSERT(thread->stopped_mutators_scope_depth_ >= 0); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) } ~StoppedMutatorsScope() { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) thread()->stopped_mutators_scope_depth_ -= 1; ASSERT(thread()->stopped_mutators_scope_depth_ >= 0); #endif // !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) } private: DISALLOW_COPY_AND_ASSIGN(StoppedMutatorsScope); }; // Within a EnterCompilerScope, the thread must operate on cloned fields. #if defined(DEBUG) class EnterCompilerScope : public ThreadStackResource { public: explicit EnterCompilerScope(Thread* thread = nullptr) : ThreadStackResource(thread != nullptr ? thread : Thread::Current()) { previously_is_inside_compiler_ = this->thread()->IsInsideCompiler(); if (!previously_is_inside_compiler_) { this->thread()->EnterCompiler(); } } ~EnterCompilerScope() { if (!previously_is_inside_compiler_) { thread()->LeaveCompiler(); } } private: bool previously_is_inside_compiler_; DISALLOW_COPY_AND_ASSIGN(EnterCompilerScope); }; #else // defined(DEBUG) class EnterCompilerScope : public ValueObject { public: explicit EnterCompilerScope(Thread* thread = nullptr) {} private: DISALLOW_COPY_AND_ASSIGN(EnterCompilerScope); }; #endif // defined(DEBUG) // Within a LeaveCompilerScope, the thread must operate on cloned fields. #if defined(DEBUG) class LeaveCompilerScope : public ThreadStackResource { public: explicit LeaveCompilerScope(Thread* thread = nullptr) : ThreadStackResource(thread != nullptr ? thread : Thread::Current()) { previously_is_inside_compiler_ = this->thread()->IsInsideCompiler(); if (previously_is_inside_compiler_) { this->thread()->LeaveCompiler(); } } ~LeaveCompilerScope() { if (previously_is_inside_compiler_) { thread()->EnterCompiler(); } } private: bool previously_is_inside_compiler_; DISALLOW_COPY_AND_ASSIGN(LeaveCompilerScope); }; #else // defined(DEBUG) class LeaveCompilerScope : public ValueObject { public: explicit LeaveCompilerScope(Thread* thread = nullptr) {} private: DISALLOW_COPY_AND_ASSIGN(LeaveCompilerScope); }; #endif // defined(DEBUG) } // namespace dart #endif // RUNTIME_VM_THREAD_H_