Bug 1198814 - Use StructuredCloneHelper in PromiseWorkerProxy, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Sat, 05 Sep 2015 12:22:13 +0300
changeset 293682 2c406b4df5f1b51cfb377f96f3e4b8b5e4819ed6
parent 293681 45f93f97b68d24618f8ae73d7e9d919da76f00db
child 293683 f86b79cedb118eb4f99e1901fc062eae3e90c3ee
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1198814
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1198814 - Use StructuredCloneHelper in PromiseWorkerProxy, r=smaug
dom/promise/Promise.cpp
dom/promise/PromiseWorkerProxy.h
dom/workers/Navigator.cpp
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -1482,24 +1482,20 @@ Promise::GetDependentPromises(nsTArray<n
 }
 
 // A WorkerRunnable to resolve/reject the Promise on the worker thread.
 // Calling thread MUST hold PromiseWorkerProxy's mutex before creating this.
 class PromiseWorkerProxyRunnable : public workers::WorkerRunnable
 {
 public:
   PromiseWorkerProxyRunnable(PromiseWorkerProxy* aPromiseWorkerProxy,
-                             const JSStructuredCloneCallbacks* aCallbacks,
-                             JSAutoStructuredCloneBuffer&& aBuffer,
                              PromiseWorkerProxy::RunCallbackFunc aFunc)
     : WorkerRunnable(aPromiseWorkerProxy->GetWorkerPrivate(),
                      WorkerThreadUnchangedBusyCount)
     , mPromiseWorkerProxy(aPromiseWorkerProxy)
-    , mCallbacks(aCallbacks)
-    , mBuffer(Move(aBuffer))
     , mFunc(aFunc)
   {
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mPromiseWorkerProxy);
   }
 
   virtual bool
   WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate)
@@ -1508,49 +1504,48 @@ public:
     aWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_ASSERT(aWorkerPrivate == mWorkerPrivate);
 
     MOZ_ASSERT(mPromiseWorkerProxy);
     nsRefPtr<Promise> workerPromise = mPromiseWorkerProxy->WorkerPromise();
 
     // Here we convert the buffer to a JS::Value.
     JS::Rooted<JS::Value> value(aCx);
-    if (!mBuffer.read(aCx, &value, mCallbacks, mPromiseWorkerProxy)) {
+    if (!mPromiseWorkerProxy->Read(aCx, &value)) {
       JS_ClearPendingException(aCx);
       return false;
     }
 
     (workerPromise->*mFunc)(aCx, value);
 
     // Release the Promise because it has been resolved/rejected for sure.
     mPromiseWorkerProxy->CleanUp(aCx);
     return true;
   }
 
 protected:
   ~PromiseWorkerProxyRunnable() {}
 
 private:
   nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
-  const JSStructuredCloneCallbacks* mCallbacks;
-  JSAutoStructuredCloneBuffer mBuffer;
 
   // Function pointer for calling Promise::{ResolveInternal,RejectInternal}.
   PromiseWorkerProxy::RunCallbackFunc mFunc;
 };
 
 /* static */
 already_AddRefed<PromiseWorkerProxy>
 PromiseWorkerProxy::Create(workers::WorkerPrivate* aWorkerPrivate,
                            Promise* aWorkerPromise,
-                           const JSStructuredCloneCallbacks* aCb)
+                           const PromiseWorkerProxyStructuredCloneCallbacks* aCb)
 {
   MOZ_ASSERT(aWorkerPrivate);
   aWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(aWorkerPromise);
+  MOZ_ASSERT_IF(aCb, !!aCb->Write && !!aCb->Read);
 
   nsRefPtr<PromiseWorkerProxy> proxy =
     new PromiseWorkerProxy(aWorkerPrivate, aWorkerPromise, aCb);
 
   // We do this to make sure the worker thread won't shut down before the
   // promise is resolved/rejected on the worker thread.
   if (!proxy->AddRefObject()) {
     // Probably the worker is terminating. We cannot complete the operation
@@ -1561,17 +1556,17 @@ PromiseWorkerProxy::Create(workers::Work
 
   return proxy.forget();
 }
 
 NS_IMPL_ISUPPORTS0(PromiseWorkerProxy)
 
 PromiseWorkerProxy::PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
                                        Promise* aWorkerPromise,
-                                       const JSStructuredCloneCallbacks* aCallbacks)
+                                       const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks)
   : mWorkerPrivate(aWorkerPrivate)
   , mWorkerPromise(aWorkerPromise)
   , mCleanedUp(false)
   , mCallbacks(aCallbacks)
   , mCleanUpLock("cleanUpLock")
   , mFeatureAdded(false)
 {
 }
