Bug 1301094 - part 2 - RemoteInputStream must be created using a ControlWorkerRunnable, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 12 Sep 2016 12:18:22 +0200
changeset 357023 03c0969b435fca2bda1e4c2bdc70ef10625c1fe0
parent 357022 19a758a7a5ae63a1842563947216b7eae176e22f
child 357024 cdc7be84b21a4092e04f1665e5eba5b31e54b63f
push id1324
push usermtabara@mozilla.com
push dateMon, 16 Jan 2017 13:07:44 +0000
treeherdermozilla-release@a01c49833940 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1301094
milestone51.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 1301094 - part 2 - RemoteInputStream must be created using a ControlWorkerRunnable, r=smaug
dom/ipc/Blob.cpp
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -40,16 +40,17 @@
 #include "nsISeekableStream.h"
 #include "nsIUUIDGenerator.h"
 #include "nsNetCID.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringStream.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
 
 #ifdef DEBUG
 #include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread().
 #endif
 
 #ifdef OS_POSIX
 #include "chrome/common/file_descriptor_set_posix.h"
 #endif
@@ -1695,20 +1696,27 @@ NS_IMPL_ISUPPORTS_INHERITED0(BlobParent:
  ******************************************************************************/
 
 class BlobChild::RemoteBlobImpl
   : public BlobImplBase
   , public nsIRemoteBlob
 {
 protected:
   class CreateStreamHelper;
+  class WorkerHolder;
 
   BlobChild* mActor;
   nsCOMPtr<nsIEventTarget> mActorTarget;
 
+  // These member variables are protected by mutex and it's set to null when the
+  // worker goes away.
+  WorkerPrivate* mWorkerPrivate;
+  nsAutoPtr<WorkerHolder> mWorkerHolder;
+  Mutex mMutex;
+
   // We use this pointer to keep a live a blobImpl coming from a different
   // process until this one is fully created. We set it to null when
   // SendCreatedFromKnownBlob() is received. This is used only with KnownBlob
   // params in the CTOR of a IPC BlobImpl.
   RefPtr<BlobImpl> mDifferentProcessBlobImpl;
 
   RefPtr<BlobImpl> mSameProcessBlobImpl;
 
@@ -1818,16 +1826,26 @@ public:
 
   void
   NullifyDifferentProcessBlobImpl()
   {
     MOZ_ASSERT(mDifferentProcessBlobImpl);
     mDifferentProcessBlobImpl = nullptr;
   }
 
+  // Used only by CreateStreamHelper, it dispatches a runnable to the target
+  // thread. This thread can be the main-thread, the background thread or a
+  // worker. If the thread is a worker, the aRunnable is wrapper into a
+  // ControlRunnable in order to avoid to be blocked into a sync event loop.
+  nsresult
+  DispatchToTarget(nsIRunnable* aRunnable);
+
+  void
+  WorkerHasNotified();
+
 protected:
   // For SliceImpl.
   RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength);
 
   ~RemoteBlobImpl()
   {
     MOZ_ASSERT_IF(mActorTarget,
                   EventTargetIsOnCurrentThread(mActorTarget));
@@ -1835,16 +1853,36 @@ protected:
 
   void
   CommonInit(BlobChild* aActor);
 
   void
   Destroy();
 };
 
+class BlobChild::RemoteBlobImpl::WorkerHolder final
+  : public workers::WorkerHolder
+{
+  // Raw pointer because this class is kept alive by the mRemoteBlobImpl.
+  RemoteBlobImpl* mRemoteBlobImpl;
+
+public:
+  explicit WorkerHolder(RemoteBlobImpl* aRemoteBlobImpl)
+    : mRemoteBlobImpl(aRemoteBlobImpl)
+  {
+    MOZ_ASSERT(aRemoteBlobImpl);
+  }
+
+  bool Notify(Status aStatus) override
+  {
+    mRemoteBlobImpl->WorkerHasNotified();
+    return true;
+  }
+};
+
 class BlobChild::RemoteBlobImpl::CreateStreamHelper final
   : public CancelableRunnable
 {
   Monitor mMonitor;
   RefPtr<RemoteBlobImpl> mRemoteBlobImpl;
   RefPtr<RemoteInputStream> mInputStream;
   const uint64_t mStart;
   const uint64_t mLength;
@@ -2048,16 +2086,18 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild
                                const nsAString& aName,
                                const nsAString& aContentType,
                                const nsAString& aPath,
                                uint64_t aLength,
                                int64_t aModDate,
                                BlobImplIsDirectory aIsDirectory,
                                bool aIsSameProcessBlob)
   : BlobImplBase(aName, aContentType, aLength, aModDate)
+  , mWorkerPrivate(nullptr)
+  , mMutex("BlobChild::RemoteBlobImpl::mMutex")
   , mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory)
 {
   SetPath(aPath);
 
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
@@ -2070,41 +2110,47 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
                                BlobImpl* aRemoteBlobImpl,
                                const nsAString& aContentType,
                                uint64_t aLength,
                                bool aIsSameProcessBlob)
   : BlobImplBase(aContentType, aLength)
+  , mWorkerPrivate(nullptr)
+  , mMutex("BlobChild::RemoteBlobImpl::mMutex")
   , mIsSlice(false), mIsDirectory(false)
 {
   if (aIsSameProcessBlob) {
     MOZ_ASSERT(aRemoteBlobImpl);
     mSameProcessBlobImpl = aRemoteBlobImpl;
     MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
   } else {
     mDifferentProcessBlobImpl = aRemoteBlobImpl;
   }
 
   CommonInit(aActor);
 }
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
   : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
