marking-worklist.h 8.28 KB
Newer Older
1 2 3 4 5 6 7
// Copyright 2019 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_HEAP_MARKING_WORKLIST_H_
#define V8_HEAP_MARKING_WORKLIST_H_

8 9
#include <cstddef>
#include <memory>
10 11 12
#include <unordered_map>
#include <vector>

13
#include "src/heap/base/worklist.h"
14
#include "src/heap/cppgc-js/cpp-marking-state.h"
15 16 17 18 19 20
#include "src/heap/marking.h"
#include "src/objects/heap-object.h"

namespace v8 {
namespace internal {

21 22 23
class CppMarkingState;
class JSObject;

24 25 26
// The index of the main thread task used by concurrent/parallel GC.
const int kMainThreadTask = 0;

27
using MarkingWorklist = ::heap::base::Worklist<HeapObject, 64>;
28
using WrapperTracingWorklist = ::heap::base::Worklist<HeapObject, 16>;
29

30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
// We piggyback on marking to compute object sizes per native context that is
// needed for the new memory measurement API. The algorithm works as follows:
// 1) At the start of marking we create a marking worklist for each context.
//    The existing shared, on_hold, and embedder worklists continue to work
//    as they did before, but they hold objects that are not attributed to any
//    context yet.
// 2) Each marker has an active worklist where it pushes newly discovered
//    objects. Initially the shared worklist is set as active for all markers.
// 3) When a marker pops an object from the active worklist:
//    a) It checks if the object has a known context (e.g. JSObjects, Maps,
//       Contexts know the context they belong to). If that's the case, then
//       the marker changes its active worklist to the worklist corresponding
//       to the context of the object.
//    b) It account the size of object to the active context.
//    c) It visits all pointers in the object and pushes new objects onto the
//       active worklist.
// 4) When the active worklist becomes empty the marker selects any other
//    non-empty worklist as the active worklist.
// 5) The write barrier pushes onto the shared worklist.
//
// The main invariant for context worklists:
//    If object X is in the worklist of context C, then either
//    a) X has a context and that context is C.
//    b) X is retained by object Y that has context C.
//
// The algorithm allows us to attribute context-independent objects such as
// strings, numbers, FixedArrays to their retaining contexts. The algorithm is
// not precise for context-independent objects that are shared between multiple
// contexts. Such objects may be attributed to any retaining context.

// Named pair of native context address and its marking worklist.
// Since native contexts are allocated in the old generation, their addresses
// a stable across Scavenges and stay valid throughout the marking phase.
struct ContextWorklistPair {
  Address context;
  MarkingWorklist* worklist;
};

68 69
// A helper class that owns all global marking worklists.
class V8_EXPORT_PRIVATE MarkingWorklists {
70
 public:
71
  class Local;
72 73 74 75 76 77 78
  // Fake addresses of special contexts used for per-context accounting.
  // - kSharedContext is for objects that are not attributed to any context.
  // - kOtherContext is for objects that are attributed to contexts that are
  //   not being measured.
  static const Address kSharedContext = 0;
  static const Address kOtherContext = 8;

79 80
  MarkingWorklists() = default;
  ~MarkingWorklists();
81

82 83 84 85 86
  // Calls the specified callback on each element of the deques and replaces
  // the element with the result of the callback. If the callback returns
  // nullptr then the element is removed from the deque.
  // The callback must accept HeapObject and return HeapObject.
  template <typename Callback>
87
  void Update(Callback callback);
88 89 90

  MarkingWorklist* shared() { return &shared_; }
  MarkingWorklist* on_hold() { return &on_hold_; }
91
  WrapperTracingWorklist* wrapper() { return &wrapper_; }
92

93 94
  // A list of (context, worklist) pairs that was set up at the start of
  // marking by CreateContextWorklists.
95
  const std::vector<ContextWorklistPair>& context_worklists() const {
96 97 98 99 100 101 102 103 104
    return context_worklists_;
  }
  // This should be invoked at the start of marking with the list of contexts
  // that require object size accounting.
  void CreateContextWorklists(const std::vector<Address>& contexts);
  // This should be invoked at the end of marking. All worklists must be
  // empty at that point.
  void ReleaseContextWorklists();

105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
  void Clear();
  void Print();

