NS_IMETHODIMP nsInputStreamPump::Cancel(nsresult status) { LOG(("nsInputStreamPump::Cancel [this=%x status=%x]\n", this, status)); if (NS_FAILED(mStatus)) { LOG((" already canceled\n")); return NS_OK; } NS_ASSERTION(NS_FAILED(status), "cancel with non-failure status code"); mStatus = status; // close input stream if (mAsyncStream) { mAsyncStream->CloseWithStatus(status); if (mSuspendCount == 0) EnsureWaiting(); // Otherwise, EnsureWaiting will be called by Resume(). // Note that while suspended, OnInputStreamReady will // not do anything, and also note that calling asyncWait // on a closed stream works and will dispatch an event immediately. } return NS_OK; }
NS_IMETHODIMP nsInputStreamPump::Resume() { LOG(("nsInputStreamPump::Resume [this=%x]\n", this)); NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); NS_ENSURE_TRUE(mState != STATE_IDLE, NS_ERROR_UNEXPECTED); if (--mSuspendCount == 0) EnsureWaiting(); return NS_OK; }
NS_IMETHODIMP nsInputStreamPump::Resume() { ReentrantMonitorAutoEnter mon(mMonitor); LOG(("nsInputStreamPump::Resume [this=%p]\n", this)); NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED); NS_ENSURE_TRUE(mState != STATE_IDLE, NS_ERROR_UNEXPECTED); if (--mSuspendCount == 0) EnsureWaiting(); return NS_OK; }
NS_IMETHODIMP nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream) { LOG(("nsInputStreamPump::OnInputStreamReady [this=%x]\n", this)); SAMPLE_LABEL("Input", "nsInputStreamPump::OnInputStreamReady"); // this function has been called from a PLEvent, so we can safely call // any listener or progress sink methods directly from here. for (;;) { if (mSuspendCount || mState == STATE_IDLE) { mWaiting = false; break; } PRUint32 nextState; switch (mState) { case STATE_START: nextState = OnStateStart(); break; case STATE_TRANSFER: nextState = OnStateTransfer(); break; case STATE_STOP: nextState = OnStateStop(); break; default: nextState = 0; NS_NOTREACHED("Unknown enum value."); return NS_ERROR_UNEXPECTED; } if (mState == nextState && !mSuspendCount) { NS_ASSERTION(mState == STATE_TRANSFER, "unexpected state"); NS_ASSERTION(NS_SUCCEEDED(mStatus), "unexpected status"); mWaiting = false; mStatus = EnsureWaiting(); if (NS_SUCCEEDED(mStatus)) break; nextState = STATE_STOP; } mState = nextState; } return NS_OK; }
NS_IMETHODIMP nsInputStreamPump::AsyncRead(nsIStreamListener *listener, nsISupports *ctxt) { NS_ENSURE_TRUE(mState == STATE_IDLE, NS_ERROR_IN_PROGRESS); NS_ENSURE_ARG_POINTER(listener); // // OK, we need to use the stream transport service if // // (1) the stream is blocking // (2) the stream does not support nsIAsyncInputStream // bool nonBlocking; nsresult rv = mStream->IsNonBlocking(&nonBlocking); if (NS_FAILED(rv)) return rv; if (nonBlocking) { mAsyncStream = do_QueryInterface(mStream); // // if the stream supports nsIAsyncInputStream, and if we need to seek // to a starting offset, then we must do so here. in the non-async // stream case, the stream transport service will take care of seeking // for us. // if (mAsyncStream && (mStreamOffset != LL_MAXUINT)) { nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mStream); if (seekable) seekable->Seek(nsISeekableStream::NS_SEEK_SET, mStreamOffset); } } if (!mAsyncStream) { // ok, let's use the stream transport service to read this stream. nsCOMPtr<nsIStreamTransportService> sts = do_GetService(kStreamTransportServiceCID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsITransport> transport; rv = sts->CreateInputTransport(mStream, mStreamOffset, mStreamLength, mCloseWhenDone, getter_AddRefs(transport)); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIInputStream> wrapper; rv = transport->OpenInputStream(0, mSegSize, mSegCount, getter_AddRefs(wrapper)); if (NS_FAILED(rv)) return rv; mAsyncStream = do_QueryInterface(wrapper, &rv); if (NS_FAILED(rv)) return rv; } // release our reference to the original stream. from this point forward, // we only reference the "stream" via mAsyncStream. mStream = 0; // mStreamOffset now holds the number of bytes currently read. we use this // to enforce the mStreamLength restriction. mStreamOffset = 0; // grab event queue (we must do this here by contract, since all notifications // must go to the thread which called AsyncRead) mTargetThread = do_GetCurrentThread(); NS_ENSURE_STATE(mTargetThread); rv = EnsureWaiting(); if (NS_FAILED(rv)) return rv; if (mLoadGroup) mLoadGroup->AddRequest(this, nullptr); mState = STATE_START; mListener = listener; mListenerContext = ctxt; return NS_OK; }
NS_IMETHODIMP nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream) { LOG(("nsInputStreamPump::OnInputStreamReady [this=%p]\n", this)); PROFILER_LABEL("nsInputStreamPump", "OnInputStreamReady", js::ProfileEntry::Category::NETWORK); // this function has been called from a PLEvent, so we can safely call // any listener or progress sink methods directly from here. for (;;) { // There should only be one iteration of this loop happening at a time. // To prevent AsyncWait() (called during callbacks or on other threads) // from creating a parallel OnInputStreamReady(), we use: // -- a monitor; and // -- a boolean mProcessingCallbacks to detect parallel loops // when exiting the monitor for callbacks. ReentrantMonitorAutoEnter lock(mMonitor); // Prevent parallel execution during callbacks, while out of monitor. if (mProcessingCallbacks) { MOZ_ASSERT(!mProcessingCallbacks); break; } mProcessingCallbacks = true; if (mSuspendCount || mState == STATE_IDLE) { mWaitingForInputStreamReady = false; mProcessingCallbacks = false; break; } uint32_t nextState; switch (mState) { case STATE_START: nextState = OnStateStart(); break; case STATE_TRANSFER: nextState = OnStateTransfer(); break; case STATE_STOP: mRetargeting = false; nextState = OnStateStop(); break; default: nextState = 0; NS_NOTREACHED("Unknown enum value."); return NS_ERROR_UNEXPECTED; } bool stillTransferring = (mState == STATE_TRANSFER && nextState == STATE_TRANSFER); if (stillTransferring) { NS_ASSERTION(NS_SUCCEEDED(mStatus), "Should not have failed status for ongoing transfer"); } else { NS_ASSERTION(mState != nextState, "Only OnStateTransfer can be called more than once."); } if (mRetargeting) { NS_ASSERTION(mState != STATE_STOP, "Retargeting should not happen during OnStateStop."); } // Set mRetargeting so EnsureWaiting will be called. It ensures that // OnStateStop is called on the main thread. if (nextState == STATE_STOP && !NS_IsMainThread()) { mRetargeting = true; } // Unset mProcessingCallbacks here (while we have lock) so our own call to // EnsureWaiting isn't blocked by it. mProcessingCallbacks = false; // We must break the loop when we're switching event delivery to another // thread and the input stream pump is suspended, otherwise // OnStateStop() might be called off the main thread. See bug 1026951 // comment #107 for the exact scenario. if (mSuspendCount && mRetargeting) { mState = nextState; mWaitingForInputStreamReady = false; break; } // Wait asynchronously if there is still data to transfer, or we're // switching event delivery to another thread. if (!mSuspendCount && (stillTransferring || mRetargeting)) { mState = nextState; mWaitingForInputStreamReady = false; nsresult rv = EnsureWaiting(); if (NS_SUCCEEDED(rv)) break; // Failure to start asynchronous wait: stop transfer. // Do not set mStatus if it was previously set to report a failure. if (NS_SUCCEEDED(mStatus)) { mStatus = rv; } nextState = STATE_STOP; } mState = nextState; } return NS_OK; }
NS_IMETHODIMP nsInputStreamPump::OnInputStreamReady(nsIAsyncInputStream *stream) { LOG(("nsInputStreamPump::OnInputStreamReady [this=%p]\n", this)); PROFILER_LABEL("Input", "nsInputStreamPump::OnInputStreamReady"); // this function has been called from a PLEvent, so we can safely call // any listener or progress sink methods directly from here. for (;;) { if (mSuspendCount || mState == STATE_IDLE) { mWaiting = false; break; } uint32_t nextState; switch (mState) { case STATE_START: nextState = OnStateStart(); break; case STATE_TRANSFER: nextState = OnStateTransfer(); break; case STATE_STOP: mRetargeting = false; nextState = OnStateStop(); break; default: nextState = 0; NS_NOTREACHED("Unknown enum value."); return NS_ERROR_UNEXPECTED; } bool stillTransferring = (mState == STATE_TRANSFER && nextState == STATE_TRANSFER); if (stillTransferring) { NS_ASSERTION(NS_SUCCEEDED(mStatus), "Should not have failed status for ongoing transfer"); } else { NS_ASSERTION(mState != nextState, "Only OnStateTransfer can be called more than once."); } if (mRetargeting) { NS_ASSERTION(mState != STATE_STOP, "Retargeting should not happen during OnStateStop."); } // Set mRetargeting so EnsureWaiting will be called. It ensures that // OnStateStop is called on the main thread. if (nextState == STATE_STOP && !NS_IsMainThread()) { mRetargeting = true; } // Wait asynchronously if there is still data to transfer, or we're // switching event delivery to another thread. if (!mSuspendCount && (stillTransferring || mRetargeting)) { mState = nextState; mWaiting = false; nsresult rv = EnsureWaiting(); if (NS_SUCCEEDED(rv)) break; // Failure to start asynchronous wait: stop transfer. // Do not set mStatus if it was previously set to report a failure. if (NS_SUCCEEDED(mStatus)) { mStatus = rv; } nextState = STATE_STOP; } mState = nextState; } return NS_OK; }