@@ -1592,16 +1587,19 @@ PromiseWorkerProxy::CleanProperties()
   MOZ_ASSERT(worker);
   worker->AssertIsOnWorkerThread();
 #endif
   // Ok to do this unprotected from Create().
   // CleanUp() holds the lock before calling this.
   mCleanedUp = true;
   mWorkerPromise = nullptr;
   mWorkerPrivate = nullptr;
+
+  // Shutdown the StructuredCloneHelperInternal class.
+  Shutdown();
 }
 
 bool
 PromiseWorkerProxy::AddRefObject()
 {
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate->AssertIsOnWorkerThread();
   MOZ_ASSERT(!mFeatureAdded);
@@ -1663,30 +1661,24 @@ PromiseWorkerProxy::RunCallback(JSContex
   MOZ_ASSERT(NS_IsMainThread());
 
   MutexAutoLock lock(Lock());
   // If the worker thread's been cancelled we don't need to resolve the Promise.
   if (CleanedUp()) {
     return;
   }
 
-  // The |aValue| is written into the buffer. Note that we also pass |this|
-  // into the structured-clone write in order to set its |mSupportsArray| to
-  // keep objects alive until the structured-clone read/write is done.
-  JSAutoStructuredCloneBuffer buffer;
-  if (!buffer.write(aCx, aValue, mCallbacks, this)) {
+  // The |aValue| is written into the StructuredCloneHelperInternal.
+  if (!Write(aCx, aValue)) {
     JS_ClearPendingException(aCx);
-    MOZ_ASSERT(false, "cannot write the JSAutoStructuredCloneBuffer!");
+    MOZ_ASSERT(false, "cannot serialize the value with the StructuredCloneAlgorithm!");
   }
 
   nsRefPtr<PromiseWorkerProxyRunnable> runnable =
-    new PromiseWorkerProxyRunnable(this,
-                                   mCallbacks,
-                                   Move(buffer),
-                                   aFunc);
+    new PromiseWorkerProxyRunnable(this, aFunc);
 
   runnable->Dispatch(aCx);
 }
 
 void
 PromiseWorkerProxy::ResolvedCallback(JSContext* aCx,
                                      JS::Handle<JS::Value> aValue)
 {
@@ -1733,16 +1725,41 @@ PromiseWorkerProxy::CleanUp(JSContext* a
     MOZ_ASSERT(mFeatureAdded);
     mWorkerPrivate->RemoveFeature(mWorkerPrivate->GetJSContext(), this);
     mFeatureAdded = false;
     CleanProperties();
   }
   Release();
 }
 
+JSObject*
+PromiseWorkerProxy::ReadCallback(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 uint32_t aTag,
+                                 uint32_t aIndex)
+{
+  if (NS_WARN_IF(!mCallbacks)) {
+    return nullptr;
+  }
+
+  return mCallbacks->Read(aCx, aReader, this, aTag, aIndex);
+}
+
+bool
+PromiseWorkerProxy::WriteCallback(JSContext* aCx,
+                                  JSStructuredCloneWriter* aWriter,
+                                  JS::Handle<JSObject*> aObj)
+{
+  if (NS_WARN_IF(!mCallbacks)) {
+    return false;
+  }
+
+  return mCallbacks->Write(aCx, aWriter, this, aObj);
+}
+
 // Specializations of MaybeRejectBrokenly we actually support.
 template<>
 void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMError>& aArg) {
   MaybeSomething(aArg, &Promise::MaybeReject);
 }
 template<>
 void Promise::MaybeRejectBrokenly(const nsRefPtr<DOMException>& aArg) {
   MaybeSomething(aArg, &Promise::MaybeReject);
--- a/dom/promise/PromiseWorkerProxy.h
+++ b/dom/promise/PromiseWorkerProxy.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_PromiseWorkerProxy_h
 #define mozilla_dom_PromiseWorkerProxy_h
 
 // Required for Promise::PromiseTaskSync.
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/workers/bindings/WorkerFeature.h"
 #include "nsProxyRelease.h"
 
 #include "WorkerRunnable.h"
 
 namespace mozilla {
 namespace dom {
 
@@ -105,28 +106,45 @@ class WorkerPrivate;
 //          mProxy->CleanUp(aCx);
 //        }
 //
 // Note: If a PromiseWorkerProxy is not cleaned up by a WorkerRunnable - this
 // can happen if the main thread Promise is never fulfilled - it will
 // stay alive till the worker reaches a Canceling state, even if all external
 // references to it are dropped.
 
-class PromiseWorkerProxy : public PromiseNativeHandler,
-                           public workers::WorkerFeature
+class PromiseWorkerProxy : public PromiseNativeHandler
+                         , public workers::WorkerFeature
+                         , public StructuredCloneHelperInternal
 {
   friend class PromiseWorkerProxyRunnable;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
 public:
+  typedef JSObject* (*ReadCallbackOp)(JSContext* aCx,
+                                      JSStructuredCloneReader* aReader,
+                                      const PromiseWorkerProxy* aProxy,
+                                      uint32_t aTag,
+                                      uint32_t aData);
+  typedef bool (*WriteCallbackOp)(JSContext* aCx,
+                                  JSStructuredCloneWriter* aWorker,
+                                  PromiseWorkerProxy* aProxy,
+                                  JS::HandleObject aObj);
+
+  struct PromiseWorkerProxyStructuredCloneCallbacks
+  {
+    ReadCallbackOp Read;
+    WriteCallbackOp Write;
+  };
+
   static already_AddRefed<PromiseWorkerProxy>
   Create(workers::WorkerPrivate* aWorkerPrivate,
          Promise* aWorkerPromise,
-         const JSStructuredCloneCallbacks* aCallbacks = nullptr);
+         const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
 
   // Main thread callers must hold Lock() and check CleanUp() before calling this.
   // Worker thread callers, this will assert that the proxy has not been cleaned
   // up.
   workers::WorkerPrivate* GetWorkerPrivate() const;
 
   // This should only be used within WorkerRunnable::WorkerRun() running on the
   // worker thread! Do not call this after calling CleanUp().
@@ -146,29 +164,40 @@ public:
   }
 
   bool CleanedUp() const
   {
     mCleanUpLock.AssertCurrentThreadOwns();
     return mCleanedUp;
   }
 
+  // StructuredCloneHelperInternal
+
+  JSObject* ReadCallback(JSContext* aCx,
+                         JSStructuredCloneReader* aReader,
+                         uint32_t aTag,
+                         uint32_t aIndex) override;
+
+  bool WriteCallback(JSContext* aCx,
+                     JSStructuredCloneWriter* aWriter,
+                     JS::Handle<JSObject*> aObj) override;
+
 protected:
   virtual void ResolvedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual void RejectedCallback(JSContext* aCx,
                                 JS::Handle<JS::Value> aValue) override;
 
   virtual bool Notify(JSContext* aCx, workers::Status aStatus) override;
 
 private:
   PromiseWorkerProxy(workers::WorkerPrivate* aWorkerPrivate,
                      Promise* aWorkerPromise,
-                     const JSStructuredCloneCallbacks* aCallbacks = nullptr);
+                     const PromiseWorkerProxyStructuredCloneCallbacks* aCallbacks = nullptr);
 
   virtual ~PromiseWorkerProxy();
 
   bool AddRefObject();
 
   // If not called from Create(), be sure to hold Lock().
   void CleanProperties();
 
@@ -186,17 +215,17 @@ private:
   // Worker thread only.
   nsRefPtr<Promise> mWorkerPromise;
 
   // Modified on the worker thread.
   // It is ok to *read* this without a lock on the worker.
   // Main thread must always acquire a lock.
   bool mCleanedUp; // To specify if the cleanUp() has been done.
 
-  const JSStructuredCloneCallbacks* mCallbacks;
+  const PromiseWorkerProxyStructuredCloneCallbacks* mCallbacks;
 
   // Aimed to keep objects alive when doing the structured-clone read/write,
   // which can be added by calling StoreISupports() on the main thread.
   nsTArray<nsMainThreadPtrHandle<nsISupports>> mSupportsArray;
 
   // Ensure the worker and the main thread won't race to access |mCleanedUp|.
   Mutex mCleanUpLock;
 
--- a/dom/workers/Navigator.cpp
+++ b/dom/workers/Navigator.cpp
@@ -89,21 +89,21 @@ public:
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
   }
 };
 
 #define WORKER_DATA_STORES_TAG JS_SCTAG_USER_MIN
 
 static JSObject*
-GetDataStoresStructuredCloneCallbacksRead(JSContext* aCx,
-                                          JSStructuredCloneReader* aReader,
-                                          uint32_t aTag,
-                                          uint32_t aData,
-                                          void* aClosure)
+GetDataStoresProxyCloneCallbacksRead(JSContext* aCx,
+                                     JSStructuredCloneReader* aReader,
+                                     const PromiseWorkerProxy* aProxy,
+                                     uint32_t aTag,
+                                     uint32_t aData)
 {
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
   MOZ_ASSERT(workerPrivate);
   workerPrivate->AssertIsOnWorkerThread();
 
   if (aTag != WORKER_DATA_STORES_TAG) {
     MOZ_ASSERT(false, "aTag must be WORKER_DATA_STORES_TAG!");
     return nullptr;
@@ -150,60 +150,57 @@ GetDataStoresStructuredCloneCallbacksRea
       }
     }
   }
 
   return workerStoreObj;
 }
 
 static bool
