Bug 1437640 - Implement nsIThreadRetargetableStreamListener in MutableBlobStreamListener, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Wed, 14 Feb 2018 13:23:01 +0100
changeset 754893 100460d7a9aff798075903e56714d2021eb32641
parent 754892 8fdd2ef1e1c0c3f8843c80be05801213f00499ec
child 754894 8ee73ed79599d7061363d1ade45e5cd9aa1dafa2
child 754915 e79aa47fc6563b0997318314634c220c91d74929
child 754978 2964d904056f3bbfdadafd1731b1dbb88c44a8b3
child 755013 04b90b86ce92b4f2859e968e61fc7d1a3bcbd6ec
child 755101 212e01592aa9ca786bb613f24d7ade23fbde80f8
child 755594 c73b33465325fe7d247b7512790d219b06ff9ebd
child 755932 b5d3eee375459da81f9b21745dde42bd3ec54896
push id99032
push userbmo:rail@mozilla.com
push dateWed, 14 Feb 2018 14:28:48 +0000
reviewerssmaug
bugs1437640
milestone60.0a1
Bug 1437640 - Implement nsIThreadRetargetableStreamListener in MutableBlobStreamListener, r=smaug
dom/fetch/Fetch.cpp
dom/fetch/FetchConsumer.cpp
dom/file/MutableBlobStorage.cpp
dom/file/MutableBlobStorage.h
dom/file/MutableBlobStreamListener.cpp
dom/file/MutableBlobStreamListener.h
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -6,17 +6,16 @@
 
 #include "Fetch.h"
 #include "FetchConsumer.h"
 #include "FetchStream.h"
 
 #include "nsIDocument.h"
 #include "nsIGlobalObject.h"
 #include "nsIStreamLoader.h"
-#include "nsIThreadRetargetableRequest.h"
 
 #include "nsCharSeparatedTokenizer.h"
 #include "nsDOMString.h"
 #include "nsJSUtils.h"
 #include "nsNetUtil.h"
 #include "nsReadableUtils.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
--- a/dom/fetch/FetchConsumer.cpp
+++ b/dom/fetch/FetchConsumer.cpp
@@ -8,16 +8,17 @@
 #include "FetchConsumer.h"
 
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/dom/WorkerScope.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "nsIInputStreamPump.h"
+#include "nsIThreadRetargetableRequest.h"
 #include "nsProxyRelease.h"
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 
 template <class Derived>
@@ -579,19 +580,17 @@ FetchBodyConsumer<Derived>::BeginConsume
   // stays alive for the lifetime of the FetchConsumer.
   mConsumeBodyPump = pump;
 
   // It is ok for retargeting to fail and reads to happen on the main thread.
   autoReject.DontFail();
 
   // Try to retarget, otherwise fall back to main thread.
   nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(pump);
-  nsCOMPtr<nsIThreadRetargetableStreamListener> rl =
-    do_QueryInterface(listener);
-  if (rr && rl) {
+  if (rr) {
     nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
     rv = rr->RetargetDeliveryTo(sts);
     if (NS_FAILED(rv)) {
       NS_WARNING("Retargeting failed");
     }
   }
 }
 
--- a/dom/file/MutableBlobStorage.cpp
+++ b/dom/file/MutableBlobStorage.cpp
@@ -103,17 +103,16 @@ private:
 // the temporary file, if its File Descriptor has not been already closed.
 class WriteRunnable final : public Runnable
 {
 public:
   static WriteRunnable*
   CopyBuffer(MutableBlobStorage* aBlobStorage,
              const void* aData, uint32_t aLength)
   {
-    MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aBlobStorage);
     MOZ_ASSERT(aData);
 
     // We have to take a copy of this buffer.
     void* data = malloc(aLength);
     if (!data) {
       return nullptr;
     }
@@ -157,17 +156,16 @@ public:
 
 private:
   WriteRunnable(MutableBlobStorage* aBlobStorage, void* aData, uint32_t aLength)
     : Runnable("dom::WriteRunnable")
     , mBlobStorage(aBlobStorage)
     , mData(aData)
     , mLength(aLength)
   {
-    MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(mBlobStorage);
     MOZ_ASSERT(aData);
   }
 
   ~WriteRunnable()
   {
     free(mData);
   }
@@ -330,17 +328,18 @@ private:
   RefPtr<MutableBlobStorageCallback> mCallback;
 };
 
 } // anonymous namespace
 
 MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType,
                                        nsIEventTarget* aEventTarget,
                                        uint32_t aMaxMemory)
