Bug 1294450 - Make AutoIPCStream favour PSendStream for large input streams. r=froydnj
☠☠ backed out by a357f5e03549 ☠ ☠
authorJosh Matthews <josh@joshmatthews.net>
Wed, 28 Sep 2016 18:38:51 -0400
changeset 315590 57e78c503c4e08f9510cd4e1763f4beb79184b5a
parent 315589 d03efada498509334d6ddfcb85855eb1e4d19c78
child 315591 b747b6b2db177893986051a8ab562dd0270caebd
push id82221
push userryanvm@gmail.com
push dateWed, 28 Sep 2016 23:58:41 +0000
treeherdermozilla-inbound@299a09f24493 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
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=froydnj
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;
+    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;