netcode-revival: receive timeout on the relay transport socket
Some checks are pending
Lint & Format Check / Format Check (push) Waiting to run

The Run thread blocked in recvfrom with no SO_RCVTIMEO, so when there was no
inbound game traffic the 4s heartbeat never fired (the relay swept the
silent transport client after ~8s) AND StopLocked()'s join() deadlocked
SetRelay on a room change (hanging the ghost network loop). A 200ms receive
timeout wakes the loop regularly: the heartbeat keeps the transport alive
and shutdown/room-change joins complete promptly.
This commit is contained in:
MiTHRAL 2026-08-28 13:28:58 -04:00
parent 01fe706c13
commit 3ec27d115b

View file

@ -60,6 +60,23 @@ void SocketClose(int64_t s) {
#endif
}
// The receive loop must wake regularly so the heartbeat fires even with no
// inbound traffic (otherwise the relay sweeps a silent transport client
// after ~8s) and so StopLocked()'s join completes promptly instead of
// deadlocking SetRelay on a room change.
void SocketSetRecvTimeout(int64_t s, int timeout_ms) {
#if REX_PLATFORM_WIN32
DWORD tv = DWORD(timeout_ms);
::setsockopt(SOCKET(s), SOL_SOCKET, SO_RCVTIMEO,
reinterpret_cast<const char*>(&tv), sizeof(tv));
#else
timeval tv = {};
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
::setsockopt(int(s), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
#endif
}
bool SocketSendTo(int64_t s, const sockaddr_in* to, const void* buf, int size) {
#if REX_PLATFORM_WIN32
const int n = ::sendto(SOCKET(s), reinterpret_cast<const char*>(buf), size, 0,
@ -176,6 +193,8 @@ void XNetTransport::Reconnect() {
REXLOG_WARN("xnet transport: cannot open relay socket");
return;
}
// Wake the receive loop every 200ms (heartbeat + prompt shutdown).
SocketSetRecvTimeout(sock_, 200);
REXLOG_INFO("xnet transport: connecting to relay '{}:{}' room '{}' (vip {}.{}.{}.{})", host_,
port_, room_.c_str(), (LocalVirtualIp() >> 24) & 0xFF, (LocalVirtualIp() >> 16) & 0xFF,
(LocalVirtualIp() >> 8) & 0xFF, LocalVirtualIp() & 0xFF);