1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- 2 * vim: set ts=8 sts=2 et sw=2 tw=80: 3 * This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef vm_SharedArrayObject_h 8 #define vm_SharedArrayObject_h 9 10 #include "mozilla/Atomics.h" 11 12 #include "jsapi.h" 13 #include "jstypes.h" 14 15 #include "gc/Barrier.h" 16 #include "gc/Memory.h" 17 #include "vm/ArrayBufferObject.h" 18 #include "vm/JSObject.h" 19 #include "wasm/WasmPages.h" 20 21 namespace js { 22 23 class FutexWaiter; 24 25 /* 26 * SharedArrayRawBuffer 27 * 28 * A bookkeeping object always stored immediately before the raw buffer. 29 * The buffer itself is mmap()'d and refcounted. 30 * SharedArrayBufferObjects and structured clone objects may hold references. 31 * 32 * |<------ sizeof ------>|<- length ->| 33 * 34 * | waste | SharedArrayRawBuffer | data array | waste | 35 * 36 * Observe that if we want to map the data array on a specific address, such 37 * as absolute zero (bug 1056027), then the SharedArrayRawBuffer cannot be 38 * prefixed to the data array, it has to be a separate object, also in 39 * shared memory. (That would get rid of ~4KB of waste, as well.) Very little 40 * else would have to change throughout the engine, the SARB would point to 41 * the data array using a constant pointer, instead of computing its 42 * address. 43 * 44 * If preparedForWasm_ is true then length_ can change following initialization; 45 * it may grow toward maxSize_. See extensive comments above WasmArrayRawBuffer 46 * in ArrayBufferObject.cpp. 47 * 48 * length_ only grows when the lock is held. 49 */ 50 class SharedArrayRawBuffer { 51 private: 52 mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_; 53 mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> length_; 54 Mutex growLock_; 55 // The maximum size of this buffer in wasm pages. If this buffer was not 56 // prepared for wasm, then this is zero. 57 wasm::Pages wasmMaxPages_; 58 size_t mappedSize_; // Does not include the page for the header 59 bool preparedForWasm_; 60 61 // A list of structures representing tasks waiting on some 62 // location within this buffer. 63 FutexWaiter* waiters_; 64 basePointer()65 uint8_t* basePointer() { 66 SharedMem<uint8_t*> p = dataPointerShared() - gc::SystemPageSize(); 67 MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0); 68 return p.unwrap(/* we trust you won't abuse it */); 69 } 70 71 protected: SharedArrayRawBuffer(uint8_t * buffer,size_t length,wasm::Pages wasmMaxPages,size_t mappedSize,bool preparedForWasm)72 SharedArrayRawBuffer(uint8_t* buffer, size_t length, wasm::Pages wasmMaxPages, 73 size_t mappedSize, bool preparedForWasm) 74 : refcount_(1), 75 length_(length), 76 growLock_(mutexid::SharedArrayGrow), 77 wasmMaxPages_(wasmMaxPages), 78 mappedSize_(mappedSize), 79 preparedForWasm_(preparedForWasm), 80 waiters_(nullptr) { 81 MOZ_ASSERT(buffer == dataPointerShared()); 82 } 83 84 // Allocate a SharedArrayRawBuffer for either Wasm or other users. 85 // `wasmMaxPages` must always be something for wasm and nothing for other 86 // users. 87 static SharedArrayRawBuffer* AllocateInternal( 88 size_t length, const mozilla::Maybe<wasm::Pages>& wasmMaxPages, 89 const mozilla::Maybe<size_t>& wasmMappedSize); 90 91 public: 92 class Lock; 93 friend class Lock; 94 95 class MOZ_STACK_CLASS Lock { 96 SharedArrayRawBuffer* buf; 97 98 public: Lock(SharedArrayRawBuffer * buf)99 explicit Lock(SharedArrayRawBuffer* buf) : buf(buf) { 100 buf->growLock_.lock(); 101 } ~Lock()102 ~Lock() { buf->growLock_.unlock(); } 103 }; 104 105 static SharedArrayRawBuffer* Allocate(size_t length); 106 static SharedArrayRawBuffer* AllocateWasm( 107 wasm::Pages initialPages, const mozilla::Maybe<wasm::Pages>& maxPages, 108 const mozilla::Maybe<size_t>& mappedSize); 109 110 // This may be called from multiple threads. The caller must take 111 // care of mutual exclusion. waiters()112 FutexWaiter* waiters() const { return waiters_; } 113 114 // This may be called from multiple threads. The caller must take 115 // care of mutual exclusion. setWaiters(FutexWaiter * waiters)116 void setWaiters(FutexWaiter* waiters) { waiters_ = waiters; } 117 dataPointerShared()118 SharedMem<uint8_t*> dataPointerShared() const { 119 uint8_t* ptr = 120 reinterpret_cast<uint8_t*>(const_cast<SharedArrayRawBuffer*>(this)); 121 return SharedMem<uint8_t*>::shared(ptr + sizeof(SharedArrayRawBuffer)); 122 } 123 fromDataPtr(const uint8_t * dataPtr)124 static const SharedArrayRawBuffer* fromDataPtr(const uint8_t* dataPtr) { 125 return reinterpret_cast<const SharedArrayRawBuffer*>( 126 dataPtr - sizeof(SharedArrayRawBuffer)); 127 } 128 volatileByteLength()129 size_t volatileByteLength() const { return length_; } 130 volatileWasmPages()131 wasm::Pages volatileWasmPages() const { 132 return wasm::Pages::fromByteLengthExact(length_); 133 } 134 wasmMaxPages()135 wasm::Pages wasmMaxPages() const { return wasmMaxPages_; } 136 mappedSize()137 size_t mappedSize() const { return mappedSize_; } 138 isWasm()139 bool isWasm() const { return preparedForWasm_; } 140 141 void tryGrowMaxPagesInPlace(wasm::Pages deltaMaxPages); 142 143 bool wasmGrowToPagesInPlace(const Lock&, wasm::Pages newPages); 144 refcount()145 uint32_t refcount() const { return refcount_; } 146 147 [[nodiscard]] bool addReference(); 148 void dropReference(); 149 150 static int32_t liveBuffers(); 151 }; 152 153 /* 154 * SharedArrayBufferObject 155 * 156 * When transferred to a WebWorker, the buffer is not detached on the 157 * parent side, and both child and parent reference the same buffer. 158 * 159 * The underlying memory is memory-mapped and reference counted 160 * (across workers and/or processes). The SharedArrayBuffer object 161 * has a finalizer that decrements the refcount, the last one to leave 162 * (globally) unmaps the memory. The sender ups the refcount before 163 * transmitting the memory to another worker. 164 * 165 * SharedArrayBufferObject (or really the underlying memory) /is 166 * racy/: more than one worker can access the memory at the same time. 167 * 168 * A TypedArrayObject (a view) references a SharedArrayBuffer 169 * and keeps it alive. The SharedArrayBuffer does /not/ reference its 170 * views. 171 */ 172 class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared { 173 static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args); 174 175 public: 176 // RAWBUF_SLOT holds a pointer (as "private" data) to the 177 // SharedArrayRawBuffer object, which is manually managed storage. 178 static const uint8_t RAWBUF_SLOT = 0; 179 180 // LENGTH_SLOT holds the length of the underlying buffer as it was when this 181 // object was created. For JS use cases this is the same length as the 182 // buffer, but for Wasm the buffer can grow, and the buffer's length may be 183 // greater than the object's length. 184 static const uint8_t LENGTH_SLOT = 1; 185 186 static_assert(LENGTH_SLOT == ArrayBufferObject::BYTE_LENGTH_SLOT, 187 "JIT code assumes the same slot is used for the length"); 188 189 static const uint8_t RESERVED_SLOTS = 2; 190 191 static const JSClass class_; 192 static const JSClass protoClass_; 193 194 static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp); 195 196 static bool class_constructor(JSContext* cx, unsigned argc, Value* vp); 197 isOriginalByteLengthGetter(Native native)198 static bool isOriginalByteLengthGetter(Native native) { 199 return native == byteLengthGetter; 200 } 201 202 // Create a SharedArrayBufferObject with a new SharedArrayRawBuffer. 203 static SharedArrayBufferObject* New(JSContext* cx, size_t length, 204 HandleObject proto = nullptr); 205 206 // Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer, 207 // recording the given length in the SharedArrayBufferObject. 208 static SharedArrayBufferObject* New(JSContext* cx, 209 SharedArrayRawBuffer* buffer, 210 size_t length, 211 HandleObject proto = nullptr); 212 213 static void Finalize(JSFreeOp* fop, JSObject* obj); 214 215 static void addSizeOfExcludingThis(JSObject* obj, 216 mozilla::MallocSizeOf mallocSizeOf, 217 JS::ClassInfo* info); 218 219 static void copyData(Handle<SharedArrayBufferObject*> toBuffer, 220 size_t toIndex, 221 Handle<SharedArrayBufferObject*> fromBuffer, 222 size_t fromIndex, size_t count); 223 224 SharedArrayRawBuffer* rawBufferObject() const; 225 226 // Invariant: This method does not cause GC and can be called 227 // without anchoring the object it is called on. globalID()228 uintptr_t globalID() const { 229 // The buffer address is good enough as an ID provided the memory is not 230 // shared between processes or, if it is, it is mapped to the same address 231 // in every process. (At the moment, shared memory cannot be shared between 232 // processes.) 233 return dataPointerShared().asValue(); 234 } 235 byteLength()236 size_t byteLength() const { 237 return size_t(getFixedSlot(LENGTH_SLOT).toPrivate()); 238 } 239 isWasm()240 bool isWasm() const { return rawBufferObject()->isWasm(); } dataPointerShared()241 SharedMem<uint8_t*> dataPointerShared() const { 242 return rawBufferObject()->dataPointerShared(); 243 } 244 245 // WebAssembly support: 246 247 // Create a SharedArrayBufferObject using the provided buffer and size. 248 // Assumes ownership of a reference to |buffer| even in case of failure, 249 // i.e. on failure |buffer->dropReference()| is performed. 250 static SharedArrayBufferObject* createFromNewRawBuffer( 251 JSContext* cx, SharedArrayRawBuffer* buffer, size_t initialSize); 252 volatileWasmPages()253 wasm::Pages volatileWasmPages() const { 254 return rawBufferObject()->volatileWasmPages(); 255 } wasmMaxPages()256 wasm::Pages wasmMaxPages() const { return rawBufferObject()->wasmMaxPages(); } 257 wasmMappedSize()258 size_t wasmMappedSize() const { return rawBufferObject()->mappedSize(); } 259 260 private: 261 [[nodiscard]] bool acceptRawBuffer(SharedArrayRawBuffer* buffer, 262 size_t length); 263 void dropRawBuffer(); 264 }; 265 266 using RootedSharedArrayBufferObject = Rooted<SharedArrayBufferObject*>; 267 using HandleSharedArrayBufferObject = Handle<SharedArrayBufferObject*>; 268 using MutableHandleSharedArrayBufferObject = 269 MutableHandle<SharedArrayBufferObject*>; 270 271 } // namespace js 272 273 #endif // vm_SharedArrayObject_h 274