Fix infinite reconnection loop caused by stale WebSocket onclose events

When AttemptReconnect() disconnects a still-CONNECTING socket and creates
a new one, the old socket's onclose fires asynchronously after Connect()
clears m_disconnectedFlag, setting it back to 1. With connectedFlag=1
and disconnectedFlag=1 both stuck, the state machine bounces between
STATE_CONNECTED and STATE_RECONNECTING every frame (30-60x/sec) without
ever reaching the backoff timer or attempt limit.

Guard all WebSocket event handlers to only modify shared flags when
their socket is still the active one in Module._mpSockets.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Semmler 2026-03-24 17:25:06 -07:00
parent 74d9f76f12
commit 08f44b9a93
No known key found for this signature in database
GPG Key ID: 086DAA1360BEEE5C

View File

@ -52,8 +52,10 @@ void WebSocketTransport::Connect(const char* p_roomId)
ws.binaryType = 'arraybuffer'; ws.binaryType = 'arraybuffer';
ws.onopen = function() { ws.onopen = function() {
if (Module._mpSockets[socketId] === ws) {
Atomics.store(HEAP32, connPtr >> 2, 1); Atomics.store(HEAP32, connPtr >> 2, 1);
Atomics.store(HEAP32, everConnPtr >> 2, 1); Atomics.store(HEAP32, everConnPtr >> 2, 1);
}
}; };
ws.onmessage = function(event) { ws.onmessage = function(event) {
@ -64,12 +66,16 @@ void WebSocketTransport::Connect(const char* p_roomId)
}; };
ws.onclose = function() { ws.onclose = function() {
if (Module._mpSockets[socketId] === ws) {
Atomics.store(HEAP32, connPtr >> 2, 0); Atomics.store(HEAP32, connPtr >> 2, 0);
Atomics.store(HEAP32, discPtr >> 2, 1); Atomics.store(HEAP32, discPtr >> 2, 1);
}
}; };
ws.onerror = function() { ws.onerror = function() {
if (Module._mpSockets[socketId] === ws) {
Atomics.store(HEAP32, connPtr >> 2, 0); Atomics.store(HEAP32, connPtr >> 2, 0);
}
}; };
Module._mpSockets[socketId] = ws; Module._mpSockets[socketId] = ws;