From a9ce969e531736ace0cd0750eedc55e9a45ed4c2 Mon Sep 17 00:00:00 2001 From: Vyacheslav Egorov Date: Fri, 11 Jan 2019 20:47:10 +0000 Subject: [PATCH] [vm] Decouple growable_array.h and zone.h from thread.h - Introduce a slimmed down version of thread.h, which just depends on the Zone and StackResource. - Introduce a layering check that would prevent the coupling in the future. This is the first step towards decoupling compiler from runtime. There are multiple reasons to introduce the decoupling but the main reason currently is to introduce a controlled surface through which compiler reaches into runtime to catch any places where runtime word size might influence the compiler and then enable building compiler that targets 32-bit runtime but is embedded into a 64-bit runtime. Issue https://github.com/dart-lang/sdk/issues/31709 Change-Id: Id63ebbaddca55dd097298e51c90d957a73fa476e Reviewed-on: https://dart-review.googlesource.com/c/87182 Commit-Queue: Vyacheslav Egorov Reviewed-by: Martin Kustermann --- PRESUBMIT.py | 29 ++++- runtime/tools/layering_check.py | 127 ++++++++++++++++++++++ runtime/vm/allocation.cc | 21 +--- runtime/vm/allocation.h | 39 ++----- runtime/vm/clustered_snapshot.cc | 4 +- runtime/vm/clustered_snapshot.h | 4 +- runtime/vm/compiler/assembler/assembler.h | 1 + runtime/vm/compiler/backend/il.h | 4 +- runtime/vm/compiler/backend/inliner.h | 1 + runtime/vm/compiler/compiler_pass.h | 1 + runtime/vm/compiler/compiler_state.h | 14 ++- runtime/vm/compiler/method_recognizer.h | 1 + runtime/vm/dart.cc | 17 ++- runtime/vm/dart_api_impl.h | 4 +- runtime/vm/dart_entry.cc | 5 +- runtime/vm/growable_array.h | 10 +- runtime/vm/handles.cc | 2 +- runtime/vm/handles.h | 3 +- runtime/vm/hash_map.h | 2 +- runtime/vm/heap/heap.cc | 8 +- runtime/vm/heap/heap.h | 8 +- runtime/vm/heap/safepoint.cc | 3 +- runtime/vm/heap/safepoint.h | 7 +- runtime/vm/heap/verifier.h | 1 + runtime/vm/interpreter.h | 1 + runtime/vm/isolate.cc | 7 +- runtime/vm/isolate.h | 5 +- runtime/vm/log.h | 4 +- runtime/vm/longjump.h | 10 +- runtime/vm/object_graph.cc | 2 +- runtime/vm/object_graph.h | 3 +- runtime/vm/os_thread.cc | 4 +- runtime/vm/os_thread.h | 17 +-- runtime/vm/os_win.cc | 2 +- runtime/vm/raw_object.h | 1 + runtime/vm/service_isolate.h | 1 + runtime/vm/simulator_dbc.h | 17 +-- runtime/vm/tags.cc | 2 +- runtime/vm/tags.h | 3 +- runtime/vm/thread.cc | 58 +++------- runtime/vm/thread.h | 76 +++++-------- runtime/vm/thread_interrupter.cc | 10 -- runtime/vm/thread_interrupter_win.cc | 2 +- runtime/vm/thread_registry.cc | 2 +- runtime/vm/thread_stack_resource.cc | 26 +++++ runtime/vm/thread_stack_resource.h | 32 ++++++ runtime/vm/thread_state.cc | 41 +++++++ runtime/vm/thread_state.h | 91 ++++++++++++++++ runtime/vm/type_testing_stubs.cc | 2 +- runtime/vm/type_testing_stubs.h | 2 +- runtime/vm/uri.cc | 16 +-- runtime/vm/vm_sources.gni | 4 + runtime/vm/zone.cc | 6 +- runtime/vm/zone.h | 5 +- 54 files changed, 518 insertions(+), 250 deletions(-) create mode 100755 runtime/tools/layering_check.py create mode 100644 runtime/vm/thread_stack_resource.cc create mode 100644 runtime/vm/thread_stack_resource.h create mode 100644 runtime/vm/thread_state.cc create mode 100644 runtime/vm/thread_state.h diff --git a/PRESUBMIT.py b/PRESUBMIT.py index 57b7bfaa8b7..f7291e8c808 100644 --- a/PRESUBMIT.py +++ b/PRESUBMIT.py @@ -168,15 +168,40 @@ def _CheckValidHostsInDEPS(input_api, output_api): 'DEPS file must have only dependencies from allowed hosts.', long_text=error.output)] +def _CheckLayering(input_api, output_api): + """Run VM layering check. + + This check validates that sources from one layer do not reference sources + from another layer accidentally. + """ + # Run only if .cc or .h file was modified. + def is_cpp_file(path): + return path.endswith('.cc') or path.endswith('.h') + if all(not is_cpp_file(f.LocalPath()) for f in input_api.AffectedFiles()): + return [] + + local_root = input_api.change.RepositoryRoot() + layering_check = imp.load_source('layering_check', + os.path.join(local_root, 'runtime', 'tools', 'layering_check.py')) + errors = layering_check.DoCheck(local_root) + if errors: + return [output_api.PresubmitError( + 'Layering check violation for C++ sources.', + long_text='\n'.join(errors))] + else: + return [] + def CheckChangeOnCommit(input_api, output_api): return (_CheckValidHostsInDEPS(input_api, output_api) + _CheckBuildStatus(input_api, output_api) + _CheckDartFormat(input_api, output_api) + - _CheckStatusFiles(input_api, output_api)) + _CheckStatusFiles(input_api, output_api) + + _CheckLayering(input_api, output_api)) def CheckChangeOnUpload(input_api, output_api): return (_CheckValidHostsInDEPS(input_api, output_api) + _CheckDartFormat(input_api, output_api) + - _CheckStatusFiles(input_api, output_api)) + _CheckStatusFiles(input_api, output_api) + + _CheckLayering(input_api, output_api)) diff --git a/runtime/tools/layering_check.py b/runtime/tools/layering_check.py new file mode 100755 index 00000000000..50a9b25644a --- /dev/null +++ b/runtime/tools/layering_check.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# +# Copyright (c) 2019, 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. + +# Simple tool for verifying that sources from one layer do not reference +# sources from another layer. +# +# Currently it only checks that core runtime headers RUNTIME_LAYER_HEADERS +# are not included into any sources listed in SHOULD_NOT_DEPEND_ON_RUNTIME. + +import glob +import os +import re +import sys + +INCLUDE_DIRECTIVE_RE = re.compile(r'^#include "(.*)"') + +RUNTIME_LAYER_HEADERS = [ + 'runtime/vm/isolate.h', + 'runtime/vm/object.h', + 'runtime/vm/raw_object.h', + 'runtime/vm/thread.h', +] + +SHOULD_NOT_DEPEND_ON_RUNTIME = [ + 'runtime/vm/allocation.h', + 'runtime/vm/growable_array.h', +] + +class LayeringChecker(object): + def __init__(self, root): + self.root = root + self.worklist = set() + # Mapping from header to a set of files it is included into. + self.included_into = dict() + # Set of files that were parsed to avoid double parsing. + self.loaded = set() + # Mapping from headers to their layer. + self.file_layers = {file: 'runtime' for file in RUNTIME_LAYER_HEADERS} + + def Check(self): + self.AddAllSourcesToWorklist(os.path.join(self.root, 'runtime/vm')) + self.BuildIncludesGraph() + errors = self.PropagateLayers() + errors += self.CheckNotInRuntime(SHOULD_NOT_DEPEND_ON_RUNTIME) + return errors + + def CheckNotInRuntime(self, files): + """Check that given files do not depend on runtime layer.""" + errors = [] + for file in files: + if not os.path.exists(os.path.join(self.root, file)): + errors.append('File %s does not exist.' % (file)) + if self.file_layers.get(file) is not None: + errors.append( + 'LAYERING ERROR: %s includes object.h or raw_object.h' % (file)) + return errors + + def BuildIncludesGraph(self): + while self.worklist: + file = self.worklist.pop() + deps = self.ExtractIncludes(file) + self.loaded.add(file) + for d in deps: + if d not in self.included_into: + self.included_into[d] = set() + self.included_into[d].add(file) + if d not in self.loaded: + self.worklist.add(d) + + def PropagateLayers(self): + """Propagate layering information through include graph. + + If A is in layer L and A is included into B then B is in layer L. + """ + errors = [] + self.worklist = set(self.file_layers.keys()) + while self.worklist: + file = self.worklist.pop() + if file not in self.included_into: + continue + file_layer = self.file_layers[file] + for tgt in self.included_into[file]: + if tgt in self.file_layers: + if self.file_layers[tgt] != file_layer: + errors.add('Layer mismatch: %s (%s) is included into %s (%s)' % ( + file, file_layer, tgt, self.file_layers[tgt])) + self.file_layers[tgt] = file_layer + self.worklist.add(tgt) + return errors + + def AddAllSourcesToWorklist(self, dir): + """Add all *.cc and *.h files from dir recursively into worklist.""" + for file in os.listdir(dir): + path = os.path.join(dir, file) + if os.path.isdir(path): + self.AddAllSourcesToWorklist(path) + elif path.endswith('.cc') or path.endswith('.h'): + self.worklist.add(os.path.relpath(path, self.root)) + + def ExtractIncludes(self, file): + """Extract the list of includes from the given file.""" + deps = set() + with open(os.path.join(self.root, file)) as file: + for line in file: + if line.startswith('namespace dart {'): + break + + m = INCLUDE_DIRECTIVE_RE.match(line) + if m is not None: + header = os.path.join('runtime', m.group(1)) + if os.path.isfile(os.path.join(self.root,header)): + deps.add(header) + return deps + +def DoCheck(sdk_root): + """Run layering check at the given root folder.""" + return LayeringChecker(sdk_root).Check() + +if __name__ == '__main__': + errors = DoCheck('.') + print '\n'.join(errors) + if errors: + sys.exit(-1) + diff --git a/runtime/vm/allocation.cc b/runtime/vm/allocation.cc index b1a7e937652..aa36221f85b 100644 --- a/runtime/vm/allocation.cc +++ b/runtime/vm/allocation.cc @@ -37,16 +37,11 @@ StackResource::~StackResource() { #if defined(DEBUG) if (thread_ != NULL) { ASSERT(Thread::Current() == thread_); - BaseIsolate::AssertCurrent(reinterpret_cast(isolate())); } #endif } -Isolate* StackResource::isolate() const { - return thread_ == NULL ? NULL : thread_->isolate(); -} - -void StackResource::Init(Thread* thread) { +void StackResource::Init(ThreadState* thread) { // We can only have longjumps and exceptions when there is a current // thread and isolate. If there is no current thread, we don't need to // protect this case. @@ -60,7 +55,7 @@ void StackResource::Init(Thread* thread) { } } -void StackResource::UnwindAbove(Thread* thread, StackResource* new_top) { +void StackResource::UnwindAbove(ThreadState* thread, StackResource* new_top) { StackResource* current_resource = thread->top_resource(); while (current_resource != new_top) { current_resource->~StackResource(); @@ -68,16 +63,4 @@ void StackResource::UnwindAbove(Thread* thread, StackResource* new_top) { } } -#if defined(DEBUG) -NoSafepointScope::NoSafepointScope(Thread* current_thread) - : StackResource(current_thread != nullptr ? current_thread - : Thread::Current()) { - thread()->IncrementNoSafepointScopeDepth(); -} - -NoSafepointScope::~NoSafepointScope() { - thread()->DecrementNoSafepointScopeDepth(); -} -#endif // defined(DEBUG) - } // namespace dart diff --git a/runtime/vm/allocation.h b/runtime/vm/allocation.h index b9af2f935f4..420b85ea187 100644 --- a/runtime/vm/allocation.h +++ b/runtime/vm/allocation.h @@ -13,8 +13,7 @@ namespace dart { // Forward declarations. -class Isolate; -class Thread; +class ThreadState; // Stack resources subclass from this base class. The VM will ensure that the // destructors of these objects are called before the stack is unwound past the @@ -23,27 +22,24 @@ class Thread; // to a stack frame above the frame where these objects were allocated. class StackResource { public: - explicit StackResource(Thread* thread) : thread_(NULL), previous_(NULL) { + explicit StackResource(ThreadState* thread) : thread_(NULL), previous_(NULL) { Init(thread); } virtual ~StackResource(); - // Convenient access to the isolate of the thread of this resource. - Isolate* isolate() const; - // The thread that owns this resource. - Thread* thread() const { return thread_; } + ThreadState* thread() const { return thread_; } // Destroy stack resources of thread until top exit frame. - static void Unwind(Thread* thread) { UnwindAbove(thread, NULL); } + static void Unwind(ThreadState* thread) { UnwindAbove(thread, NULL); } // Destroy stack resources of thread above new_top, exclusive. - static void UnwindAbove(Thread* thread, StackResource* new_top); + static void UnwindAbove(ThreadState* thread, StackResource* new_top); private: - void Init(Thread* thread); + void Init(ThreadState* thread); - Thread* thread_; + ThreadState* thread_; StackResource* previous_; DISALLOW_ALLOCATION(); @@ -77,27 +73,6 @@ class ZoneAllocated { DISALLOW_COPY_AND_ASSIGN(ZoneAllocated); }; -// 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 StackResource { - public: - explicit NoSafepointScope(Thread* thread = nullptr); - ~NoSafepointScope(); - - 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) - } // namespace dart #endif // RUNTIME_VM_ALLOCATION_H_ diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc index ffc49f075ab..779e9fed95e 100644 --- a/runtime/vm/clustered_snapshot.cc +++ b/runtime/vm/clustered_snapshot.cc @@ -4202,7 +4202,7 @@ Serializer::Serializer(Thread* thread, ImageWriter* image_writer, bool vm, V8SnapshotProfileWriter* profile_writer) - : StackResource(thread), + : ThreadStackResource(thread), heap_(thread->isolate()->heap()), zone_(thread->zone()), kind_(kind), @@ -4866,7 +4866,7 @@ Deserializer::Deserializer(Thread* thread, const uint8_t* instructions_buffer, const uint8_t* shared_data_buffer, const uint8_t* shared_instructions_buffer) - : StackResource(thread), + : ThreadStackResource(thread), heap_(thread->isolate()->heap()), zone_(thread->zone()), kind_(kind), diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h index f1eb0c97ddf..420742db44e 100644 --- a/runtime/vm/clustered_snapshot.h +++ b/runtime/vm/clustered_snapshot.h @@ -128,7 +128,7 @@ class SmiObjectIdPairTrait { typedef DirectChainedHashMap SmiObjectIdMap; -class Serializer : public StackResource { +class Serializer : public ThreadStackResource { public: Serializer(Thread* thread, Snapshot::Kind kind, @@ -431,7 +431,7 @@ struct SerializerWritingObjectScope { Serializer* serializer_; }; -class Deserializer : public StackResource { +class Deserializer : public ThreadStackResource { public: Deserializer(Thread* thread, Snapshot::Kind kind, diff --git a/runtime/vm/compiler/assembler/assembler.h b/runtime/vm/compiler/assembler/assembler.h index 8d600d5279c..2ecb376cc8b 100644 --- a/runtime/vm/compiler/assembler/assembler.h +++ b/runtime/vm/compiler/assembler/assembler.h @@ -11,6 +11,7 @@ #include "vm/growable_array.h" #include "vm/hash_map.h" #include "vm/object.h" +#include "vm/thread.h" namespace dart { diff --git a/runtime/vm/compiler/backend/il.h b/runtime/vm/compiler/backend/il.h index 8bbb6d1cbb0..7020590d11f 100644 --- a/runtime/vm/compiler/backend/il.h +++ b/runtime/vm/compiler/backend/il.h @@ -181,10 +181,10 @@ struct CidRange : public ZoneAllocated { typedef MallocGrowableArray CidRangeVector; -class HierarchyInfo : public StackResource { +class HierarchyInfo : public ThreadStackResource { public: explicit HierarchyInfo(Thread* thread) - : StackResource(thread), + : ThreadStackResource(thread), cid_subtype_ranges_(NULL), cid_subtype_ranges_abstract_(NULL), cid_subclass_ranges_(NULL) { diff --git a/runtime/vm/compiler/backend/inliner.h b/runtime/vm/compiler/backend/inliner.h index 96639a24fa2..34366cf9181 100644 --- a/runtime/vm/compiler/backend/inliner.h +++ b/runtime/vm/compiler/backend/inliner.h @@ -7,6 +7,7 @@ #include "vm/allocation.h" #include "vm/growable_array.h" +#include "vm/token_position.h" namespace dart { diff --git a/runtime/vm/compiler/compiler_pass.h b/runtime/vm/compiler/compiler_pass.h index 128343a1d61..1e0f61352a2 100644 --- a/runtime/vm/compiler/compiler_pass.h +++ b/runtime/vm/compiler/compiler_pass.h @@ -51,6 +51,7 @@ class FlowGraph; class Function; class Precompiler; class SpeculativeInliningPolicy; +class TimelineStream; struct CompilerPassState { CompilerPassState(Thread* thread, diff --git a/runtime/vm/compiler/compiler_state.h b/runtime/vm/compiler/compiler_state.h index e9bc6751eb4..774f74d9476 100644 --- a/runtime/vm/compiler/compiler_state.h +++ b/runtime/vm/compiler/compiler_state.h @@ -6,6 +6,7 @@ #define RUNTIME_VM_COMPILER_COMPILER_STATE_H_ #include "vm/compiler/cha.h" +#include "vm/heap/safepoint.h" #include "vm/thread.h" namespace dart { @@ -53,9 +54,10 @@ class DeoptId : public AllStatic { }; // Global compiler state attached to the thread. -class CompilerState : public StackResource { +class CompilerState : public ThreadStackResource { public: - explicit CompilerState(Thread* thread) : StackResource(thread), cha_(thread) { + explicit CompilerState(Thread* thread) + : ThreadStackResource(thread), cha_(thread) { previous_ = thread->SetCompilerState(this); } @@ -127,10 +129,10 @@ class CompilerState : public StackResource { CompilerState* previous_; }; -class DeoptIdScope : public StackResource { +class DeoptIdScope : public ThreadStackResource { public: DeoptIdScope(Thread* thread, intptr_t deopt_id) - : StackResource(thread), + : ThreadStackResource(thread), prev_deopt_id_(thread->compiler_state().deopt_id()) { thread->compiler_state().set_deopt_id(deopt_id); } @@ -145,10 +147,10 @@ class DeoptIdScope : public StackResource { /// Ensures that there were no deopt id allocations during the lifetime of this /// object. -class AssertNoDeoptIdsAllocatedScope : public StackResource { +class AssertNoDeoptIdsAllocatedScope : public ThreadStackResource { public: explicit AssertNoDeoptIdsAllocatedScope(Thread* thread) - : StackResource(thread), + : ThreadStackResource(thread), prev_deopt_id_(thread->compiler_state().deopt_id()) {} ~AssertNoDeoptIdsAllocatedScope() { diff --git a/runtime/vm/compiler/method_recognizer.h b/runtime/vm/compiler/method_recognizer.h index c3310b87474..e901118b35b 100644 --- a/runtime/vm/compiler/method_recognizer.h +++ b/runtime/vm/compiler/method_recognizer.h @@ -504,6 +504,7 @@ namespace dart { // Forward declarations. class Function; class Library; +class Object; class RawFunction; class RawGrowableObjectArray; class String; diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc index e1ee86a8026..a9519916e30 100644 --- a/runtime/vm/dart.cc +++ b/runtime/vm/dart.cc @@ -84,16 +84,19 @@ class ReadOnlyHandles { }; static void CheckOffsets() { + bool ok = true; #define CHECK_OFFSET(expr, offset) \ if ((expr) != (offset)) { \ - FATAL2("%s == %" Pd, #expr, (expr)); \ + OS::PrintErr("%s got %" Pd " expected %" Pd "\n", #expr, (expr), \ + static_cast(offset)); \ + ok = false; \ } #if defined(TARGET_ARCH_ARM) // These offsets are embedded in precompiled instructions. We need simarm // (compiler) and arm (runtime) to agree. - CHECK_OFFSET(Thread::stack_limit_offset(), 4); - CHECK_OFFSET(Thread::object_null_offset(), 64); + CHECK_OFFSET(Thread::stack_limit_offset(), 28); + CHECK_OFFSET(Thread::object_null_offset(), 88); CHECK_OFFSET(SingleTargetCache::upper_limit_offset(), 14); CHECK_OFFSET(Isolate::object_store_offset(), 20); NOT_IN_PRODUCT(CHECK_OFFSET(sizeof(ClassHeapStats), 168)); @@ -101,12 +104,16 @@ static void CheckOffsets() { #if defined(TARGET_ARCH_ARM64) // These offsets are embedded in precompiled instructions. We need simarm64 // (compiler) and arm64 (runtime) to agree. - CHECK_OFFSET(Thread::stack_limit_offset(), 8); - CHECK_OFFSET(Thread::object_null_offset(), 120); + CHECK_OFFSET(Thread::stack_limit_offset(), 56); + CHECK_OFFSET(Thread::object_null_offset(), 168); CHECK_OFFSET(SingleTargetCache::upper_limit_offset(), 26); CHECK_OFFSET(Isolate::object_store_offset(), 40); NOT_IN_PRODUCT(CHECK_OFFSET(sizeof(ClassHeapStats), 288)); #endif + + if (!ok) { + FATAL("CheckOffsets failed."); + } #undef CHECK_OFFSET } diff --git a/runtime/vm/dart_api_impl.h b/runtime/vm/dart_api_impl.h index adb718267ae..8d91094d2eb 100644 --- a/runtime/vm/dart_api_impl.h +++ b/runtime/vm/dart_api_impl.h @@ -131,9 +131,9 @@ const char* CanonicalFunction(const char* func); class Api : AllStatic { public: // Create on the stack to provide a new throw-safe api scope. - class Scope : public StackResource { + class Scope : public ThreadStackResource { public: - explicit Scope(Thread* thread) : StackResource(thread) { + explicit Scope(Thread* thread) : ThreadStackResource(thread) { thread->EnterApiScope(); } ~Scope() { thread()->ExitApiScope(); } diff --git a/runtime/vm/dart_entry.cc b/runtime/vm/dart_entry.cc index 4e0acbce24f..a741a5ef5dc 100644 --- a/runtime/vm/dart_entry.cc +++ b/runtime/vm/dart_entry.cc @@ -93,10 +93,11 @@ class ScopedIsolateStackLimits : public ValueObject { // Clears/restores Thread::long_jump_base on construction/destruction. // Ensures that we do not attempt to long jump across Dart frames. -class SuspendLongJumpScope : public StackResource { +class SuspendLongJumpScope : public ThreadStackResource { public: explicit SuspendLongJumpScope(Thread* thread) - : StackResource(thread), saved_long_jump_base_(thread->long_jump_base()) { + : ThreadStackResource(thread), + saved_long_jump_base_(thread->long_jump_base()) { thread->set_long_jump_base(NULL); } diff --git a/runtime/vm/growable_array.h b/runtime/vm/growable_array.h index 4788bffa624..5cd2d0bf2bb 100644 --- a/runtime/vm/growable_array.h +++ b/runtime/vm/growable_array.h @@ -11,7 +11,7 @@ #define RUNTIME_VM_GROWABLE_ARRAY_H_ #include "platform/growable_array.h" -#include "vm/thread.h" +#include "vm/thread_state.h" #include "vm/zone.h" namespace dart { @@ -25,10 +25,10 @@ class GrowableArray : public BaseGrowableArray { explicit GrowableArray(intptr_t initial_capacity) : BaseGrowableArray( initial_capacity, - ASSERT_NOTNULL(Thread::Current()->zone())) {} + ASSERT_NOTNULL(ThreadState::Current()->zone())) {} GrowableArray() : BaseGrowableArray( - ASSERT_NOTNULL(Thread::Current()->zone())) {} + ASSERT_NOTNULL(ThreadState::Current()->zone())) {} }; template @@ -40,10 +40,10 @@ class ZoneGrowableArray : public BaseGrowableArray { explicit ZoneGrowableArray(intptr_t initial_capacity) : BaseGrowableArray( initial_capacity, - ASSERT_NOTNULL(Thread::Current()->zone())) {} + ASSERT_NOTNULL(ThreadState::Current()->zone())) {} ZoneGrowableArray() : BaseGrowableArray( - ASSERT_NOTNULL(Thread::Current()->zone())) {} + ASSERT_NOTNULL(ThreadState::Current()->zone())) {} }; // T must be a Handle type. diff --git a/runtime/vm/handles.cc b/runtime/vm/handles.cc index 33e2e70a159..3c429ef015f 100644 --- a/runtime/vm/handles.cc +++ b/runtime/vm/handles.cc @@ -84,7 +84,7 @@ void HandleScope::Initialize() { #endif } -HandleScope::HandleScope(Thread* thread) : StackResource(thread) { +HandleScope::HandleScope(Thread* thread) : ThreadStackResource(thread) { Initialize(); } diff --git a/runtime/vm/handles.h b/runtime/vm/handles.h index 7eaf8a635c3..522aa029c41 100644 --- a/runtime/vm/handles.h +++ b/runtime/vm/handles.h @@ -8,6 +8,7 @@ #include "vm/allocation.h" #include "vm/flags.h" #include "vm/os.h" +#include "vm/thread_stack_resource.h" namespace dart { @@ -278,7 +279,7 @@ class VMHandles : public Handles( - ASSERT_NOTNULL(Thread::Current()->zone())) {} + ASSERT_NOTNULL(ThreadState::Current()->zone())) {} explicit DirectChainedHashMap(Zone* zone) : BaseDirectChainedHashMap( diff --git a/runtime/vm/heap/heap.cc b/runtime/vm/heap/heap.cc index a9c9e5d872e..e2848d34cd6 100644 --- a/runtime/vm/heap/heap.cc +++ b/runtime/vm/heap/heap.cc @@ -200,7 +200,7 @@ void Heap::VisitObjectsImagePages(ObjectVisitor* visitor) const { } HeapIterationScope::HeapIterationScope(Thread* thread, bool writable) - : StackResource(thread), + : ThreadStackResource(thread), heap_(isolate()->heap()), old_space_(heap_->old_space()), writable_(writable) { @@ -955,7 +955,7 @@ void Heap::PrintStatsToTimeline(TimelineEventScope* event, GCReason reason) { } NoHeapGrowthControlScope::NoHeapGrowthControlScope() - : StackResource(Thread::Current()) { + : ThreadStackResource(Thread::Current()) { Heap* heap = reinterpret_cast(isolate())->heap(); current_growth_controller_state_ = heap->GrowthControlState(); heap->DisableGrowthControl(); @@ -967,7 +967,7 @@ NoHeapGrowthControlScope::~NoHeapGrowthControlScope() { } WritableVMIsolateScope::WritableVMIsolateScope(Thread* thread) - : StackResource(thread) { + : ThreadStackResource(thread) { if (FLAG_write_protect_vm_isolate) { Dart::vm_isolate()->heap()->WriteProtect(false); } @@ -990,7 +990,7 @@ WritableCodePages::~WritableCodePages() { } BumpAllocateScope::BumpAllocateScope(Thread* thread) - : StackResource(thread), no_reload_scope_(thread->isolate(), thread) { + : ThreadStackResource(thread), no_reload_scope_(thread->isolate(), thread) { ASSERT(!thread->bump_allocate()); // If the background compiler thread is not disabled, there will be a cycle // between the symbol table lock and the old space data lock. diff --git a/runtime/vm/heap/heap.h b/runtime/vm/heap/heap.h index 8cedd894106..23a02d6f474 100644 --- a/runtime/vm/heap/heap.h +++ b/runtime/vm/heap/heap.h @@ -391,7 +391,7 @@ class Heap { DISALLOW_COPY_AND_ASSIGN(Heap); }; -class HeapIterationScope : public StackResource { +class HeapIterationScope : public ThreadStackResource { public: explicit HeapIterationScope(Thread* thread, bool writable = false); ~HeapIterationScope(); @@ -416,7 +416,7 @@ class HeapIterationScope : public StackResource { DISALLOW_COPY_AND_ASSIGN(HeapIterationScope); }; -class NoHeapGrowthControlScope : public StackResource { +class NoHeapGrowthControlScope : public ThreadStackResource { public: NoHeapGrowthControlScope(); ~NoHeapGrowthControlScope(); @@ -428,7 +428,7 @@ class NoHeapGrowthControlScope : public StackResource { // Note: During this scope all pages are writable and the code pages are // non-executable. -class WritableVMIsolateScope : StackResource { +class WritableVMIsolateScope : ThreadStackResource { public: explicit WritableVMIsolateScope(Thread* thread); ~WritableVMIsolateScope(); @@ -446,7 +446,7 @@ class WritableCodePages : StackResource { // This scope forces heap growth, forces use of the bump allocator, and // takes the page lock. It is useful e.g. at program startup when allocating // many objects into old gen (like libraries, classes, and functions). -class BumpAllocateScope : StackResource { +class BumpAllocateScope : ThreadStackResource { public: explicit BumpAllocateScope(Thread* thread); ~BumpAllocateScope(); diff --git a/runtime/vm/heap/safepoint.cc b/runtime/vm/heap/safepoint.cc index bf5def148a1..c231b9c87b6 100644 --- a/runtime/vm/heap/safepoint.cc +++ b/runtime/vm/heap/safepoint.cc @@ -11,7 +11,8 @@ namespace dart { DEFINE_FLAG(bool, trace_safepoint, false, "Trace Safepoint logic."); -SafepointOperationScope::SafepointOperationScope(Thread* T) : StackResource(T) { +SafepointOperationScope::SafepointOperationScope(Thread* T) + : ThreadStackResource(T) { ASSERT(T != NULL); Isolate* I = T->isolate(); ASSERT(I != NULL); diff --git a/runtime/vm/heap/safepoint.h b/runtime/vm/heap/safepoint.h index 191323fc52f..ce84f859a28 100644 --- a/runtime/vm/heap/safepoint.h +++ b/runtime/vm/heap/safepoint.h @@ -8,13 +8,14 @@ #include "vm/globals.h" #include "vm/lockers.h" #include "vm/thread.h" +#include "vm/thread_stack_resource.h" namespace dart { // A stack based scope that can be used to perform an operation after getting // all threads to a safepoint. At the end of the operation all the threads are // resumed. -class SafepointOperationScope : public StackResource { +class SafepointOperationScope : public ThreadStackResource { public: explicit SafepointOperationScope(Thread* T); ~SafepointOperationScope(); @@ -136,9 +137,9 @@ class SafepointHandler { * ==> kThreadInGenerated * - Invalid transition. */ -class TransitionSafepointState : public StackResource { +class TransitionSafepointState : public ThreadStackResource { public: - explicit TransitionSafepointState(Thread* T) : StackResource(T) {} + explicit TransitionSafepointState(Thread* T) : ThreadStackResource(T) {} ~TransitionSafepointState() {} SafepointHandler* handler() const { diff --git a/runtime/vm/heap/verifier.h b/runtime/vm/heap/verifier.h index f12ba8204a3..d811be6096a 100644 --- a/runtime/vm/heap/verifier.h +++ b/runtime/vm/heap/verifier.h @@ -8,6 +8,7 @@ #include "vm/flags.h" #include "vm/globals.h" #include "vm/handles.h" +#include "vm/thread.h" #include "vm/visitor.h" namespace dart { diff --git a/runtime/vm/interpreter.h b/runtime/vm/interpreter.h index 22d27a2473a..4e98f3e07fe 100644 --- a/runtime/vm/interpreter.h +++ b/runtime/vm/interpreter.h @@ -24,6 +24,7 @@ class RawImmutableArray; class RawArray; class RawObjectPool; class RawFunction; +class RawString; class RawSubtypeTestCache; class ObjectPointerVisitor; diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc index df87172ea7d..1d18e971729 100644 --- a/runtime/vm/isolate.cc +++ b/runtime/vm/isolate.cc @@ -148,7 +148,8 @@ bool IsolateVisitor::IsVMInternalIsolate(Isolate* isolate) const { return Isolate::IsVMInternalIsolate(isolate); } -NoOOBMessageScope::NoOOBMessageScope(Thread* thread) : StackResource(thread) { +NoOOBMessageScope::NoOOBMessageScope(Thread* thread) + : ThreadStackResource(thread) { thread->DeferOOBMessageInterrupts(); } @@ -157,7 +158,7 @@ NoOOBMessageScope::~NoOOBMessageScope() { } NoReloadScope::NoReloadScope(Isolate* isolate, Thread* thread) - : StackResource(thread), isolate_(isolate) { + : ThreadStackResource(thread), isolate_(isolate) { #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME) ASSERT(isolate_ != NULL); AtomicOperations::FetchAndIncrement(&(isolate_->no_reload_scope_depth_)); @@ -2865,7 +2866,7 @@ void Isolate::UnscheduleThread(Thread* thread, } } else { ASSERT(thread->api_top_scope_ == NULL); - ASSERT(thread->zone_ == NULL); + ASSERT(thread->zone() == NULL); ASSERT(thread->sticky_error() == Error::null()); } if (!bypass_safepoint) { diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h index 278857f5e93..ffa2a03e313 100644 --- a/runtime/vm/isolate.h +++ b/runtime/vm/isolate.h @@ -21,6 +21,7 @@ #include "vm/random.h" #include "vm/tags.h" #include "vm/thread.h" +#include "vm/thread_stack_resource.h" #include "vm/token_position.h" namespace dart { @@ -106,7 +107,7 @@ class IsolateVisitor { }; // Disallow OOB message handling within this scope. -class NoOOBMessageScope : public StackResource { +class NoOOBMessageScope : public ThreadStackResource { public: explicit NoOOBMessageScope(Thread* thread); ~NoOOBMessageScope(); @@ -116,7 +117,7 @@ class NoOOBMessageScope : public StackResource { }; // Disallow isolate reload. -class NoReloadScope : public StackResource { +class NoReloadScope : public ThreadStackResource { public: NoReloadScope(Isolate* isolate, Thread* thread); ~NoReloadScope(); diff --git a/runtime/vm/log.h b/runtime/vm/log.h index b644884aa25..1c4a6d0461c 100644 --- a/runtime/vm/log.h +++ b/runtime/vm/log.h @@ -74,13 +74,13 @@ class Log { // Can be nested. class LogBlock : public StackResource { public: - LogBlock(Thread* thread, Log* log) + LogBlock(ThreadState* thread, Log* log) : StackResource(thread), log_(log), cursor_(log->cursor()) { Initialize(); } LogBlock() - : StackResource(Thread::Current()), + : StackResource(ThreadState::Current()), log_(Log::Current()), cursor_(Log::Current()->cursor()) { Initialize(); diff --git a/runtime/vm/longjump.h b/runtime/vm/longjump.h index 9da35b93f0e..a334f15467f 100644 --- a/runtime/vm/longjump.h +++ b/runtime/vm/longjump.h @@ -1,4 +1,4 @@ -// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2019, 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. @@ -8,7 +8,7 @@ #include #include "vm/allocation.h" -#include "vm/isolate.h" +#include "vm/thread_state.h" namespace dart { @@ -17,14 +17,14 @@ class Error; class LongJumpScope : public StackResource { public: LongJumpScope() - : StackResource(Thread::Current()), - top_(NULL), + : StackResource(ThreadState::Current()), + top_(nullptr), base_(thread()->long_jump_base()) { thread()->set_long_jump_base(this); } ~LongJumpScope() { - ASSERT(thread() == Thread::Current()); + ASSERT(thread() == ThreadState::Current()); thread()->set_long_jump_base(base_); } diff --git a/runtime/vm/object_graph.cc b/runtime/vm/object_graph.cc index 89fe81a173e..6edb4246706 100644 --- a/runtime/vm/object_graph.cc +++ b/runtime/vm/object_graph.cc @@ -199,7 +199,7 @@ static void IterateUserFields(ObjectPointerVisitor* visitor) { } } -ObjectGraph::ObjectGraph(Thread* thread) : StackResource(thread) { +ObjectGraph::ObjectGraph(Thread* thread) : ThreadStackResource(thread) { // The VM isolate has all its objects pre-marked, so iterating over it // would be a no-op. ASSERT(thread->isolate() != Dart::vm_isolate()); diff --git a/runtime/vm/object_graph.h b/runtime/vm/object_graph.h index 838c61d2296..deb700feda7 100644 --- a/runtime/vm/object_graph.h +++ b/runtime/vm/object_graph.h @@ -6,6 +6,7 @@ #define RUNTIME_VM_OBJECT_GRAPH_H_ #include "vm/allocation.h" +#include "vm/thread_stack_resource.h" namespace dart { @@ -19,7 +20,7 @@ class WriteStream; // Example uses: // - find a retaining path from the isolate roots to a particular object, or // - determine how much memory is retained by some particular object(s). -class ObjectGraph : public StackResource { +class ObjectGraph : public ThreadStackResource { public: class Stack; diff --git a/runtime/vm/os_thread.cc b/runtime/vm/os_thread.cc index 21203808611..22abcbbd7c5 100644 --- a/runtime/vm/os_thread.cc +++ b/runtime/vm/os_thread.cc @@ -20,7 +20,7 @@ Mutex* OSThread::thread_list_lock_ = NULL; bool OSThread::creation_enabled_ = false; #if defined(HAS_C11_THREAD_LOCAL) -thread_local Thread* OSThread::current_vm_thread_ = NULL; +thread_local ThreadState* OSThread::current_vm_thread_ = NULL; #endif OSThread::OSThread() @@ -214,7 +214,7 @@ void OSThread::EnableOSThreadCreation() { creation_enabled_ = true; } -OSThread* OSThread::GetOSThreadFromThread(Thread* thread) { +OSThread* OSThread::GetOSThreadFromThread(ThreadState* thread) { ASSERT(thread->os_thread() != NULL); return thread->os_thread(); } diff --git a/runtime/vm/os_thread.h b/runtime/vm/os_thread.h index aefca34db24..1fcf0dada12 100644 --- a/runtime/vm/os_thread.h +++ b/runtime/vm/os_thread.h @@ -35,7 +35,7 @@ namespace dart { // Forward declarations. class Log; class Mutex; -class Thread; +class ThreadState; class TimelineEventBlock; class BaseThread { @@ -48,6 +48,7 @@ class BaseThread { bool is_os_thread_; + friend class ThreadState; friend class Thread; friend class OSThread; @@ -140,7 +141,7 @@ class OSThread : public BaseThread { if (thread->is_os_thread()) { os_thread = reinterpret_cast(thread); } else { - Thread* vm_thread = reinterpret_cast(thread); + ThreadState* vm_thread = reinterpret_cast(thread); os_thread = GetOSThreadFromThread(vm_thread); } } @@ -159,7 +160,7 @@ class OSThread : public BaseThread { static void SetCurrent(OSThread* current) { SetCurrentTLS(current); } #if defined(HAS_C11_THREAD_LOCAL) - static Thread* CurrentVMThread() { return current_vm_thread_; } + static ThreadState* CurrentVMThread() { return current_vm_thread_; } #endif // TODO(5411455): Use flag to override default value and Validate the @@ -225,14 +226,14 @@ class OSThread : public BaseThread { // in the windows thread interrupter which is used for profiling. // We could eliminate this requirement if the windows thread interrupter // is implemented differently. - Thread* thread() const { return thread_; } - void set_thread(Thread* value) { thread_ = value; } + ThreadState* thread() const { return thread_; } + void set_thread(ThreadState* value) { thread_ = value; } static void Cleanup(); #ifndef PRODUCT static ThreadId GetCurrentThreadTraceId(); #endif // PRODUCT - static OSThread* GetOSThreadFromThread(Thread* thread); + static OSThread* GetOSThreadFromThread(ThreadState* thread); static void AddThreadToListLocked(OSThread* thread); static void RemoveThreadFromList(OSThread* thread); static OSThread* CreateAndSetUnknownThread(); @@ -260,7 +261,7 @@ class OSThread : public BaseThread { Log* log_; uword stack_base_; uword stack_limit_; - Thread* thread_; + ThreadState* thread_; // thread_list_lock_ cannot have a static lifetime because the order in which // destructors run is undefined. At the moment this lock cannot be deleted @@ -272,7 +273,7 @@ class OSThread : public BaseThread { static bool creation_enabled_; #if defined(HAS_C11_THREAD_LOCAL) - static thread_local Thread* current_vm_thread_; + static thread_local ThreadState* current_vm_thread_; #endif friend class Isolate; // to access set_thread(Thread*). diff --git a/runtime/vm/os_win.cc b/runtime/vm/os_win.cc index 8b8ce38123e..885009c5585 100644 --- a/runtime/vm/os_win.cc +++ b/runtime/vm/os_win.cc @@ -79,7 +79,7 @@ const char* OS::GetTimeZoneName(int64_t seconds_since_epoch) { : zone_information.StandardName; intptr_t utf8_len = WideCharToMultiByte(CP_UTF8, 0, wchar_name, -1, NULL, 0, NULL, NULL); - char* name = Thread::Current()->zone()->Alloc(utf8_len + 1); + char* name = ThreadState::Current()->zone()->Alloc(utf8_len + 1); WideCharToMultiByte(CP_UTF8, 0, wchar_name, -1, name, utf8_len, NULL, NULL); name[utf8_len] = '\0'; return name; diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h index 3fcb8cf7f61..dcde53e8acc 100644 --- a/runtime/vm/raw_object.h +++ b/runtime/vm/raw_object.h @@ -10,6 +10,7 @@ #include "vm/compiler/method_recognizer.h" #include "vm/exceptions.h" #include "vm/globals.h" +#include "vm/object_graph.h" #include "vm/snapshot.h" #include "vm/token.h" #include "vm/token_position.h" diff --git a/runtime/vm/service_isolate.h b/runtime/vm/service_isolate.h index 5759658bfaf..e6ecc405f51 100644 --- a/runtime/vm/service_isolate.h +++ b/runtime/vm/service_isolate.h @@ -12,6 +12,7 @@ namespace dart { +class Isolate; class ObjectPointerVisitor; class SendPort; diff --git a/runtime/vm/simulator_dbc.h b/runtime/vm/simulator_dbc.h index 03608d0ed39..8b87a7ab2e1 100644 --- a/runtime/vm/simulator_dbc.h +++ b/runtime/vm/simulator_dbc.h @@ -14,18 +14,19 @@ namespace dart { -class Isolate; -class RawObject; -class SimulatorSetjmpBuffer; -class Thread; -class Code; class Array; +class Code; +class Isolate; +class ObjectPointerVisitor; +class RawArray; +class RawCode; +class RawFunction; class RawICData; class RawImmutableArray; -class RawArray; +class RawObject; class RawObjectPool; -class RawFunction; -class ObjectPointerVisitor; +class SimulatorSetjmpBuffer; +class Thread; // Simulator intrinsic handler. It is invoked on entry to the intrinsified // function via Intrinsic bytecode before the frame is setup. diff --git a/runtime/vm/tags.cc b/runtime/vm/tags.cc index fad8fe7cf05..b20b42e9e9b 100644 --- a/runtime/vm/tags.cc +++ b/runtime/vm/tags.cc @@ -77,7 +77,7 @@ VMTag::TagEntry VMTag::entries_[] = { }; VMTagScope::VMTagScope(Thread* thread, uword tag, bool conditional_set) - : StackResource(thread) { + : ThreadStackResource(thread) { ASSERT(isolate() != NULL); previous_tag_ = thread->vm_tag(); if (conditional_set) { diff --git a/runtime/vm/tags.h b/runtime/vm/tags.h index 7bd6aa9ee17..4f3b665859e 100644 --- a/runtime/vm/tags.h +++ b/runtime/vm/tags.h @@ -6,6 +6,7 @@ #define RUNTIME_VM_TAGS_H_ #include "vm/allocation.h" +#include "vm/thread_stack_resource.h" namespace dart { @@ -75,7 +76,7 @@ class VMTag : public AllStatic { static TagEntry entries_[]; }; -class VMTagScope : StackResource { +class VMTagScope : ThreadStackResource { public: VMTagScope(Thread* thread, uword tag, bool conditional_set = true); ~VMTagScope(); diff --git a/runtime/vm/thread.cc b/runtime/vm/thread.cc index 109ccd421b3..87561130bdf 100644 --- a/runtime/vm/thread.cc +++ b/runtime/vm/thread.cc @@ -58,7 +58,7 @@ Thread::~Thread() { #define REUSABLE_HANDLE_INITIALIZERS(object) object##_handle_(NULL), Thread::Thread(Isolate* isolate) - : BaseThread(false), + : ThreadState(false), stack_limit_(0), stack_overflow_flags_(0), write_barrier_mask_(RawObject::kGenerationalBarrierMask), @@ -77,15 +77,9 @@ Thread::Thread(Isolate* isolate) resume_pc_(0), task_kind_(kUnknownTask), dart_stream_(NULL), - os_thread_(NULL), thread_lock_(new Monitor()), - zone_(NULL), - current_zone_capacity_(0), - zone_high_watermark_(0), api_reusable_scope_(NULL), api_top_scope_(NULL), - top_resource_(NULL), - long_jump_base_(NULL), no_callback_scope_depth_(0), #if defined(DEBUG) top_handle_scope_(NULL), @@ -141,20 +135,6 @@ Thread::Thread(Isolate* isolate) if ((Dart::vm_isolate() != NULL) && (isolate != Dart::vm_isolate())) { InitVMConstants(); } - - // This thread should not yet own any zones. If it does, we need to make sure - // we've accounted for any memory it has already allocated. - if (zone_ == NULL) { - ASSERT(current_zone_capacity_ == 0); - } else { - Zone* current = zone_; - uintptr_t total_zone_capacity = 0; - while (current != NULL) { - total_zone_capacity += current->CapacityInBytes(); - current = current->previous(); - } - ASSERT(current_zone_capacity_ == total_zone_capacity); - } } static const double double_nan_constant = NAN; @@ -245,8 +225,8 @@ void Thread::PrintJSON(JSONStream* stream) const { jsobj.AddPropertyF("id", "threads/%" Pd "", OSThread::ThreadIdToIntPtr(os_thread()->trace_id())); jsobj.AddProperty("kind", TaskKindToCString(task_kind())); - jsobj.AddPropertyF("_zoneHighWatermark", "%" Pu "", zone_high_watermark_); - jsobj.AddPropertyF("_zoneCapacity", "%" Pu "", current_zone_capacity_); + jsobj.AddPropertyF("_zoneHighWatermark", "%" Pu "", zone_high_watermark()); + jsobj.AddPropertyF("_zoneCapacity", "%" Pu "", current_zone_capacity()); } #endif @@ -471,18 +451,6 @@ uword Thread::GetAndClearInterrupts() { return interrupt_bits; } -bool Thread::ZoneIsOwnedByThread(Zone* zone) const { - ASSERT(zone != NULL); - Zone* current = zone_; - while (current != NULL) { - if (current == zone) { - return true; - } - current = current->previous(); - } - return false; -} - void Thread::DeferOOBMessageInterrupts() { MonitorLocker ml(thread_lock_); defer_oob_messages_count_++; @@ -687,8 +655,8 @@ void Thread::VisitObjectPointers(ObjectPointerVisitor* visitor, ValidationPolicy validation_policy) { ASSERT(visitor != NULL); - if (zone_ != NULL) { - zone_->VisitObjectPointers(visitor); + if (zone() != NULL) { + zone()->VisitObjectPointers(visitor); } // Visit objects in thread specific handles area. @@ -832,7 +800,7 @@ intptr_t Thread::OffsetFromThread(const RuntimeEntry* runtime_entry) { #if defined(DEBUG) bool Thread::TopErrorHandlerIsSetJump() const { - if (long_jump_base_ == nullptr) return false; + if (long_jump_base() == nullptr) return false; if (top_exit_frame_info_ == 0) return true; #if defined(USING_SIMULATOR) || defined(USING_SAFE_STACK) // False positives: simulator stack and native stack are unordered. @@ -843,13 +811,13 @@ bool Thread::TopErrorHandlerIsSetJump() const { if ((interpreter_ != nullptr) && interpreter_->HasFrame(top_exit_frame_info_)) return true; #endif - return reinterpret_cast(long_jump_base_) < top_exit_frame_info_; + return reinterpret_cast(long_jump_base()) < top_exit_frame_info_; #endif } bool Thread::TopErrorHandlerIsExitFrame() const { if (top_exit_frame_info_ == 0) return false; - if (long_jump_base_ == nullptr) return true; + if (long_jump_base() == nullptr) return true; #if defined(USING_SIMULATOR) || defined(USING_SAFE_STACK) // False positives: simulator stack and native stack are unordered. return true; @@ -859,7 +827,7 @@ bool Thread::TopErrorHandlerIsExitFrame() const { if ((interpreter_ != nullptr) && interpreter_->HasFrame(top_exit_frame_info_)) return true; #endif - return top_exit_frame_info_ < reinterpret_cast(long_jump_base_); + return top_exit_frame_info_ < reinterpret_cast(long_jump_base()); #endif } #endif // defined(DEBUG) @@ -891,7 +859,7 @@ intptr_t Thread::CountLocalHandles() const { } bool Thread::IsValidZoneHandle(Dart_Handle object) const { - Zone* zone = zone_; + Zone* zone = this->zone(); while (zone != NULL) { if (zone->handles()->IsValidZoneHandle(reinterpret_cast(object))) { return true; @@ -903,7 +871,7 @@ bool Thread::IsValidZoneHandle(Dart_Handle object) const { intptr_t Thread::CountZoneHandles() const { intptr_t count = 0; - Zone* zone = zone_; + Zone* zone = this->zone(); while (zone != NULL) { count += zone->handles()->CountZoneHandles(); zone = zone->previous(); @@ -913,7 +881,7 @@ intptr_t Thread::CountZoneHandles() const { } bool Thread::IsValidScopedHandle(Dart_Handle object) const { - Zone* zone = zone_; + Zone* zone = this->zone(); while (zone != NULL) { if (zone->handles()->IsValidScopedHandle(reinterpret_cast(object))) { return true; @@ -925,7 +893,7 @@ bool Thread::IsValidScopedHandle(Dart_Handle object) const { intptr_t Thread::CountScopedHandles() const { intptr_t count = 0; - Zone* zone = zone_; + Zone* zone = this->zone(); while (zone != NULL) { count += zone->handles()->CountScopedHandles(); zone = zone->previous(); diff --git a/runtime/vm/thread.h b/runtime/vm/thread.h index 9cd5fb5ed7a..f344ffc299e 100644 --- a/runtime/vm/thread.h +++ b/runtime/vm/thread.h @@ -16,6 +16,7 @@ #include "vm/heap/pointer_block.h" #include "vm/os_thread.h" #include "vm/runtime_entry_list.h" +#include "vm/thread_state.h" namespace dart { @@ -38,7 +39,6 @@ class Instance; class Interpreter; class Isolate; class Library; -class LongJumpScope; class Object; class OSThread; class JSONObject; @@ -201,7 +201,7 @@ enum class ValidationPolicy { // a thread is allocated by EnsureInit before entering an isolate, and destroyed // automatically when the underlying OS thread exits. NOTE: On Windows, CleanUp // must currently be called manually (issue 23474). -class Thread : public BaseThread { +class Thread : public ThreadState { public: // The kind of task this thread is performing. Sampled by the profiler. enum TaskKind { @@ -220,13 +220,13 @@ class Thread : public BaseThread { // The currently executing thread, or NULL if not yet initialized. static Thread* Current() { #if defined(HAS_C11_THREAD_LOCAL) - return OSThread::CurrentVMThread(); + return static_cast(OSThread::CurrentVMThread()); #else BaseThread* thread = OSThread::GetCurrentTLS(); if (thread == NULL || thread->is_os_thread()) { return NULL; } - return reinterpret_cast(thread); + return static_cast(thread); #endif } @@ -317,36 +317,9 @@ class Thread : public BaseThread { return (stack_limit_ & kInterruptsMask) != 0; } - // OSThread corresponding to this thread. - OSThread* os_thread() const { return os_thread_; } - void set_os_thread(OSThread* os_thread) { os_thread_ = os_thread; } - // Monitor corresponding to this thread. Monitor* thread_lock() const { return thread_lock_; } - // The topmost zone used for allocation in this thread. - Zone* zone() const { return zone_; } - - bool ZoneIsOwnedByThread(Zone* zone) const; - - void IncrementMemoryCapacity(uintptr_t value) { - current_zone_capacity_ += value; - if (current_zone_capacity_ > zone_high_watermark_) { - zone_high_watermark_ = current_zone_capacity_; - } - } - - void DecrementMemoryCapacity(uintptr_t value) { - ASSERT(current_zone_capacity_ >= value); - current_zone_capacity_ -= value; - } - - uintptr_t current_zone_capacity() { return current_zone_capacity_; } - - uintptr_t zone_high_watermark() const { return zone_high_watermark_; } - - void ResetHighWatermark() { zone_high_watermark_ = current_zone_capacity_; } - // The reusable api local scope for this thread. ApiLocalScope* api_reusable_scope() const { return api_reusable_scope_; } void set_api_reusable_scope(ApiLocalScope* value) { @@ -454,12 +427,6 @@ class Thread : public BaseThread { return OFFSET_OF(Thread, top_exit_frame_info_); } - StackResource* top_resource() const { return top_resource_; } - void set_top_resource(StackResource* value) { top_resource_ = value; } - static intptr_t top_resource_offset() { - return OFFSET_OF(Thread, top_resource_); - } - // Heap of the isolate that this thread is operating on. Heap* heap() const { return heap_; } static intptr_t heap_offset() { return OFFSET_OF(Thread, heap_); } @@ -564,9 +531,6 @@ class Thread : public BaseThread { static bool ObjectAtOffset(intptr_t offset, Object* object); static intptr_t OffsetFromThread(const RuntimeEntry* runtime_entry); - LongJumpScope* long_jump_base() const { return long_jump_base_; } - void set_long_jump_base(LongJumpScope* value) { long_jump_base_ = value; } - #if defined(DEBUG) // For asserts only. Has false positives when running with a simulator or // SafeStack. @@ -869,15 +833,9 @@ class Thread : public BaseThread { TaskKind task_kind_; TimelineStream* dart_stream_; - OSThread* os_thread_; Monitor* thread_lock_; - Zone* zone_; - uintptr_t current_zone_capacity_; - uintptr_t zone_high_watermark_; ApiLocalScope* api_reusable_scope_; ApiLocalScope* api_top_scope_; - StackResource* top_resource_; - LongJumpScope* long_jump_base_; int32_t no_callback_scope_depth_; #if defined(DEBUG) HandleScope* top_handle_scope_; @@ -939,8 +897,6 @@ class Thread : public BaseThread { void DeferredMarkingStackRelease(); void DeferredMarkingStackAcquire(); - void set_zone(Zone* zone) { zone_ = zone; } - void set_safepoint_state(uint32_t value) { safepoint_state_ = value; } void EnterSafepointUsingLock(); void ExitSafepointUsingLock(); @@ -981,6 +937,30 @@ class DisableThreadInterruptsScope : public StackResource { ~DisableThreadInterruptsScope(); }; +// 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) + } // namespace dart #endif // RUNTIME_VM_THREAD_H_ diff --git a/runtime/vm/thread_interrupter.cc b/runtime/vm/thread_interrupter.cc index 1de2c0ceaf6..d8d56b14dc2 100644 --- a/runtime/vm/thread_interrupter.cc +++ b/runtime/vm/thread_interrupter.cc @@ -36,16 +36,6 @@ namespace dart { // The ThreadInterrupter has a single monitor (monitor_). This monitor is used // to synchronize startup, shutdown, and waking up from a deep sleep. // -// A thread can only register and unregister itself. Each thread has a heap -// allocated ThreadState. A thread's ThreadState is lazily allocated the first -// time the thread is registered. A pointer to a thread's ThreadState is stored -// in the list of threads registered to receive interrupts (threads_) and in -// thread local storage. When a thread's ThreadState is being modified, the -// thread local storage pointer is temporarily set to NULL while the -// modification is occurring. After the ThreadState has been updated, the -// thread local storage pointer is set again. This has an important side -// effect: if the thread is interrupted by a signal handler during a ThreadState -// update the signal handler will immediately return. DEFINE_FLAG(bool, trace_thread_interrupter, false, "Trace thread interrupter"); diff --git a/runtime/vm/thread_interrupter_win.cc b/runtime/vm/thread_interrupter_win.cc index 13c6f8dcd32..c862a225279 100644 --- a/runtime/vm/thread_interrupter_win.cc +++ b/runtime/vm/thread_interrupter_win.cc @@ -81,7 +81,7 @@ class ThreadInterrupterWin : public AllStatic { // Currently we sample only threads that are associated // with an isolate. It is safe to call 'os_thread->thread()' // here as the thread which is being queried is suspended. - Thread* thread = os_thread->thread(); + Thread* thread = static_cast(os_thread->thread()); if (thread != NULL) { Profiler::SampleThread(thread, its); } diff --git a/runtime/vm/thread_registry.cc b/runtime/vm/thread_registry.cc index a3905286465..2052d4a560e 100644 --- a/runtime/vm/thread_registry.cc +++ b/runtime/vm/thread_registry.cc @@ -192,7 +192,7 @@ Thread* ThreadRegistry::GetFromFreelistLocked(Isolate* isolate) { void ThreadRegistry::ReturnToFreelistLocked(Thread* thread) { ASSERT(thread != NULL); - ASSERT(thread->os_thread_ == NULL); + ASSERT(thread->os_thread() == NULL); ASSERT(thread->isolate_ == NULL); ASSERT(thread->heap_ == NULL); ASSERT(threads_lock()->IsOwnedByCurrentThread()); diff --git a/runtime/vm/thread_stack_resource.cc b/runtime/vm/thread_stack_resource.cc new file mode 100644 index 00000000000..69faf9471f4 --- /dev/null +++ b/runtime/vm/thread_stack_resource.cc @@ -0,0 +1,26 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/thread_stack_resource.h" + +#include "platform/assert.h" +#include "vm/isolate.h" +#include "vm/thread.h" +#include "vm/zone.h" + +namespace dart { + +ThreadStackResource::~ThreadStackResource() { +#if defined(DEBUG) + if (thread() != nullptr) { + BaseIsolate::AssertCurrent(reinterpret_cast(isolate())); + } +#endif +} + +Isolate* ThreadStackResource::isolate() const { + return thread()->isolate(); +} + +} // namespace dart diff --git a/runtime/vm/thread_stack_resource.h b/runtime/vm/thread_stack_resource.h new file mode 100644 index 00000000000..abbf5215491 --- /dev/null +++ b/runtime/vm/thread_stack_resource.h @@ -0,0 +1,32 @@ +// Copyright (c) 2019, 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_STACK_RESOURCE_H_ +#define RUNTIME_VM_THREAD_STACK_RESOURCE_H_ + +#include "vm/allocation.h" +#include "vm/globals.h" + +namespace dart { + +class Isolate; +class ThreadState; +class Thread; + +class ThreadStackResource : public StackResource { + public: + explicit ThreadStackResource(Thread* T) + : StackResource(reinterpret_cast(T)) {} + + ~ThreadStackResource(); + + Thread* thread() const { + return reinterpret_cast(StackResource::thread()); + } + Isolate* isolate() const; +}; + +} // namespace dart + +#endif // RUNTIME_VM_THREAD_STACK_RESOURCE_H_ diff --git a/runtime/vm/thread_state.cc b/runtime/vm/thread_state.cc new file mode 100644 index 00000000000..d5e37e31e2f --- /dev/null +++ b/runtime/vm/thread_state.cc @@ -0,0 +1,41 @@ +// Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +#include "vm/thread_state.h" + +#include "vm/zone.h" + +namespace dart { + +ThreadState::ThreadState(bool is_os_thread) : BaseThread(is_os_thread) { + // This thread should not yet own any zones. If it does, we need to make sure + // we've accounted for any memory it has already allocated. + if (zone_ == nullptr) { + ASSERT(current_zone_capacity_ == 0); + } else { + Zone* current = zone_; + uintptr_t total_zone_capacity = 0; + while (current != nullptr) { + total_zone_capacity += current->CapacityInBytes(); + current = current->previous(); + } + ASSERT(current_zone_capacity_ == total_zone_capacity); + } +} + +ThreadState::~ThreadState() {} + +bool ThreadState::ZoneIsOwnedByThread(Zone* zone) const { + ASSERT(zone != nullptr); + Zone* current = zone_; + while (current != nullptr) { + if (current == zone) { + return true; + } + current = current->previous(); + } + return false; +} + +} // namespace dart diff --git a/runtime/vm/thread_state.h b/runtime/vm/thread_state.h new file mode 100644 index 00000000000..85106d21fdf --- /dev/null +++ b/runtime/vm/thread_state.h @@ -0,0 +1,91 @@ +// Copyright (c) 2019, 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_STATE_H_ +#define RUNTIME_VM_THREAD_STATE_H_ + +#include "vm/os_thread.h" + +namespace dart { + +class LongJumpScope; +class Zone; + +// ThreadState is a container for auxiliary thread-local state: e.g. it +// owns a stack of Zones for allocation and a stack of StackResources +// for stack unwinding. +// +// Important: this class is shared between compiler and runtime and +// as such it should not expose any runtime internals due to layering +// restrictions. +class ThreadState : public BaseThread { + public: + // The currently executing thread, or NULL if not yet initialized. + static ThreadState* Current() { +#if defined(HAS_C11_THREAD_LOCAL) + return OSThread::CurrentVMThread(); +#else + BaseThread* thread = OSThread::GetCurrentTLS(); + if (thread == NULL || thread->is_os_thread()) { + return NULL; + } + return static_cast(thread); +#endif + } + + explicit ThreadState(bool is_os_thread); + ~ThreadState(); + + // OSThread corresponding to this thread. + OSThread* os_thread() const { return os_thread_; } + void set_os_thread(OSThread* os_thread) { os_thread_ = os_thread; } + + // The topmost zone used for allocation in this thread. + Zone* zone() const { return zone_; } + + bool ZoneIsOwnedByThread(Zone* zone) const; + + void IncrementMemoryCapacity(uintptr_t value) { + current_zone_capacity_ += value; + if (current_zone_capacity_ > zone_high_watermark_) { + zone_high_watermark_ = current_zone_capacity_; + } + } + + void DecrementMemoryCapacity(uintptr_t value) { + ASSERT(current_zone_capacity_ >= value); + current_zone_capacity_ -= value; + } + + uintptr_t current_zone_capacity() const { return current_zone_capacity_; } + uintptr_t zone_high_watermark() const { return zone_high_watermark_; } + + void ResetHighWatermark() { zone_high_watermark_ = current_zone_capacity_; } + + StackResource* top_resource() const { return top_resource_; } + void set_top_resource(StackResource* value) { top_resource_ = value; } + static intptr_t top_resource_offset() { + return OFFSET_OF(ThreadState, top_resource_); + } + + LongJumpScope* long_jump_base() const { return long_jump_base_; } + void set_long_jump_base(LongJumpScope* value) { long_jump_base_ = value; } + + private: + void set_zone(Zone* zone) { zone_ = zone; } + + OSThread* os_thread_ = nullptr; + Zone* zone_ = nullptr; + uintptr_t current_zone_capacity_ = 0; + uintptr_t zone_high_watermark_ = 0; + StackResource* top_resource_ = nullptr; + LongJumpScope* long_jump_base_ = nullptr; + + friend class ApiZone; + friend class StackZone; +}; + +} // namespace dart + +#endif // RUNTIME_VM_THREAD_STATE_H_ diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc index c38fd7607cd..bca4938536d 100644 --- a/runtime/vm/type_testing_stubs.cc +++ b/runtime/vm/type_testing_stubs.cc @@ -748,7 +748,7 @@ RawAbstractType* TypeArgumentInstantiator::InstantiateType( } TypeUsageInfo::TypeUsageInfo(Thread* thread) - : StackResource(thread), + : ThreadStackResource(thread), zone_(thread->zone()), finder_(zone_), assert_assignable_types_(), diff --git a/runtime/vm/type_testing_stubs.h b/runtime/vm/type_testing_stubs.h index 423b4d73f1d..e8c704462ed 100644 --- a/runtime/vm/type_testing_stubs.h +++ b/runtime/vm/type_testing_stubs.h @@ -317,7 +317,7 @@ class TypeArgumentInstantiator { }; // Collects data on how [Type] objects are used in generated code. -class TypeUsageInfo : public StackResource { +class TypeUsageInfo : public ThreadStackResource { public: explicit TypeUsageInfo(Thread* thread); ~TypeUsageInfo(); diff --git a/runtime/vm/uri.cc b/runtime/vm/uri.cc index 67fbcbfd00d..5e75ef185a4 100644 --- a/runtime/vm/uri.cc +++ b/runtime/vm/uri.cc @@ -80,7 +80,7 @@ static int GetEscapedValue(const char* str, intptr_t pos, intptr_t len) { static char* NormalizeEscapes(const char* str, intptr_t len) { // Allocate the buffer. - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); // We multiply len by three because a percent-escape sequence is // three characters long (e.g. ' ' -> '%20). +1 for '\0'. We could // take two passes through the string and avoid the excess @@ -156,7 +156,7 @@ static void ClearParsedUri(ParsedUri* parsed_uri) { } static intptr_t ParseAuthority(const char* authority, ParsedUri* parsed_uri) { - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); const char* current = authority; intptr_t len = 0; @@ -191,7 +191,7 @@ static intptr_t ParseAuthority(const char* authority, ParsedUri* parsed_uri) { // Performs a simple parse of a uri into its components. // See RFC 3986 Section 3: Syntax. bool ParseUri(const char* uri, ParsedUri* parsed_uri) { - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); // The first ':' separates the scheme from the rest of the uri. If // a ':' occurs after the first '/' it doesn't count. @@ -284,7 +284,7 @@ static const char* RemoveDotSegments(const char* path) { // The output path will always be less than or equal to the size of // the input path. - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); char* buffer = zone->Alloc(strlen(path) + 1); // +1 for '\0' char* output = buffer; @@ -342,7 +342,7 @@ static const char* RemoveDotSegments(const char* path) { // See RFC 3986 Section 5.2.3: Merge Paths. static const char* MergePaths(const char* base_path, const char* ref_path) { - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); if (base_path[0] == '\0') { // If the base_path is empty, we prepend '/'. return zone->PrintToString("/%s", ref_path); @@ -378,7 +378,7 @@ static const char* MergePaths(const char* base_path, const char* ref_path) { } static char* BuildUri(const ParsedUri& uri) { - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); ASSERT(uri.path != NULL); const char* fragment = uri.fragment == NULL ? "" : uri.fragment; @@ -436,7 +436,7 @@ bool ResolveUri(const char* ref_uri, ParsedUri target; if (ref.scheme != NULL) { if (strcmp(ref.scheme, "dart") == 0) { - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); *target_uri = zone->MakeCopyOfString(ref_uri); return true; } @@ -461,7 +461,7 @@ bool ResolveUri(const char* ref_uri, } if ((base.scheme != NULL) && strcmp(base.scheme, "dart") == 0) { - Zone* zone = Thread::Current()->zone(); + Zone* zone = ThreadState::Current()->zone(); *target_uri = zone->MakeCopyOfString(ref_uri); return true; } diff --git a/runtime/vm/vm_sources.gni b/runtime/vm/vm_sources.gni index 593b76d191c..82b96456a5d 100644 --- a/runtime/vm/vm_sources.gni +++ b/runtime/vm/vm_sources.gni @@ -312,6 +312,10 @@ vm_sources = [ "thread_pool.h", "thread_registry.cc", "thread_registry.h", + "thread_stack_resource.cc", + "thread_stack_resource.h", + "thread_state.cc", + "thread_state.h", "timeline.cc", "timeline.h", "timeline_analysis.cc", diff --git a/runtime/vm/zone.cc b/runtime/vm/zone.cc index 313e4528aa9..8227e48e79d 100644 --- a/runtime/vm/zone.cc +++ b/runtime/vm/zone.cc @@ -75,7 +75,7 @@ void Zone::Segment::DeleteSegmentList(Segment* head) { } void Zone::Segment::IncrementMemoryCapacity(uintptr_t size) { - Thread* current_thread = Thread::Current(); + ThreadState* current_thread = ThreadState::Current(); if (current_thread != NULL) { current_thread->IncrementMemoryCapacity(size); } else if (ApiNativeScope::Current() != NULL) { @@ -85,7 +85,7 @@ void Zone::Segment::IncrementMemoryCapacity(uintptr_t size) { } void Zone::Segment::DecrementMemoryCapacity(uintptr_t size) { - Thread* current_thread = Thread::Current(); + ThreadState* current_thread = ThreadState::Current(); if (current_thread != NULL) { current_thread->DecrementMemoryCapacity(size); } else if (ApiNativeScope::Current() != NULL) { @@ -288,7 +288,7 @@ char* Zone::VPrint(const char* format, va_list args) { return OS::VSCreate(this, format, args); } -StackZone::StackZone(Thread* thread) : StackResource(thread), zone_() { +StackZone::StackZone(ThreadState* thread) : StackResource(thread), zone_() { if (FLAG_trace_zones) { OS::PrintErr("*** Starting a new Stack zone 0x%" Px "(0x%" Px ")\n", reinterpret_cast(this), diff --git a/runtime/vm/zone.h b/runtime/vm/zone.h index b892cdcbdc8..b4aba7f7f05 100644 --- a/runtime/vm/zone.h +++ b/runtime/vm/zone.h @@ -8,9 +8,8 @@ #include "platform/utils.h" #include "vm/allocation.h" #include "vm/handles.h" -#include "vm/json_stream.h" #include "vm/memory_region.h" -#include "vm/thread.h" +#include "vm/thread_state.h" namespace dart { @@ -175,7 +174,7 @@ class Zone { class StackZone : public StackResource { public: // Create an empty zone and set is at the current zone for the Thread. - explicit StackZone(Thread* thread); + explicit StackZone(ThreadState* thread); // Delete all memory associated with the zone. ~StackZone();