Bug 1294450 - Make AutoIPCStream favour PSendStream for large input streams. r=nfroydj a=gchang
authorJosh Matthews <josh@joshmatthews.net>
Thu, 29 Sep 2016 06:20:00 +0200
changeset 356433 e0d815a12d03e830e8a20d68e3a161b7412bdb75
parent 356432 9c3e470967fe4ba780f6dd09e7df01d142376792
child 356434 97c00e285b209e36e503dd3f66c168e1f5ae7fdd
push id6570
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:26:13 +0000
treeherdermozilla-beta@f455459b2ae5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroydj, gchang
bugs1294450
milestone51.0a2
Bug 1294450 - Make AutoIPCStream favour PSendStream for large input streams. r=nfroydj a=gchang
dom/indexedDB/FileSnapshot.cpp
dom/ipc/Blob.cpp
ipc/glue/IPCStreamUtils.cpp
ipc/glue/nsIIPCSerializableInputStream.h
netwerk/base/nsBufferedStreams.cpp
netwerk/base/nsFileStreams.cpp
netwerk/base/nsMIMEInputStream.cpp
netwerk/base/nsTemporaryFileInputStream.cpp
xpcom/io/nsMultiplexInputStream.cpp
xpcom/io/nsStorageStream.cpp
xpcom/io/nsStringStream.cpp
--- a/dom/indexedDB/FileSnapshot.cpp
+++ b/dom/indexedDB/FileSnapshot.cpp
@@ -277,16 +277,28 @@ StreamWrapper::Deserialize(const InputSt
 
   if (stream) {
     return stream->Deserialize(aParams, aFileDescriptors);
   }
 
   return false;
 }
 
