ecfcbb6276
Follow-up on the cherry-picked #106372 fix (@gaoanze888), which reused the existing 10s _WS_WRITE_TIMEOUT_S as the deadline and only latched _closed. Why a separate, longer constant: _WS_WRITE_TIMEOUT_S is the worker-side wait for the LOOP to run the scheduled send; it deliberately does not mark the transport dead because a GIL-heavy turn stalls the loop for >10s routinely (#48445/#55545). The new clock is different in kind — asyncio.wait_for starts it only once the coroutine actually runs on the loop, and a healthy socket returns from send_text without waiting, so only real kernel backpressure can consume it (verified: a loop stall 3x longer than the deadline does not trip wait_for on 3.11/3.12/3.13 because the wakeup and the timer land in the same iteration and the result wins). Still, tying it to the 10s worker constant would let one test-time monkeypatch flip both semantics, and the issue asks explicitly not to declare the peer dead at the 10s worker wait. 30s is 3x the worker wait and under the client's 45s heartbeat deadline. Why close the socket: latching _closed alone leaves handle_ws parked in receive_text on a half-open peer, so the disconnect teardown (session detach/reap, live-transport unregister, client reconnect) never runs — the frontend keeps waiting on a socket that will never carry another frame. ws.close(1011) unblocks the read loop; websockets bounds the close itself (close_timeout -> abort) when the same full buffer stalls the close frame. Diagnostic now says which side stalled: "socket stalled, loop responsive" versus write()'s "loop stalled". Tests trimmed to the two invariants (≤2 per fix): a stalled send terminates within the deadline, fails both queued writes, latches closed and closes the socket; progress → complete → RPC reply ordering is preserved on a healthy socket. The stalled test uses the issue's own shape (two queued write_async calls) and monkeypatches the deadline with raising=False so on a base without the fix it fails on the symptom (sends never terminate), not on a missing attribute. Refs #106369