forked from mirrors/gecko-dev
		
	 2bd348aaf4
			
		
	
	
		2bd348aaf4
		
	
	
	
	
		
			
			Differential Revision: https://phabricator.services.mozilla.com/D69656 --HG-- extra : moz-landing-system : lando
		
			
				
	
	
		
			400 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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
 |