 private:
  // Prints the stats about the global pool of the worklist.
  void PrintWorklist(const char* worklist_name, MarkingWorklist* worklist);

  // Worklist used for most objects.
  MarkingWorklist shared_;

  // Concurrent marking uses this worklist to bail out of marking objects
  // in new space's linear allocation area. Used to avoid black allocation
  // for new space. This allow the compiler to remove write barriers
  // for freshly allocatd objects.
  MarkingWorklist on_hold_;

  // Worklist for objects that potentially require embedder tracing, i.e.,
  // these objects need to be handed over to the embedder to find the full
  // transitive closure.
124
  WrapperTracingWorklist wrapper_;
125 126 127 128 129

  // Per-context worklists.
  std::vector<ContextWorklistPair> context_worklists_;
  // This is used only for lifetime management of the per-context worklists.
  std::vector<std::unique_ptr<MarkingWorklist>> worklists_;
130 131 132 133

  // Worklist used for objects that are attributed to contexts that are
  // not being measured.
  MarkingWorklist other_;
134 135
};

136 137 138 139 140 141 142 143 144
// A thread-local view of the marking worklists. It owns all local marking
// worklists and keeps track of the currently active local marking worklist
// for per-context marking. In order to avoid additional indirections for
// pushing and popping entries, the active_ worklist is not a pointer to
// Local but an actual instance of Local with the following invariants:
// - active_owner == worlist_by_context[active_context_].get()
// - *active_owner is empty (all fields are null) because its content has
//   been moved to active_.
class V8_EXPORT_PRIVATE MarkingWorklists::Local {
145
 public:
146 147 148
  static constexpr Address kSharedContext = MarkingWorklists::kSharedContext;
  static constexpr Address kOtherContext = MarkingWorklists::kOtherContext;
  static constexpr std::nullptr_t kNoCppMarkingState = nullptr;
149

150 151 152
  Local(
      MarkingWorklists* global,
      std::unique_ptr<CppMarkingState> cpp_marking_state = kNoCppMarkingState);
153
  ~Local();
154

155 156
  inline void Push(HeapObject object);
  inline bool Pop(HeapObject* object);
157

158 159
  inline void PushOnHold(HeapObject object);
  inline bool PopOnHold(HeapObject* object);
160

161 162 163 164 165
  using WrapperSnapshot = CppMarkingState::EmbedderDataSnapshot;
  inline bool ExtractWrapper(Map map, JSObject object,
                             WrapperSnapshot& snapshot);
  inline void PushExtractedWrapper(const WrapperSnapshot& snapshot);
  inline bool SupportsExtractWrapper();
166 167
  inline void PushWrapper(HeapObject object);
  inline bool PopWrapper(HeapObject* object);
168

169
  void Publish();
170
  bool IsEmpty();
171
  bool IsWrapperEmpty() const;
172 173 174 175 176
  // Publishes the local active marking worklist if its global worklist is
  // empty. In the per-context marking mode it also publishes the shared
  // worklist.
  void ShareWork();
  // Merges the on-hold worklist to the shared worklist.
177 178
  void MergeOnHold();

179 180 181 182
  // Returns true if wrapper objects could be directly pushed. Otherwise,
  // objects need to be processed one by one.
  inline bool PublishWrapper();

183
  // Returns the context of the active worklist.
184 185 186 187
  Address Context() const { return active_context_; }
  inline Address SwitchToContext(Address context);
  inline Address SwitchToShared();
  bool IsPerContextMode() const { return is_per_context_mode_; }
188

189 190 191 192
  CppMarkingState* cpp_marking_state() const {
    return cpp_marking_state_.get();
  }

193
 private:
194 195
  bool PopContext(HeapObject* object);
  Address SwitchToContextSlow(Address context);
196 197 198
  inline void SwitchToContext(Address context,
                              MarkingWorklist::Local* worklist);
  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 205
  std::unordered_map<Address, std::unique_ptr<MarkingWorklist::Local>>
      worklist_by_context_;
206 207

  std::unique_ptr<CppMarkingState> cpp_marking_state_;
208 209 210 211 212 213
};

}  // namespace internal
}  // namespace v8

#endif  // V8_HEAP_MARKING_WORKLIST_H_