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