Bug 933001 - Part 3/5 - StructuredClone changes for SharedArrayBuffer. r=sfink
authorSean Stangl <sstangl@mozilla.com>
Thu, 20 Feb 2014 14:47:58 -0800
changeset 171169 79c2a2e387610a8bb5800e5ffdd3fc76e4eb7105
parent 171168 d5ebf76f501095c46a1306dcd0d4de8797c2bb5e
child 171170 b21841db83df66f3f1b15fba09f24433c5e74878
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssfink
bugs933001
milestone30.0a1
Bug 933001 - Part 3/5 - StructuredClone changes for SharedArrayBuffer. r=sfink
js/src/vm/StructuredClone.cpp
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -34,16 +34,17 @@
 
 #include <algorithm>
 
 #include "jsapi.h"
 #include "jscntxt.h"
 #include "jsdate.h"
 #include "jswrapper.h"
 
+#include "vm/SharedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 #include "vm/WrapperObject.h"
 
 #include "jscntxtinlines.h"
 #include "jsobjinlines.h"
 
 using namespace js;
 
@@ -107,18 +108,21 @@ enum TransferableObjectType {
     SCTAG_TM_UNFILLED = 0,
 
     // Structured clone buffer does not yet own the data
     SCTAG_TM_UNOWNED = 1,
 
     // All values at least this large are owned by the clone buffer
     SCTAG_TM_FIRST_OWNED = 2,
 
+    // Data is a pointer to a SharedArrayRawBuffer.
+    SCTAG_TM_SHARED_BUFFER = 2,
+
     // Data is a pointer that can be freed
-    SCTAG_TM_ALLOC_DATA = 2,
+    SCTAG_TM_ALLOC_DATA = 3,
 };
 
 namespace js {
 
 struct SCOutput {
   public:
     explicit SCOutput(JSContext *cx);
     ~SCOutput();
@@ -331,19 +335,26 @@ ReadStructuredClone(JSContext *cx, uint6
     return r.read(vp.address());
 }
 
 // This may acquire new ways of discarding transfer map entries as new
 // Transferables are implemented.
 static void
 DiscardEntry(uint32_t mapEntryDescriptor, const uint64_t *ptr)
 {
-    JS_ASSERT(mapEntryDescriptor == SCTAG_TM_ALLOC_DATA);
-    uint64_t u = LittleEndian::readUint64(ptr);
-    js_free(reinterpret_cast<void*>(u));
+    if (mapEntryDescriptor == SCTAG_TM_ALLOC_DATA) {
+        uint64_t u = LittleEndian::readUint64(ptr);
+        js_free(reinterpret_cast<void*>(u));
+    } else {
+        JS_ASSERT(mapEntryDescriptor == SCTAG_TM_SHARED_BUFFER);
+        uint64_t u = LittleEndian::readUint64(ptr);
+        SharedArrayRawBuffer *raw = reinterpret_cast<SharedArrayRawBuffer *>(u);
+        if (raw)
+            raw->dropReference();
+    }
 }
 
 static void
 Discard(const uint64_t *begin, const uint64_t *end)
 {
     const uint64_t *point = begin;
     if (begin >= end)
         return; // Empty buffer
@@ -713,17 +724,17 @@ JSStructuredCloneWriter::parseTransferab
             return false;
         }
 
         JSObject* tObj = CheckedUnwrap(&v.toObject());
         if (!tObj) {
             JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_UNWRAP_DENIED);
             return false;
         }
-        if (!tObj->is<ArrayBufferObject>()) {
+        if (!tObj->is<ArrayBufferObject>() && !tObj->is<SharedArrayBufferObject>()) {
             reportErrorTransferable();
             return false;
         }
 
         // No duplicates allowed
         if (std::find(transferableObjects.begin(), transferableObjects.end(), tObj) != transferableObjects.end()) {
             JS_ReportErrorNumber(context(), js_GetErrorMessage, nullptr, JSMSG_SC_DUP_TRANSFERABLE);
             return false;
@@ -949,32 +960,30 @@ JSStructuredCloneWriter::writeTransferMa
         return true;
 
     if (!out.writePair(SCTAG_TRANSFER_MAP_HEADER, (uint32_t)SCTAG_TM_UNREAD))
         return false;
 
     if (!out.write(transferableObjects.length()))
         return false;
 
-    for (JS::AutoObjectVector::Range tr = transferableObjects.all();
-         !tr.empty(); tr.popFront())
-    {
+    for (JS::AutoObjectVector::Range tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
         JSObject *obj = tr.front();
 
         if (!memory.put(obj, memory.count()))
             return false;
 
         // Emit a placeholder pointer. We will steal the data and neuter the
-        // transferable later.
-        if (!out.writePair(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_UNFILLED) ||
-            !out.writePtr(nullptr) ||
-            !out.write(0))
-        {
+        // transferable later, in the case of ArrayBufferObject.
+        if (!out.writePair(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_UNFILLED))
             return false;
-        }
+        if (!out.writePtr(nullptr)) // Pointer to ArrayBuffer contents or to SharedArrayRawBuffer.
+            return false;
+        if (!out.write(0)) // |userdata|, intended to be passed to callbacks.
+            return false;
     }
 
     return true;
 }
 
 bool
 JSStructuredCloneWriter::transferOwnership()
 {
@@ -985,30 +994,44 @@ JSStructuredCloneWriter::transferOwnersh
     // grabbing out pointers from the transferables and stuffing them into the
     // transfer map.
     uint64_t *point = out.rawBuffer();
     JS_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_HEADER);
     point++;
     JS_ASSERT(LittleEndian::readUint64(point) == transferableObjects.length());
     point++;
 
-    for (JS::AutoObjectVector::Range tr = transferableObjects.all();
-         !tr.empty();
-         tr.popFront())
-    {
+    for (JS::AutoObjectVector::Range tr = transferableObjects.all(); !tr.empty(); tr.popFront()) {
         RootedObject obj(context(), tr.front());
-        void *content;
-        uint8_t *data;
-        if (!JS_StealArrayBufferContents(context(), obj, &content, &data))
-            return false; // Destructor will clean up the already-transferred data
 
         MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point) >> 32) == SCTAG_TRANSFER_MAP_ENTRY);
-        LittleEndian::writeUint64(point++, PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_ALLOC_DATA));
-        LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(content));
-        LittleEndian::writeUint64(point++, 0);
+        MOZ_ASSERT(uint32_t(LittleEndian::readUint64(point)) == SCTAG_TM_UNFILLED);
+
+        if (obj->is<ArrayBufferObject>()) {
+            void *content;
+            uint8_t *data;
+            if (!JS_StealArrayBufferContents(context(), obj, &content, &data))
+                return false; // Destructor will clean up the already-transferred data
+
+            uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_ALLOC_DATA);
+            LittleEndian::writeUint64(point++, entryTag);
+            LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(content));
+            LittleEndian::writeUint64(point++, 0);
+        } else {
+            SharedArrayRawBuffer *rawbuf = obj->as<SharedArrayBufferObject>().rawBufferObject();
+
+            // Avoids a race condition where the parent thread frees the buffer
+            // before the child has accepted the transferable.
+            rawbuf->addReference();
+
+            uint64_t entryTag = PairToUInt64(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_SHARED_BUFFER);
+            LittleEndian::writeUint64(point++, entryTag);
+            LittleEndian::writeUint64(point++, reinterpret_cast<uint64_t>(rawbuf));
+            LittleEndian::writeUint64(point++, 0);
+        }
     }
 
     JS_ASSERT(point <= out.rawBuffer() + out.count());
     JS_ASSERT_IF(point < out.rawBuffer() + out.count(),
                  uint32_t(LittleEndian::readUint64(point) >> 32) != SCTAG_TRANSFER_MAP_ENTRY);
 
     return true;
 }
