tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 26a55e242dd744539791dc24d9b396f22dfed603
parent bc6c7da176c6f6383491b72494307a3593257323
Author: Kershaw Chang <kershaw@mozilla.com>
Date:   Wed, 26 Nov 2025 09:27:03 +0000

Bug 2001721 - Prevent busy loop when tunneled connection returns WOULD_BLOCK, r=necko-reviewers,valentin

When a tunneled connection returns WOULD_BLOCK, the consumer calls AyncWait() and we enqueue the stream on the ready queue. We then call ForceSend() -> ProcessOutput() and immediately attempt to send again, which still would block, causing the stream to be re-queued and retried in a busy loop.

This patch introduces a mBlockedByFlowControl flag that is only cleared when neqo notifies us the stream is writable. While the flag is set, we skip send attempts. This avoids the busy waiting.

Differential Revision: https://phabricator.services.mozilla.com/D274000

Diffstat:
Mnetwerk/protocol/http/Http3Session.cpp | 15+++++++++++++++
Mnetwerk/protocol/http/Http3Stream.cpp | 1+
Mnetwerk/protocol/http/Http3StreamBase.h | 4++++
3 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/netwerk/protocol/http/Http3Session.cpp b/netwerk/protocol/http/Http3Session.cpp @@ -589,6 +589,7 @@ nsresult Http3Session::ProcessEvents() { if (stream) { StreamReadyToWrite(stream); + stream->SetBlockedByFlowControl(false); } } break; case Http3Event::Tag::Reset: @@ -1823,11 +1824,18 @@ nsresult Http3Session::SendData(nsIUDPSocket* socket) { nsresult rv = NS_OK; RefPtr<Http3StreamBase> stream; + nsTArray<RefPtr<Http3StreamBase>> blockedStreams; + // Step 1) while (CanSendData() && (stream = mReadyForWrite.PopFront())) { LOG(("Http3Session::SendData call ReadSegments from stream=%p [this=%p]", stream.get(), this)); stream->SetInTxQueue(false); + if (stream->BlockedByFlowControl()) { + LOG(("stream %p blocked by flow control", stream.get())); + blockedStreams.AppendElement(stream); + continue; + } rv = stream->ReadSegments(); // on stream error we return earlier to let the error be handled. @@ -1863,6 +1871,13 @@ nsresult Http3Session::SendData(nsIUDPSocket* socket) { if (NS_FAILED(rv)) { return rv; } + + // Put the blocked streams back to the queue, since they are ready to write. + for (const auto& stream : blockedStreams) { + mReadyForWrite.Push(stream); + stream->SetInTxQueue(true); + } + rv = ProcessEvents(); // Let the connection know we sent some app data successfully. diff --git a/netwerk/protocol/http/Http3Stream.cpp b/netwerk/protocol/http/Http3Stream.cpp @@ -184,6 +184,7 @@ nsresult Http3Stream::OnReadSegment(const char* buf, uint32_t count, rv = mSession->SendRequestBody(mStreamId, buf, count, countRead); if (rv == NS_BASE_STREAM_WOULD_BLOCK) { mSendingBlockedByFlowControlCount++; + mBlockedByFlowControl = true; } if (NS_FAILED(rv)) { diff --git a/netwerk/protocol/http/Http3StreamBase.h b/netwerk/protocol/http/Http3StreamBase.h @@ -59,6 +59,9 @@ class Http3StreamBase : public SupportsWeakPtr, public ARefBase { void SetInTxQueue(bool aValue) { mInTxQueue = aValue; } bool IsInTxQueue() const { return mInTxQueue; } + void SetBlockedByFlowControl(bool aValue) { mBlockedByFlowControl = aValue; } + bool BlockedByFlowControl() const { return mBlockedByFlowControl; } + protected: ~Http3StreamBase(); @@ -71,6 +74,7 @@ class Http3StreamBase : public SupportsWeakPtr, public ARefBase { bool mFin{false}; bool mResetRecv{false}; bool mInTxQueue{false}; + bool mBlockedByFlowControl{false}; }; } // namespace mozilla::net