summaryrefslogtreecommitdiffstats
path: root/js/src/vm/SharedArrayObject.h
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/vm/SharedArrayObject.h257
1 files changed, 257 insertions, 0 deletions
diff --git a/js/src/vm/SharedArrayObject.h b/js/src/vm/SharedArrayObject.h
new file mode 100644
index 0000000000..14eea9b5f6
--- /dev/null
+++ b/js/src/vm/SharedArrayObject.h
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set ts=8 sts=2 et sw=2 tw=80:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef vm_SharedArrayObject_h
+#define vm_SharedArrayObject_h
+
+#include "mozilla/Atomics.h"
+
+#include "jsapi.h"
+#include "jstypes.h"
+
+#include "gc/Barrier.h"
+#include "gc/Memory.h"
+#include "vm/ArrayBufferObject.h"
+#include "vm/JSObject.h"
+
+namespace js {
+
+class FutexWaiter;
+
+/*
+ * SharedArrayRawBuffer
+ *
+ * A bookkeeping object always stored immediately before the raw buffer.
+ * The buffer itself is mmap()'d and refcounted.
+ * SharedArrayBufferObjects and structured clone objects may hold references.
+ *
+ * |<------ sizeof ------>|<- length ->|
+ *
+ * | waste | SharedArrayRawBuffer | data array | waste |
+ *
+ * Observe that if we want to map the data array on a specific address, such
+ * as absolute zero (bug 1056027), then the SharedArrayRawBuffer cannot be
+ * prefixed to the data array, it has to be a separate object, also in
+ * shared memory. (That would get rid of ~4KB of waste, as well.) Very little
+ * else would have to change throughout the engine, the SARB would point to
+ * the data array using a constant pointer, instead of computing its
+ * address.
+ *
+ * If preparedForWasm_ is true then length_ can change following initialization;
+ * it may grow toward maxSize_. See extensive comments above WasmArrayRawBuffer
+ * in ArrayBufferObject.cpp.
+ *
+ * length_ only grows when the lock is held.
+ */
+class SharedArrayRawBuffer {
+ private:
+ mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
+ mozilla::Atomic<size_t, mozilla::SequentiallyConsistent> length_;
+ Mutex growLock_;
+ uint64_t maxSize_;
+ size_t mappedSize_; // Does not include the page for the header
+ bool preparedForWasm_;
+
+ // A list of structures representing tasks waiting on some
+ // location within this buffer.
+ FutexWaiter* waiters_;
+
+ uint8_t* basePointer() {
+ SharedMem<uint8_t*> p = dataPointerShared() - gc::SystemPageSize();
+ MOZ_ASSERT(p.asValue() % gc::SystemPageSize() == 0);
+ return p.unwrap(/* we trust you won't abuse it */);
+ }
+
+ protected:
+ SharedArrayRawBuffer(uint8_t* buffer, BufferSize length, uint64_t maxSize,
+ size_t mappedSize, bool preparedForWasm)
+ : refcount_(1),
+ length_(length.get()),
+ growLock_(mutexid::SharedArrayGrow),
+ maxSize_(maxSize),
+ mappedSize_(mappedSize),
+ preparedForWasm_(preparedForWasm),
+ waiters_(nullptr) {
+ MOZ_ASSERT(buffer == dataPointerShared());
+ }
+
+ public:
+ class Lock;
+ friend class Lock;
+
+ class MOZ_STACK_CLASS Lock {
+ SharedArrayRawBuffer* buf;
+
+ public:
+ explicit Lock(SharedArrayRawBuffer* buf) : buf(buf) {
+ buf->growLock_.lock();
+ }
+ ~Lock() { buf->growLock_.unlock(); }
+ };
+
+ // max must be Something for wasm, Nothing for other uses
+ static SharedArrayRawBuffer* Allocate(
+ BufferSize length, const mozilla::Maybe<uint64_t>& maxSize,
+ const mozilla::Maybe<size_t>& mappedSize);
+
+ // This may be called from multiple threads. The caller must take
+ // care of mutual exclusion.
+ FutexWaiter* waiters() const { return waiters_; }
+
+ // This may be called from multiple threads. The caller must take
+ // care of mutual exclusion.
+ void setWaiters(FutexWaiter* waiters) { waiters_ = waiters; }
+
+ SharedMem<uint8_t*> dataPointerShared() const {
+ uint8_t* ptr =
+ reinterpret_cast<uint8_t*>(const_cast<SharedArrayRawBuffer*>(this));
+ return SharedMem<uint8_t*>::shared(ptr + sizeof(SharedArrayRawBuffer));
+ }
+
+ static const SharedArrayRawBuffer* fromDataPtr(const uint8_t* dataPtr) {
+ return reinterpret_cast<const SharedArrayRawBuffer*>(
+ dataPtr - sizeof(SharedArrayRawBuffer));
+ }
+
+ BufferSize volatileByteLength() const { return BufferSize(length_); }
+
+ uint64_t maxSize() const { return maxSize_; }
+
+ size_t mappedSize() const { return mappedSize_; }
+
+ bool isWasm() const { return preparedForWasm_; }
+
+ void tryGrowMaxSizeInPlace(uint64_t deltaMaxSize);
+
+ bool wasmGrowToSizeInPlace(const Lock&, BufferSize newLength);
+
+ uint32_t refcount() const { return refcount_; }
+
+ MOZ_MUST_USE bool addReference();
+ void dropReference();
+
+ static int32_t liveBuffers();
+};
+
+/*
+ * SharedArrayBufferObject
+ *
+ * When transferred to a WebWorker, the buffer is not detached on the
+ * parent side, and both child and parent reference the same buffer.
+ *
+ * The underlying memory is memory-mapped and reference counted
+ * (across workers and/or processes). The SharedArrayBuffer object
+ * has a finalizer that decrements the refcount, the last one to leave
+ * (globally) unmaps the memory. The sender ups the refcount before
+ * transmitting the memory to another worker.
+ *
+ * SharedArrayBufferObject (or really the underlying memory) /is
+ * racy/: more than one worker can access the memory at the same time.
+ *
+ * A TypedArrayObject (a view) references a SharedArrayBuffer
+ * and keeps it alive. The SharedArrayBuffer does /not/ reference its
+ * views.
+ */
+class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared {
+ static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
+
+ public:
+ // RAWBUF_SLOT holds a pointer (as "private" data) to the
+ // SharedArrayRawBuffer object, which is manually managed storage.
+ static const uint8_t RAWBUF_SLOT = 0;
+
+ // LENGTH_SLOT holds the length of the underlying buffer as it was when this
+ // object was created. For JS use cases this is the same length as the
+ // buffer, but for Wasm the buffer can grow, and the buffer's length may be
+ // greater than the object's length.
+ static const uint8_t LENGTH_SLOT = 1;
+
+ static const uint8_t RESERVED_SLOTS = 2;
+
+ static const JSClass class_;
+ static const JSClass protoClass_;
+
+ static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
+
+ static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
+
+ // Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
+ static SharedArrayBufferObject* New(JSContext* cx, BufferSize length,
+ HandleObject proto = nullptr);
+
+ // Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer,
+ // recording the given length in the SharedArrayBufferObject.
+ static SharedArrayBufferObject* New(JSContext* cx,
+ SharedArrayRawBuffer* buffer,
+ BufferSize length,
+ HandleObject proto = nullptr);
+
+ static void Finalize(JSFreeOp* fop, JSObject* obj);
+
+ static void addSizeOfExcludingThis(JSObject* obj,
+ mozilla::MallocSizeOf mallocSizeOf,
+ JS::ClassInfo* info);
+
+ static void copyData(Handle<SharedArrayBufferObject*> toBuffer,
+ size_t toIndex,
+ Handle<SharedArrayBufferObject*> fromBuffer,
+ size_t fromIndex, size_t count);
+
+ SharedArrayRawBuffer* rawBufferObject() const;
+
+ // Invariant: This method does not cause GC and can be called
+ // without anchoring the object it is called on.
+ uintptr_t globalID() const {
+ // The buffer address is good enough as an ID provided the memory is not
+ // shared between processes or, if it is, it is mapped to the same address
+ // in every process. (At the moment, shared memory cannot be shared between
+ // processes.)
+ return dataPointerShared().asValue();
+ }
+
+ BufferSize byteLength() const {
+ return BufferSize(size_t(getReservedSlot(LENGTH_SLOT).toPrivate()));
+ }
+
+ bool isWasm() const { return rawBufferObject()->isWasm(); }
+ SharedMem<uint8_t*> dataPointerShared() const {
+ return rawBufferObject()->dataPointerShared();
+ }
+
+ // WebAssembly support:
+
+ // Create a SharedArrayBufferObject using the provided buffer and size.
+ // Assumes ownership of a reference to |buffer| even in case of failure,
+ // i.e. on failure |buffer->dropReference()| is performed.
+ static SharedArrayBufferObject* createFromNewRawBuffer(
+ JSContext* cx, SharedArrayRawBuffer* buffer, BufferSize initialSize);
+
+ mozilla::Maybe<uint64_t> wasmMaxSize() const {
+ return mozilla::Some(rawBufferObject()->maxSize());
+ }
+
+ size_t wasmMappedSize() const { return rawBufferObject()->mappedSize(); }
+
+ private:
+ MOZ_MUST_USE bool acceptRawBuffer(SharedArrayRawBuffer* buffer,
+ BufferSize length);
+ void dropRawBuffer();
+};
+
+bool IsSharedArrayBuffer(HandleValue v);
+bool IsSharedArrayBuffer(HandleObject o);
+bool IsSharedArrayBuffer(JSObject* o);
+
+SharedArrayBufferObject& AsSharedArrayBuffer(HandleObject o);
+
+using RootedSharedArrayBufferObject = Rooted<SharedArrayBufferObject*>;
+using HandleSharedArrayBufferObject = Handle<SharedArrayBufferObject*>;
+using MutableHandleSharedArrayBufferObject =
+ MutableHandle<SharedArrayBufferObject*>;
+
+} // namespace js
+
+#endif // vm_SharedArrayObject_h