-  : mData(nullptr)
+  : mMutex("MutableBlobStorage::mMutex")
+  , mData(nullptr)
   , mDataLen(0)
   , mDataBufferLen(0)
   , mStorageState(aType == eOnlyInMemory ? eKeepInMemory : eInMemory)
   , mFD(nullptr)
   , mErrorResult(NS_OK)
   , mEventTarget(aEventTarget)
   , mMaxMemory(aMaxMemory)
 {
@@ -380,16 +379,18 @@ MutableBlobStorage::~MutableBlobStorage(
 void
 MutableBlobStorage::GetBlobWhenReady(nsISupports* aParent,
                                      const nsACString& aContentType,
                                      MutableBlobStorageCallback* aCallback)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(aCallback);
 
+  MutexAutoLock lock(mMutex);
+
   // GetBlob can be called just once.
   MOZ_ASSERT(mStorageState != eClosed);
   StorageState previousState = mStorageState;
   mStorageState = eClosed;
 
   if (previousState == eInTemporaryFile) {
     if (NS_FAILED(mErrorResult)) {
       MOZ_ASSERT(!mActor);
@@ -444,28 +445,30 @@ MutableBlobStorage::GetBlobWhenReady(nsI
   if (NS_WARN_IF(NS_FAILED(error))) {
     return;
   }
 }
 
 nsresult
 MutableBlobStorage::Append(const void* aData, uint32_t aLength)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  // This method can be called on any thread.
+
+  MutexAutoLock lock(mMutex);
   MOZ_ASSERT(mStorageState != eClosed);
   NS_ENSURE_ARG_POINTER(aData);
 
   if (!aLength) {
     return NS_OK;
   }
 
   // If eInMemory is the current Storage state, we could maybe migrate to
   // a temporary file.
-  if (mStorageState == eInMemory && ShouldBeTemporaryStorage(aLength) &&
-      !MaybeCreateTemporaryFile()) {
+  if (mStorageState == eInMemory && ShouldBeTemporaryStorage(lock, aLength) &&
+      !MaybeCreateTemporaryFile(lock)) {
     return NS_ERROR_FAILURE;
   }
 
   // If we are already in the temporaryFile mode, we have to dispatch a
   // runnable.
   if (mStorageState == eInTemporaryFile) {
     // If a previous operation failed, let's return that error now.
     if (NS_FAILED(mErrorResult)) {
@@ -486,28 +489,28 @@ MutableBlobStorage::Append(const void* a
     mDataLen += aLength;
     return NS_OK;
   }
 
   // By default, we store in memory.
 
   uint64_t offset = mDataLen;
 
-  if (!ExpandBufferSize(aLength)) {
+  if (!ExpandBufferSize(lock, aLength)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   memcpy((char*)mData + offset, aData, aLength);
   return NS_OK;
 }
 
 bool
-MutableBlobStorage::ExpandBufferSize(uint64_t aSize)
+MutableBlobStorage::ExpandBufferSize(const MutexAutoLock& aProofOfLock,
+                                     uint64_t aSize)
 {
-  MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mStorageState < eInTemporaryFile);
 
   if (mDataBufferLen >= mDataLen + aSize) {
     mDataLen += aSize;
     return true;
   }
 
   // Start at 1 or we'll loop forever.
@@ -528,60 +531,78 @@ MutableBlobStorage::ExpandBufferSize(uin
 
   mData = data;
   mDataBufferLen = bufferLen.value();
   mDataLen += aSize;
   return true;
 }
 
 bool
-MutableBlobStorage::ShouldBeTemporaryStorage(uint64_t aSize) const
+MutableBlobStorage::ShouldBeTemporaryStorage(const MutexAutoLock& aProofOfLock,
+                                             uint64_t aSize) const
 {
   MOZ_ASSERT(mStorageState == eInMemory);
 
   CheckedUint32 bufferSize = mDataLen;
   bufferSize += aSize;
 
   if (!bufferSize.isValid()) {
     return false;
   }
 
   return bufferSize.value() >= mMaxMemory;
 }
 
 bool
-MutableBlobStorage::MaybeCreateTemporaryFile()
+MutableBlobStorage::MaybeCreateTemporaryFile(const MutexAutoLock& aProofOfLock)
+{
+  mStorageState = eWaitingForTemporaryFile;
+
+  if (!NS_IsMainThread()) {
+    RefPtr<MutableBlobStorage> self = this;
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
+      "MutableBlobStorage::MaybeCreateTemporaryFile",
+      [self]() { self->MaybeCreateTemporaryFileOnMainThread(); });
+    EventTarget()->Dispatch(r.forget(), NS_DISPATCH_SYNC);
+    return !!mActor;
+  }
+
+  MaybeCreateTemporaryFileOnMainThread();
+  return !!mActor;
+}
+
+void
+MutableBlobStorage::MaybeCreateTemporaryFileOnMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread());
-
-  mStorageState = eWaitingForTemporaryFile;
+  MOZ_ASSERT(!mActor);
 
   mozilla::ipc::PBackgroundChild* actorChild =
     mozilla::ipc::BackgroundChild::GetOrCreateForCurrentThread();
   if (NS_WARN_IF(!actorChild)) {
-    return false;
+    return;
   }
 
   mActor = new TemporaryIPCBlobChild(this);
   actorChild->SendPTemporaryIPCBlobConstructor(mActor);
 
   // We need manually to increase the reference for this actor because the
   // IPC allocator method is not triggered. The Release() is called by IPDL
   // when the actor is deleted.
   mActor.get()->AddRef();
 
   // The actor will call us when the FileDescriptor is received.
-
-  return true;
 }
 
 void
 MutableBlobStorage::TemporaryFileCreated(PRFileDesc* aFD)
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  MutexAutoLock lock(mMutex);
   MOZ_ASSERT(mStorageState == eWaitingForTemporaryFile ||
              mStorageState == eClosed);
   MOZ_ASSERT_IF(mPendingCallback, mStorageState == eClosed);
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(aFD);
 
   // If the object has been already closed and we don't need to execute a
   // callback, we need just to close the file descriptor in the correct thread.
@@ -638,16 +659,18 @@ MutableBlobStorage::TemporaryFileCreated
   }
 }
 
 void
 MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback,
                                const nsACString& aContentType)
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  MutexAutoLock lock(mMutex);
   MOZ_ASSERT(mStorageState == eClosed);
   MOZ_ASSERT(mFD);
   MOZ_ASSERT(mActor);
   MOZ_ASSERT(aCallback);
 
   // Let's pass the FileDescriptor to the parent actor in order to keep the file
   // locked on windows.
   mActor->AskForBlob(aCallback, aContentType, mFD);
@@ -661,16 +684,18 @@ MutableBlobStorage::AskForBlob(Temporary
   mFD = nullptr;
   mActor = nullptr;
 }
 
 void
 MutableBlobStorage::ErrorPropagated(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  MutexAutoLock lock(mMutex);
   mErrorResult = aRv;
 
   if (mActor) {
     mActor->SendOperationFailed();
     mActor = nullptr;
   }
 }
 
@@ -690,33 +715,36 @@ MutableBlobStorage::DispatchToIOThread(a
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return NS_OK;
 }
 
 size_t
-MutableBlobStorage::SizeOfCurrentMemoryBuffer() const
+MutableBlobStorage::SizeOfCurrentMemoryBuffer()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  MutexAutoLock lock(mMutex);
   return mStorageState < eInTemporaryFile ? mDataLen : 0;
 }
 
 PRFileDesc*
-MutableBlobStorage::GetFD() const
+MutableBlobStorage::GetFD()
 {
   MOZ_ASSERT(!NS_IsMainThread());
+  MutexAutoLock lock(mMutex);
   return mFD;
 }
 
 void
 MutableBlobStorage::CloseFD()
 {
   MOZ_ASSERT(!NS_IsMainThread());
+  MutexAutoLock lock(mMutex);
   MOZ_ASSERT(mFD);
 
   PR_Close(mFD);
   mFD = nullptr;
 }
 
 } // dom namespace
 } // mozilla namespace
