v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
external-entity-table.h
Go to the documentation of this file.
1// Copyright 2022 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#ifndef V8_SANDBOX_EXTERNAL_ENTITY_TABLE_H_
6#define V8_SANDBOX_EXTERNAL_ENTITY_TABLE_H_
7
8#include <set>
9
10#include "include/v8-platform.h"
11#include "include/v8config.h"
12#include "src/base/atomicops.h"
13#include "src/base/memory.h"
16#include "src/common/globals.h"
18
19namespace v8 {
20namespace internal {
21
22class Isolate;
23
46template <typename Entry, size_t size>
48 : public SegmentedTable<Entry, size> {
49 protected:
54 static constexpr size_t kSegmentSize = Base::kSegmentSize;
55 static constexpr size_t kEntriesPerSegment = Base::kEntriesPerSegment;
56 static constexpr size_t kEntrySize = Base::kEntrySize;
57
58 // A collection of segments in an external entity table.
59 //
60 // For the purpose of memory management, a table is partitioned into segments
61 // of a fixed size (e.g. 64kb). A Space is a collection of segments that all
62 // share the same freelist. As such, entry allocation and freeing (e.g.
63 // through garbage collection) all happen on the level of spaces.
64 //
65 // Spaces allow implementing features such as:
66 // * Young generation GC support (a separate space is used for all entries
67 // belonging to the young generation)
68 // * Having double-width entries in a table (a dedicated space is used that
69 // contains only double-width entries)
70 // * Sharing one table between multiple isolates that perform GC independently
71 // (each Isolate owns one space)
72 struct Space {
73 public:
74 Space() = default;
75 Space(const Space&) = delete;
76 Space& operator=(const Space&) = delete;
77 ~Space();
78
79 // Determines the number of entries currently on the freelist.
80 // As entries can be allocated from other threads, the freelist size may
81 // have changed by the time this method returns. As such, the returned
82 // value should only be treated as an approximation.
83 uint32_t freelist_length() const;
84
85 // Returns the current number of segments currently associated with this
86 // space.
87 // The caller must lock the mutex.
88 uint32_t num_segments();
89
90 // Returns whether this space is currently empty.
91 // The caller must lock the mutex.
92 bool is_empty() { return num_segments() == 0; }
93
94 // Returns the current capacity of this space.
95 // The capacity of a space is the total number of entries it can contain.
96 // The caller must lock the mutex.
97 uint32_t capacity() { return num_segments() * kEntriesPerSegment; }
98
99 // Returns true if this space contains the entry with the given index.
100 bool Contains(uint32_t index);
101
102 // Whether this space is attached to a table's internal read-only segment.
104 return is_internal_read_only_space_;
105 }
106
107#ifdef DEBUG
108 // Check whether this space belongs to the given external entity table.
109 bool BelongsTo(const void* table) const { return owning_table_ == table; }
110#endif // DEBUG
111
112 // Similar to `num_segments()` but also locks the mutex.
113 uint32_t NumSegmentsForTesting() {
114 base::MutexGuard guard(&mutex_);
115 return num_segments();
116 }
117
118 protected:
119 friend class ExternalEntityTable<Entry, size>;
120
121#ifdef DEBUG
122 // In debug builds we keep track of which table a space belongs to to be
123 // able to insert additional DCHECKs that verify that spaces are always used
124 // with the correct table.
125 std::atomic<void*> owning_table_ = nullptr;
126#endif
127
128 // The freelist used by this space.
129 // This contains both the index of the first entry in the freelist and the
130 // total length of the freelist as both values need to be updated together
131 // in a single atomic operation to stay consistent in the case of concurrent
132 // entry allocations.
133 std::atomic<FreelistHead> freelist_head_ = FreelistHead();
134
135 // The collection of segments belonging to this space.
136 std::set<Segment> segments_;
137
138 // Whether this is the internal RO space, which has special semantics:
139 // - read-only page permissions after initialization,
140 // - the space is not swept since slots are live by definition,
141 // - contains exactly one segment, located at offset 0, and
142 // - the segment's lifecycle is managed by `owning_table_`.
143 bool is_internal_read_only_space_ = false;
144
145 // Mutex guarding access to the segments_ set.
147 };
148
149 // A Space that supports black allocations.
151 bool allocate_black() { return allocate_black_; }
152 void set_allocate_black(bool allocate_black) {
153 allocate_black_ = allocate_black;
154 }
155
156 private:
157 bool allocate_black_ = false;
158 };
159
163
164 // Allocates a new entry in the given space and return its index.
165 //
166 // If there are no free entries, then this will extend the space by
167 // allocating a new segment.
168 // This method is atomic and can be called from background threads.
169 uint32_t AllocateEntry(Space* space);
170 std::optional<uint32_t> TryAllocateEntry(Space* space);
171
172 // Attempts to allocate an entry in the given space below the specified index.
173 //
174 // If there are no free entries at a lower index, this method will fail and
175 // return zero. This method will therefore never allocate a new segment.
176 // This method is atomic and can be called from background threads.
177 uint32_t AllocateEntryBelow(Space* space, uint32_t threshold_index);
178
179 // Try to allocate the first entry of the freelist.
180 //
181 // This method is mostly a wrapper around an atomic compare-and-swap which
182 // replaces the current freelist head with the next entry in the freelist,
183 // thereby allocating the entry at the start of the freelist.
184 bool TryAllocateEntryFromFreelist(Space* space, FreelistHead freelist);
185
186 // Trey to allocate a new segment and add it to the given space.
187 //
188 // This should only be called when the freelist of the space is currently
189 // empty. It will then refill the freelist with all entries in the newly
190 // allocated segment. Fails if there is no space left.
191 std::optional<FreelistHead> TryExtend(Space* space);
192
193 // Sweeps the given space.
194 //
195 // This will free all unmarked entries to the freelist and unmark all live
196 // entries. The table is swept top-to-bottom so that the freelist ends up
197 // sorted. During sweeping, new entries must not be allocated.
198 //
199 // This is a generic implementation of table sweeping and requires that the
200 // Entry type implements the following additional methods:
201 // - bool IsMarked()
202 // - void Unmark()
203 //
204 // Returns the number of live entries after sweeping.
205 uint32_t GenericSweep(Space* space);
206
207 // Variant of the above that invokes a callback for every live entry.
208 template <typename Callback>
209 uint32_t GenericSweep(Space* space, Callback marked);
210
211 // Iterate over all entries in the given space.
212 //
213 // The callback function will be invoked for every entry and be passed the
214 // index of that entry as argument.
215 template <typename Callback>
216 void IterateEntriesIn(Space* space, Callback callback);
217
218 // Marker value for the freelist_head_ member to indicate that entry
219 // allocation is currently forbidden, for example because the table is being
220 // swept as part of a mark+sweep garbage collection. This value should never
221 // occur as freelist_head_ value during normal operations and should be easy
222 // to recognize.
223 static constexpr FreelistHead kEntryAllocationIsForbiddenMarker =
224 FreelistHead(-1, -1);
225
226 public:
227 // Generally, ExternalEntityTables are not compactible. The exception are
228 // CompactibleExternalEntityTables such as the ExternalPointerTable. This
229 // constant can be used to static_assert this property in locations that rely
230 // on a table (not) supporting compaction.
231 static constexpr bool kSupportsCompaction = false;
232
233 // Initializes the table by reserving the backing memory, allocating an
234 // initial segment, and populating the freelist.
235 void Initialize();
236
237 // Deallocates all memory associated with this table.
238 void TearDown();
239
240 // Initializes the given space for use with this table.
241 void InitializeSpace(Space* space);
242
243 // Deallocates all segments owned by the given space.
244 void TearDownSpace(Space* space);
245
246 // Attaches/detaches the given space to the internal read-only segment. Note
247 // the lifetime of the underlying segment itself is managed by the table.
248 void AttachSpaceToReadOnlySegment(Space* space);
249 void DetachSpaceFromReadOnlySegment(Space* space);
250
251 // Use this scope to temporarily unseal the read-only segment (i.e. change
252 // permissions to RW).
254 public:
256 : table_(table) {
257 table_->UnsealReadOnlySegment();
258 }
259
260 ~UnsealReadOnlySegmentScope() { table_->SealReadOnlySegment(); }
261
262 private:
264 };
265
266 protected:
267 static constexpr uint32_t kInternalReadOnlySegmentOffset = 0;
268 static constexpr uint32_t kInternalNullEntryIndex = 0;
269 static constexpr uint32_t kEndOfInternalReadOnlySegment = kEntriesPerSegment;
270
271 private:
272 // Required for Isolate::CheckIsolateLayout().
273 friend class Isolate;
274
275 // Helpers to toggle the first segment's permissions between kRead (sealed)
276 // and kReadWrite (unsealed).
277 void UnsealReadOnlySegment();
278 void SealReadOnlySegment();
279
280 // Extends the given space with the given segment.
281 void Extend(Space* space, Segment segment, FreelistHead freelist);
282};
283
284} // namespace internal
285} // namespace v8
286
287#endif // V8_SANDBOX_EXTERNAL_ENTITY_TABLE_H_
UnsealReadOnlySegmentScope(ExternalEntityTable< Entry, size > *table)
ExternalEntityTable(const ExternalEntityTable &)=delete
ExternalEntityTable & operator=(const ExternalEntityTable &)=delete
base::Mutex & mutex_
TNode< Object > callback
SourcePositionTable *const table_
Definition pipeline.cc:227
#define V8_EXPORT_PRIVATE
Definition macros.h:460
Space & operator=(const Space &)=delete