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
15namespace v8 {
16namespace internal {
17
18class Isolate;
19class WasmMemoryObject;
20
21// Whether the backing store is shared or not.
22enum class SharedFlag : uint8_t { kNotShared, kShared };
23
24// Whether the backing store is resizable or not.
25enum class ResizableFlag : uint8_t { kNotResizable, kResizable };
26
27// Whether the backing store memory is initialied to zero or not.
28enum 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.
33struct 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.
43class 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.
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  }
93  size_t max_byte_length() const { return max_byte_length_; }
94  size_t byte_capacity() const { return byte_capacity_; }
95  bool is_shared() const { return is_shared_; }
96  bool is_resizable() const { return is_resizable_; }
97  bool is_wasm_memory() const { return is_wasm_memory_; }
98  bool has_guard_regions() const { return has_guard_regions_; }
99  bool free_on_destruct() const { return free_on_destruct_; }
100
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.
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
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 {
195    TypeSpecificData() : v8_api_array_buffer_allocator(nullptr) {}
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.
243class 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