Bug 1371699 - Implement NonBlockingAsyncInputStream - nsIAsyncInputStream wrapper for non-blocking non-async streams, r=froydnj
☠☠ backed out by dcedc6ad433d ☠ ☠
authorAndrea Marchesini <amarchesini@mozilla.com>
Tue, 03 Oct 2017 07:20:17 +0200
changeset 384653 2e253e370b8d59e6eb36a12aec04cce18a0ae4a6
parent 384652 67d619db4280348eeca6bc355b83520550255ce5
child 384654 097c44e151657618fef9e0144de161315ed06ab8
push id32634
push userkwierso@gmail.com
push dateFri, 06 Oct 2017 19:55:44 +0000
treeherdermozilla-central@2d7b8b5dd174 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1371699
milestone58.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 1371699 - Implement NonBlockingAsyncInputStream - nsIAsyncInputStream wrapper for non-blocking non-async streams, r=froydnj
xpcom/io/NonBlockingAsyncInputStream.cpp
xpcom/io/NonBlockingAsyncInputStream.h
xpcom/io/moz.build
xpcom/io/nsLocalFileCommon.cpp
new file mode 100644
--- /dev/null
+++ b/xpcom/io/NonBlockingAsyncInputStream.cpp
@@ -0,0 +1,300 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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/. */
+
+#include "NonBlockingAsyncInputStream.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "nsISeekableStream.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace {
+
+class AsyncWaitRunnable final : public CancelableRunnable
+{
+  RefPtr<NonBlockingAsyncInputStream> mStream;
+  nsCOMPtr<nsIInputStreamCallback> mCallback;
+
+public:
+  AsyncWaitRunnable(NonBlockingAsyncInputStream* aStream,
+                    nsIInputStreamCallback* aCallback)
+    : CancelableRunnable("AsyncWaitRunnable")
+    , mStream(aStream)
+    , mCallback(aCallback)
+  {}
+
+  NS_IMETHOD
+  Run() override
+  {
+    mCallback->OnInputStreamReady(mStream);
+    return NS_OK;
+  }
+};
+
+} // anonymous
+
+NS_IMPL_ADDREF(NonBlockingAsyncInputStream);
+NS_IMPL_RELEASE(NonBlockingAsyncInputStream);
+
+NS_INTERFACE_MAP_BEGIN(NonBlockingAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+  NS_INTERFACE_MAP_ENTRY(nsIAsyncInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsICloneableInputStream,
+                                     mWeakCloneableInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
+                                     mWeakIPCSerializableInputStream)
+  NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream,
+                                     mWeakSeekableInputStream)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+/* static */ nsresult
+NonBlockingAsyncInputStream::Create(nsIInputStream* aInputStream,
+                                    nsIAsyncInputStream** aResult)
+{
+  MOZ_DIAGNOSTIC_ASSERT(aInputStream);
+  MOZ_DIAGNOSTIC_ASSERT(aResult);
+
+  bool nonBlocking = false;
+  nsresult rv = aInputStream->IsNonBlocking(&nonBlocking);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  MOZ_DIAGNOSTIC_ASSERT(nonBlocking);
+
+  nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
+    do_QueryInterface(aInputStream);
+  MOZ_DIAGNOSTIC_ASSERT(!asyncInputStream);
+
+  RefPtr<NonBlockingAsyncInputStream> stream =
+    new NonBlockingAsyncInputStream(aInputStream);
+
+  stream.forget(aResult);
+  return NS_OK;
+}
+
+NonBlockingAsyncInputStream::NonBlockingAsyncInputStream(nsIInputStream* aInputStream)
+  : mInputStream(aInputStream)
+  , mWeakCloneableInputStream(nullptr)
+  , mWeakIPCSerializableInputStream(nullptr)
+  , mWeakSeekableInputStream(nullptr)
+  , mClosed(false)
+{
+  MOZ_ASSERT(mInputStream);
+
+  nsCOMPtr<nsICloneableInputStream> cloneableStream =
+    do_QueryInterface(aInputStream);
+  if (cloneableStream && SameCOMIdentity(aInputStream, cloneableStream)) {
+    mWeakCloneableInputStream = cloneableStream;
+  }
+
+  nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
+    do_QueryInterface(aInputStream);
+  if (serializableStream &&
+      SameCOMIdentity(aInputStream, serializableStream)) {
+    mWeakIPCSerializableInputStream = serializableStream;
+  }
+
+  nsCOMPtr<nsISeekableStream> seekableStream =
+    do_QueryInterface(aInputStream);
+  if (seekableStream && SameCOMIdentity(aInputStream, seekableStream)) {
+    mWeakSeekableInputStream = seekableStream;
+  }
+}
+
+NonBlockingAsyncInputStream::~NonBlockingAsyncInputStream()
+{}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Close()
+{
+  if (mClosed) {
+    return NS_BASE_STREAM_CLOSED;
+  }
+
+  mClosed = true;
+
+  NS_ENSURE_STATE(mInputStream);
+  nsresult rv = mInputStream->Close();
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    mWaitClosureOnly.reset();
+    return rv;
+  }
+
+  // If we have a WaitClosureOnly runnable, it's time to use it.
+  if (mWaitClosureOnly.isSome()) {
+    nsCOMPtr<nsIRunnable> waitClosureOnlyRunnable =
+      Move(mWaitClosureOnly->mRunnable);
+
+    nsCOMPtr<nsIEventTarget> waitClosureOnlyEventTarget =
+      Move(mWaitClosureOnly->mEventTarget);
+
+    mWaitClosureOnly.reset();
+
+    if (waitClosureOnlyEventTarget) {
+      waitClosureOnlyEventTarget->Dispatch(waitClosureOnlyRunnable,
+                                           NS_DISPATCH_NORMAL);
+    } else {
+      waitClosureOnlyRunnable->Run();
+    }
+  }
+
+  return NS_OK;
+}
+
+// nsIInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Available(uint64_t* aLength)
+{
+  return mInputStream->Available(aLength);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Read(char* aBuffer, uint32_t aCount,
+                                  uint32_t* aReadCount)
+{
+  return mInputStream->Read(aBuffer, aCount, aReadCount);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+                                          void* aClosure, uint32_t aCount,
+                                          uint32_t *aResult)
+{
+  return mInputStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+  *aNonBlocking = true;
+  return NS_OK;
+}
+
+// nsICloneableInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::GetCloneable(bool* aCloneable)
+{
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+  return mWeakCloneableInputStream->GetCloneable(aCloneable);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Clone(nsIInputStream** aResult)
+{
+  NS_ENSURE_STATE(mWeakCloneableInputStream);
+
+  nsCOMPtr<nsIInputStream> clonedStream;
+  nsresult rv = mWeakCloneableInputStream->Clone(getter_AddRefs(clonedStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  nsCOMPtr<nsIAsyncInputStream> asyncStream;
+  rv = Create(clonedStream, getter_AddRefs(asyncStream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  asyncStream.forget(aResult);
+  return NS_OK;
+}
+
+// nsIAsyncInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::CloseWithStatus(nsresult aStatus)
+{
+  return Close();
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::AsyncWait(nsIInputStreamCallback* aCallback,
+                                       uint32_t aFlags,
+                                       uint32_t aRequestedCount,
+                                       nsIEventTarget* aEventTarget)
+{
+  if (aCallback && mWaitClosureOnly.isSome()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!aCallback) {
+    // Canceling previous callback.
+    mWaitClosureOnly.reset();
+    return NS_OK;
+  }
+
+  RefPtr<NonBlockingAsyncInputStream> self = this;
+  nsCOMPtr<nsIInputStreamCallback> callback = aCallback;
+
+  nsCOMPtr<nsIRunnable> runnable = new AsyncWaitRunnable(this, aCallback);
+  if ((aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) && !mClosed) {
+    mWaitClosureOnly.emplace(runnable, aEventTarget);
+    return NS_OK;
+  }
+
+  if (aEventTarget) {
+    return aEventTarget->Dispatch(runnable.forget());
+  }
+
+  return runnable->Run();
+}
+
+// nsIIPCSerializableInputStream
+
+void
+NonBlockingAsyncInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
+                                       FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_ASSERT(mWeakIPCSerializableInputStream);
+  InputStreamHelper::SerializeInputStream(mInputStream, aParams,
+                                          aFileDescriptors);
+}
+
+bool
+NonBlockingAsyncInputStream::Deserialize(const mozilla::ipc::InputStreamParams& aParams,
+                                         const FileDescriptorArray& aFileDescriptors)
+{
+  MOZ_CRASH("NonBlockingAsyncInputStream cannot be deserialized!");
+  return true;
+}
+
+Maybe<uint64_t>
+NonBlockingAsyncInputStream::ExpectedSerializedLength()
+{
+  NS_ENSURE_TRUE(mWeakIPCSerializableInputStream, Nothing());
+  return mWeakIPCSerializableInputStream->ExpectedSerializedLength();
+}
+
+// nsISeekableStream
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+  return mWeakSeekableInputStream->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Tell(int64_t *aResult)
+{
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+  return mWeakSeekableInputStream->Tell(aResult);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::SetEOF()
+{
+  NS_ENSURE_STATE(mWeakSeekableInputStream);
+  mClosed = true;
+  return mWeakSeekableInputStream->SetEOF();
+}
+
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/xpcom/io/NonBlockingAsyncInputStream.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 NonBlockingAsyncInputStream_h
+#define NonBlockingAsyncInputStream_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
+#include "nsICloneableInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsISeekableStream.h"
+
+// This class aims to wrap a non-blocking and non-async inputStream and expose
+// it as nsIAsyncInputStream.
+// Probably you don't want to use this class directly. Instead use
+// NS_MakeAsyncNonBlockingInputStream() as it will handle different stream
+// variants without requiring you to special-case them yourself.
+
+namespace mozilla {
+
+class NonBlockingAsyncInputStream final : public nsIAsyncInputStream
+                                        , public nsICloneableInputStream
+                                        , public nsIIPCSerializableInputStream
+                                        , public nsISeekableStream
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIINPUTSTREAM
+  NS_DECL_NSIASYNCINPUTSTREAM
+  NS_DECL_NSICLONEABLEINPUTSTREAM
+  NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+  NS_DECL_NSISEEKABLESTREAM
+
+  // |aInputStream| must be a non-blocking, non-async inputSteam.
+  static nsresult
+  Create(nsIInputStream* aInputStream,
+         nsIAsyncInputStream** aAsyncInputStream);
+
+private:
+  explicit NonBlockingAsyncInputStream(nsIInputStream* aInputStream);
+  ~NonBlockingAsyncInputStream();
+
+  nsCOMPtr<nsIInputStream> mInputStream;
+
+  // Raw pointers because these are just QI of mInputStream.
+  nsICloneableInputStream* MOZ_NON_OWNING_REF mWeakCloneableInputStream;
+  nsIIPCSerializableInputStream* MOZ_NON_OWNING_REF mWeakIPCSerializableInputStream;
+  nsISeekableStream* MOZ_NON_OWNING_REF mWeakSeekableInputStream;
+
+  struct WaitClosureOnly
+  {
+    WaitClosureOnly(nsIRunnable* aRunnable, nsIEventTarget* aEventTarget)
+      : mRunnable(aRunnable)
+      , mEventTarget(aEventTarget)
+    {}
+
+    nsCOMPtr<nsIRunnable> mRunnable;
+    nsCOMPtr<nsIEventTarget> mEventTarget;
+  };
+
+  // This is set when AsyncWait is called with a callback and with
+  // WAIT_CLOSURE_ONLY as flag.
+  Maybe<WaitClosureOnly> mWaitClosureOnly;
+
+  bool mClosed;
+};
+
+} // mozilla namespace
+
+#endif // NonBlockingAsyncInputStream_h
--- a/xpcom/io/moz.build
+++ b/xpcom/io/moz.build
@@ -79,25 +79,27 @@ EXPORTS += [
     'nsUnicharInputStream.h',
     'nsWildCard.h',
     'SlicedInputStream.h',
     'SpecialSystemDirectory.h',
 ]
 
 EXPORTS.mozilla += [
     'Base64.h',
+    'NonBlockingAsyncInputStream.h',
     'SnappyCompressOutputStream.h',
     'SnappyFrameUtils.h',
     'SnappyUncompressInputStream.h',
 ]
 
 UNIFIED_SOURCES += [
     'Base64.cpp',
     'crc32c.c',
     'FileDescriptorFile.cpp',
+    'NonBlockingAsyncInputStream.cpp',
     'nsAnonymousTemporaryFile.cpp',
     'nsAppFileLocationProvider.cpp',
     'nsBinaryStream.cpp',
     'nsDirectoryService.cpp',
     'nsEscape.cpp',
     'nsInputStreamTee.cpp',
     'nsIOUtil.cpp',
     'nsLinebreakConverter.cpp',
--- a/xpcom/io/nsLocalFileCommon.cpp
+++ b/xpcom/io/nsLocalFileCommon.cpp
@@ -9,16 +9,17 @@
 #include "nsLocalFile.h" // includes platform-specific headers
 
 #include "nsString.h"
 #include "nsCOMPtr.h"
 #include "nsReadableUtils.h"
 #include "nsPrintfCString.h"
 #include "nsCRT.h"
 #include "nsNativeCharsetUtils.h"
+#include "nsTArray.h"
 #include "nsUTF8Utils.h"
 
 #ifdef XP_WIN
 #include <string.h>
 #endif
 
 
 void