• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
10 #include "include/v8-array-buffer.h"
11 #include "include/v8-internal.h"
12 #include "src/base/optional.h"
13 #include "src/handles/handles.h"
14 
15 namespace v8 {
16 namespace internal {
17 
18 class Isolate;
19 class WasmMemoryObject;
20 
21 // Whether the backing store is shared or not.
22 enum class SharedFlag : uint8_t { kNotShared, kShared };
23 
24 // Whether the backing store is resizable or not.
25 enum class ResizableFlag : uint8_t { kNotResizable, kResizable };
26 
27 // Whether the backing store memory is initialied to zero or not.
28 enum class InitializedFlag : uint8_t { kUninitialized, kZeroInitialized };
29 
30 // Internal information for shared wasm memories. E.g. contains
31 // a list of all memory objects (across all isolates) that share this
32 // backing store.
33 struct SharedWasmMemoryData;
34 
35 // The {BackingStore} data structure stores all the low-level details about the
36 // backing store of an array buffer or Wasm memory, including its base address
37 // and length, whether it is shared, provided by the embedder, has guard
38 // regions, etc. Instances of this classes *own* the underlying memory
39 // when they are created through one of the {Allocate()} methods below,
40 // and the destructor frees the memory (and page allocation if necessary).
41 // Backing stores can also *wrap* embedder-allocated memory. In this case,
42 // they do not own the memory, and upon destruction, they do not deallocate it.
43 class V8_EXPORT_PRIVATE BackingStore : public BackingStoreBase {
44  public:
45   ~BackingStore();
46 
47   // Allocate an array buffer backing store using the default method,
48   // which currently is the embedder-provided array buffer allocator.
49   static std::unique_ptr<BackingStore> Allocate(Isolate* isolate,
50                                                 size_t byte_length,
51                                                 SharedFlag shared,
52                                                 InitializedFlag initialized);
53 
54 #if V8_ENABLE_WEBASSEMBLY
55   // Allocate the backing store for a Wasm memory.
56   static std::unique_ptr<BackingStore> AllocateWasmMemory(Isolate* isolate,
57                                                           size_t initial_pages,
58                                                           size_t maximum_pages,
59                                                           SharedFlag shared);
60 #endif  // V8_ENABLE_WEBASSEMBLY
61 
62   // Tries to allocate `maximum_pages` of memory and commit `initial_pages`.
63   static std::unique_ptr<BackingStore> TryAllocateAndPartiallyCommitMemory(
64       Isolate* isolate, size_t byte_length, size_t max_byte_length,
65       size_t page_size, size_t initial_pages, size_t maximum_pages,
66       bool is_wasm_memory, SharedFlag shared);
67 
68   // Create a backing store that wraps existing allocated memory.
69   // If {free_on_destruct} is {true}, the memory will be freed using the
70   // ArrayBufferAllocator::Free() callback when this backing store is
71   // destructed. Otherwise destructing the backing store will do nothing
72   // to the allocated memory.
73   static std::unique_ptr<BackingStore> WrapAllocation(Isolate* isolate,
74                                                       void* allocation_base,
75                                                       size_t allocation_length,
76                                                       SharedFlag shared,
77                                                       bool free_on_destruct);
78 
79   static std::unique_ptr<BackingStore> WrapAllocation(
80       void* allocation_base, size_t allocation_length,
81       v8::BackingStore::DeleterCallback deleter, void* deleter_data,
82       SharedFlag shared);
83 
84   // Create an empty backing store.
85   static std::unique_ptr<BackingStore> EmptyBackingStore(SharedFlag shared);
86 
87   // Accessors.
buffer_start()88   void* buffer_start() const { return buffer_start_; }
89   size_t byte_length(
90       std::memory_order memory_order = std::memory_order_relaxed) const {
91     return byte_length_.load(memory_order);
92   }
max_byte_length()93   size_t max_byte_length() const { return max_byte_length_; }
byte_capacity()94   size_t byte_capacity() const { return byte_capacity_; }
is_shared()95   bool is_shared() const { return is_shared_; }
is_resizable()96   bool is_resizable() const { return is_resizable_; }
is_wasm_memory()97   bool is_wasm_memory() const { return is_wasm_memory_; }
has_guard_regions()98   bool has_guard_regions() const { return has_guard_regions_; }
free_on_destruct()99   bool free_on_destruct() const { return free_on_destruct_; }
100 
IsEmpty()101   bool IsEmpty() const {
102     DCHECK_GE(byte_capacity_, byte_length_);
103     return byte_capacity_ == 0;
104   }
105 
106   enum ResizeOrGrowResult { kSuccess, kFailure, kRace };
107 
108   ResizeOrGrowResult ResizeInPlace(Isolate* isolate, size_t new_byte_length,
109                                    size_t new_committed_length);
110   ResizeOrGrowResult GrowInPlace(Isolate* isolate, size_t new_byte_length,
111                                  size_t new_committed_length);
112 
113   // Wrapper around ArrayBuffer::Allocator::Reallocate.
114   bool Reallocate(Isolate* isolate, size_t new_byte_length);
115 
116 #if V8_ENABLE_WEBASSEMBLY
117   // Attempt to grow this backing store in place.
118   base::Optional<size_t> GrowWasmMemoryInPlace(Isolate* isolate,
119                                                size_t delta_pages,
120                                                size_t max_pages);
121 
122   // Allocate a new, larger, backing store for this Wasm memory and copy the
123   // contents of this backing store into it.
124   std::unique_ptr<BackingStore> CopyWasmMemory(Isolate* isolate,
125                                                size_t new_pages,
126                                                size_t max_pages);
127 
128   // Attach the given memory object to this backing store. The memory object
129   // will be updated if this backing store is grown.
130   void AttachSharedWasmMemoryObject(Isolate* isolate,
131                                     Handle<WasmMemoryObject> memory_object);
132 
133   // Send asynchronous updates to attached memory objects in other isolates
134   // after the backing store has been grown. Memory objects in this
135   // isolate are updated synchronously.
136   static void BroadcastSharedWasmMemoryGrow(Isolate* isolate,
137                                             std::shared_ptr<BackingStore>);
138 
139   // Remove all memory objects in the given isolate that refer to this
140   // backing store.
141   static void RemoveSharedWasmMemoryObjects(Isolate* isolate);
142 
143   // Update all shared memory objects in this isolate (after a grow operation).
144   static void UpdateSharedWasmMemoryObjects(Isolate* isolate);
145 #endif  // V8_ENABLE_WEBASSEMBLY
146 
147   // Returns the size of the external memory owned by this backing store.
148   // It is used for triggering GCs based on the external memory pressure.
PerIsolateAccountingLength()149   size_t PerIsolateAccountingLength() {
150     if (is_shared_) {
151       // TODO(titzer): SharedArrayBuffers and shared WasmMemorys cause problems
152       // with accounting for per-isolate external memory. In particular, sharing
153       // the same array buffer or memory multiple times, which happens in stress
154       // tests, can cause overcounting, leading to GC thrashing. Fix with global
155       // accounting?
156       return 0;
157     }
158     if (empty_deleter_) {
159       // The backing store has an empty deleter. Even if the backing store is
160       // freed after GC, it would not free the memory block.
161       return 0;
162     }
163     return byte_length();
164   }
165 
id()166   uint32_t id() const { return id_; }
167 
168  private:
169   friend class GlobalBackingStoreRegistry;
170 
171   BackingStore(void* buffer_start, size_t byte_length, size_t max_byte_length,
172                size_t byte_capacity, SharedFlag shared, ResizableFlag resizable,
173                bool is_wasm_memory, bool free_on_destruct,
174                bool has_guard_regions, bool custom_deleter, bool empty_deleter);
175   BackingStore(const BackingStore&) = delete;
176   BackingStore& operator=(const BackingStore&) = delete;
177   void SetAllocatorFromIsolate(Isolate* isolate);
178 
179   void* buffer_start_ = nullptr;
180   std::atomic<size_t> byte_length_;
181   // Max byte length of the corresponding JSArrayBuffer(s).
182   size_t max_byte_length_;
183   // Amount of the memory allocated
184   size_t byte_capacity_;
185   // Unique ID of this backing store. Currently only used by DevTools, to
186   // identify stores used by several ArrayBuffers or WebAssembly memories
187   // (reported by the inspector as [[ArrayBufferData]] internal property)
188   uint32_t id_;
189   struct DeleterInfo {
190     v8::BackingStore::DeleterCallback callback;
191     void* data;
192   };
193 
194   union TypeSpecificData {
TypeSpecificData()195     TypeSpecificData() : v8_api_array_buffer_allocator(nullptr) {}
~TypeSpecificData()196     ~TypeSpecificData() {}
197 
198     // If this backing store was allocated through the ArrayBufferAllocator API,
199     // this is a direct pointer to the API object for freeing the backing
200     // store.
201     v8::ArrayBuffer::Allocator* v8_api_array_buffer_allocator;
202 
203     // Holds a shared_ptr to the ArrayBuffer::Allocator instance, if requested
204     // so by the embedder through setting
205     // Isolate::CreateParams::array_buffer_allocator_shared.
206     std::shared_ptr<v8::ArrayBuffer::Allocator>
207         v8_api_array_buffer_allocator_shared;
208 
209     // For shared Wasm memories, this is a list of all the attached memory
210     // objects, which is needed to grow shared backing stores.
211     SharedWasmMemoryData* shared_wasm_memory_data;
212 
213     // Custom deleter for the backing stores that wrap memory blocks that are
214     // allocated with a custom allocator.
215     DeleterInfo deleter;
216   } type_specific_data_;
217 
218   bool is_shared_ : 1;
219   // Backing stores for (Resizable|GrowableShared)ArrayBuffer
220   bool is_resizable_ : 1;
221   bool is_wasm_memory_ : 1;
222   bool holds_shared_ptr_to_allocator_ : 1;
223   bool free_on_destruct_ : 1;
224   bool has_guard_regions_ : 1;
225   bool globally_registered_ : 1;
226   bool custom_deleter_ : 1;
227   bool empty_deleter_ : 1;
228 
229   // Accessors for type-specific data.
230   v8::ArrayBuffer::Allocator* get_v8_api_array_buffer_allocator();
231   SharedWasmMemoryData* get_shared_wasm_memory_data();
232 
233   void Clear();  // Internally clears fields after deallocation.
234 #if V8_ENABLE_WEBASSEMBLY
235   static std::unique_ptr<BackingStore> TryAllocateWasmMemory(
236       Isolate* isolate, size_t initial_pages, size_t maximum_pages,
237       SharedFlag shared);
238 #endif  // V8_ENABLE_WEBASSEMBLY
239 };
240 
241 // A global, per-process mapping from buffer addresses to backing stores
242 // of wasm memory objects.
243 class GlobalBackingStoreRegistry {
244  public:
245   // Register a backing store in the global registry. A mapping from the
246   // {buffer_start} to the backing store object will be added. The backing
247   // store will automatically unregister itself upon destruction.
248   // Only wasm memory backing stores are supported.
249   static void Register(std::shared_ptr<BackingStore> backing_store);
250 
251  private:
252   friend class BackingStore;
253   // Unregister a backing store in the global registry.
254   static void Unregister(BackingStore* backing_store);
255 
256   // Adds the given memory object to the backing store's weak list
257   // of memory objects (under the registry lock).
258   static void AddSharedWasmMemoryObject(Isolate* isolate,
259                                         BackingStore* backing_store,
260                                         Handle<WasmMemoryObject> memory_object);
261 
262   // Purge any shared wasm memory lists that refer to this isolate.
263   static void Purge(Isolate* isolate);
264 
265   // Broadcast updates to all attached memory objects.
266   static void BroadcastSharedWasmMemoryGrow(
267       Isolate* isolate, std::shared_ptr<BackingStore> backing_store);
268 
269   // Update all shared memory objects in the given isolate.
270   static void UpdateSharedWasmMemoryObjects(Isolate* isolate);
271 };
272 
273 }  // namespace internal
274 }  // namespace v8
275 
276 #endif  // V8_OBJECTS_BACKING_STORE_H_
277