1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sts=4 et sw=4 tw=99:
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 "jsobj.h"
14 #include "jstypes.h"
15 
16 #include "gc/Barrier.h"
17 #include "vm/ArrayBufferObject.h"
18 
19 typedef struct JSProperty JSProperty;
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 AsmJS code 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 class SharedArrayRawBuffer
45 {
46   private:
47     mozilla::Atomic<uint32_t, mozilla::ReleaseAcquire> refcount_;
48     uint32_t length;
49     bool preparedForAsmJS;
50 
51     // A list of structures representing tasks waiting on some
52     // location within this buffer.
53     FutexWaiter* waiters_;
54 
55   protected:
56     SharedArrayRawBuffer(uint8_t* buffer, uint32_t length, bool preparedForAsmJS)
57       : refcount_(1),
58         length(length),
59         preparedForAsmJS(preparedForAsmJS),
60         waiters_(nullptr)
61     {
62         MOZ_ASSERT(buffer == dataPointerShared());
63     }
64 
65   public:
66     static SharedArrayRawBuffer* New(JSContext* cx, uint32_t length);
67 
68     // This may be called from multiple threads.  The caller must take
69     // care of mutual exclusion.
70     FutexWaiter* waiters() const {
71         return waiters_;
72     }
73 
74     // This may be called from multiple threads.  The caller must take
75     // care of mutual exclusion.
76     void setWaiters(FutexWaiter* waiters) {
77         waiters_ = waiters;
78     }
79 
80     SharedMem<uint8_t*> dataPointerShared() const {
81         uint8_t* ptr = reinterpret_cast<uint8_t*>(const_cast<SharedArrayRawBuffer*>(this));
82         return SharedMem<uint8_t*>::shared(ptr + sizeof(SharedArrayRawBuffer));
83     }
84 
85     uint32_t byteLength() const {
86         return length;
87     }
88 
89     bool isPreparedForAsmJS() const {
90         return preparedForAsmJS;
91     }
92 
93     uint32_t refcount() const { return refcount_; }
94 
95     void addReference();
96     void dropReference();
97 };
98 
99 /*
100  * SharedArrayBufferObject
101  *
102  * When transferred to a WebWorker, the buffer is not detached on the
103  * parent side, and both child and parent reference the same buffer.
104  *
105  * The underlying memory is memory-mapped and reference counted
106  * (across workers and/or processes).  The SharedArrayBuffer object
107  * has a finalizer that decrements the refcount, the last one to leave
108  * (globally) unmaps the memory.  The sender ups the refcount before
109  * transmitting the memory to another worker.
110  *
111  * SharedArrayBufferObject (or really the underlying memory) /is
112  * racy/: more than one worker can access the memory at the same time.
113  *
114  * A TypedArrayObject (a view) references a SharedArrayBuffer
115  * and keeps it alive.  The SharedArrayBuffer does /not/ reference its
116  * views.
117  */
118 class SharedArrayBufferObject : public ArrayBufferObjectMaybeShared
119 {
120     static bool byteLengthGetterImpl(JSContext* cx, const CallArgs& args);
121 
122   public:
123     // RAWBUF_SLOT holds a pointer (as "private" data) to the
124     // SharedArrayRawBuffer object, which is manually managed storage.
125     static const uint8_t RAWBUF_SLOT = 0;
126 
127     static const uint8_t RESERVED_SLOTS = 1;
128 
129     static const Class class_;
130 
131     static bool byteLengthGetter(JSContext* cx, unsigned argc, Value* vp);
132 
133     static bool class_constructor(JSContext* cx, unsigned argc, Value* vp);
134 
135     // Create a SharedArrayBufferObject with a new SharedArrayRawBuffer.
136     static SharedArrayBufferObject* New(JSContext* cx,
137                                         uint32_t length,
138                                         HandleObject proto = nullptr);
139 
140     // Create a SharedArrayBufferObject using an existing SharedArrayRawBuffer.
141     static SharedArrayBufferObject* New(JSContext* cx,
142                                         SharedArrayRawBuffer* buffer,
143                                         HandleObject proto = nullptr);
144 
145     static void Finalize(FreeOp* fop, JSObject* obj);
146 
147     static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
148                                        JS::ClassInfo* info);
149 
150     static void copyData(Handle<SharedArrayBufferObject*> toBuffer,
151                          Handle<SharedArrayBufferObject*> fromBuffer,
152                          uint32_t fromIndex, uint32_t count);
153 
154     SharedArrayRawBuffer* rawBufferObject() const;
155 
156     // Invariant: This method does not cause GC and can be called
157     // without anchoring the object it is called on.
158     uintptr_t globalID() const {
159         // The buffer address is good enough as an ID provided the memory is not shared
160         // between processes or, if it is, it is mapped to the same address in every
161         // process.  (At the moment, shared memory cannot be shared between processes.)
162         return dataPointerShared().asValue();
163     }
164 
165     uint32_t byteLength() const {
166         return rawBufferObject()->byteLength();
167     }
168     bool isPreparedForAsmJS() const {
169         return rawBufferObject()->isPreparedForAsmJS();
170     }
171 
172     SharedMem<uint8_t*> dataPointerShared() const {
173         return rawBufferObject()->dataPointerShared();
174     }
175 
176 private:
177     void acceptRawBuffer(SharedArrayRawBuffer* buffer);
178     void dropRawBuffer();
179 };
180 
181 bool IsSharedArrayBuffer(HandleValue v);
182 bool IsSharedArrayBuffer(HandleObject o);
183 bool IsSharedArrayBuffer(JSObject* o);
184 
185 SharedArrayBufferObject& AsSharedArrayBuffer(HandleObject o);
186 
187 } // namespace js
188 
189 #endif // vm_SharedArrayObject_h
190