-GetDataStoresStructuredCloneCallbacksWrite(JSContext* aCx,
-                                           JSStructuredCloneWriter* aWriter,
-                                           JS::Handle<JSObject*> aObj,
-                                           void* aClosure)
+GetDataStoresProxyCloneCallbacksWrite(JSContext* aCx,
+                                      JSStructuredCloneWriter* aWriter,
+                                      PromiseWorkerProxy* aProxy,
+                                      JS::Handle<JSObject*> aObj)
 {
   AssertIsOnMainThread();
 
-  PromiseWorkerProxy* proxy = static_cast<PromiseWorkerProxy*>(aClosure);
-  NS_ASSERTION(proxy, "must have proxy!");
-
   if (!JS_WriteUint32Pair(aWriter, WORKER_DATA_STORES_TAG, 0)) {
     MOZ_ASSERT(false, "cannot write pair for WORKER_DATA_STORES_TAG!");
     return false;
   }
 
   JS::Rooted<JSObject*> storeObj(aCx, aObj);
 
   DataStore* store = nullptr;
   nsresult rv = UNWRAP_OBJECT(DataStore, storeObj, store);
   if (NS_FAILED(rv)) {
     MOZ_ASSERT(false, "cannot unwrap the DataStore object!");
     return false;
   }
 
   // We keep the data store alive here.
-  proxy->StoreISupports(store);
+  aProxy->StoreISupports(store);
 
   // Construct the nsMainThreadPtrHolder pointing to the data store.
   nsMainThreadPtrHolder<DataStore>* dataStoreholder =
     new nsMainThreadPtrHolder<DataStore>(store);
 
   // And write the dataStoreholder into the buffer.
   if (!JS_WriteBytes(aWriter, &dataStoreholder, sizeof(dataStoreholder))) {
     MOZ_ASSERT(false, "cannot write bytes for dataStoreholder!");
     return false;
   }
 
   return true;
 }
 
