v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
backing-store.h
Go to the documentation of this file.
1// Copyright 2019 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_OBJECTS_BACKING_STORE_H_
6#define V8_OBJECTS_BACKING_STORE_H_
7
8#include <memory>
9#include <optional>
10
12#include "include/v8-internal.h"
13#include "src/base/bit-field.h"
14#include "src/handles/handles.h"
15#include "src/sandbox/sandbox.h"
16
17namespace v8::internal {
18
19class Isolate;
20class WasmMemoryObject;
21
22// Whether this is Wasm memory, and if 32 or 64 bit.
24
25// Whether the backing store is shared or not.
26enum class SharedFlag : uint8_t { kNotShared, kShared };
27
28// Whether the backing store is resizable or not.
29enum class ResizableFlag : uint8_t { kNotResizable, kResizable };
30
31// Whether the backing store memory is initialied to zero or not.
33
34// Internal information for shared wasm memories. E.g. contains
35// a list of all memory objects (across all isolates) that share this
36// backing store.
37struct SharedWasmMemoryData;
38
39// The {BackingStore} data structure stores all the low-level details about the
40// backing store of an array buffer or Wasm memory, including its base address
41// and length, whether it is shared, provided by the embedder, has guard
42// regions, etc. Instances of this classes *own* the underlying memory
43// when they are created through one of the {Allocate()} methods below,
44// and the destructor frees the memory (and page allocation if necessary).
46 public:
48
49 // Allocate an array buffer backing store using the default method,
50 // which currently is the embedder-provided array buffer allocator.
51 static std::unique_ptr<BackingStore> Allocate(Isolate* isolate,
52 size_t byte_length,
53 SharedFlag shared,
54 InitializedFlag initialized);
55
56#if V8_ENABLE_WEBASSEMBLY
57 // Allocate the backing store for a Wasm memory.
58 static std::unique_ptr<BackingStore> AllocateWasmMemory(
59 Isolate* isolate, size_t initial_pages, size_t maximum_pages,
60 WasmMemoryFlag wasm_memory, SharedFlag shared);
61#endif // V8_ENABLE_WEBASSEMBLY
62
63 // Tries to allocate `maximum_pages` of memory and commit `initial_pages`.
64 //
65 // If {isolate} is not null, initial failure to allocate the backing store may
66 // trigger GC, after which the allocation is retried. If {isolate} is null, no
67 // GC will be triggered.
68 static std::unique_ptr<BackingStore> TryAllocateAndPartiallyCommitMemory(
69 Isolate* isolate, size_t byte_length, size_t max_byte_length,
70 size_t page_size, size_t initial_pages, size_t maximum_pages,
71 WasmMemoryFlag wasm_memory, SharedFlag shared,
72 bool has_guard_regions = false);
73
74 // Create a backing store that wraps existing allocated memory.
75 static std::unique_ptr<BackingStore> WrapAllocation(
76 void* allocation_base, size_t allocation_length,
77 v8::BackingStore::DeleterCallback deleter, void* deleter_data,
78 SharedFlag shared);
79
80 // Create an empty backing store.
81 static std::unique_ptr<BackingStore> EmptyBackingStore(SharedFlag shared);
82
83 // Accessors.
84 // Internally, we treat nullptr as the empty buffer value. However,
85 // externally, we should use the EmptyBackingStoreBuffer() constant for that
86 // purpose as the buffer pointer should always point into the sandbox. As
87 // such, this is the place where we convert between these two.
88 void* buffer_start() const {
89 return buffer_start_ != nullptr ? buffer_start_ : EmptyBackingStoreBuffer();
90 }
92 std::memory_order memory_order = std::memory_order_relaxed) const {
93 return byte_length_.load(memory_order);
94 }
95 size_t max_byte_length() const { return max_byte_length_; }
96 size_t byte_capacity() const { return byte_capacity_; }
97 bool is_shared() const { return has_flag(kIsShared); }
98 bool is_resizable_by_js() const { return has_flag(kIsResizableByJs); }
99 bool is_wasm_memory() const { return has_flag(kIsWasmMemory); }
100 bool is_wasm_memory64() const { return has_flag(kIsWasmMemory64); }
101 bool has_guard_regions() const { return has_flag(kHasGuardRegions); }
102
103 bool IsEmpty() const {
104 DCHECK_GE(byte_capacity_, byte_length_);
105 return byte_capacity_ == 0;
106 }
107
109
110 ResizeOrGrowResult ResizeInPlace(Isolate* isolate, size_t new_byte_length);
111 ResizeOrGrowResult GrowInPlace(Isolate* isolate, size_t new_byte_length);
112
113#if V8_ENABLE_WEBASSEMBLY
114 // The IsResizableByJs flag is set for backing stores for a resizable
115 // ArrayBuffer or a WebAssembly.Memory that exposes its buffer as resizable
116 // ArrayBuffer.
117 //
118 // For backing stores of ArrayBuffers that are not Wasm memories, the flag
119 // never changes. It always matches whether ArrayBuffers backed by it are
120 // resizable.
121 //
122 // For unshared Wasm memories, this flag may change. WebAssembly.Memory
123 // instances are born with their buffers exposed as fixed-length ArrayBuffers
124 // (for backwards compat), but may transition to exposing their buffers as
125 // resizable. It always matches whether the ArrayBuffer it backs is resizable,
126 // since unshared ArrayBuffers never alias the same BackingStore.
127 //
128 // For shared Wasm memories, this field never changes, but may differ from the
129 // value of the is_resizable_by_js field of SharedArrayBuffers it backs.
130 // WebAssembly.Memory can create multiple SharedArrayBuffers backed by the
131 // same BackingStore, some of which are exposed as growable, and some of which
132 // as fixed-length.
133 void MakeWasmMemoryResizableByJS(bool resizable);
134
135 // Attempt to grow this backing store in place.
136 std::optional<size_t> GrowWasmMemoryInPlace(Isolate* isolate,
137 size_t delta_pages,
138 size_t max_pages);
139
140 // Allocate a new, larger, backing store for this Wasm memory and copy the
141 // contents of this backing store into it.
142 std::unique_ptr<BackingStore> CopyWasmMemory(Isolate* isolate,
143 size_t new_pages,
144 size_t max_pages,
145 WasmMemoryFlag wasm_memory);
146
147 // Attach the given memory object to this backing store. The memory object
148 // will be updated if this backing store is grown.
149 void AttachSharedWasmMemoryObject(
150 Isolate* isolate, DirectHandle<WasmMemoryObject> memory_object);
151
152 // Send asynchronous updates to attached memory objects in other isolates
153 // after the backing store has been grown. Memory objects in this
154 // isolate are updated synchronously.
155 void BroadcastSharedWasmMemoryGrow(Isolate* isolate) const;
156
157 // Remove all memory objects in the given isolate that refer to this
158 // backing store.
159 static void RemoveSharedWasmMemoryObjects(Isolate* isolate);
160
161 // Update all shared memory objects in this isolate (after a grow operation).
162 static void UpdateSharedWasmMemoryObjects(Isolate* isolate);
163#endif // V8_ENABLE_WEBASSEMBLY
164
165 // Returns the size of the external memory owned by this backing store.
166 // It is used for triggering GCs based on the external memory pressure.
168 if (has_flag(kIsShared)) {
169 // TODO(titzer): SharedArrayBuffers and shared WasmMemorys cause problems
170 // with accounting for per-isolate external memory. In particular, sharing
171 // the same array buffer or memory multiple times, which happens in stress
172 // tests, can cause overcounting, leading to GC thrashing. Fix with global
173 // accounting?
174 return 0;
175 }
176 if (has_flag(kEmptyDeleter)) {
177 // The backing store has an empty deleter. Even if the backing store is
178 // freed after GC, it would not free the memory block.
179 return 0;
180 }
181 return byte_length();
182 }
183
184 uint32_t id() const { return id_; }
185
186 private:
188
200
201 BackingStore(PageAllocator* page_allocator, void* buffer_start,
202 size_t byte_length, size_t max_byte_length, size_t byte_capacity,
203 SharedFlag shared, ResizableFlag resizable, bool is_wasm_memory,
204 bool is_wasm_memory64, bool has_guard_regions,
205 bool custom_deleter, bool empty_deleter);
206 BackingStore(const BackingStore&) = delete;
208 void SetAllocatorFromIsolate(Isolate* isolate);
209
210 // Accessors for type-specific data.
211 v8::ArrayBuffer::Allocator* get_v8_api_array_buffer_allocator();
212 SharedWasmMemoryData* get_shared_wasm_memory_data() const;
213
214 bool has_flag(Flag flag) const {
215 return flags_.load(std::memory_order_relaxed).contains(flag);
216 }
217 void set_flag(Flag flag) {
219 flags_.load(std::memory_order_relaxed);
220 while (!flags_.compare_exchange_weak(old_flags, old_flags | flag,
221 std::memory_order_relaxed)) {
222 // Retry with updated `old_flags`.
223 }
224 }
225 void clear_flag(Flag flag) {
227 flags_.load(std::memory_order_relaxed);
228 while (!flags_.compare_exchange_weak(old_flags, old_flags - flag,
229 std::memory_order_relaxed)) {
230 // Retry with updated `old_flags`.
231 }
232 }
233
235 return has_flag(kHoldsSharedPtrToAllocater);
236 }
237 bool custom_deleter() const { return has_flag(kCustomDeleter); }
238 bool globally_registered() const { return has_flag(kGloballyRegistered); }
239
240 void* buffer_start_ = nullptr;
241 std::atomic<size_t> byte_length_;
242 // Max byte length of the corresponding JSArrayBuffer(s).
244 // Amount of the memory allocated
246 // Unique ID of this backing store. Currently only used by DevTools, to
247 // identify stores used by several ArrayBuffers or WebAssembly memories
248 // (reported by the inspector as [[ArrayBufferData]] internal property)
249 const uint32_t id_;
250
252
254 TypeSpecificData() : v8_api_array_buffer_allocator(nullptr) {}
256
257 // If this backing store was allocated through the ArrayBufferAllocator API,
258 // this is a direct pointer to the API object for freeing the backing
259 // store.
261
262 // Holds a shared_ptr to the ArrayBuffer::Allocator instance, if requested
263 // so by the embedder through setting
264 // Isolate::CreateParams::array_buffer_allocator_shared.
265 std::shared_ptr<v8::ArrayBuffer::Allocator>
267
268 // For shared Wasm memories, this is a list of all the attached memory
269 // objects, which is needed to grow shared backing stores.
271
272 // Custom deleter for the backing stores that wrap memory blocks that are
273 // allocated with a custom allocator.
278 } type_specific_data_;
279
280 std::atomic<base::EnumSet<Flag, uint16_t>> flags_;
281};
282
283// A global, per-process mapping from buffer addresses to backing stores
284// of wasm memory objects.
286 public:
287 // Register a backing store in the global registry. A mapping from the
288 // {buffer_start} to the backing store object will be added. The backing
289 // store will automatically unregister itself upon destruction.
290 // Only wasm memory backing stores are supported.
291 static void Register(std::shared_ptr<BackingStore> backing_store);
292
293 private:
294 friend class BackingStore;
295 // Unregister a backing store in the global registry.
296 static void Unregister(BackingStore* backing_store);
297
298 // Adds the given memory object to the backing store's weak list
299 // of memory objects (under the registry lock).
301 Isolate* isolate, BackingStore* backing_store,
302 DirectHandle<WasmMemoryObject> memory_object);
303
304 // Purge any shared wasm memory lists that refer to this isolate.
305 static void Purge(Isolate* isolate);
306
307 // Broadcast updates to all attached memory objects.
309 const BackingStore* backing_store);
310
311 // Update all shared memory objects in the given isolate.
313};
314
315} // namespace v8::internal
316
317#endif // V8_OBJECTS_BACKING_STORE_H_
void(*)(void *data, size_t length, void *deleter_data) DeleterCallback
BackingStore(const BackingStore &)=delete
std::atomic< base::EnumSet< Flag, uint16_t > > flags_
bool holds_shared_ptr_to_allocator() const
size_t max_byte_length() const
std::atomic< size_t > byte_length_
bool has_flag(Flag flag) const
BackingStore & operator=(const BackingStore &)=delete
size_t byte_length(std::memory_order memory_order=std::memory_order_relaxed) const
static void Purge(Isolate *isolate)
static void Register(std::shared_ptr< BackingStore > backing_store)
static void AddSharedWasmMemoryObject(Isolate *isolate, BackingStore *backing_store, DirectHandle< WasmMemoryObject > memory_object)
static void Unregister(BackingStore *backing_store)
static void BroadcastSharedWasmMemoryGrow(Isolate *isolate, const BackingStore *backing_store)
static void UpdateSharedWasmMemoryObjects(Isolate *isolate)
JSRegExp::Flags flags_
cppgc::PageAllocator * page_allocator_
Definition cpp-heap.cc:194
V8_INLINE void * EmptyBackingStoreBuffer()
Definition sandbox.h:345
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define V8_EXPORT_PRIVATE
Definition macros.h:460
std::shared_ptr< v8::ArrayBuffer::Allocator > v8_api_array_buffer_allocator_shared
v8::ArrayBuffer::Allocator * v8_api_array_buffer_allocator