// 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 <setjmp.h>
#include <memory>
#include <utility>

#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<uword>(&double_nan_constant), \
    0)                                                                         \
  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)

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<void*>(&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<Thread> {
 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<Thread*>(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<uword>(&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<SafepointLevel>(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<ExecutionState>(execution_state_);
  }
  void set_execution_state(ExecutionState state) {
    execution_state_ = static_cast<uword>(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_DYNAMIC_MODULES)
  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<ObjectPtr>* 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 <class T>
  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<uword> stack_limit_ = 0;
  uword write_barrier_mask_;
#if defined(DART_COMPRESSED_POINTERS)
  uword heap_base_ = 0;
#endif
  std::atomic<uword> 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<uword> 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<TaskKind> 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<uword, bool>;
  using SafepointRequestedField =
      BitField<uword, bool, AtSafepointField::kNextBit>;
  using AtDeoptSafepointField =
      BitField<uword, bool, SafepointRequestedField::kNextBit>;
  using DeoptSafepointRequestedField =
      BitField<uword, bool, AtDeoptSafepointField::kNextBit>;
  using AtReloadSafepointField =
      BitField<uword, bool, DeoptSafepointRequestedField::kNextBit>;
  using ReloadSafepointRequestedField =
      BitField<uword, bool, AtReloadSafepointField::kNextBit>;
  using ActiveMutatorStealableField =
      BitField<uword, bool, ReloadSafepointRequestedField::kNextBit>;
  using ActiveMutatorStolenField =
      BitField<uword, bool, ActiveMutatorStealableField::kNextBit>;
  using BlockedForSafepointField =
      BitField<uword, bool, ActiveMutatorStolenField::kNextBit>;
  using BypassSafepointsField =
      BitField<uword, bool, BlockedForSafepointField::kNextBit>;
  using UnwindErrorInProgressField =
      BitField<uword, bool, BypassSafepointsField::kNextBit>;
  using NoReloadScopeField =
      BitField<uword, bool, UnwindErrorInProgressField::kNextBit>;

  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_DYNAMIC_MODULES)
  Interpreter* interpreter_ = nullptr;
  bytecode::BytecodeLoader* bytecode_loader_ = nullptr;
#endif

  DeoptContext* deopt_context_ = nullptr;

  // Used during message sending of messages between isolates.
  std::unique_ptr<WeakTable> forward_table_new_;
  std::unique_ptr<WeakTable> forward_table_old_;

  MallocGrowableArray<ObjectPtr> 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<Thread*>(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<RawReloadParticipationScope>;

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_
