1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "include/cppgc/internal/persistent-node.h" 6 7#include <algorithm> 8#include <numeric> 9 10#include "include/cppgc/cross-thread-persistent.h" 11#include "include/cppgc/persistent.h" 12#include "src/base/platform/platform.h" 13#include "src/heap/cppgc/platform.h" 14#include "src/heap/cppgc/process-heap.h" 15 16namespace cppgc { 17namespace internal { 18 19PersistentRegionBase::PersistentRegionBase( 20 const FatalOutOfMemoryHandler& oom_handler) 21 : oom_handler_(oom_handler) {} 22 23PersistentRegionBase::~PersistentRegionBase() { ClearAllUsedNodes(); } 24 25template <typename PersistentBaseClass> 26void PersistentRegionBase::ClearAllUsedNodes() { 27 for (auto& slots : nodes_) { 28 for (auto& node : *slots) { 29 if (!node.IsUsed()) continue; 30 31 static_cast<PersistentBaseClass*>(node.owner())->ClearFromGC(); 32 33 // Add nodes back to the free list to allow reusing for subsequent 34 // creation calls. 35 node.InitializeAsFreeNode(free_list_head_); 36 free_list_head_ = &node; 37 CPPGC_DCHECK(nodes_in_use_ > 0); 38 nodes_in_use_--; 39 } 40 } 41 CPPGC_DCHECK(0u == nodes_in_use_); 42} 43 44template void 45PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>(); 46template void PersistentRegionBase::ClearAllUsedNodes<PersistentBase>(); 47 48void PersistentRegionBase::ClearAllUsedNodes() { 49 ClearAllUsedNodes<PersistentBase>(); 50} 51 52size_t PersistentRegionBase::NodesInUse() const { 53#ifdef DEBUG 54 const size_t accumulated_nodes_in_use_ = std::accumulate( 55 nodes_.cbegin(), nodes_.cend(), 0u, [](size_t acc, const auto& slots) { 56 return acc + std::count_if(slots->cbegin(), slots->cend(), 57 [](const PersistentNode& node) { 58 return node.IsUsed(); 59 }); 60 }); 61 DCHECK_EQ(accumulated_nodes_in_use_, nodes_in_use_); 62#endif // DEBUG 63 return nodes_in_use_; 64} 65 66void PersistentRegionBase::RefillFreeList() { 67 auto node_slots = std::make_unique<PersistentNodeSlots>(); 68 if (!node_slots.get()) { 69 oom_handler_("Oilpan: PersistentRegionBase::RefillFreeList()"); 70 } 71 nodes_.push_back(std::move(node_slots)); 72 for (auto& node : *nodes_.back()) { 73 node.InitializeAsFreeNode(free_list_head_); 74 free_list_head_ = &node; 75 } 76} 77 78PersistentNode* PersistentRegionBase::RefillFreeListAndAllocateNode( 79 void* owner, TraceCallback trace) { 80 RefillFreeList(); 81 auto* node = TryAllocateNodeFromFreeList(owner, trace); 82 CPPGC_DCHECK(node); 83 return node; 84} 85 86void PersistentRegionBase::Trace(Visitor* visitor) { 87 free_list_head_ = nullptr; 88 for (auto& slots : nodes_) { 89 bool is_empty = true; 90 for (auto& node : *slots) { 91 if (node.IsUsed()) { 92 node.Trace(visitor); 93 is_empty = false; 94 } else { 95 node.InitializeAsFreeNode(free_list_head_); 96 free_list_head_ = &node; 97 } 98 } 99 if (is_empty) { 100 PersistentNode* first_next = (*slots)[0].FreeListNext(); 101 // First next was processed first in the loop above, guaranteeing that it 102 // either points to null or into a different node block. 103 CPPGC_DCHECK(!first_next || first_next < &slots->front() || 104 first_next > &slots->back()); 105 free_list_head_ = first_next; 106 slots.reset(); 107 } 108 } 109 nodes_.erase(std::remove_if(nodes_.begin(), nodes_.end(), 110 [](const auto& ptr) { return !ptr; }), 111 nodes_.end()); 112} 113 114PersistentRegion::PersistentRegion(const FatalOutOfMemoryHandler& oom_handler) 115 : PersistentRegionBase(oom_handler), 116 creation_thread_id_(v8::base::OS::GetCurrentThreadId()) { 117 USE(creation_thread_id_); 118} 119 120bool PersistentRegion::IsCreationThread() { 121 return creation_thread_id_ == v8::base::OS::GetCurrentThreadId(); 122} 123 124PersistentRegionLock::PersistentRegionLock() { 125 g_process_mutex.Pointer()->Lock(); 126} 127 128PersistentRegionLock::~PersistentRegionLock() { 129 g_process_mutex.Pointer()->Unlock(); 130} 131 132// static 133void PersistentRegionLock::AssertLocked() { 134 return g_process_mutex.Pointer()->AssertHeld(); 135} 136 137CrossThreadPersistentRegion::CrossThreadPersistentRegion( 138 const FatalOutOfMemoryHandler& oom_handler) 139 : PersistentRegionBase(oom_handler) {} 140 141CrossThreadPersistentRegion::~CrossThreadPersistentRegion() { 142 PersistentRegionLock guard; 143 PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>(); 144 nodes_.clear(); 145 // PersistentRegionBase destructor will be a noop. 146} 147 148void CrossThreadPersistentRegion::Trace(Visitor* visitor) { 149 PersistentRegionLock::AssertLocked(); 150 PersistentRegionBase::Trace(visitor); 151} 152 153size_t CrossThreadPersistentRegion::NodesInUse() const { 154 // This method does not require a lock. 155 return PersistentRegionBase::NodesInUse(); 156} 157 158void CrossThreadPersistentRegion::ClearAllUsedNodes() { 159 PersistentRegionLock::AssertLocked(); 160 PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>(); 161} 162 163} // namespace internal 164} // namespace cppgc 165