Bug 1294450 - Make AutoIPCStream favour PSendStream for large input streams. r=nfroydj
authorJosh Matthews <josh@joshmatthews.net>
Thu, 29 Sep 2016 06:20:00 +0200
changeset 358671 6e6856e101c6e70c5f2f19089719f3e6df2bc3db
parent 358670 2614d75c2100368dd92c6fe40490fe573c12c759
child 358672 c572919bea3524d38e1a3364a4acba332910b84a
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-beta@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersnfroydj
bugs1294450
milestone52.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 1294450 - Make AutoIPCStream favour PSendStream for large input streams. r=nfroydj
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
@@ -1302,16 +1302,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;