1 // Copyright 2019 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef V8_HEAP_MARKING_WORKLIST_H_ 6 #define V8_HEAP_MARKING_WORKLIST_H_ 7 8 #include <cstddef> 9 #include <memory> 10 #include <unordered_map> 11 #include <vector> 12 13 #include "src/heap/base/worklist.h" 14 #include "src/heap/cppgc-js/cpp-marking-state.h" 15 #include "src/heap/marking.h" 16 #include "src/objects/heap-object.h" 17 18 namespace v8 { 19 namespace internal { 20 21 class CppMarkingState; 22 class JSObject; 23 24 // The index of the main thread task used by concurrent/parallel GC. 25 const int kMainThreadTask = 0; 26 27 using MarkingWorklist = ::heap::base::Worklist<HeapObject, 64>; 28 using WrapperTracingWorklist = ::heap::base::Worklist<HeapObject, 16>; 29 30 // We piggyback on marking to compute object sizes per native context that is 31 // needed for the new memory measurement API. The algorithm works as follows: 32 // 1) At the start of marking we create a marking worklist for each context. 33 // The existing shared, on_hold, and embedder worklists continue to work 34 // as they did before, but they hold objects that are not attributed to any 35 // context yet. 36 // 2) Each marker has an active worklist where it pushes newly discovered 37 // objects. Initially the shared worklist is set as active for all markers. 38 // 3) When a marker pops an object from the active worklist: 39 // a) It checks if the object has a known context (e.g. JSObjects, Maps, 40 // Contexts know the context they belong to). If that's the case, then 41 // the marker changes its active worklist to the worklist corresponding 42 // to the context of the object. 43 // b) It account the size of object to the active context. 44 // c) It visits all pointers in the object and pushes new objects onto the 45 // active worklist. 46 // 4) When the active worklist becomes empty the marker selects any other 47 // non-empty worklist as the active worklist. 48 // 5) The write barrier pushes onto the shared worklist. 49 // 50 // The main invariant for context worklists: 51 // If object X is in the worklist of context C, then either 52 // a) X has a context and that context is C. 53 // b) X is retained by object Y that has context C. 54 // 55 // The algorithm allows us to attribute context-independent objects such as 56 // strings, numbers, FixedArrays to their retaining contexts. The algorithm is 57 // not precise for context-independent objects that are shared between multiple 58 // contexts. Such objects may be attributed to any retaining context. 59 60 // Named pair of native context address and its marking worklist. 61 // Since native contexts are allocated in the old generation, their addresses 62 // a stable across Scavenges and stay valid throughout the marking phase. 63 struct ContextWorklistPair { 64 Address context; 65 MarkingWorklist* worklist; 66 }; 67 68 // A helper class that owns all global marking worklists. 69 class V8_EXPORT_PRIVATE MarkingWorklists { 70 public: 71 class Local; 72 // Fake addresses of special contexts used for per-context accounting. 73 // - kSharedContext is for objects that are not attributed to any context. 74 // - kOtherContext is for objects that are attributed to contexts that are 75 // not being measured. 76 static const Address kSharedContext = 0; 77 static const Address kOtherContext = 8; 78 79 MarkingWorklists() = default; 80 ~MarkingWorklists(); 81 82 // Calls the specified callback on each element of the deques and replaces 83 // the element with the result of the callback. If the callback returns 84 // nullptr then the element is removed from the deque. 85 // The callback must accept HeapObject and return HeapObject. 86 template <typename Callback> 87 void Update(Callback callback); 88 shared()89 MarkingWorklist* shared() { return &shared_; } on_hold()90 MarkingWorklist* on_hold() { return &on_hold_; } wrapper()91 WrapperTracingWorklist* wrapper() { return &wrapper_; } 92 93 // A list of (context, worklist) pairs that was set up at the start of 94 // marking by CreateContextWorklists. context_worklists()95 const std::vector<ContextWorklistPair>& context_worklists() const { 96 return context_worklists_; 97 } 98 // This should be invoked at the start of marking with the list of contexts 99 // that require object size accounting. 100 void CreateContextWorklists(const std::vector<Address>& contexts); 101 // This should be invoked at the end of marking. All worklists must be 102 // empty at that point. 103 void ReleaseContextWorklists(); 104 105 void Clear(); 106 void Print(); 107 108 private: 109 // Prints the stats about the global pool of the worklist. 110 void PrintWorklist(const char* worklist_name, MarkingWorklist* worklist); 111 112 // Worklist used for most objects. 113 MarkingWorklist shared_; 114 115 // Concurrent marking uses this worklist to bail out of marking objects 116 // in new space's linear allocation area. Used to avoid black allocation 117 // for new space. This allow the compiler to remove write barriers 118 // for freshly allocatd objects. 119 MarkingWorklist on_hold_; 120 121 // Worklist for objects that potentially require embedder tracing, i.e., 122 // these objects need to be handed over to the embedder to find the full 123 // transitive closure. 124 WrapperTracingWorklist wrapper_; 125 126 // Per-context worklists. 127 std::vector<ContextWorklistPair> context_worklists_; 128 // This is used only for lifetime management of the per-context worklists. 129 std::vector<std::unique_ptr<MarkingWorklist>> worklists_; 130 131 // Worklist used for objects that are attributed to contexts that are 132 // not being measured. 133 MarkingWorklist other_; 134 }; 135 136 // A thread-local view of the marking worklists. It owns all local marking 137 // worklists and keeps track of the currently active local marking worklist 138 // for per-context marking. In order to avoid additional indirections for 139 // pushing and popping entries, the active_ worklist is not a pointer to 140 // Local but an actual instance of Local with the following invariants: 141 // - active_owner == worlist_by_context[active_context_].get() 142 // - *active_owner is empty (all fields are null) because its content has 143 // been moved to active_. 144 class V8_EXPORT_PRIVATE MarkingWorklists::Local { 145 public: 146 static constexpr Address kSharedContext = MarkingWorklists::kSharedContext; 147 static constexpr Address kOtherContext = MarkingWorklists::kOtherContext; 148 static constexpr std::nullptr_t kNoCppMarkingState = nullptr; 149 150 Local( 151 MarkingWorklists* global, 152 std::unique_ptr<CppMarkingState> cpp_marking_state = kNoCppMarkingState); 153 ~Local(); 154 155 inline void Push(HeapObject object); 156 inline bool Pop(HeapObject* object); 157 158 inline void PushOnHold(HeapObject object); 159 inline bool PopOnHold(HeapObject* object); 160 161 using WrapperSnapshot = CppMarkingState::EmbedderDataSnapshot; 162 inline bool ExtractWrapper(Map map, JSObject object, 163 WrapperSnapshot& snapshot); 164 inline void PushExtractedWrapper(const WrapperSnapshot& snapshot); 165 inline bool SupportsExtractWrapper(); 166 inline void PushWrapper(HeapObject object); 167 inline bool PopWrapper(HeapObject* object); 168 169 void Publish(); 170 bool IsEmpty(); 171 bool IsWrapperEmpty() const; 172 // Publishes the local active marking worklist if its global worklist is 173 // empty. In the per-context marking mode it also publishes the shared 174 // worklist. 175 void ShareWork(); 176 // Merges the on-hold worklist to the shared worklist. 177 void MergeOnHold(); 178 179 // Returns true if wrapper objects could be directly pushed. Otherwise, 180 // objects need to be processed one by one. 181 inline bool PublishWrapper(); 182 183 // Returns the context of the active worklist. Context()184 Address Context() const { return active_context_; } 185 inline Address SwitchToContext(Address context); 186 inline Address SwitchToShared(); IsPerContextMode()187 bool IsPerContextMode() const { return is_per_context_mode_; } 188 cpp_marking_state()189 CppMarkingState* cpp_marking_state() const { 190 return cpp_marking_state_.get(); 191 } 192 193 private: 194 bool PopContext(HeapObject* object); 195 Address SwitchToContextSlow(Address context); 196 inline void SwitchToContext(Address context, 197 MarkingWorklist::Local* worklist); 198 MarkingWorklist::Local on_hold_; 199 WrapperTracingWorklist::Local wrapper_; 200 MarkingWorklist::Local active_; 201 Address active_context_; 202 MarkingWorklist::Local* active_owner_; 203 bool is_per_context_mode_; 204 std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>> 205 worklist_by_context_; 206 207 std::unique_ptr<CppMarkingState> cpp_marking_state_; 208 }; 209 210 } // namespace internal 211 } // namespace v8 212 213 #endif // V8_HEAP_MARKING_WORKLIST_H_ 214