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