-static const JSStructuredCloneCallbacks kGetDataStoresStructuredCloneCallbacks = {
-  GetDataStoresStructuredCloneCallbacksRead,
-  GetDataStoresStructuredCloneCallbacksWrite,
-  nullptr
+static const PromiseWorkerProxy::PromiseWorkerProxyStructuredCloneCallbacks
+kGetDataStoresCloneCallbacks= {
+  GetDataStoresProxyCloneCallbacksRead,
+  GetDataStoresProxyCloneCallbacksWrite
 };
 
 // A WorkerMainThreadRunnable to run WorkerNavigator::GetDataStores(...) on the
 // main thread.
 class NavigatorGetDataStoresRunnable final : public WorkerMainThreadRunnable
 {
   nsRefPtr<PromiseWorkerProxy> mPromiseWorkerProxy;
   const nsString mName;
@@ -223,17 +220,17 @@ public:
   {
     MOZ_ASSERT(aWorkerPrivate);
     aWorkerPrivate->AssertIsOnWorkerThread();
 
     // this might return null if the worker has started the close handler.
     mPromiseWorkerProxy =
       PromiseWorkerProxy::Create(aWorkerPrivate,
                                  aWorkerPromise,
-                                 &kGetDataStoresStructuredCloneCallbacks);
+                                 &kGetDataStoresCloneCallbacks);
   }
 
   bool Dispatch(JSContext* aCx)
   {
     if (mPromiseWorkerProxy) {
       return WorkerMainThreadRunnable::Dispatch(aCx);
     }