summaryrefslogtreecommitdiffstats
path: root/xpcom/io/NonBlockingAsyncInputStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xpcom/io/NonBlockingAsyncInputStream.cpp')
-rw-r--r--xpcom/io/NonBlockingAsyncInputStream.cpp400
1 files changed, 400 insertions, 0 deletions
diff --git a/xpcom/io/NonBlockingAsyncInputStream.cpp b/xpcom/io/NonBlockingAsyncInputStream.cpp
new file mode 100644
index 0000000000..6e8eebc5db
--- /dev/null
+++ b/xpcom/io/NonBlockingAsyncInputStream.cpp
@@ -0,0 +1,400 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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 "nsIAsyncInputStream.h"
+#include "nsICloneableInputStream.h"
+#include "nsIInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsISeekableStream.h"
+#include "nsStreamUtils.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+class NonBlockingAsyncInputStream::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 {
+ mStream->RunAsyncWaitCallback(this, mCallback.forget());
+ return NS_OK;
+ }
+
+ nsresult Cancel() override {
+ mStream = nullptr;
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ADDREF(NonBlockingAsyncInputStream);
+NS_IMPL_RELEASE(NonBlockingAsyncInputStream);
+
+NonBlockingAsyncInputStream::WaitClosureOnly::WaitClosureOnly(
+ AsyncWaitRunnable* aRunnable, nsIEventTarget* aEventTarget)
+ : mRunnable(aRunnable), mEventTarget(aEventTarget) {}
+
+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_CONDITIONAL(nsITellableStream,
+ mWeakTellableInputStream)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+/* static */
+nsresult NonBlockingAsyncInputStream::Create(
+ already_AddRefed<nsIInputStream> aInputStream,
+ nsIAsyncInputStream** aResult) {
+ MOZ_DIAGNOSTIC_ASSERT(aResult);
+
+ nsCOMPtr<nsIInputStream> inputStream = std::move(aInputStream);
+
+ bool nonBlocking = false;
+ nsresult rv = inputStream->IsNonBlocking(&nonBlocking);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(nonBlocking);
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ nsCOMPtr<nsIAsyncInputStream> asyncInputStream =
+ do_QueryInterface(inputStream);
+ MOZ_DIAGNOSTIC_ASSERT(!asyncInputStream);
+#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED
+
+ RefPtr<NonBlockingAsyncInputStream> stream =
+ new NonBlockingAsyncInputStream(inputStream.forget());
+
+ stream.forget(aResult);
+ return NS_OK;
+}
+
+NonBlockingAsyncInputStream::NonBlockingAsyncInputStream(
+ already_AddRefed<nsIInputStream> aInputStream)
+ : mInputStream(std::move(aInputStream)),
+ mWeakCloneableInputStream(nullptr),
+ mWeakIPCSerializableInputStream(nullptr),
+ mWeakSeekableInputStream(nullptr),
+ mWeakTellableInputStream(nullptr),
+ mLock("NonBlockingAsyncInputStream::mLock"),
+ mClosed(false) {
+ MOZ_ASSERT(mInputStream);
+
+ nsCOMPtr<nsICloneableInputStream> cloneableStream =
+ do_QueryInterface(mInputStream);
+ if (cloneableStream && SameCOMIdentity(mInputStream, cloneableStream)) {
+ mWeakCloneableInputStream = cloneableStream;
+ }
+
+ nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
+ do_QueryInterface(mInputStream);
+ if (serializableStream && SameCOMIdentity(mInputStream, serializableStream)) {
+ mWeakIPCSerializableInputStream = serializableStream;
+ }
+
+ nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(mInputStream);
+ if (seekableStream && SameCOMIdentity(mInputStream, seekableStream)) {
+ mWeakSeekableInputStream = seekableStream;
+ }
+
+ nsCOMPtr<nsITellableStream> tellableStream = do_QueryInterface(mInputStream);
+ if (tellableStream && SameCOMIdentity(mInputStream, tellableStream)) {
+ mWeakTellableInputStream = tellableStream;
+ }
+}
+
+NonBlockingAsyncInputStream::~NonBlockingAsyncInputStream() = default;
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Close() {
+ RefPtr<AsyncWaitRunnable> waitClosureOnlyRunnable;
+ nsCOMPtr<nsIEventTarget> waitClosureOnlyEventTarget;
+
+ {
+ MutexAutoLock lock(mLock);
+
+ if (mClosed) {
+ // Here we could return NS_BASE_STREAM_CLOSED as well, but just to avoid
+ // warning messages, let's make everybody happy with a NS_OK.
+ return NS_OK;
+ }
+
+ 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()) {
+ waitClosureOnlyRunnable = std::move(mWaitClosureOnly->mRunnable);
+ waitClosureOnlyEventTarget = std::move(mWaitClosureOnly->mEventTarget);
+
+ mWaitClosureOnly.reset();
+
+ // Now we want to dispatch the asyncWaitCallback.
+ mAsyncWaitCallback = waitClosureOnlyRunnable;
+ }
+ }
+
+ if (waitClosureOnlyRunnable) {
+ if (waitClosureOnlyEventTarget) {
+ waitClosureOnlyEventTarget->Dispatch(waitClosureOnlyRunnable,
+ NS_DISPATCH_NORMAL);
+ } else {
+ waitClosureOnlyRunnable->Run();
+ }
+ }
+
+ return NS_OK;
+}
+
+// nsIInputStream interface
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Available(uint64_t* aLength) {
+ nsresult rv = mInputStream->Available(aLength);
+ // Don't issue warnings for legal condition NS_BASE_STREAM_CLOSED.
+ if (rv == NS_BASE_STREAM_CLOSED || NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // Nothing more to read. Let's close the stream now.
+ if (*aLength == 0) {
+ mInputStream->Close();
+ mClosed = true;
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Read(char* aBuffer, uint32_t aCount,
+ uint32_t* aReadCount) {
+ return mInputStream->Read(aBuffer, aCount, aReadCount);
+}
+
+namespace {
+
+class MOZ_RAII ReadSegmentsData {
+ public:
+ ReadSegmentsData(NonBlockingAsyncInputStream* aStream,
+ nsWriteSegmentFun aFunc, void* aClosure)
+ : mStream(aStream), mFunc(aFunc), mClosure(aClosure) {}
+
+ NonBlockingAsyncInputStream* mStream;
+ nsWriteSegmentFun mFunc;
+ void* mClosure;
+};
+
+nsresult ReadSegmentsWriter(nsIInputStream* aInStream, void* aClosure,
+ const char* aFromSegment, uint32_t aToOffset,
+ uint32_t aCount, uint32_t* aWriteCount) {
+ ReadSegmentsData* data = static_cast<ReadSegmentsData*>(aClosure);
+ return data->mFunc(data->mStream, data->mClosure, aFromSegment, aToOffset,
+ aCount, aWriteCount);
+}
+
+} // namespace
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure, uint32_t aCount,
+ uint32_t* aResult) {
+ ReadSegmentsData data(this, aWriter, aClosure);
+ return mInputStream->ReadSegments(ReadSegmentsWriter, &data, 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.forget(), 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) {
+ RefPtr<AsyncWaitRunnable> runnable;
+ {
+ MutexAutoLock lock(mLock);
+
+ if (aCallback && (mWaitClosureOnly.isSome() || mAsyncWaitCallback)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aCallback) {
+ // Canceling previous callbacks.
+ mWaitClosureOnly.reset();
+ mAsyncWaitCallback = nullptr;
+ return NS_OK;
+ }
+
+ // Maybe the stream is already closed.
+ if (!mClosed) {
+ uint64_t length;
+ nsresult rv = mInputStream->Available(&length);
+ if (NS_SUCCEEDED(rv) && length == 0) {
+ mInputStream->Close();
+ mClosed = true;
+ }
+ }
+
+ runnable = new AsyncWaitRunnable(this, aCallback);
+ if ((aFlags & nsIAsyncInputStream::WAIT_CLOSURE_ONLY) && !mClosed) {
+ mWaitClosureOnly.emplace(runnable, aEventTarget);
+ return NS_OK;
+ }
+
+ mAsyncWaitCallback = runnable;
+ }
+
+ MOZ_ASSERT(runnable);
+
+ if (aEventTarget) {
+ return aEventTarget->Dispatch(runnable.forget());
+ }
+
+ return runnable->Run();
+}
+
+// nsIIPCSerializableInputStream
+
+void NonBlockingAsyncInputStream::Serialize(
+ mozilla::ipc::InputStreamParams& aParams,
+ FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
+ uint32_t aMaxSize, uint32_t* aSizeUsed,
+ mozilla::ipc::ParentToChildStreamActorManager* aManager) {
+ SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
+ aSizeUsed, aManager);
+}
+
+void NonBlockingAsyncInputStream::Serialize(
+ mozilla::ipc::InputStreamParams& aParams,
+ FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
+ uint32_t aMaxSize, uint32_t* aSizeUsed,
+ mozilla::ipc::ChildToParentStreamActorManager* aManager) {
+ SerializeInternal(aParams, aFileDescriptors, aDelayedStart, aMaxSize,
+ aSizeUsed, aManager);
+}
+
+template <typename M>
+void NonBlockingAsyncInputStream::SerializeInternal(
+ mozilla::ipc::InputStreamParams& aParams,
+ FileDescriptorArray& aFileDescriptors, bool aDelayedStart,
+ uint32_t aMaxSize, uint32_t* aSizeUsed, M* aManager) {
+ MOZ_ASSERT(mWeakIPCSerializableInputStream);
+ InputStreamHelper::SerializeInputStream(mInputStream, aParams,
+ aFileDescriptors, aDelayedStart,
+ aMaxSize, aSizeUsed, aManager);
+}
+
+bool NonBlockingAsyncInputStream::Deserialize(
+ const mozilla::ipc::InputStreamParams& aParams,
+ const FileDescriptorArray& aFileDescriptors) {
+ MOZ_CRASH("NonBlockingAsyncInputStream cannot be deserialized!");
+ return true;
+}
+
+// nsISeekableStream
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Seek(int32_t aWhence, int64_t aOffset) {
+ NS_ENSURE_STATE(mWeakSeekableInputStream);
+ return mWeakSeekableInputStream->Seek(aWhence, aOffset);
+}
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::SetEOF() {
+ NS_ENSURE_STATE(mWeakSeekableInputStream);
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsITellableStream
+
+NS_IMETHODIMP
+NonBlockingAsyncInputStream::Tell(int64_t* aResult) {
+ NS_ENSURE_STATE(mWeakTellableInputStream);
+ return mWeakTellableInputStream->Tell(aResult);
+}
+
+void NonBlockingAsyncInputStream::RunAsyncWaitCallback(
+ NonBlockingAsyncInputStream::AsyncWaitRunnable* aRunnable,
+ already_AddRefed<nsIInputStreamCallback> aCallback) {
+ nsCOMPtr<nsIInputStreamCallback> callback = std::move(aCallback);
+
+ {
+ MutexAutoLock lock(mLock);
+ if (mAsyncWaitCallback != aRunnable) {
+ // The callback has been canceled in the meantime.
+ return;
+ }
+
+ mAsyncWaitCallback = nullptr;
+ }
+
+ callback->OnInputStreamReady(this);
+}
+
+} // namespace mozilla