local-heap.h 8.12 KB
Newer Older
1 2 3 4 5 6 7 8
// Copyright 2020 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_LOCAL_HEAP_H_
#define V8_HEAP_LOCAL_HEAP_H_

#include <atomic>
9
#include <memory>
10

11
#include "src/base/macros.h"
12 13
#include "src/base/platform/condition-variable.h"
#include "src/base/platform/mutex.h"
14
#include "src/common/assert-scope.h"
15
#include "src/execution/isolate.h"
16
#include "src/handles/persistent-handles.h"
17
#include "src/heap/concurrent-allocator.h"
18 19 20 21 22

namespace v8 {
namespace internal {

class Heap;
23
class Safepoint;
24
class LocalHandles;
25

26 27 28 29 30 31 32 33 34
// LocalHeap is used by the GC to track all threads with heap access in order to
// stop them before performing a collection. LocalHeaps can be either Parked or
// Running and are in Parked mode when initialized.
//   Running: Thread is allowed to access the heap but needs to give the GC the
//            chance to run regularly by manually invoking Safepoint(). The
//            thread can be parked using ParkedScope.
//   Parked:  Heap access is not allowed, so the GC will not stop this thread
//            for a collection. Useful when threads do not need heap access for
//            some time or for blocking operations like locking a mutex.
35
class V8_EXPORT_PRIVATE LocalHeap {
36
 public:
37 38
  using GCEpilogueCallback = void(void* data);

39
  explicit LocalHeap(
40
      Heap* heap, ThreadKind kind,
41
      std::unique_ptr<PersistentHandles> persistent_handles = nullptr);
42
  ~LocalHeap();
43 44 45

  // Frequently invoked by local thread to check whether safepoint was requested
  // from the main thread.
46
  void Safepoint() {
47
    DCHECK(AllowSafepoints::IsAllowed());
48
    ThreadState current = state_relaxed();
49
    STATIC_ASSERT(kSafepointRequested == kCollectionRequested);
50

51 52 53
    // The following condition checks for both kSafepointRequested (background
    // thread) and kCollectionRequested (main thread).
    if (V8_UNLIKELY(current == kSafepointRequested)) {
54
      SafepointSlowPath();
55 56
    }
  }
57

58 59
  LocalHandles* handles() { return handles_.get(); }

60
  template <typename T>
61 62 63 64 65 66 67
  Handle<T> NewPersistentHandle(T object) {
    if (!persistent_handles_) {
      EnsurePersistentHandles();
    }
    return persistent_handles_->NewHandle(object);
  }

68
  template <typename T>
69 70 71 72
  Handle<T> NewPersistentHandle(Handle<T> object) {
    return NewPersistentHandle(*object);
  }

73 74 75 76 77 78 79 80 81
  template <typename T>
  MaybeHandle<T> NewPersistentMaybeHandle(MaybeHandle<T> maybe_handle) {
    Handle<T> handle;
    if (maybe_handle.ToHandle(&handle)) {
      return NewPersistentHandle(handle);
    }
    return kNullMaybeHandle;
  }

82 83
  void AttachPersistentHandles(
      std::unique_ptr<PersistentHandles> persistent_handles);
84
  std::unique_ptr<PersistentHandles> DetachPersistentHandles();
85 86
#ifdef DEBUG
  bool ContainsPersistentHandle(Address* location);
87
  bool ContainsLocalHandle(Address* location);
88
  bool IsHandleDereferenceAllowed();
89
#endif
90 91 92

  bool IsParked();

93 94
  Heap* heap() { return heap_; }

95
  MarkingBarrier* marking_barrier() { return marking_barrier_.get(); }
96 97
  ConcurrentAllocator* old_space_allocator() { return &old_space_allocator_; }

98 99 100 101 102 103 104 105 106 107 108
  // Mark/Unmark linear allocation areas black. Used for black allocation.
  void MarkLinearAllocationAreaBlack();
  void UnmarkLinearAllocationArea();

  // Give up linear allocation areas. Used for mark-compact GC.
  void FreeLinearAllocationArea();

  // Create filler object in linear allocation areas. Verifying requires
  // iterable heap.
  void MakeLinearAllocationAreaIterable();

109 110 111 112 113 114 115
  // Fetches a pointer to the local heap from the thread local storage.
  // It is intended to be used in handle and write barrier code where it is
  // difficult to get a pointer to the current instance of local heap otherwise.
  // The result may be a nullptr if there is no local heap instance associated
  // with the current thread.
  static LocalHeap* Current();

116 117 118 119
#ifdef DEBUG
  void VerifyCurrent();
#endif

120 121 122 123 124 125 126 127 128 129 130 131 132
  // Allocate an uninitialized object.
  V8_WARN_UNUSED_RESULT inline AllocationResult AllocateRaw(
      int size_in_bytes, AllocationType allocation,
      AllocationOrigin origin = AllocationOrigin::kRuntime,
      AllocationAlignment alignment = kWordAligned);

