blob: a8060e1cd3b767a16ee9bbdc57e89f6b1e890718 [file] [log] [blame] [edit]
// 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 "include/cppgc/internal/persistent-node.h"
#include <algorithm>
#include <numeric>
#include "include/cppgc/cross-thread-persistent.h"
#include "include/cppgc/persistent.h"
#include "src/base/platform/platform.h"
#include "src/heap/cppgc/heap-base.h"
#include "src/heap/cppgc/platform.h"
#include "src/heap/cppgc/process-heap.h"
namespace cppgc {
namespace internal {
PersistentRegionBase::PersistentRegionBase(
const FatalOutOfMemoryHandler& oom_handler)
: oom_handler_(oom_handler) {}
PersistentRegionBase::~PersistentRegionBase() { ClearAllUsedNodes(); }
template <typename PersistentBaseClass>
void PersistentRegionBase::ClearAllUsedNodes() {
for (auto& slots : nodes_) {
for (auto& node : *slots) {
if (!node.IsUsed()) continue;
static_cast<PersistentBaseClass*>(node.owner())->ClearFromGC();
// Add nodes back to the free list to allow reusing for subsequent
// creation calls.
node.InitializeAsFreeNode(free_list_head_);
free_list_head_ = &node;
CPPGC_DCHECK(nodes_in_use_ > 0);
nodes_in_use_--;
}
}
CPPGC_DCHECK(0u == nodes_in_use_);
}
template void
PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
template void PersistentRegionBase::ClearAllUsedNodes<PersistentBase>();
void PersistentRegionBase::ClearAllUsedNodes() {
ClearAllUsedNodes<PersistentBase>();
}
size_t PersistentRegionBase::NodesInUse() const {
#ifdef DEBUG
const size_t accumulated_nodes_in_use_ = std::accumulate(
nodes_.cbegin(), nodes_.cend(), 0u, [](size_t acc, const auto& slots) {
return acc + std::count_if(slots->cbegin(), slots->cend(),
[](const PersistentNode& node) {
return node.IsUsed();
});
});
DCHECK_EQ(accumulated_nodes_in_use_, nodes_in_use_);
#endif // DEBUG
return nodes_in_use_;
}
void PersistentRegionBase::RefillFreeList() {
auto node_slots = std::make_unique<PersistentNodeSlots>();
if (!node_slots.get()) {
oom_handler_("Oilpan: PersistentRegionBase::RefillFreeList()");
}
nodes_.push_back(std::move(node_slots));
for (auto& node : *nodes_.back()) {
node.InitializeAsFreeNode(free_list_head_);
free_list_head_ = &node;
}
}
PersistentNode* PersistentRegionBase::RefillFreeListAndAllocateNode(
void* owner, TraceRootCallback trace) {
RefillFreeList();
auto* node = TryAllocateNodeFromFreeList(owner, trace);
CPPGC_DCHECK(node);
return node;
}
void PersistentRegionBase::Iterate(RootVisitor& root_visitor) {
free_list_head_ = nullptr;
for (auto& slots : nodes_) {
bool is_empty = true;
for (auto& node : *slots) {
if (node.IsUsed()) {
node.Trace(root_visitor);
is_empty = false;
} else {
node.InitializeAsFreeNode(free_list_head_);
free_list_head_ = &node;
}
}
if (is_empty) {
PersistentNode* first_next = (*slots)[0].FreeListNext();
// First next was processed first in the loop above, guaranteeing that it
// either points to null or into a different node block.
CPPGC_DCHECK(!first_next || first_next < &slots->front() ||
first_next > &slots->back());
free_list_head_ = first_next;
slots.reset();
}
}
nodes_.erase(std::remove_if(nodes_.begin(), nodes_.end(),
[](const auto& ptr) { return !ptr; }),
nodes_.end());
}
bool PersistentRegion::IsCreationThread() {
return heap_.CurrentThreadIsHeapThread();
}
PersistentRegionLock::PersistentRegionLock() {
ProcessGlobalLock::Lock<
ProcessGlobalLock::Reason::kForCrossThreadHandleCreation>();
}
PersistentRegionLock::~PersistentRegionLock() {
ProcessGlobalLock::Unlock<
ProcessGlobalLock::Reason::kForCrossThreadHandleCreation>();
}
// static
void PersistentRegionLock::AssertLocked() { ProcessGlobalLock::AssertHeld(); }
CrossThreadPersistentRegion::CrossThreadPersistentRegion(
const FatalOutOfMemoryHandler& oom_handler)
: PersistentRegionBase(oom_handler) {}
CrossThreadPersistentRegion::~CrossThreadPersistentRegion() {
PersistentRegionLock guard;
PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
nodes_.clear();
// PersistentRegionBase destructor will be a noop.
}
void CrossThreadPersistentRegion::Iterate(RootVisitor& root_visitor) {
PersistentRegionLock::AssertLocked();
PersistentRegionBase::Iterate(root_visitor);
}
size_t CrossThreadPersistentRegion::NodesInUse() const {
// This method does not require a lock.
return PersistentRegionBase::NodesInUse();
}
void CrossThreadPersistentRegion::ClearAllUsedNodes() {
PersistentRegionLock::AssertLocked();
PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
}
} // namespace internal
} // namespace cppgc