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