fix(masque): avoid polling completed relay task handles - #128
Conversation
Benchmark ResultsPerformance Comparison
SummaryConfiguration
|
ReviewThe bug is real and correctly diagnosed. In the stream forwarding loop, the The fix is correct. The core invariant holds: in
The Blocker 1 guarantee is preserved: the reader task owns the moved Tests are valid regression coverage — each exercises the exact drop-vs-await decision and would panic if the completed handle were re-polled. No blocking concerns. The change is minimal, correct, and well-reasoned. One minor, non-blocking suggestion: the tests cover the extracted LGTM. 👍 |
|
Assessed the integration-test suggestion. I agree with the coverage boundary, but I do not think expanding this PR to construct a live MASQUE session/QUIC stream harness is proportionate. The regression is in the cleanup decision itself, which is exhaustively covered for Reader, Writer and Neither; the production wiring consists of branch-local assignments immediately before handling each irrefutable |
Summary
JoinHandlecompleted throughtokio::select!Why
The relay forwarding loop selected over
reader_handleandwriter_handleby mutable reference, then unconditionally awaited both handles during cleanup. When either handle won the select, its output had already been consumed; awaiting it again panicked with Tokio'sJoinHandle polled after completioninvariant. Inpanic = "abort"consumers such asant-node, this aborts the whole process during relay teardown, including upgrade-triggered graceful shutdown.Closes #108.
Verification
cargo test forwarding_cleanup --lib -- --nocapture— 3 passedcargo test --lib— 1,483 passed, 3 ignoredcargo clippy --all-targets -- -D warnings— passedcargo fmt --all— passedgit diff --check— passedcargo test --all-features— progressed through the suite but timed out at 10 minutes in existing macOS BLE transport tests; no failure was reported before timeoutReview notes
The completed handle is dropped without a second poll. The other handle is aborted and awaited so its task has finished and released its socket clone. If cancellation or the inline stream-to-UDP branch wins, neither task output has been consumed, so both handles are aborted and awaited as before.