Bug 1301094 - Part 2: RemoteInputStream must be created using a ControlWorkerRunnable. r=smaug, a=ritu
authorAndrea Marchesini <amarchesini@mozilla.com>
Mon, 12 Sep 2016 12:18:22 +0200
changeset 350267 a3d30b90a3fb710034d459d8e8b101f77cb1e7b5
parent 350266 3c8cb854bf01e69b0384f96e1edd4141bead5f7a
child 350268 0a758b4f6289f29b5bda37c4e52873242e9cf669
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug, ritu
bugs1301094
milestone50.0a2
Bug 1301094 - Part 2: RemoteInputStream must be created using a ControlWorkerRunnable. r=smaug, a=ritu
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);