Give a message up only once every listener has, and only count a response the wire took

This commit is contained in:
2026-09-02 12:36:01 +02:00
parent 3aadb64df8
commit 3eeb0b82c0
5 changed files with 88 additions and 22 deletions
+10 -9
View File
@@ -351,15 +351,16 @@ Grouped by what each one constrains.
drain waits for. Counting every inbound request until `sendReturn()` answered it was rejected —
an `onRequest` that deliberately answers nothing would then cost a full `shutdownTimeout` on every
close — and a message no listener took is released at once, since nothing is going to answer it.
A listener that threw or rejected before answering releases it the same way, for the same reason,
with `sessionError` carrying the failure. What ends the wait is the response reaching the wire, not
the call: a `sendResp()` the library refused leaves the message held, so `close()` still reports the
one the peer is owed. `teardown()` drops what is still held for the same reason it drops inbound
segments. The release
is one turn late, so a listener that sends its receipt straight after the response is still
holding when the drain looks; `sendDlr()` is the one send that goes out past the drain's refusal,
and only while the message is still held — past that it is an ordinary send, because the drain it
would slip past is no longer waiting for it. `shutdownTimeout: 0` does not carry over to this half:
A listener that failed before answering gives it up the same way, but only once every listener has:
a throw stops `emit()` where it stands, while a rejection leaves the others running, so the release
waits for the last of them rather than answering on their behalf. What ends the wait is the response
reaching the wire, not the call — a `sendResp()` the library refused, or one the socket would not
carry, leaves the message held, so `close()` still reports the one the peer is owed. `teardown()`
drops what is still held for the same reason it drops inbound segments. The release is one turn
late, so a listener that sends its receipt straight after the response is still holding when the
drain looks; `sendDlr()` is the one send that goes out past the drain's refusal, and only while the
message is still held — past that it is an ordinary send, because the drain it would slip past is
no longer waiting for it. `shutdownTimeout: 0` does not carry over to this half:
waiting forever is safe for the peer, whose every request is bounded by `responseTimeout` unless the
caller set that to 0 as well, and unsafe for the application, which nothing bounds — `close()` is
what you reach for when the application is stuck, so it may not block on the application coming