A task ended from outside (JoinHandle::abort, as a server's shutdown ends a task past its
bound) drops the RIST listener it holds unclosed. That drop only tells the receiver's task,
which holds the sockets, to go: the port stays bound until a worker has run it to its end.
`RistListener::closer` now gives a `RistCloser`. It closes the listener the way `close` does
(the receiver's task ended and awaited), from outside the task that holds it, after that task
has been ended. Two closes of one listener at once both return once it is closed, and a close
cut short leaves the task to the next. `close` and the drop behave as before.
tests/listener_close.rs holds it for each profile, from a task on a runtime of one worker, 40
rounds, with a flow connected every other round. The listener's task is ended, the closer
closes the listener, and both ports bind at once.
The file's tests now take fixed ports below the range a system hands out for port 0. On a
shared Linux host, 5 of 1000 runs of the binary failed with the earlier ports, the unchanged
SRT check among them: a port first handed out for port 0 went to another socket between a close
and the check. With the low ports: 1000 of 1000 pass on Linux and 300 of 300 on macOS.
Fail-first: the new test was run on unfixed origin/main (b56b175e9, Linux) with the closer
left out, the drop alone being all that tree offers. Both profiles failed at round 1:
"127.0.0.1:21232 is still bound when the close returns: Address already in use (os error 98)".
Co-Authored-By: Claude Opus 5.5 <[email protected]>