Bug 1452950 - Make IPCBlobInputStream thread-safe, r=smaug
authorAndrea Marchesini <amarchesini@mozilla.com>
Thu, 03 May 2018 18:41:26 +0200
changeset 472957 b9272151070ad37940c0f53d80150c2092c568a9
parent 472956 22adcf8598ed82665bf95ea7f15867ec743d63b4
child 472958 058b1fd37003fe261c859961df63888b615e0d4c
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1452950
milestone61.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 1452950 - Make IPCBlobInputStream thread-safe, r=smaug
dom/file/ipc/IPCBlobInputStream.cpp
dom/file/ipc/IPCBlobInputStream.h
--- a/dom/file/ipc/IPCBlobInputStream.cpp
+++ b/dom/file/ipc/IPCBlobInputStream.cpp
@@ -156,161 +156,191 @@ IPCBlobInputStream::~IPCBlobInputStream(
   Close();
 }
 
 // nsIInputStream interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::Available(uint64_t* aLength)
 {
-  // We don't have a remoteStream yet: let's return 0.
-  if (mState == eInit || mState == ePending) {
-    *aLength = 0;
-    return NS_OK;
-  }
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  if (mState == eRunning) {
+    // We don't have a remoteStream yet: let's return 0.
+    if (mState == eInit || mState == ePending) {
+      *aLength = 0;
+      return NS_OK;
+    }
+
+    if (mState == eClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    MOZ_ASSERT(mState == eRunning);
     MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
 
-    nsresult rv = EnsureAsyncRemoteStream();
+    nsresult rv = EnsureAsyncRemoteStream(lock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->Available(aLength);
+    asyncRemoteStream = mAsyncRemoteStream;
   }
 
-  MOZ_ASSERT(mState == eClosed);
-  return NS_BASE_STREAM_CLOSED;
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->Available(aLength);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aReadCount)
 {
-  // Read is not available is we don't have a remoteStream.
-  if (mState == eInit || mState == ePending) {
-    return NS_BASE_STREAM_WOULD_BLOCK;
-  }
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  if (mState == eRunning) {
+    // Read is not available is we don't have a remoteStream.
+    if (mState == eInit || mState == ePending) {
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    if (mState == eClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    MOZ_ASSERT(mState == eRunning);
     MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
 
-    nsresult rv = EnsureAsyncRemoteStream();
+    nsresult rv = EnsureAsyncRemoteStream(lock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->Read(aBuffer, aCount, aReadCount);
+    asyncRemoteStream = mAsyncRemoteStream;
   }
 
-  MOZ_ASSERT(mState == eClosed);
-  return NS_BASE_STREAM_CLOSED;
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->Read(aBuffer, aCount, aReadCount);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure,
                                  uint32_t aCount, uint32_t *aResult)
 {
-  // ReadSegments is not available is we don't have a remoteStream.
-  if (mState == eInit || mState == ePending) {
-    return NS_BASE_STREAM_WOULD_BLOCK;
-  }
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  if (mState == eRunning) {
+    // ReadSegments is not available is we don't have a remoteStream.
+    if (mState == eInit || mState == ePending) {
+      return NS_BASE_STREAM_WOULD_BLOCK;
+    }
+
+    if (mState == eClosed) {
+      return NS_BASE_STREAM_CLOSED;
+    }
+
+    MOZ_ASSERT(mState == eRunning);
     MOZ_ASSERT(mRemoteStream || mAsyncRemoteStream);
 
-    nsresult rv = EnsureAsyncRemoteStream();
+    nsresult rv = EnsureAsyncRemoteStream(lock);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return rv;
     }
 
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+    asyncRemoteStream = mAsyncRemoteStream;
   }
 
-  MOZ_ASSERT(mState == eClosed);
-  return NS_BASE_STREAM_CLOSED;
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->ReadSegments(aWriter, aClosure, aCount, aResult);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::IsNonBlocking(bool* aNonBlocking)
 {
   *aNonBlocking = true;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Close()
 {
-  if (mActor) {
-    mActor->ForgetStream(this);
-    mActor = nullptr;
-  }
-
-  if (mAsyncRemoteStream) {
-    mAsyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
-    mAsyncRemoteStream = nullptr;
-  }
-
-  if (mRemoteStream) {
-    mRemoteStream->Close();
-    mRemoteStream = nullptr;
-  }
-
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  nsCOMPtr<nsIInputStream> remoteStream;
   {
     MutexAutoLock lock(mMutex);
 
+    if (mActor) {
+      mActor->ForgetStream(this);
+      mActor = nullptr;
+    }
+
+    asyncRemoteStream.swap(mAsyncRemoteStream);
+    remoteStream.swap(mRemoteStream);
+
     mInputStreamCallback = nullptr;
     mInputStreamCallbackEventTarget = nullptr;
+
+    mFileMetadataCallback = nullptr;
+    mFileMetadataCallbackEventTarget = nullptr;
+
+    mState = eClosed;
   }
 
-  mFileMetadataCallback = nullptr;
-  mFileMetadataCallbackEventTarget = nullptr;
+  if (asyncRemoteStream) {
+    asyncRemoteStream->CloseWithStatus(NS_BASE_STREAM_CLOSED);
+  }
 
-  mState = eClosed;
+  if (remoteStream) {
+    remoteStream->Close();
+  }
+
   return NS_OK;
 }
 
 // nsICloneableInputStream interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetCloneable(bool* aCloneable)
 {
+  MutexAutoLock lock(mMutex);
   *aCloneable = mState != eClosed;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::Clone(nsIInputStream** aResult)
 {
+  MutexAutoLock lock(mMutex);
+
   if (mState == eClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   MOZ_ASSERT(mActor);
 
   RefPtr<IPCBlobInputStream> stream = mActor->CreateStream();
   if (!stream) {
     return NS_ERROR_FAILURE;
   }
 
-  stream->InitWithExistingRange(mStart, mLength);
+  stream->InitWithExistingRange(mStart, mLength, lock);
 
   stream.forget(aResult);
   return NS_OK;
 }
 
 // nsICloneableInputStreamWithRange interface
 
 NS_IMETHODIMP
 IPCBlobInputStream::CloneWithRange(uint64_t aStart, uint64_t aLength,
                                    nsIInputStream** aResult)
 {
+  MutexAutoLock lock(mMutex);
+
   if (mState == eClosed) {
     return NS_BASE_STREAM_CLOSED;
   }
 
   // Too short or out of range.
   if (aLength == 0 || aStart >= mLength) {
     return NS_NewCStringInputStream(aResult, EmptyCString());
   }
@@ -327,17 +357,17 @@ IPCBlobInputStream::CloneWithRange(uint6
   if (!streamSize.isValid()) {
     return NS_ERROR_FAILURE;
   }
 
   if (aLength > streamSize.value()) {
     aLength = streamSize.value();
   }
 
-  stream->InitWithExistingRange(aStart + mStart, aLength);
+  stream->InitWithExistingRange(aStart + mStart, aLength, lock);
 
   stream.forget(aResult);
   return NS_OK;
 }
 
 // nsIAsyncInputStream interface
 
 NS_IMETHODIMP
@@ -346,138 +376,151 @@ IPCBlobInputStream::CloseWithStatus(nsre
   return Close();
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
                               uint32_t aFlags, uint32_t aRequestedCount,
                               nsIEventTarget* aEventTarget)
 {
-  // See IPCBlobInputStream.h for more information about this state machine.
-
-  switch (mState) {
-  // First call, we need to retrieve the stream from the parent actor.
-  case eInit:
-    MOZ_ASSERT(mActor);
-
-    mInputStreamCallback = aCallback;
-    mInputStreamCallbackEventTarget = aEventTarget;
-    mState = ePending;
-
-    mActor->StreamNeeded(this, aEventTarget);
-    return NS_OK;
-
-  // We are still waiting for the remote inputStream
-  case ePending: {
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
     MutexAutoLock lock(mMutex);
 
-    if (mInputStreamCallback && aCallback) {
-      return NS_ERROR_FAILURE;
+    // See IPCBlobInputStream.h for more information about this state machine.
+
+    switch (mState) {
+    // First call, we need to retrieve the stream from the parent actor.
+    case eInit:
+      MOZ_ASSERT(mActor);
+
+      mInputStreamCallback = aCallback;
+      mInputStreamCallbackEventTarget = aEventTarget;
+      mState = ePending;
+
+      mActor->StreamNeeded(this, aEventTarget);
+      return NS_OK;
+
+    // We are still waiting for the remote inputStream
+    case ePending: {
+      if (mInputStreamCallback && aCallback) {
+        return NS_ERROR_FAILURE;
+      }
+
+      mInputStreamCallback = aCallback;
+      mInputStreamCallbackEventTarget = aEventTarget;
+      return NS_OK;
     }
 
-    mInputStreamCallback = aCallback;
-    mInputStreamCallbackEventTarget = aEventTarget;
-    return NS_OK;
-  }
+    // We have the remote inputStream, let's check if we can execute the callback.
+    case eRunning: {
+      if (mInputStreamCallback && aCallback) {
+        return NS_ERROR_FAILURE;
+      }
 
-  // We have the remote inputStream, let's check if we can execute the callback.
-  case eRunning: {
-    {
-      MutexAutoLock lock(mMutex);
+      nsresult rv = EnsureAsyncRemoteStream(lock);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return rv;
+      }
+
       mInputStreamCallback = aCallback;
       mInputStreamCallbackEventTarget = aEventTarget;
+
+      asyncRemoteStream = mAsyncRemoteStream;
+      break;
     }
 
-    nsresult rv = EnsureAsyncRemoteStream();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+    // Stream is closed.
+    default:
+      MOZ_ASSERT(mState == eClosed);
+      return NS_BASE_STREAM_CLOSED;
     }
-
-    MOZ_ASSERT(mAsyncRemoteStream);
-    return mAsyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
-                                         0, 0, aEventTarget);
   }
 
-  // Stream is closed.
-  default:
-    MOZ_ASSERT(mState == eClosed);
-    return NS_BASE_STREAM_CLOSED;
-  }
+  MOZ_ASSERT(asyncRemoteStream);
+  return asyncRemoteStream->AsyncWait(aCallback ? this : nullptr,
+                                      0, 0, aEventTarget);
 }
 
 void
 IPCBlobInputStream::StreamReady(already_AddRefed<nsIInputStream> aInputStream)
 {
   nsCOMPtr<nsIInputStream> inputStream = Move(aInputStream);
 
-  // We have been closed in the meantime.
-  if (mState == eClosed) {
-    if (inputStream) {
-      inputStream->Close();
-    }
-    return;
-  }
-
   // If inputStream is null, it means that the serialization went wrong or the
   // stream is not available anymore. We keep the state as pending just to block
   // any additional operation.
 
   if (!inputStream) {
     return;
   }
 
-  // Now it's the right time to apply a slice if needed.
-  if (mStart > 0 || mLength < mActor->Size()) {
-    inputStream =
-      new SlicedInputStream(inputStream.forget(), mStart, mLength);
-  }
+  nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
+  nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
+  nsCOMPtr<nsIInputStreamCallback> inputStreamCallback;
+  nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
+  nsCOMPtr<nsIAsyncInputStream> asyncRemoteStream;
+  {
+    MutexAutoLock lock(mMutex);
 
-  mRemoteStream = inputStream;
+    // We have been closed in the meantime.
+    if (mState == eClosed) {
+      if (inputStream) {
+        MutexAutoUnlock unlock(mMutex);
+        inputStream->Close();
+      }
+      return;
+    }
 
-  MOZ_ASSERT(mState == ePending);
-  mState = eRunning;
+    // Now it's the right time to apply a slice if needed.
+    if (mStart > 0 || mLength < mActor->Size()) {
+      inputStream =
+        new SlicedInputStream(inputStream.forget(), mStart, mLength);
+    }
+
+    mRemoteStream = inputStream;
+
+    MOZ_ASSERT(mState == ePending);
+    mState = eRunning;
+
+    fileMetadataCallback.swap(mFileMetadataCallback);
+    fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
 
-  nsCOMPtr<nsIFileMetadataCallback> fileMetadataCallback;
-  fileMetadataCallback.swap(mFileMetadataCallback);
+    inputStreamCallback = mInputStreamCallback ? this : nullptr;
+    inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
 
-  nsCOMPtr<nsIEventTarget> fileMetadataCallbackEventTarget;
-  fileMetadataCallbackEventTarget.swap(mFileMetadataCallbackEventTarget);
+    if (inputStreamCallback) {
+      nsresult rv = EnsureAsyncRemoteStream(lock);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+
+      MOZ_ASSERT(mAsyncRemoteStream);
+      asyncRemoteStream = mAsyncRemoteStream;
+    }
+  }
 
   if (fileMetadataCallback) {
     FileMetadataCallbackRunnable::Execute(fileMetadataCallback,
                                           fileMetadataCallbackEventTarget,
                                           this);
   }
 
-  nsCOMPtr<nsIInputStreamCallback> inputStreamCallback = this;
-  nsCOMPtr<nsIEventTarget> inputStreamCallbackEventTarget;
-  {
-    MutexAutoLock lock(mMutex);
-    inputStreamCallbackEventTarget = mInputStreamCallbackEventTarget;
-    if (!mInputStreamCallback) {
-      inputStreamCallback = nullptr;
-    }
-  }
+  if (inputStreamCallback) {
+    MOZ_ASSERT(asyncRemoteStream);
 
-  if (inputStreamCallback) {
-    nsresult rv = EnsureAsyncRemoteStream();
-    if (NS_WARN_IF(NS_FAILED(rv))) {
-      return;
-    }
-
-    MOZ_ASSERT(mAsyncRemoteStream);
-
-    rv = mAsyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
-                                       inputStreamCallbackEventTarget);
+    nsresult rv = asyncRemoteStream->AsyncWait(inputStreamCallback, 0, 0,
+                                               inputStreamCallbackEventTarget);
     Unused << NS_WARN_IF(NS_FAILED(rv));
   }
 }
 
 void
-IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength)
+IPCBlobInputStream::InitWithExistingRange(uint64_t aStart, uint64_t aLength,
+                                          const MutexAutoLock& aProofOfLock)
 {
   MOZ_ASSERT(mActor->Size() >= aStart + aLength);
   mStart = aStart;
   mLength = aLength;
 
   // In the child, we slice in StreamReady() when we set mState to eRunning.
   // But in the parent, we start out eRunning, so it's necessary to slice the
   // stream as soon as we have the information during the initialization phase
@@ -524,16 +567,18 @@ IPCBlobInputStream::OnInputStreamReady(n
 }
 
 // nsIIPCSerializableInputStream
 
 void
 IPCBlobInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                               FileDescriptorArray& aFileDescriptors)
 {
+  MutexAutoLock lock(mMutex);
+
   mozilla::ipc::IPCBlobInputStreamParams params;
   params.id() = mActor->ID();
   params.start() = mStart;
   params.length() = mLength;
 
   aParams = params;
 }
 
@@ -561,87 +606,107 @@ IPCBlobInputStream::AsyncFileMetadataWai
 
   // If we have the callback, we must have the event target.
   if (NS_WARN_IF(!!aCallback != !!aEventTarget)) {
     return NS_ERROR_FAILURE;
   }
 
   // See IPCBlobInputStream.h for more information about this state machine.
 
-  switch (mState) {
-  // First call, we need to retrieve the stream from the parent actor.
-  case eInit:
-    MOZ_ASSERT(mActor);
+  {
+    MutexAutoLock lock(mMutex);
+
+    switch (mState) {
+    // First call, we need to retrieve the stream from the parent actor.
+    case eInit:
+      MOZ_ASSERT(mActor);
+
+      mFileMetadataCallback = aCallback;
+      mFileMetadataCallbackEventTarget = aEventTarget;
+      mState = ePending;
+
+      mActor->StreamNeeded(this, aEventTarget);
+      return NS_OK;
 
-    mFileMetadataCallback = aCallback;
-    mFileMetadataCallbackEventTarget = aEventTarget;
-    mState = ePending;
+    // We are still waiting for the remote inputStream
+    case ePending:
+      if (mFileMetadataCallback && aCallback) {
+        return NS_ERROR_FAILURE;
+      }
 
-    mActor->StreamNeeded(this, aEventTarget);
-    return NS_OK;
+      mFileMetadataCallback = aCallback;
+      mFileMetadataCallbackEventTarget = aEventTarget;
+      return NS_OK;
 
-  // We are still waiting for the remote inputStream
-  case ePending:
-    if (mFileMetadataCallback && aCallback) {
-      return NS_ERROR_FAILURE;
+    // We have the remote inputStream, let's check if we can execute the callback.
+    case eRunning:
+      break;
+
+    // Stream is closed.
+    default:
+      MOZ_ASSERT(mState == eClosed);
+      return NS_BASE_STREAM_CLOSED;
     }
 
-    mFileMetadataCallback = aCallback;
-    mFileMetadataCallbackEventTarget = aEventTarget;
-    return NS_OK;
+    MOZ_ASSERT(mState == eRunning);
+  }
 
-  // We have the remote inputStream, let's check if we can execute the callback.
-  case eRunning:
-    FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
-    return NS_OK;
-
-  // Stream is closed.
-  default:
-    MOZ_ASSERT(mState == eClosed);
-    return NS_BASE_STREAM_CLOSED;
-  }
+  FileMetadataCallbackRunnable::Execute(aCallback, aEventTarget, this);
+  return NS_OK;
 }
 
 // nsIFileMetadata
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetSize(int64_t* aRetval)
 {
-  nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
-  if (!fileMetadata) {
-    return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+  nsCOMPtr<nsIFileMetadata> fileMetadata;
+  {
+    MutexAutoLock lock(mMutex);
+    fileMetadata = do_QueryInterface(mRemoteStream);
+    if (!fileMetadata) {
+      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+    }
   }
 
   return fileMetadata->GetSize(aRetval);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetLastModified(int64_t* aRetval)
 {
-  nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
-  if (!fileMetadata) {
-    return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+  nsCOMPtr<nsIFileMetadata> fileMetadata;
+  {
+    MutexAutoLock lock(mMutex);
+    fileMetadata = do_QueryInterface(mRemoteStream);
+    if (!fileMetadata) {
+      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+    }
   }
 
   return fileMetadata->GetLastModified(aRetval);
 }
 
 NS_IMETHODIMP
 IPCBlobInputStream::GetFileDescriptor(PRFileDesc** aRetval)
 {
-  nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(mRemoteStream);
-  if (!fileMetadata) {
-    return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+  nsCOMPtr<nsIFileMetadata> fileMetadata;
+  {
+    MutexAutoLock lock(mMutex);
+    fileMetadata = do_QueryInterface(mRemoteStream);
+    if (!fileMetadata) {
+      return mState == eClosed ? NS_BASE_STREAM_CLOSED : NS_ERROR_FAILURE;
+    }
   }
 
   return fileMetadata->GetFileDescriptor(aRetval);
 }
 
 nsresult
-IPCBlobInputStream::EnsureAsyncRemoteStream()
+IPCBlobInputStream::EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock)
 {
   // We already have an async remote stream.
   if (mAsyncRemoteStream) {
     return NS_OK;
   }
 
   if (!mRemoteStream) {
     return NS_ERROR_FAILURE;
--- a/dom/file/ipc/IPCBlobInputStream.h
+++ b/dom/file/ipc/IPCBlobInputStream.h
@@ -40,20 +40,21 @@ public:
 
   void
   StreamReady(already_AddRefed<nsIInputStream> aInputStream);
 
 private:
   ~IPCBlobInputStream();
 
   nsresult
-  EnsureAsyncRemoteStream();
+  EnsureAsyncRemoteStream(const MutexAutoLock& aProofOfLock);
 
   void
-  InitWithExistingRange(uint64_t aStart, uint64_t aLength);
+  InitWithExistingRange(uint64_t aStart, uint64_t aLength,
+                        const MutexAutoLock& aProofOfLock);
 
   RefPtr<IPCBlobInputStreamChild> mActor;
 
   // This is the list of possible states.
   enum {
     // The initial state. Only ::Available() can be used without receiving an
     // error. The available size is known by the actor.
     eInit,
@@ -75,23 +76,24 @@ private:
 
   uint64_t mStart;
   uint64_t mLength;
 
   nsCOMPtr<nsIInputStream> mRemoteStream;
   nsCOMPtr<nsIAsyncInputStream> mAsyncRemoteStream;
 
   // These 2 values are set only if mState is ePending.
-  // They are protected by mutex.
   nsCOMPtr<nsIInputStreamCallback> mInputStreamCallback;
   nsCOMPtr<nsIEventTarget> mInputStreamCallbackEventTarget;
 
   // These 2 values are set only if mState is ePending.
   nsCOMPtr<nsIFileMetadataCallback> mFileMetadataCallback;
   nsCOMPtr<nsIEventTarget> mFileMetadataCallbackEventTarget;
 
+  // Any member of this class is protected by mutex because touched on
+  // multiple threads.
   Mutex mMutex;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ipc_IPCBlobInputStream_h