  // Allocates an uninitialized object and crashes when object
  // cannot be allocated.
  V8_WARN_UNUSED_RESULT inline Address AllocateRawOrFail(
      int size_in_bytes, AllocationType allocation,
      AllocationOrigin origin = AllocationOrigin::kRuntime,
      AllocationAlignment alignment = kWordAligned);

133
  bool is_main_thread() const { return is_main_thread_; }
134

135
  // Requests GC and blocks until the collection finishes.
136
  bool TryPerformCollection();
137

138 139 140 141 142 143 144
  // Adds a callback that is invoked with the given |data| after each GC.
  // The callback is invoked on the main thread before any background thread
  // resumes. The callback must not allocate or make any other calls that
  // can trigger GC.
  void AddGCEpilogueCallback(GCEpilogueCallback* callback, void* data);
  void RemoveGCEpilogueCallback(GCEpilogueCallback* callback, void* data);

145
 private:
146
  enum ThreadState {
147
    // Threads in this state are allowed to access the heap.
148
    kRunning,
149
    // Thread was parked, which means that the thread is not allowed to access
150
    // or manipulate the heap in any way. This is considered to be a safepoint.
151
    kParked,
152

153 154
    // SafepointRequested is used for Running background threads to force
    // Safepoint() and
155
    // Park() into the slow path.
156 157 158
    kSafepointRequested,
    // A background thread transitions into this state from SafepointRequested
    // when it
159
    // enters a safepoint.
160 161 162
    kSafepoint,
    // This state is used for Parked background threads and forces Unpark() into
    // the slow
163 164
    // path. It prevents Unpark() to succeed before the safepoint operation is
    // finished.
165 166 167 168 169 170 171 172 173 174 175 176 177
    kParkedSafepointRequested,

    // This state is used on the main thread when at least one background thread
    // requested a GC while the main thread was Running.
    // We can use the same value for CollectionRequested and SafepointRequested
    // since the first is only used on the main thread, while the other one only
    // occurs on background threads. This property is used to have a faster
    // check in Safepoint().
    kCollectionRequested = kSafepointRequested,

    // This state is used on the main thread when at least one background thread
    // requested a GC while the main thread was Parked.
    kParkedCollectionRequested,
178 179
  };

180 181
  ThreadState state_relaxed() { return state_.load(std::memory_order_relaxed); }

182 183 184 185 186 187 188
  // Slow path of allocation that performs GC and then retries allocation in
  // loop.
  Address PerformCollectionAndAllocateAgain(int object_size,
                                            AllocationType type,
                                            AllocationOrigin origin,
                                            AllocationAlignment alignment);

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
  void Park() {
    DCHECK(AllowGarbageCollection::IsAllowed());
    ThreadState expected = kRunning;
    if (!state_.compare_exchange_strong(expected, kParked)) {
      ParkSlowPath(expected);
    }
  }

  void Unpark() {
    DCHECK(AllowGarbageCollection::IsAllowed());
    ThreadState expected = kParked;
    if (!state_.compare_exchange_strong(expected, kRunning)) {
      UnparkSlowPath();
    }
  }

  void ParkSlowPath(ThreadState state);
  void UnparkSlowPath();
207
  void EnsureParkedBeforeDestruction();
208
  void SafepointSlowPath();
209

210 211
  void EnsurePersistentHandles();

212 213
  void InvokeGCEpilogueCallbacksInSafepoint();

214
  Heap* heap_;
215
  bool is_main_thread_;
216

217
  std::atomic<ThreadState> state_;
218

219
  bool allocation_failed_;
220
  bool main_thread_parked_;
221

222 223 224
  LocalHeap* prev_;
  LocalHeap* next_;

225
  std::unique_ptr<LocalHandles> handles_;
226
  std::unique_ptr<PersistentHandles> persistent_handles_;
227
  std::unique_ptr<MarkingBarrier> marking_barrier_;
228

229 230
  std::vector<std::pair<GCEpilogueCallback*, void*>> gc_epilogue_callbacks_;

231 232
  ConcurrentAllocator old_space_allocator_;

233 234
  friend class CollectionBarrier;
  friend class ConcurrentAllocator;
235
  friend class GlobalSafepoint;
236 237
  friend class Heap;
  friend class Isolate;
238
  friend class ParkedScope;
239
  friend class UnparkedScope;
240 241
};

242 243 244 245
}  // namespace internal
}  // namespace v8

#endif  // V8_HEAP_LOCAL_HEAP_H_