heap: ArrayBufferSweeper refactoring

The refactoring is triggered by https://crrev.com/c/3121905 where we
noticed that a bunch of tricky counter paths could be simplified,
making reasoning about corectness easier.

In this CL:
1. Use uniqe_ptr instead of Optional to allow moving SweepingJob away
   from the header file.
2. sweeping_in_progress_ is replaced with simply checking for a job.
3. freed_bytes_ are moved to the job and the dependency is reversed,
   avoiding the inside-out (Job->Sweeper) dependency completely.
4. Merge() and counter updates are merged into a Finalize() method.
5. FinishIfDone() allows for conditional finization.
6. young_bytes_ and old_bytes_ are removed as they were always updated
   when the corresponding bytes in the ArrayBufferList was updated.

Change-Id: I56e5b04087166ce03d3a9195ac48359122a84c73
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3124776
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Michael Lippautz <mlippautz@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76795}
This commit is contained in:
Michael Lippautz 2021-08-27 14:56:39 +02:00 committed by V8 LUCI CQ
parent 6d457a9005
commit 210987a552
7 changed files with 192 additions and 238 deletions

View File

@ -5,6 +5,7 @@
#include "src/heap/array-buffer-sweeper.h"
#include <atomic>
#include <memory>
#include "src/heap/gc-tracer.h"
#include "src/heap/heap-inl.h"
@ -24,7 +25,9 @@ void ArrayBufferList::Append(ArrayBufferExtension* extension) {
tail_ = extension;
}
bytes_ += extension->accounting_length();
const size_t accounting_length = extension->accounting_length();
DCHECK_GE(bytes_ + accounting_length, bytes_);
bytes_ += accounting_length;
extension->set_next(nullptr);
}
@ -41,115 +44,119 @@ void ArrayBufferList::Append(ArrayBufferList* list) {
DCHECK_NULL(list->tail_);
}
bytes_ += list->Bytes();
list->Reset();
bytes_ += list->ApproximateBytes();
*list = ArrayBufferList();
}
bool ArrayBufferList::Contains(ArrayBufferExtension* extension) {
ArrayBufferExtension* current = head_;
while (current) {
bool ArrayBufferList::ContainsSlow(ArrayBufferExtension* extension) const {
for (ArrayBufferExtension* current = head_; current;
current = current->next()) {
if (current == extension) return true;
current = current->next();
}
return false;
}
size_t ArrayBufferList::BytesSlow() {
size_t ArrayBufferList::BytesSlow() const {
ArrayBufferExtension* current = head_;
size_t sum = 0;
while (current) {
sum += current->accounting_length();
current = current->next();
}
DCHECK_GE(sum, ApproximateBytes());
return sum;
}
bool ArrayBufferList::IsEmpty() const {
DCHECK_IMPLIES(head_, tail_);
DCHECK_IMPLIES(!head_, bytes_ == 0);
return head_ == nullptr;
}
struct ArrayBufferSweeper::SweepingJob final {
SweepingJob(ArrayBufferList young, ArrayBufferList old, SweepingType type)
: state_(SweepingState::kInProgress),
young_(std::move(young)),
old_(std::move(old)),
type_(type) {}
void Sweep();
void SweepYoung();
void SweepFull();
ArrayBufferList SweepListFull(ArrayBufferList* list);
private:
CancelableTaskManager::Id id_ = CancelableTaskManager::kInvalidTaskId;
std::atomic<SweepingState> state_;
ArrayBufferList young_;
ArrayBufferList old_;
const SweepingType type_;
std::atomic<size_t> freed_bytes_{0};
friend class ArrayBufferSweeper;
};
ArrayBufferSweeper::ArrayBufferSweeper(Heap* heap) : heap_(heap) {}
ArrayBufferSweeper::~ArrayBufferSweeper() {
EnsureFinished();
ReleaseAll(&old_);
ReleaseAll(&young_);
}
void ArrayBufferSweeper::EnsureFinished() {
if (!sweeping_in_progress_) return;
if (!sweeping_in_progress()) return;
TRACE_GC(heap_->tracer(), GCTracer::Scope::MC_COMPLETE_SWEEP_ARRAY_BUFFERS);
TryAbortResult abort_result =
heap_->isolate()->cancelable_task_manager()->TryAbort(job_->id_);
switch (abort_result) {
case TryAbortResult::kTaskAborted: {
case TryAbortResult::kTaskAborted:
// Task has not run, so we need to run it synchronously here.
job_->Sweep();
Merge();
break;
}
case TryAbortResult::kTaskRemoved: {
if (job_->state_ == SweepingState::kInProgress) job_->Sweep();
if (job_->state_ == SweepingState::kDone) Merge();
case TryAbortResult::kTaskRemoved:
// Task was removed, but did actually run, just ensure we are in the right
// state.
CHECK_EQ(SweepingState::kDone, job_->state_);
break;
}
case TryAbortResult::kTaskRunning: {
// Task is running. Wait until task is finished with its work.
base::MutexGuard guard(&sweeping_mutex_);
// Wait until task is finished with its work.
while (job_->state_ != SweepingState::kDone) {
job_finished_.Wait(&sweeping_mutex_);
}
Merge();
break;
}
default:
UNREACHABLE();
}
UpdateCountersForConcurrentlySweptExtensions();
Finalize();
DCHECK_LE(heap_->backing_store_bytes(), SIZE_MAX);
sweeping_in_progress_ = false;
DCHECK(!sweeping_in_progress());
}
void ArrayBufferSweeper::MergeBackExtensionsWhenSwept() {
if (sweeping_in_progress_) {
DCHECK(job_.has_value());
void ArrayBufferSweeper::FinishIfDone() {
if (sweeping_in_progress()) {
DCHECK(job_);
if (job_->state_ == SweepingState::kDone) {
Merge();
sweeping_in_progress_ = false;
Finalize();
}
// Update freed counters either way. It is necessary to update the counter
// in case sweeping is done to avoid counter overflows.
UpdateCountersForConcurrentlySweptExtensions();
}
}
void ArrayBufferSweeper::UpdateCountersForConcurrentlySweptExtensions() {
size_t freed_bytes = freed_bytes_.exchange(0, std::memory_order_relaxed);
DecrementExternalMemoryCounters(freed_bytes);
}
void ArrayBufferSweeper::RequestSweep(SweepingType type) {
DCHECK(!sweeping_in_progress());
void ArrayBufferSweeper::RequestSweepYoung() {
RequestSweep(SweepingScope::kYoung);
}
void ArrayBufferSweeper::RequestSweepFull() {
RequestSweep(SweepingScope::kFull);
}
size_t ArrayBufferSweeper::YoungBytes() { return young_bytes_; }
size_t ArrayBufferSweeper::OldBytes() { return old_bytes_; }
void ArrayBufferSweeper::RequestSweep(SweepingScope scope) {
DCHECK(!sweeping_in_progress_);
if (young_.IsEmpty() && (old_.IsEmpty() || scope == SweepingScope::kYoung))
if (young_.IsEmpty() && (old_.IsEmpty() || type == SweepingType::kYoung))
return;
Prepare(type);
if (!heap_->IsTearingDown() && !heap_->ShouldReduceMemory() &&
FLAG_concurrent_array_buffer_sweeping) {
Prepare(scope);
auto task = MakeCancelableTask(heap_->isolate(), [this, scope] {
auto task = MakeCancelableTask(heap_->isolate(), [this, type] {
GCTracer::Scope::ScopeId scope_id =
scope == SweepingScope::kYoung
type == SweepingType::kYoung
? GCTracer::Scope::BACKGROUND_YOUNG_ARRAY_BUFFER_SWEEP
: GCTracer::Scope::BACKGROUND_FULL_ARRAY_BUFFER_SWEEP;
TRACE_GC_EPOCH(heap_->tracer(), scope_id, ThreadKind::kBackground);
@ -159,74 +166,64 @@ void ArrayBufferSweeper::RequestSweep(SweepingScope scope) {
});
job_->id_ = task->id();
V8::GetCurrentPlatform()->CallOnWorkerThread(std::move(task));
sweeping_in_progress_ = true;
} else {
Prepare(scope);
job_->Sweep();
Merge();
UpdateCountersForConcurrentlySweptExtensions();
Finalize();
}
}
void ArrayBufferSweeper::Prepare(SweepingScope scope) {
DCHECK(!job_.has_value());
if (scope == SweepingScope::kYoung) {
job_.emplace(this, young_, ArrayBufferList(), SweepingScope::kYoung);
young_.Reset();
young_bytes_ = 0;
} else {
CHECK_EQ(scope, SweepingScope::kFull);
job_.emplace(this, young_, old_, SweepingScope::kFull);
young_.Reset();
old_.Reset();
young_bytes_ = old_bytes_ = 0;
void ArrayBufferSweeper::Prepare(SweepingType type) {
DCHECK(!sweeping_in_progress());
switch (type) {
case SweepingType::kYoung: {
job_ = std::make_unique<SweepingJob>(std::move(young_), ArrayBufferList(),
type);
young_ = ArrayBufferList();
} break;
case SweepingType::kFull: {
job_ = std::make_unique<SweepingJob>(std::move(young_), std::move(old_),
type);
young_ = ArrayBufferList();
old_ = ArrayBufferList();
} break;
}
DCHECK(sweeping_in_progress());
}
void ArrayBufferSweeper::Merge() {
DCHECK(job_.has_value());
void ArrayBufferSweeper::Finalize() {
DCHECK(sweeping_in_progress());
CHECK_EQ(job_->state_, SweepingState::kDone);
young_.Append(&job_->young_);
old_.Append(&job_->old_);
young_bytes_ = young_.Bytes();
old_bytes_ = old_.Bytes();
const size_t freed_bytes =
job_->freed_bytes_.exchange(0, std::memory_order_relaxed);
DecrementExternalMemoryCounters(freed_bytes);
job_.reset();
}
void ArrayBufferSweeper::ReleaseAll() {
EnsureFinished();
ReleaseAll(&old_);
ReleaseAll(&young_);
old_bytes_ = young_bytes_ = 0;
DCHECK(!sweeping_in_progress());
}
void ArrayBufferSweeper::ReleaseAll(ArrayBufferList* list) {
ArrayBufferExtension* current = list->head_;
while (current) {
ArrayBufferExtension* next = current->next();
delete current;
current = next;
}
list->Reset();
*list = ArrayBufferList();
}
void ArrayBufferSweeper::Append(JSArrayBuffer object,
ArrayBufferExtension* extension) {
size_t bytes = extension->accounting_length();
FinishIfDone();
if (Heap::InYoungGeneration(object)) {
young_.Append(extension);
young_bytes_ += bytes;
} else {
old_.Append(extension);
old_bytes_ += bytes;
}
MergeBackExtensionsWhenSwept();
IncrementExternalMemoryCounters(bytes);
}
@ -237,21 +234,21 @@ void ArrayBufferSweeper::Detach(JSArrayBuffer object,
// We cannot free the extension eagerly here, since extensions are tracked in
// a singly linked list. The next GC will remove it automatically.
if (!sweeping_in_progress_) {
FinishIfDone();
if (!sweeping_in_progress()) {
// If concurrent sweeping isn't running at the moment, we can also adjust
// young_bytes_ or old_bytes_ right away.
// the respective bytes in the corresponding ArraybufferLists as they are
// only approximate.
if (Heap::InYoungGeneration(object)) {
DCHECK_GE(young_bytes_, bytes);
young_bytes_ -= bytes;
DCHECK_GE(young_.bytes_, bytes);
young_.bytes_ -= bytes;
} else {
DCHECK_GE(old_bytes_, bytes);
old_bytes_ -= bytes;
DCHECK_GE(old_.bytes_, bytes);
old_.bytes_ -= bytes;
}
}
MergeBackExtensionsWhenSwept();
DecrementExternalMemoryCounters(bytes);
}
@ -272,29 +269,25 @@ void ArrayBufferSweeper::DecrementExternalMemoryCounters(size_t bytes) {
heap_->update_external_memory(-static_cast<int64_t>(bytes));
}
void ArrayBufferSweeper::IncrementFreedBytes(size_t bytes) {
if (bytes == 0) return;
freed_bytes_.fetch_add(bytes, std::memory_order_relaxed);
}
void ArrayBufferSweeper::SweepingJob::Sweep() {
CHECK_EQ(state_, SweepingState::kInProgress);
if (scope_ == SweepingScope::kYoung) {
SweepYoung();
} else {
CHECK_EQ(scope_, SweepingScope::kFull);
SweepFull();
switch (type_) {
case SweepingType::kYoung:
SweepYoung();
break;
case SweepingType::kFull:
SweepFull();
break;
}
state_ = SweepingState::kDone;
}
void ArrayBufferSweeper::SweepingJob::SweepFull() {
CHECK_EQ(scope_, SweepingScope::kFull);
DCHECK_EQ(SweepingType::kFull, type_);
ArrayBufferList promoted = SweepListFull(&young_);
ArrayBufferList survived = SweepListFull(&old_);
old_ = promoted;
old_ = std::move(promoted);
old_.Append(&survived);
}
@ -307,9 +300,9 @@ ArrayBufferList ArrayBufferSweeper::SweepingJob::SweepListFull(
ArrayBufferExtension* next = current->next();
if (!current->IsMarked()) {
size_t bytes = current->accounting_length();
const size_t bytes = current->accounting_length();
delete current;
sweeper_->IncrementFreedBytes(bytes);
if (bytes) freed_bytes_.fetch_add(bytes, std::memory_order_relaxed);
} else {
current->Unmark();
survivor_list.Append(current);
@ -318,12 +311,12 @@ ArrayBufferList ArrayBufferSweeper::SweepingJob::SweepListFull(
current = next;
}
list->Reset();
*list = ArrayBufferList();
return survivor_list;
}
void ArrayBufferSweeper::SweepingJob::SweepYoung() {
CHECK_EQ(scope_, SweepingScope::kYoung);
DCHECK_EQ(SweepingType::kYoung, type_);
ArrayBufferExtension* current = young_.head_;
ArrayBufferList new_young;
@ -335,7 +328,7 @@ void ArrayBufferSweeper::SweepingJob::SweepYoung() {
if (!current->IsYoungMarked()) {
size_t bytes = current->accounting_length();
delete current;
sweeper_->IncrementFreedBytes(bytes);
if (bytes) freed_bytes_.fetch_add(bytes, std::memory_order_relaxed);
} else if (current->IsYoungPromoted()) {
current->YoungUnmark();
new_old.Append(current);

View File

@ -5,6 +5,9 @@
#ifndef V8_HEAP_ARRAY_BUFFER_SWEEPER_H_
#define V8_HEAP_ARRAY_BUFFER_SWEEPER_H_
#include <memory>
#include "src/base/logging.h"
#include "src/base/platform/mutex.h"
#include "src/objects/js-array-buffer.h"
#include "src/tasks/cancelable-task.h"
@ -17,47 +20,38 @@ class Heap;
// Singly linked-list of ArrayBufferExtensions that stores head and tail of the
// list to allow for concatenation of lists.
struct ArrayBufferList {
ArrayBufferList() : head_(nullptr), tail_(nullptr), bytes_(0) {}
ArrayBufferExtension* head_;
ArrayBufferExtension* tail_;
size_t bytes_;
bool IsEmpty() {
DCHECK_IMPLIES(head_, tail_);
return head_ == nullptr;
}
size_t Bytes() { return bytes_; }
size_t BytesSlow();
void Reset() {
head_ = tail_ = nullptr;
bytes_ = 0;
}
struct ArrayBufferList final {
bool IsEmpty() const;
size_t ApproximateBytes() const { return bytes_; }
size_t BytesSlow() const;
void Append(ArrayBufferExtension* extension);
void Append(ArrayBufferList* list);
V8_EXPORT_PRIVATE bool Contains(ArrayBufferExtension* extension);
V8_EXPORT_PRIVATE bool ContainsSlow(ArrayBufferExtension* extension) const;
private:
ArrayBufferExtension* head_ = nullptr;
ArrayBufferExtension* tail_ = nullptr;
// Bytes are approximate as they may be subtracted eagerly, while the
// `ArrayBufferExtension` is still in the list. The extension will only be
// dropped on next sweep.
size_t bytes_ = 0;
friend class ArrayBufferSweeper;
};
// The ArrayBufferSweeper iterates and deletes ArrayBufferExtensions
// concurrently to the application.
class ArrayBufferSweeper {
class ArrayBufferSweeper final {
public:
explicit ArrayBufferSweeper(Heap* heap)
: heap_(heap),
sweeping_in_progress_(false),
freed_bytes_(0),
young_bytes_(0),
old_bytes_(0) {}
~ArrayBufferSweeper() { ReleaseAll(); }
enum class SweepingType { kYoung, kFull };
explicit ArrayBufferSweeper(Heap* heap);
~ArrayBufferSweeper();
void RequestSweep(SweepingType sweeping_type);
void EnsureFinished();
void RequestSweepYoung();
void RequestSweepFull();
// Track the given ArrayBufferExtension for the given JSArrayBuffer.
void Append(JSArrayBuffer object, ArrayBufferExtension* extension);
@ -65,70 +59,40 @@ class ArrayBufferSweeper {
// Detaches an ArrayBufferExtension from a JSArrayBuffer.
void Detach(JSArrayBuffer object, ArrayBufferExtension* extension);
ArrayBufferList young() { return young_; }
ArrayBufferList old() { return old_; }
const ArrayBufferList& young() const { return young_; }
const ArrayBufferList& old() const { return old_; }
size_t YoungBytes();
size_t OldBytes();
// Bytes accounted in the young generation. Rebuilt during sweeping.
size_t YoungBytes() const { return young().ApproximateBytes(); }
// Bytes accounted in the old generation. Rebuilt during sweeping.
size_t OldBytes() const { return old().ApproximateBytes(); }
private:
enum class SweepingScope { kYoung, kFull };
struct SweepingJob;
enum class SweepingState { kInProgress, kDone };
struct SweepingJob {
ArrayBufferSweeper* sweeper_;
CancelableTaskManager::Id id_;
std::atomic<SweepingState> state_;
ArrayBufferList young_;
ArrayBufferList old_;
SweepingScope scope_;
bool sweeping_in_progress() const { return job_.get(); }
SweepingJob(ArrayBufferSweeper* sweeper, ArrayBufferList young,
ArrayBufferList old, SweepingScope scope)
: sweeper_(sweeper),
id_(0),
state_(SweepingState::kInProgress),
young_(young),
old_(old),
scope_(scope) {}
// Finishes sweeping if it is already done.
void FinishIfDone();
void Sweep();
void SweepYoung();
void SweepFull();
ArrayBufferList SweepListFull(ArrayBufferList* list);
};
base::Optional<SweepingJob> job_;
void Merge();
void MergeBackExtensionsWhenSwept();
void UpdateCountersForConcurrentlySweptExtensions();
// Increments external memory counters outside of ArrayBufferSweeper.
// Increment may trigger GC.
void IncrementExternalMemoryCounters(size_t bytes);
void DecrementExternalMemoryCounters(size_t bytes);
void IncrementFreedBytes(size_t bytes);
void RequestSweep(SweepingScope sweeping_task);
void Prepare(SweepingScope sweeping_task);
void Prepare(SweepingType type);
void Finalize();
ArrayBufferList SweepYoungGen();
void SweepOldGen(ArrayBufferExtension* extension);
void ReleaseAll();
void ReleaseAll(ArrayBufferList* extension);
Heap* const heap_;
bool sweeping_in_progress_;
std::unique_ptr<SweepingJob> job_;
base::Mutex sweeping_mutex_;
base::ConditionVariable job_finished_;
std::atomic<size_t> freed_bytes_;
ArrayBufferList young_;
ArrayBufferList old_;
size_t young_bytes_;
size_t old_bytes_;
};
} // namespace internal

View File

@ -4143,11 +4143,13 @@ void Heap::RemoveNearHeapLimitCallback(v8::NearHeapLimitCallback callback,
void Heap::AppendArrayBufferExtension(JSArrayBuffer object,
ArrayBufferExtension* extension) {
// ArrayBufferSweeper is managing all counters and updating Heap counters.
array_buffer_sweeper_->Append(object, extension);
}
void Heap::DetachArrayBufferExtension(JSArrayBuffer object,
ArrayBufferExtension* extension) {
// ArrayBufferSweeper is managing all counters and updating Heap counters.
return array_buffer_sweeper_->Detach(object, extension);
}

View File

@ -1019,7 +1019,8 @@ void MarkCompactCollector::Finish() {
void MarkCompactCollector::SweepArrayBufferExtensions() {
TRACE_GC(heap()->tracer(), GCTracer::Scope::MC_FINISH_SWEEP_ARRAY_BUFFERS);
heap_->array_buffer_sweeper()->RequestSweepFull();
heap_->array_buffer_sweeper()->RequestSweep(
ArrayBufferSweeper::SweepingType::kFull);
}
class MarkCompactCollector::RootMarkingVisitor final : public RootVisitor {
@ -4622,7 +4623,8 @@ void MinorMarkCompactCollector::CleanupSweepToIteratePages() {
}
void MinorMarkCompactCollector::SweepArrayBufferExtensions() {
heap_->array_buffer_sweeper()->RequestSweepYoung();
heap_->array_buffer_sweeper()->RequestSweep(
ArrayBufferSweeper::SweepingType::kYoung);
}
class YoungGenerationMigrationObserver final : public MigrationObserver {

View File

@ -490,7 +490,8 @@ void ScavengerCollector::IterateStackAndScavenge(
}
void ScavengerCollector::SweepArrayBufferExtensions() {
heap_->array_buffer_sweeper()->RequestSweepYoung();
heap_->array_buffer_sweeper()->RequestSweep(
ArrayBufferSweeper::SweepingType::kYoung);
}
void ScavengerCollector::HandleSurvivingNewLargeObjects() {

View File

@ -160,52 +160,27 @@ class JSArrayBuffer
// extension-object. The GC periodically iterates all extensions concurrently
// and frees unmarked ones.
// https://docs.google.com/document/d/1-ZrLdlFX1nXT3z-FAgLbKal1gI8Auiaya_My-a0UJ28/edit
class ArrayBufferExtension : public Malloced {
enum class GcState : uint8_t { Dead = 0, Copied, Promoted };
std::atomic<bool> marked_;
std::atomic<GcState> young_gc_state_;
std::shared_ptr<BackingStore> backing_store_;
ArrayBufferExtension* next_;
std::atomic<size_t> accounting_length_;
GcState young_gc_state() {
return young_gc_state_.load(std::memory_order_relaxed);
}
void set_young_gc_state(GcState value) {
young_gc_state_.store(value, std::memory_order_relaxed);
}
class ArrayBufferExtension final : public Malloced {
public:
ArrayBufferExtension()
: marked_(false),
young_gc_state_(GcState::Dead),
backing_store_(std::shared_ptr<BackingStore>()),
next_(nullptr),
accounting_length_(0) {}
ArrayBufferExtension() : backing_store_(std::shared_ptr<BackingStore>()) {}
explicit ArrayBufferExtension(std::shared_ptr<BackingStore> backing_store)
: marked_(false),
young_gc_state_(GcState::Dead),
backing_store_(backing_store),
next_(nullptr),
accounting_length_(0) {}
: backing_store_(backing_store) {}
void Mark() { marked_.store(true, std::memory_order_relaxed); }
void Unmark() { marked_.store(false, std::memory_order_relaxed); }
bool IsMarked() { return marked_.load(std::memory_order_relaxed); }
bool IsMarked() const { return marked_.load(std::memory_order_relaxed); }
void YoungMark() { set_young_gc_state(GcState::Copied); }
void YoungMarkPromoted() { set_young_gc_state(GcState::Promoted); }
void YoungUnmark() { set_young_gc_state(GcState::Dead); }
bool IsYoungMarked() { return young_gc_state() != GcState::Dead; }
bool IsYoungMarked() const { return young_gc_state() != GcState::Dead; }
bool IsYoungPromoted() { return young_gc_state() == GcState::Promoted; }
bool IsYoungPromoted() const { return young_gc_state() == GcState::Promoted; }
std::shared_ptr<BackingStore> backing_store() { return backing_store_; }
BackingStore* backing_store_raw() { return backing_store_.get(); }
size_t accounting_length() {
size_t accounting_length() const {
return accounting_length_.load(std::memory_order_relaxed);
}
@ -227,8 +202,25 @@ class ArrayBufferExtension : public Malloced {
void reset_backing_store() { backing_store_.reset(); }
ArrayBufferExtension* next() { return next_; }
ArrayBufferExtension* next() const { return next_; }
void set_next(ArrayBufferExtension* extension) { next_ = extension; }
private:
enum class GcState : uint8_t { Dead = 0, Copied, Promoted };
std::atomic<bool> marked_{false};
std::atomic<GcState> young_gc_state_{GcState::Dead};
std::shared_ptr<BackingStore> backing_store_;
ArrayBufferExtension* next_ = nullptr;
std::atomic<size_t> accounting_length_{0};
GcState young_gc_state() const {
return young_gc_state_.load(std::memory_order_relaxed);
}
void set_young_gc_state(GcState value) {
young_gc_state_.store(value, std::memory_order_relaxed);
}
};
class JSArrayBufferView

View File

@ -15,22 +15,22 @@
namespace {
bool IsTrackedYoung(i::Heap* heap, i::ArrayBufferExtension* extension) {
bool in_young = heap->array_buffer_sweeper()->young().Contains(extension);
bool in_old = heap->array_buffer_sweeper()->old().Contains(extension);
bool in_young = heap->array_buffer_sweeper()->young().ContainsSlow(extension);
bool in_old = heap->array_buffer_sweeper()->old().ContainsSlow(extension);
CHECK(!(in_young && in_old));
return in_young;
}
bool IsTrackedOld(i::Heap* heap, i::ArrayBufferExtension* extension) {
bool in_young = heap->array_buffer_sweeper()->young().Contains(extension);
bool in_old = heap->array_buffer_sweeper()->old().Contains(extension);
bool in_young = heap->array_buffer_sweeper()->young().ContainsSlow(extension);
bool in_old = heap->array_buffer_sweeper()->old().ContainsSlow(extension);
CHECK(!(in_young && in_old));
return in_old;
}
bool IsTracked(i::Heap* heap, i::ArrayBufferExtension* extension) {
bool in_young = heap->array_buffer_sweeper()->young().Contains(extension);
bool in_old = heap->array_buffer_sweeper()->old().Contains(extension);
bool in_young = heap->array_buffer_sweeper()->young().ContainsSlow(extension);
bool in_old = heap->array_buffer_sweeper()->old().ContainsSlow(extension);
CHECK(!(in_young && in_old));
return in_young || in_old;
}