+Maybe<uint64_t>
+StreamWrapper::ExpectedSerializedLength()
+{
+  nsCOMPtr<nsIIPCSerializableInputStream> stream =
+    do_QueryInterface(mInputStream);
+
+  if (stream) {
+    return stream->ExpectedSerializedLength();
+  }
+  return Nothing();
+}
+
 NS_IMPL_ISUPPORTS_INHERITED0(StreamWrapper::CloseRunnable,
                              Runnable)
 
 NS_IMETHODIMP
 StreamWrapper::
 CloseRunnable::Run()
 {
   mStreamWrapper->Finish();
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -1308,16 +1308,22 @@ bool
 RemoteInputStream::Deserialize(const InputStreamParams& /* aParams */,
                                const FileDescriptorArray& /* aFDs */)
 {
   // See InputStreamUtils.cpp to see how deserialization of a
   // RemoteInputStream is special-cased.
   MOZ_CRASH("RemoteInputStream should never be deserialized");
 }
 
+Maybe<uint64_t>
+RemoteInputStream::ExpectedSerializedLength()
+{
+  return Nothing();
+}
+
 nsIInputStream*
 RemoteInputStream::BlockAndGetInternalStream()
 {
   MOZ_ASSERT(!IsOnOwningThread());
 
   nsresult rv = BlockAndWaitForStream();
   NS_ENSURE_SUCCESS(rv, nullptr);
 
--- a/ipc/glue/IPCStreamUtils.cpp
+++ b/ipc/glue/IPCStreamUtils.cpp
@@ -15,16 +15,19 @@
 #include "mozilla/ipc/FileDescriptorSetChild.h"
 #include "mozilla/ipc/FileDescriptorSetParent.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/ipc/PBackgroundChild.h"
 #include "mozilla/ipc/PBackgroundParent.h"
 #include "mozilla/ipc/SendStream.h"
 #include "mozilla/Unused.h"
 #include "nsIAsyncInputStream.h"
+#include "nsIAsyncOutputStream.h"
+#include "nsIPipe.h"
+#include "nsStreamUtils.h"
 
 namespace mozilla {
 namespace ipc {
 
 namespace {
 
 // These serialization and cleanup functions could be externally exposed.  For
 // now, though, keep them private to encourage use of the safer RAII
@@ -117,27 +120,51 @@ SerializeInputStreamWithFdsParent(nsIInp
 
 template<typename M>
 void
 SerializeInputStream(nsIInputStream* aStream, IPCStream& aValue, M* aManager)
 {
   MOZ_ASSERT(aStream);
   MOZ_ASSERT(aManager);
 
+  // If a stream is known to be larger than 1MB, prefer sending it in chunks.
+  const uint64_t kTooLargeStream = 1024 * 1024;
+
   // First attempt simple stream serialization
   nsCOMPtr<nsIIPCSerializableInputStream> serializable =
     do_QueryInterface(aStream);
-  if (serializable) {
+  uint64_t expectedLength =
+    serializable ? serializable->ExpectedSerializedLength().valueOr(0) : 0;
+  if (serializable && expectedLength < kTooLargeStream) {
     SerializeInputStreamWithFdsChild(aStream, aValue, aManager);
     return;
   }
 
   // As a fallback, attempt to stream the data across using a SendStream
-  // actor.  This will fail for blocking streams.
+  // actor. For blocking streams, create a nonblocking pipe instead,
   nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(aStream);
+  if (!asyncStream) {
+    const uint32_t kBufferSize = 32768; // matches SendStream buffer size.
+    nsCOMPtr<nsIAsyncOutputStream> sink;
+    DebugOnly<nsresult> rv = NS_NewPipe2(getter_AddRefs(asyncStream),
+                                         getter_AddRefs(sink),
+                                         true,
+                                         false,
+                                         kBufferSize,
+                                         UINT32_MAX);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+    nsCOMPtr<nsIEventTarget> target =
+        do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+
+    rv = NS_AsyncCopy(aStream, sink, target, NS_ASYNCCOPY_VIA_READSEGMENTS, kBufferSize);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+  }
+
+  MOZ_ASSERT(asyncStream);
   aValue = SendStreamChild::Create(asyncStream, aManager);
 
   if (!aValue.get_PSendStreamChild()) {
     MOZ_CRASH("SendStream creation failed!");
   }
 }
 
 template<typename M>
--- a/ipc/glue/nsIIPCSerializableInputStream.h
+++ b/ipc/glue/nsIIPCSerializableInputStream.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_ipc_nsIIPCSerializableInputStream_h
 #define mozilla_ipc_nsIIPCSerializableInputStream_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
 #include "nsISupports.h"
 #include "nsTArrayForwardDeclare.h"
 
 namespace mozilla {
 namespace ipc {
 
 class FileDescriptor;
 class InputStreamParams;
@@ -33,43 +34,63 @@ public:
 
   virtual void
   Serialize(mozilla::ipc::InputStreamParams& aParams,
             FileDescriptorArray& aFileDescriptors) = 0;
 
   virtual bool
   Deserialize(const mozilla::ipc::InputStreamParams& aParams,
               const FileDescriptorArray& aFileDescriptors) = 0;
+
+  // The number of bytes that are expected to be written when this
+  // stream is serialized. A value of Some(N) indicates that N bytes
+  // will be written to the IPC buffer, and will be used to decide
+  // upon an optimal transmission mechanism. A value of Nothing
+  // indicates that either serializing this stream will not require
+  // serializing its contents (eg. a file-backed stream, or a stream
+  // backed by an IPC actor), or the length of the stream's contents
+  // cannot be determined.
+  virtual mozilla::Maybe<uint64_t>
+  ExpectedSerializedLength() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCSerializableInputStream,
                               NS_IIPCSERIALIZABLEINPUTSTREAM_IID)
 
 #define NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM                                  \
   virtual void                                                                 \
   Serialize(mozilla::ipc::InputStreamParams&,                                  \
             FileDescriptorArray&) override;                                    \
                                                                                \
   virtual bool                                                                 \
   Deserialize(const mozilla::ipc::InputStreamParams&,                          \
-              const FileDescriptorArray&) override;
+              const FileDescriptorArray&) override;                            \
+                                                                               \
+  virtual mozilla::Maybe<uint64_t>                                             \
+  ExpectedSerializedLength() override;
 
 #define NS_FORWARD_NSIIPCSERIALIZABLEINPUTSTREAM(_to)                          \
   virtual void                                                                 \
   Serialize(mozilla::ipc::InputStreamParams& aParams,                          \
             FileDescriptorArray& aFileDescriptors) override                    \
   {                                                                            \
     _to Serialize(aParams, aFileDescriptors);                                  \
   }                                                                            \
                                                                                \
   virtual bool                                                                 \
   Deserialize(const mozilla::ipc::InputStreamParams& aParams,                  \
               const FileDescriptorArray& aFileDescriptors) override            \
   {                                                                            \
     return _to Deserialize(aParams, aFileDescriptors);                         \
+  }                                                                            \
+                                                                               \
+  virtual mozilla::Maybe<uint64_t>                                             \
+  ExpectedSerializedLength() override                                          \
+  {                                                                            \
+    return _to ExpectedSerializedLength();                                     \
   }
 
 #define NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(_to)                     \
   virtual void                                                                 \
   Serialize(mozilla::ipc::InputStreamParams& aParams,                          \
             FileDescriptorArray& aFileDescriptors) override                    \
   {                                                                            \
     if (_to) {                                                                 \
@@ -77,11 +98,17 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIIPCSeri
     }                                                                          \
   }                                                                            \
                                                                                \
   virtual bool                                                                 \
   Deserialize(const mozilla::ipc::InputStreamParams& aParams,                  \
               const FileDescriptorArray& aFileDescriptors) override            \
   {                                                                            \
     return _to ? _to->Deserialize(aParams, aFileDescriptors) : false;          \
+  }                                                                            \
+                                                                               \
+  virtual mozilla::Maybe<uint64_t>                                             \
+  ExpectedSerializedLength() override                                          \
+  {                                                                            \
+    return _to ? _to->ExpectedSerializedLength() : Nothing();                  \
   }
 
 #endif // mozilla_ipc_nsIIPCSerializableInputStream_h
--- a/netwerk/base/nsBufferedStreams.cpp
+++ b/netwerk/base/nsBufferedStreams.cpp
@@ -33,16 +33,19 @@ static struct {
         int64_t         mNewOffset;
     } mBigSeek[MAX_BIG_SEEKS];
 } bufstats;
 #else
 # define METER(x)       /* nothing */
 #endif
 
 using namespace mozilla::ipc;
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::Some;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsBufferedStream
 
 nsBufferedStream::nsBufferedStream()
     : mBuffer(nullptr),
       mBufferStartOffset(0),
       mCursor(0), 
@@ -536,16 +539,26 @@ nsBufferedInputStream::Deserialize(const
     }
 
     nsresult rv = Init(stream, params.bufferSize());
     NS_ENSURE_SUCCESS(rv, false);
 
     return true;
 }
 
+Maybe<uint64_t>
+nsBufferedInputStream::ExpectedSerializedLength()
+{
+    nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStream);
+    if (stream) {
+        return stream->ExpectedSerializedLength();
+    }
+    return Nothing();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsBufferedOutputStream
 
 NS_IMPL_ADDREF_INHERITED(nsBufferedOutputStream, nsBufferedStream)
 NS_IMPL_RELEASE_INHERITED(nsBufferedOutputStream, nsBufferedStream)
 // This QI uses NS_INTERFACE_MAP_ENTRY_CONDITIONAL to check for
 // non-nullness of mSafeStream.
 NS_INTERFACE_MAP_BEGIN(nsBufferedOutputStream)
--- a/netwerk/base/nsFileStreams.cpp
+++ b/netwerk/base/nsFileStreams.cpp
@@ -27,16 +27,19 @@
 #include "nsXULAppAPI.h"
 
 #define NS_NO_INPUT_BUFFERING 1 // see http://bugzilla.mozilla.org/show_bug.cgi?id=41067
 
 typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType;
 
 using namespace mozilla::ipc;
 using mozilla::DebugOnly;
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::Some;
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsFileStreamBase
 
 nsFileStreamBase::nsFileStreamBase()
     : mFD(nullptr)
     , mBehaviorFlags(0)
     , mDeferredOpen(false)
@@ -657,16 +660,22 @@ nsFileInputStream::Deserialize(const Inp
         mBehaviorFlags &= ~nsIFileInputStream::REOPEN_ON_REWIND;
     }
 
     mIOFlags = params.ioFlags();
 
     return true;
 }
 
+Maybe<uint64_t>
+nsFileInputStream::ExpectedSerializedLength()
+{
+    return Nothing();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsPartialFileInputStream
 
 NS_IMPL_ADDREF_INHERITED(nsPartialFileInputStream, nsFileStreamBase)
 NS_IMPL_RELEASE_INHERITED(nsPartialFileInputStream, nsFileStreamBase)
 
 NS_IMPL_CLASSINFO(nsPartialFileInputStream, nullptr, nsIClassInfo::THREADSAFE,
                   NS_PARTIALLOCALFILEINPUTSTREAM_CID)
@@ -855,16 +864,23 @@ nsPartialFileInputStream::Deserialize(
     if (!mStart) {
       return true;
     }
 
     // XXX This is so broken. Main thread IO alert.
     return NS_SUCCEEDED(nsFileInputStream::Seek(NS_SEEK_SET, mStart));
 }
 
+Maybe<uint64_t>
+nsPartialFileInputStream::ExpectedSerializedLength()
+{
+    return Some(mLength);
+}
+
+
 nsresult
 nsPartialFileInputStream::DoPendingSeek()
 {
     if (!mDeferredSeek) {
        return NS_OK;
     }
 
     mDeferredSeek = false;
--- a/netwerk/base/nsMIMEInputStream.cpp
+++ b/netwerk/base/nsMIMEInputStream.cpp
@@ -18,16 +18,17 @@
 #include "nsIStringStream.h"
 #include "nsString.h"
 #include "nsMIMEInputStream.h"
 #include "nsIClassInfoImpl.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 using namespace mozilla::ipc;
+using mozilla::Maybe;
 
 class nsMIMEInputStream : public nsIMIMEInputStream,
                           public nsISeekableStream,
                           public nsIIPCSerializableInputStream
 {
     virtual ~nsMIMEInputStream();
 
 public:
@@ -374,8 +375,16 @@ nsMIMEInputStream::Deserialize(const Inp
         NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
                      "Unknown type for OptionalInputStreamParams!");
     }
 
     mAddContentLength = params.addContentLength();
 
     return true;
 }
+
+Maybe<uint64_t>
+nsMIMEInputStream::ExpectedSerializedLength()
+{
+    nsCOMPtr<nsIIPCSerializableInputStream> serializable = do_QueryInterface(mStream);
+    return serializable ? serializable->ExpectedSerializedLength() : Nothing();
+}
+
--- a/netwerk/base/nsTemporaryFileInputStream.cpp
+++ b/netwerk/base/nsTemporaryFileInputStream.cpp
@@ -234,8 +234,14 @@ nsTemporaryFileInputStream::Deserialize(
   } else {
     mClosed = true;
   }
 
   mStartPos = mCurPos = params.startPos();
   mEndPos = params.endPos();
   return true;
 }
+
+Maybe<uint64_t>
+nsTemporaryFileInputStream::ExpectedSerializedLength()
+{
+  return Nothing();
+}
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -24,16 +24,19 @@
 #include "nsIClassInfoImpl.h"
 #include "nsIIPCSerializableInputStream.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 using namespace mozilla;
 using namespace mozilla::ipc;
 
 using mozilla::DeprecatedAbs;
+using mozilla::Maybe;
+using mozilla::Nothing;
+using mozilla::Some;
 
 class nsMultiplexInputStream final
   : public nsIMultiplexInputStream
   , public nsISeekableStream
   , public nsIIPCSerializableInputStream
   , public nsICloneableInputStream
 {
 public:
@@ -742,16 +745,39 @@ nsMultiplexInputStream::Deserialize(cons
 
   mCurrentStream = params.currentStream();
   mStatus = params.status();
   mStartedReadingCurrent = params.startedReadingCurrent();
 
   return true;
 }
 
+Maybe<uint64_t>
+nsMultiplexInputStream::ExpectedSerializedLength()
+{
+  MutexAutoLock lock(mLock);
+
+  bool lengthValueExists = false;
+  uint64_t expectedLength = 0;
+  uint32_t streamCount = mStreams.Length();
+  for (uint32_t index = 0; index < streamCount; index++) {
+    nsCOMPtr<nsIIPCSerializableInputStream> stream = do_QueryInterface(mStreams[index]);
+    if (!stream) {
+      continue;
+    }
+    Maybe<uint64_t> length = stream->ExpectedSerializedLength();
+    if (length.isNothing()) {
+      continue;
+    }
+    lengthValueExists = true;
+    expectedLength += length.value();
+  }
+  return lengthValueExists ? Some(expectedLength) : Nothing();
+}
+
 NS_IMETHODIMP
 nsMultiplexInputStream::GetCloneable(bool* aCloneable)
 {
   MutexAutoLock lock(mLock);
   //XXXnsm Cloning a multiplex stream which has started reading is not permitted
   //right now.
   if (mCurrentStream > 0 || mStartedReadingCurrent) {
     *aCloneable = false;
--- a/xpcom/io/nsStorageStream.cpp
+++ b/xpcom/io/nsStorageStream.cpp
@@ -24,16 +24,18 @@
 #include "mozilla/Logging.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 
 using mozilla::ipc::InputStreamParams;
 using mozilla::ipc::StringInputStreamParams;
+using mozilla::Maybe;
+using mozilla::Some;
 
 //
 // Log module for StorageStream logging...
 //
 // To enable logging (see prlog.h for full details):
 //
 //    set MOZ_LOG=StorageStreamLog:5
 //    set MOZ_LOG_FILE=storage.log
@@ -593,16 +595,25 @@ nsStorageInputStream::Serialize(InputStr
   rv = Seek(NS_SEEK_SET, offset);
   MOZ_ASSERT(NS_SUCCEEDED(rv));
 
   StringInputStreamParams params;
   params.data() = combined;
   aParams = params;
 }
 
+Maybe<uint64_t>
+nsStorageInputStream::ExpectedSerializedLength()
+{
+  uint64_t remaining = 0;
+  DebugOnly<nsresult> rv = Available(&remaining);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  return Some(remaining);
+}
+
 bool
 nsStorageInputStream::Deserialize(const InputStreamParams& aParams,
                                   const FileDescriptorArray&)
 {
   NS_NOTREACHED("We should never attempt to deserialize a storage input stream.");
   return false;
 }
 
--- a/xpcom/io/nsStringStream.cpp
+++ b/xpcom/io/nsStringStream.cpp
@@ -20,16 +20,18 @@
 #include "prerror.h"
 #include "plstr.h"
 #include "nsIClassInfoImpl.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "nsIIPCSerializableInputStream.h"
 
 using namespace mozilla::ipc;
+using mozilla::Maybe;
+using mozilla::Some;
 
 //-----------------------------------------------------------------------------
 // nsIStringInputStream implementation
 //-----------------------------------------------------------------------------
 
 class nsStringInputStream final
   : public nsIStringInputStream
   , public nsISeekableStream
@@ -349,16 +351,22 @@ nsStringInputStream::Deserialize(const I
   if (NS_FAILED(SetData(params.data()))) {
     NS_WARNING("SetData failed!");
     return false;
   }
 
   return true;
 }
 
+Maybe<uint64_t>
+nsStringInputStream::ExpectedSerializedLength()
+{
+  return Some(static_cast<uint64_t>(Length()));
+}
+
 /////////
 // nsICloneableInputStream implementation
 /////////
 
 NS_IMETHODIMP
 nsStringInputStream::GetCloneable(bool* aCloneableOut)
 {
   *aCloneableOut = true;