+  , mWorkerPrivate(nullptr)
+  , mMutex("BlobChild::RemoteBlobImpl::mMutex")
   , mIsSlice(false), mIsDirectory(false)
 {
   CommonInit(aActor);
 }
 
 BlobChild::
 RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength)
   : BlobImplBase(aContentType, aLength)
   , mActor(nullptr)
+  , mWorkerPrivate(nullptr)
+  , mMutex("BlobChild::RemoteBlobImpl::mMutex")
   , mIsSlice(true)
   , mIsDirectory(false)
 {
   mImmutable = true;
 }
 
 void
 BlobChild::
@@ -2112,16 +2158,32 @@ RemoteBlobImpl::CommonInit(BlobChild* aA
 {
   MOZ_ASSERT(aActor);
   aActor->AssertIsOnOwningThread();
   MOZ_ASSERT(!mIsSlice);
 
   mActor = aActor;
   mActorTarget = aActor->EventTarget();
 
+  if (!NS_IsMainThread()) {
+    mWorkerPrivate = GetCurrentThreadWorkerPrivate();
+    // We must comunicate via IPC in the owning thread, so, if this BlobImpl has
+    // been created on a Workerr and then it's sent to a different thread (for
+    // instance the main-thread), we still need to keep alive that Worker.
+    if (mWorkerPrivate) {
+      mWorkerHolder = new RemoteBlobImpl::WorkerHolder(this);
+      if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Closing))) {
+        // We don't care too much if the worker is already going away because no
+        // sync-event-loop can be created at this point.
+        mWorkerPrivate = nullptr;
+        mWorkerHolder = nullptr;
+      }
+    }
+  }
+
   mImmutable = true;
 }
 
 void
 BlobChild::
 RemoteBlobImpl::NoteDyingActor()
 {
   MOZ_ASSERT(mActor);
@@ -2155,16 +2217,23 @@ BlobChild::
 RemoteBlobImpl::Destroy()
 {
   if (EventTargetIsOnCurrentThread(mActorTarget)) {
     if (mActor) {
       mActor->AssertIsOnOwningThread();
       mActor->NoteDyingRemoteBlobImpl();
     }
 
+    if (mWorkerHolder) {
+      // We are in the worker thread.
+      MutexAutoLock lock(mMutex);
+      mWorkerPrivate = nullptr;
+      mWorkerHolder = nullptr;
+    }
+
     delete this;
     return;
   }
 
   nsCOMPtr<nsIRunnable> destroyRunnable =
     NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
 
   if (mActorTarget) {
@@ -2339,16 +2408,81 @@ RemoteBlobImpl::GetBlobChild()
 
 BlobParent*
 BlobChild::
 RemoteBlobImpl::GetBlobParent()
 {
   return nullptr;
 }
 
+class RemoteBlobControlRunnable : public WorkerControlRunnable
+{
+  nsCOMPtr<nsIRunnable> mRunnable;
+
+public:
+  RemoteBlobControlRunnable(WorkerPrivate* aWorkerPrivate,
+                            nsIRunnable* aRunnable)
+    : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+    , mRunnable(aRunnable)
+  {
+    MOZ_ASSERT(aRunnable);
+  }
+
+  bool
+  WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+  {
+    mRunnable->Run();
+    return true;
+  }
+};
+
+nsresult
+BlobChild::
+RemoteBlobImpl::DispatchToTarget(nsIRunnable* aRunnable)
+{
+  MOZ_ASSERT(aRunnable);
+
+  // We have to protected mWorkerPrivate because this method can be called by
+  // any thread (sort of).
+  MutexAutoLock lock(mMutex);
+
+  if (mWorkerPrivate) {
+    MOZ_ASSERT(mWorkerHolder);
+
+    RefPtr<RemoteBlobControlRunnable> controlRunnable =
+      new RemoteBlobControlRunnable(mWorkerPrivate, aRunnable);
+    if (!controlRunnable->Dispatch()) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIEventTarget> target = BaseRemoteBlobImpl()->GetActorEventTarget();
+  if (!target) {
+    target = do_GetMainThread();
+  }
+
+  MOZ_ASSERT(target);
+
+  return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+}
+
+void
+BlobChild::
+RemoteBlobImpl::WorkerHasNotified()
+{
+  MutexAutoLock lock(mMutex);
+
+  mWorkerHolder->ReleaseWorker();
+
+  mWorkerHolder = nullptr;
+  mWorkerPrivate = nullptr;
+}
+
 /*******************************************************************************
  * BlobChild::RemoteBlobImpl::CreateStreamHelper
  ******************************************************************************/
 
 BlobChild::RemoteBlobImpl::
 CreateStreamHelper::CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl)
   : mMonitor("BlobChild::RemoteBlobImpl::CreateStreamHelper::mMonitor")
   , mRemoteBlobImpl(aRemoteBlobImpl)
@@ -2376,24 +2510,17 @@ CreateStreamHelper::GetStream(nsIInputSt
 
   RefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
     mRemoteBlobImpl->BaseRemoteBlobImpl();
   MOZ_ASSERT(baseRemoteBlobImpl);
 
   if (EventTargetIsOnCurrentThread(baseRemoteBlobImpl->GetActorEventTarget())) {
     RunInternal(baseRemoteBlobImpl, false);
   } else {
-    nsCOMPtr<nsIEventTarget> target = baseRemoteBlobImpl->GetActorEventTarget();
-    if (!target) {
-      target = do_GetMainThread();
-    }
-
-    MOZ_ASSERT(target);
-
-    nsresult rv = target->Dispatch(this, NS_DISPATCH_NORMAL);
+    nsresult rv = baseRemoteBlobImpl->DispatchToTarget(this);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
     DebugOnly<bool> warned = false;
 
     {
       MonitorAutoLock lock(mMonitor);