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 
16 namespace cppgc {
17 namespace internal {
18 
PersistentRegionBase( const FatalOutOfMemoryHandler& oom_handler)19 PersistentRegionBase::PersistentRegionBase(
20     const FatalOutOfMemoryHandler& oom_handler)
21     : oom_handler_(oom_handler) {}
22 
~PersistentRegionBase()23 PersistentRegionBase::~PersistentRegionBase() { ClearAllUsedNodes(); }
24 
25 template <typename PersistentBaseClass>
ClearAllUsedNodes()26 void 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 
44 template void
45 PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
46 template void PersistentRegionBase::ClearAllUsedNodes<PersistentBase>();
47 
ClearAllUsedNodes()48 void PersistentRegionBase::ClearAllUsedNodes() {
49   ClearAllUsedNodes<PersistentBase>();
50 }
51 
NodesInUse() const52 size_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 
RefillFreeList()66 void 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 
RefillFreeListAndAllocateNode( void* owner, TraceCallback trace)78 PersistentNode* PersistentRegionBase::RefillFreeListAndAllocateNode(
79     void* owner, TraceCallback trace) {
80   RefillFreeList();
81   auto* node = TryAllocateNodeFromFreeList(owner, trace);
82   CPPGC_DCHECK(node);
83   return node;
84 }
85 
Trace(Visitor* visitor)86 void 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 
PersistentRegion(const FatalOutOfMemoryHandler& oom_handler)114 PersistentRegion::PersistentRegion(const FatalOutOfMemoryHandler& oom_handler)
115     : PersistentRegionBase(oom_handler),
116       creation_thread_id_(v8::base::OS::GetCurrentThreadId()) {
117   USE(creation_thread_id_);
118 }
119 
IsCreationThread()120 bool PersistentRegion::IsCreationThread() {
121   return creation_thread_id_ == v8::base::OS::GetCurrentThreadId();
122 }
123 
PersistentRegionLock()124 PersistentRegionLock::PersistentRegionLock() {
125   g_process_mutex.Pointer()->Lock();
126 }
127 
~PersistentRegionLock()128 PersistentRegionLock::~PersistentRegionLock() {
129   g_process_mutex.Pointer()->Unlock();
130 }
131 
132 // static
AssertLocked()133 void PersistentRegionLock::AssertLocked() {
134   return g_process_mutex.Pointer()->AssertHeld();
135 }
136 
CrossThreadPersistentRegion( const FatalOutOfMemoryHandler& oom_handler)137 CrossThreadPersistentRegion::CrossThreadPersistentRegion(
138     const FatalOutOfMemoryHandler& oom_handler)
139     : PersistentRegionBase(oom_handler) {}
140 
~CrossThreadPersistentRegion()141 CrossThreadPersistentRegion::~CrossThreadPersistentRegion() {
142   PersistentRegionLock guard;
143   PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
144   nodes_.clear();
145   // PersistentRegionBase destructor will be a noop.
146 }
147 
Trace(Visitor* visitor)148 void CrossThreadPersistentRegion::Trace(Visitor* visitor) {
149   PersistentRegionLock::AssertLocked();
150   PersistentRegionBase::Trace(visitor);
151 }
152 
NodesInUse() const153 size_t CrossThreadPersistentRegion::NodesInUse() const {
154   // This method does not require a lock.
155   return PersistentRegionBase::NodesInUse();
156 }
157 
ClearAllUsedNodes()158 void CrossThreadPersistentRegion::ClearAllUsedNodes() {
159   PersistentRegionLock::AssertLocked();
160   PersistentRegionBase::ClearAllUsedNodes<CrossThreadPersistentBase>();
161 }
162 
163 }  // namespace internal
164 }  // namespace cppgc
165