@@ -1478,27 +1501,33 @@ JSStructuredCloneReader::readTransferMap
         return false;
 
     for (uint64_t i = 0; i < numTransferables; i++) {
         uint64_t *pos = in.tell();
 
         if (!in.readPair(&tag, &data))
             return false;
         JS_ASSERT(tag == SCTAG_TRANSFER_MAP_ENTRY);
-        JS_ASSERT(data == SCTAG_TM_ALLOC_DATA);
+        JS_ASSERT(data == SCTAG_TM_ALLOC_DATA || data == SCTAG_TM_SHARED_BUFFER);
 
         void *content;
         if (!in.readPtr(&content))
             return false;
 
         uint64_t userdata;
         if (!in.read(&userdata))
             return false;
 
-        RootedObject obj(context(), JS_NewArrayBufferWithContents(context(), content));
+        RootedObject obj(context());
+
+        if (data == SCTAG_TM_ALLOC_DATA)
+            obj = JS_NewArrayBufferWithContents(context(), content);
+        else if (data == SCTAG_TM_SHARED_BUFFER)
+            obj = SharedArrayBufferObject::New(context(), (SharedArrayRawBuffer *)content);
+
         if (!obj)
             return false;
 
         // Rewind to the SCTAG_TRANSFER_MAP_ENTRY and mark this entry as unowned by
         // the input buffer.
         uint64_t *next = in.tell();
         in.seek(pos);
         MOZ_ALWAYS_TRUE(in.replacePair(SCTAG_TRANSFER_MAP_ENTRY, SCTAG_TM_UNOWNED));