--- a/dom/file/MutableBlobStorage.h
+++ b/dom/file/MutableBlobStorage.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_MutableBlobStorage_h
 #define mozilla_dom_MutableBlobStorage_h
 
 #include "mozilla/RefPtr.h"
+#include "mozilla/Mutex.h"
 #include "prio.h"
 
 class nsIEventTarget;
 class nsIRunnable;
 
 namespace mozilla {
 
 class TaskQueue;
@@ -30,17 +31,18 @@ class MutableBlobStorageCallback
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
   virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage,
                                   Blob* aBlob,
                                   nsresult aRv) = 0;
 };
 
-// This class is main-thread only.
+// This class is must be created and used on main-thread, except for Append()
+// that can be called on any thread.
 class MutableBlobStorage final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage)
 
   enum MutableBlobStorageType
   {
     eOnlyInMemory,
@@ -69,35 +71,41 @@ public:
   nsIEventTarget* EventTarget()
   {
     MOZ_ASSERT(mEventTarget);
     return mEventTarget;
   }
 
   // Returns the heap size in bytes of our internal buffers.
   // Note that this intentionally ignores the data in the temp file.
-  size_t SizeOfCurrentMemoryBuffer() const;
+  size_t SizeOfCurrentMemoryBuffer();
 
-  PRFileDesc* GetFD() const;
+  PRFileDesc* GetFD();
 
   void CloseFD();
 
 private:
   ~MutableBlobStorage();
 
-  bool ExpandBufferSize(uint64_t aSize);
+  bool ExpandBufferSize(const MutexAutoLock& aProofOfLock,
+                        uint64_t aSize);
 
-  bool ShouldBeTemporaryStorage(uint64_t aSize) const;
+  bool ShouldBeTemporaryStorage(const MutexAutoLock& aProofOfLock,
+                                uint64_t aSize) const;
 
-  bool MaybeCreateTemporaryFile();
+  bool MaybeCreateTemporaryFile(const MutexAutoLock& aProofOfLock);
+  void MaybeCreateTemporaryFileOnMainThread();
 
   MOZ_MUST_USE nsresult
   DispatchToIOThread(already_AddRefed<nsIRunnable> aRunnable);
 
-  // All these variables are touched on the main thread only.
+  Mutex mMutex;
+
+  // All these variables are touched on the main thread only or in the
+  // retargeted thread when used by Append(). They are protected by mMutex.
 
   void* mData;
   uint64_t mDataLen;
   uint64_t mDataBufferLen;
 
   enum StorageState {
     eKeepInMemory,
     eInMemory,
--- a/dom/file/MutableBlobStreamListener.cpp
+++ b/dom/file/MutableBlobStreamListener.cpp
@@ -33,16 +33,17 @@ MutableBlobStreamListener::MutableBlobSt
 
 MutableBlobStreamListener::~MutableBlobStreamListener()
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 NS_IMPL_ISUPPORTS(MutableBlobStreamListener,
                   nsIStreamListener,
+                  nsIThreadRetargetableStreamListener,
                   nsIRequestObserver)
 
 NS_IMETHODIMP
 MutableBlobStreamListener::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mStorage);
   MOZ_ASSERT(mEventTarget);
@@ -74,39 +75,45 @@ MutableBlobStreamListener::OnStopRequest
 
 NS_IMETHODIMP
 MutableBlobStreamListener::OnDataAvailable(nsIRequest* aRequest,
                                            nsISupports* aContext,
                                            nsIInputStream* aStream,
                                            uint64_t aSourceOffset,
                                            uint32_t aCount)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  // This method could be called on any thread.
   MOZ_ASSERT(mStorage);
 
   uint32_t countRead;
   return aStream->ReadSegments(WriteSegmentFun, this, aCount, &countRead);
 }
 
 nsresult
 MutableBlobStreamListener::WriteSegmentFun(nsIInputStream* aWriterStream,
                                            void* aClosure,
                                            const char* aFromSegment,
                                            uint32_t aToOffset,
                                            uint32_t aCount,
                                            uint32_t* aWriteCount)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  // This method could be called on any thread.
 
   MutableBlobStreamListener* self = static_cast<MutableBlobStreamListener*>(aClosure);
   MOZ_ASSERT(self->mStorage);
 
   nsresult rv = self->mStorage->Append(aFromSegment, aCount);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   *aWriteCount = aCount;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+MutableBlobStreamListener::CheckListenerChain()
+{
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/dom/file/MutableBlobStreamListener.h
+++ b/dom/file/MutableBlobStreamListener.h
@@ -3,29 +3,31 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_MutableBlobStreamListener_h
 #define mozilla_dom_MutableBlobStreamListener_h
 
 #include "nsIStreamListener.h"
+#include "nsIThreadRetargetableStreamListener.h"
 #include "mozilla/dom/MutableBlobStorage.h"
 
 class nsIEventTarget;
 
 namespace mozilla {
 namespace dom {
 
-// This class is main-thread only.
 class MutableBlobStreamListener final : public nsIStreamListener
+                                      , public nsIThreadRetargetableStreamListener
 {
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
+  NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
 
   MutableBlobStreamListener(MutableBlobStorage::MutableBlobStorageType aType,
                             nsISupports* aParent,
                             const nsACString& aContentType,
                             MutableBlobStorageCallback* aCallback,
                             nsIEventTarget* aEventTarget = nullptr);