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