local-heap.cc 7.48 KB
Newer Older
1 2 3 4 5
// 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.

#include "src/heap/local-heap.h"
6

7
#include <atomic>
8 9
#include <memory>

10
#include "src/base/logging.h"
11
#include "src/base/platform/mutex.h"
12
#include "src/common/globals.h"
13
#include "src/execution/isolate.h"
14
#include "src/handles/local-handles.h"
15
#include "src/heap/gc-tracer.h"
16
#include "src/heap/heap-inl.h"
17
#include "src/heap/heap-write-barrier.h"
18
#include "src/heap/local-heap-inl.h"
19
#include "src/heap/marking-barrier.h"
20
#include "src/heap/parked-scope.h"
21
#include "src/heap/safepoint.h"
22 23 24 25

namespace v8 {
namespace internal {

26 27 28 29 30 31
namespace {
thread_local LocalHeap* current_local_heap = nullptr;
}  // namespace

LocalHeap* LocalHeap::Current() { return current_local_heap; }

32 33 34 35 36 37 38 39 40 41 42
#ifdef DEBUG
void LocalHeap::VerifyCurrent() {
  LocalHeap* current = LocalHeap::Current();

  if (is_main_thread())
    DCHECK_NULL(current);
  else
    DCHECK_EQ(current, this);
}
#endif

43
LocalHeap::LocalHeap(Heap* heap, ThreadKind kind,
44
                     std::unique_ptr<PersistentHandles> persistent_handles)
45
    : heap_(heap),
46
      is_main_thread_(kind == ThreadKind::kMain),
47
      state_(ThreadState::Parked),
48
      allocation_failed_(false),
49
      prev_(nullptr),
50
      next_(nullptr),
51
      handles_(new LocalHandles),
52
      persistent_handles_(std::move(persistent_handles)),
53
      marking_barrier_(new MarkingBarrier(this)),
54
      old_space_allocator_(this, heap->old_space()) {
55
  heap_->safepoint()->AddLocalHeap(this, [this] {
56
    if (!is_main_thread()) {
57 58 59 60 61 62 63 64
      WriteBarrier::SetForThread(marking_barrier_.get());
      if (heap_->incremental_marking()->IsMarking()) {
        marking_barrier_->Activate(
            heap_->incremental_marking()->IsCompacting());
      }
    }
  });

65 66 67
  if (persistent_handles_) {
    persistent_handles_->Attach(this);
  }
68
  DCHECK_NULL(current_local_heap);
69
  if (!is_main_thread()) current_local_heap = this;
70 71
}

72 73 74 75
LocalHeap::~LocalHeap() {
  // Park thread since removing the local heap could block.
  EnsureParkedBeforeDestruction();

76 77 78
  heap_->safepoint()->RemoveLocalHeap(this, [this] {
    old_space_allocator_.FreeLinearAllocationArea();

79
    if (!is_main_thread()) {
80 81 82 83
      marking_barrier_->Publish();
      WriteBarrier::ClearForThread(marking_barrier_.get());
    }
  });
84

85 86 87 88
  if (!is_main_thread()) {
    DCHECK_EQ(current_local_heap, this);
    current_local_heap = nullptr;
  }
89 90

  DCHECK(gc_epilogue_callbacks_.empty());
91
}
92

93
void LocalHeap::EnsurePersistentHandles() {
94 95 96
  if (!persistent_handles_) {
    persistent_handles_.reset(
        heap_->isolate()->NewPersistentHandles().release());
97
    persistent_handles_->Attach(this);
98 99 100
  }
}

101 102 103 104 105 106 107
void LocalHeap::AttachPersistentHandles(
    std::unique_ptr<PersistentHandles> persistent_handles) {
  DCHECK_NULL(persistent_handles_);
  persistent_handles_ = std::move(persistent_handles);
  persistent_handles_->Attach(this);
}

108 109 110 111 112
std::unique_ptr<PersistentHandles> LocalHeap::DetachPersistentHandles() {
  if (persistent_handles_) persistent_handles_->Detach();
  return std::move(persistent_handles_);
}

113 114 115 116
#ifdef DEBUG
bool LocalHeap::ContainsPersistentHandle(Address* location) {
  return persistent_handles_ ? persistent_handles_->Contains(location) : false;
}
117

118 119 120 121
bool LocalHeap::ContainsLocalHandle(Address* location) {
  return handles_ ? handles_->Contains(location) : false;
}

122
bool LocalHeap::IsHandleDereferenceAllowed() {
123 124 125
#ifdef DEBUG
  VerifyCurrent();
#endif
126 127 128
  ThreadState state = state_relaxed();
  return state == ThreadState::Running ||
         state == ThreadState::SafepointRequested;
129
}
130 131
#endif

132
bool LocalHeap::IsParked() {
133 134 135
#ifdef DEBUG
  VerifyCurrent();
#endif
136 137
  ThreadState state = state_relaxed();
  return state == ThreadState::Parked || state == ThreadState::ParkedSafepoint;
138 139
}

140
void LocalHeap::Park() {
141 142 143 144 145 146 147 148
  ThreadState expected = ThreadState::Running;
  if (!state_.compare_exchange_strong(expected, ThreadState::Parked)) {
    CHECK_EQ(expected, ThreadState::SafepointRequested);
    expected = ThreadState::SafepointRequested;
    CHECK(
        state_.compare_exchange_strong(expected, ThreadState::ParkedSafepoint));
    heap_->safepoint()->NotifyPark();
  }
149 150 151
}

void LocalHeap::Unpark() {
152 153 154 155 156 157 158 159 160 161 162 163 164
  while (true) {
    ThreadState expected = ThreadState::Parked;
    if (!state_.compare_exchange_strong(expected, ThreadState::Running)) {
      CHECK_EQ(expected, ThreadState::ParkedSafepoint);
      DCHECK(!is_main_thread());
      DCHECK_EQ(LocalHeap::Current(), this);
      TRACE_GC1(heap_->tracer(), GCTracer::Scope::BACKGROUND_UNPARK,
                ThreadKind::kBackground);
      heap_->safepoint()->WaitInUnpark();
    } else {
      break;
    }
  }
165 166
}

167
void LocalHeap::EnsureParkedBeforeDestruction() {
168
  DCHECK_IMPLIES(!is_main_thread(), IsParked());
169 170
}

171 172
void LocalHeap::SafepointSlowPath() {
  DCHECK(!is_main_thread());
173
  DCHECK_EQ(LocalHeap::Current(), this);
174 175 176 177 178 179 180 181 182
  TRACE_GC1(heap_->tracer(), GCTracer::Scope::BACKGROUND_SAFEPOINT,
            ThreadKind::kBackground);
  LocalHeap::ThreadState expected = LocalHeap::ThreadState::SafepointRequested;
  CHECK(state_.compare_exchange_strong(expected,
                                       LocalHeap::ThreadState::Safepoint));
  heap_->safepoint()->WaitInSafepoint();
  // This might be a bit surprising, GlobalSafepoint transitions the state from
  // Safepoint (--> Running) --> Parked when returning from the safepoint.
  Unpark();
183
}
184

185 186 187 188
void LocalHeap::FreeLinearAllocationArea() {
  old_space_allocator_.FreeLinearAllocationArea();
}

189 190 191 192
void LocalHeap::MakeLinearAllocationAreaIterable() {
  old_space_allocator_.MakeLinearAllocationAreaIterable();
}

193 194 195 196 197 198 199 200
void LocalHeap::MarkLinearAllocationAreaBlack() {
  old_space_allocator_.MarkLinearAllocationAreaBlack();
}

void LocalHeap::UnmarkLinearAllocationArea() {
  old_space_allocator_.UnmarkLinearAllocationArea();
}

201 202 203 204 205
void LocalHeap::PerformCollection() {
  ParkedScope scope(this);
  heap_->RequestCollectionBackground(this);
}

206 207 208 209 210 211 212
Address LocalHeap::PerformCollectionAndAllocateAgain(
    int object_size, AllocationType type, AllocationOrigin origin,
    AllocationAlignment alignment) {
  allocation_failed_ = true;
  static const int kMaxNumberOfRetries = 3;

  for (int i = 0; i < kMaxNumberOfRetries; i++) {
213
    PerformCollection();
214 215 216 217 218 219 220 221 222 223 224

    AllocationResult result = AllocateRaw(object_size, type, origin, alignment);
    if (!result.IsRetry()) {
      allocation_failed_ = false;
      return result.ToObjectChecked().address();
    }
  }

  heap_->FatalProcessOutOfMemory("LocalHeap: allocation failed");
}

225 226
void LocalHeap::AddGCEpilogueCallback(GCEpilogueCallback* callback,
                                      void* data) {
227
  DCHECK(!IsParked());
228 229 230 231 232 233 234 235 236
  std::pair<GCEpilogueCallback*, void*> callback_and_data(callback, data);
  DCHECK_EQ(std::find(gc_epilogue_callbacks_.begin(),
                      gc_epilogue_callbacks_.end(), callback_and_data),
            gc_epilogue_callbacks_.end());
  gc_epilogue_callbacks_.push_back(callback_and_data);
}

void LocalHeap::RemoveGCEpilogueCallback(GCEpilogueCallback* callback,
                                         void* data) {
237
  DCHECK(!IsParked());
238 239 240 241 242 243 244 245 246 247 248 249 250
  std::pair<GCEpilogueCallback*, void*> callback_and_data(callback, data);
  auto it = std::find(gc_epilogue_callbacks_.begin(),
                      gc_epilogue_callbacks_.end(), callback_and_data);
  *it = gc_epilogue_callbacks_.back();
  gc_epilogue_callbacks_.pop_back();
}

void LocalHeap::InvokeGCEpilogueCallbacksInSafepoint() {
  for (auto callback_and_data : gc_epilogue_callbacks_) {
    callback_and_data.first(callback_and_data.second);
  }
}

251 252
}  // namespace internal
}  // namespace v8