• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "src/heap/cppgc/gc-info-table.h"
6 
7 #include <algorithm>
8 #include <limits>
9 #include <memory>
10 
11 #include "include/cppgc/internal/gc-info.h"
12 #include "include/cppgc/platform.h"
13 #include "src/base/bits.h"
14 #include "src/base/lazy-instance.h"
15 
16 namespace cppgc {
17 namespace internal {
18 
19 namespace {
20 
21 // GCInfoTable::table_, the table which holds GCInfos, is maintained as a
22 // contiguous array reserved upfront. Subparts of the array are (re-)committed
23 // as read/write or read-only in OS pages, whose size is a power of 2. To avoid
24 // having GCInfos that cross the boundaries between these subparts we force the
25 // size of GCInfo to be a power of 2 as well.
26 constexpr size_t kEntrySize = sizeof(GCInfo);
27 static_assert(v8::base::bits::IsPowerOfTwo(kEntrySize),
28               "GCInfoTable entries size must be power of "
29               "two");
30 
31 }  // namespace
32 
33 GCInfoTable* GlobalGCInfoTable::global_table_ = nullptr;
34 constexpr GCInfoIndex GCInfoTable::kMaxIndex;
35 constexpr GCInfoIndex GCInfoTable::kMinIndex;
36 constexpr GCInfoIndex GCInfoTable::kInitialWantedLimit;
37 
Create(PageAllocator * page_allocator)38 void GlobalGCInfoTable::Create(PageAllocator* page_allocator) {
39   static v8::base::LeakyObject<GCInfoTable> table(page_allocator);
40   if (!global_table_) {
41     global_table_ = table.get();
42   }
43 }
44 
GCInfoTable(PageAllocator * page_allocator)45 GCInfoTable::GCInfoTable(PageAllocator* page_allocator)
46     : page_allocator_(page_allocator),
47       table_(static_cast<decltype(table_)>(page_allocator_->AllocatePages(
48           nullptr, MaxTableSize(), page_allocator_->AllocatePageSize(),
49           PageAllocator::kNoAccess))),
50       read_only_table_end_(reinterpret_cast<uint8_t*>(table_)) {
51   CHECK(table_);
52   Resize();
53 }
54 
~GCInfoTable()55 GCInfoTable::~GCInfoTable() {
56   page_allocator_->ReleasePages(const_cast<GCInfo*>(table_), MaxTableSize(), 0);
57 }
58 
MaxTableSize() const59 size_t GCInfoTable::MaxTableSize() const {
60   return RoundUp(GCInfoTable::kMaxIndex * kEntrySize,
61                  page_allocator_->AllocatePageSize());
62 }
63 
InitialTableLimit() const64 GCInfoIndex GCInfoTable::InitialTableLimit() const {
65   // Different OSes have different page sizes, so we have to choose the minimum
66   // of memory wanted and OS page size.
67   constexpr size_t memory_wanted = kInitialWantedLimit * kEntrySize;
68   const size_t initial_limit =
69       RoundUp(memory_wanted, page_allocator_->AllocatePageSize()) / kEntrySize;
70   CHECK_GT(std::numeric_limits<GCInfoIndex>::max(), initial_limit);
71   return static_cast<GCInfoIndex>(
72       std::min(static_cast<size_t>(kMaxIndex), initial_limit));
73 }
74 
Resize()75 void GCInfoTable::Resize() {
76   const GCInfoIndex new_limit = (limit_) ? 2 * limit_ : InitialTableLimit();
77   CHECK_GT(new_limit, limit_);
78   const size_t old_committed_size = limit_ * kEntrySize;
79   const size_t new_committed_size = new_limit * kEntrySize;
80   CHECK(table_);
81   CHECK_EQ(0u, new_committed_size % page_allocator_->AllocatePageSize());
82   CHECK_GE(MaxTableSize(), new_committed_size);
83   // Recommit new area as read/write.
84   uint8_t* current_table_end =
85       reinterpret_cast<uint8_t*>(table_) + old_committed_size;
86   const size_t table_size_delta = new_committed_size - old_committed_size;
87   CHECK(page_allocator_->SetPermissions(current_table_end, table_size_delta,
88                                         PageAllocator::kReadWrite));
89   // Recommit old area as read-only.
90   if (read_only_table_end_ != current_table_end) {
91     DCHECK_GT(current_table_end, read_only_table_end_);
92     const size_t read_only_delta = current_table_end - read_only_table_end_;
93     CHECK(page_allocator_->SetPermissions(read_only_table_end_, read_only_delta,
94                                           PageAllocator::kRead));
95     read_only_table_end_ += read_only_delta;
96   }
97 
98   // Check that newly-committed memory is zero-initialized.
99   CheckMemoryIsZeroed(reinterpret_cast<uintptr_t*>(current_table_end),
100                       table_size_delta / sizeof(uintptr_t));
101 
102   limit_ = new_limit;
103 }
104 
CheckMemoryIsZeroed(uintptr_t * base,size_t len)105 void GCInfoTable::CheckMemoryIsZeroed(uintptr_t* base, size_t len) {
106 #if DEBUG
107   for (size_t i = 0; i < len; ++i) {
108     DCHECK(!base[i]);
109   }
110 #endif  // DEBUG
111 }
112 
RegisterNewGCInfo(const GCInfo & info)113 GCInfoIndex GCInfoTable::RegisterNewGCInfo(const GCInfo& info) {
114   // Ensuring a new index involves current index adjustment as well as
115   // potentially resizing the table. For simplicity we use a lock.
116   v8::base::MutexGuard guard(&table_mutex_);
117 
118   if (current_index_ == limit_) {
119     Resize();
120   }
121 
122   GCInfoIndex new_index = current_index_++;
123   CHECK_LT(new_index, GCInfoTable::kMaxIndex);
124   table_[new_index] = info;
125   return new_index;
126 }
127 
128 }  // namespace internal
129 }  // namespace cppgc
130