Fix write to check poll_ready before sending data - #6
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces support for QPACK dynamic tables in the HTTP/3 implementation, including state synchronization, encoder/decoder stream handling, and blocked stream management. It also transitions the QUIC stream sending API from a synchronous send_data to an asynchronous poll_send_data model across h3, h3-quinn, and h3-webtransport. Feedback on these changes highlights a critical issue in h3/src/connection.rs where calling self.waker().wake() unconditionally inside poll_qpack_encoder can trigger an infinite busy-loop and 100% CPU usage when a partial QPACK instruction is received; the waker should instead only be signaled when progress is actually made.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
I am having trouble creating individual review comments. Click here to see my feedback.
h3/src/connection.rs (599-611)
Calling self.waker().wake() unconditionally on every iteration of the loop in poll_qpack_encoder can cause an infinite busy-loop (100% CPU usage) when a partial QPACK instruction is received.
Specifically, if encoder_recv contains some bytes but not enough to form a complete instruction, decoder.on_encoder_recv will consume 0 bytes and return Ok. Since encoder_recv.buf().remaining() == before is true, the loop will break and return Poll::Pending. However, because self.waker().wake() was already called, the executor will immediately poll the connection task again, repeating the same process and busy-looping until more data arrives to complete the instruction.
We should only call self.waker().wake() if we actually made progress (i.e., after checking that encoder_recv.buf().remaining() != before).
if let Err(err) = decode_result {
return Poll::Ready(Err(self.handle_connection_error(
InternalConnectionError::new(
Code::QPACK_ENCODER_STREAM_ERROR,
format!("invalid QPACK encoder stream instruction: {}", err),
),
)));
}
if encoder_recv.buf().remaining() == before {
break;
}
self.waker().wake();Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
No description provided.