• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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