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