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:
parent
6d457a9005
commit
210987a552
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user