Repository navigation
Fix disconnect() while reconnecting waiting for the pong timeout - #378
Keith-wright wants to merge 2 commits into
Conversation
…connecting In the RECONNECTING state the previous socket has already closed, so close() never produces an onClose callback and the connection stayed in DISCONNECTING until the ping and pong timers ran out. Transition straight to DISCONNECTED instead, and cancel the pending reconnect timer. Fixes #376
dchankhour-godaddy
left a comment
There was a problem hiding this comment.
LGTM, I'd approve, but GitHub only allows approvals from accounts with repository access.
Thanks for picking up #376. I reviewed this against the full WebSocketConnection and Factory sources, and it fixes the case we reported. With disconnect() called during RECONNECTING, the connection now reaches DISCONNECTED immediately and its threads shut down, instead of waiting out the ping/pong timeouts. Cancelling reconnectTimer matters here because shutdownThreads() uses timers.shutdown(), and ScheduledThreadPoolExecutor still runs delayed tasks that were already scheduled after shutdown by default.
A few follow-ups. I've left inline comments on the first two:
- Reconnect timer race (existed before this PR). The reconnect runnable runs on the
timersthread outsideeventLock, so itsstate == RECONNECTINGcheck isn't serialized withdisconnect(). Details are inline. - The PR description doesn't match the diff. The CHANGELOG section lists six entries (#372, #367, #350, #371, #359), but the diff only contains the #376 fix and doesn't touch
CHANGELOG.md. Also,Closes #374would auto-close "Maintenance status for this repo" when this merges, and that issue looks unrelated. Could you trim the description so only the relevant issue gets closed? CONNECTINGisn't covered. That seems reasonable:close()on a socket that's mid-handshake should still produceonClose, although it can be delayed if the TCP connect is blocked. It might be worth mentioning in the PR.- Test suggestion. Capture the scheduled reconnect runnable, run it after
disconnect(), and assert thatnewWebSocketClientWrapperisn't called a second time. That covers the cancel/race path end-to-end.
It would be great to see this in a 2.4.5 release.
The reconnect task ran on the timers thread, so its RECONNECTING check could race with disconnect() and open a new socket after the connection was closed. Queue the check onto the event thread instead. Also remove the old socket's listener when disconnecting from RECONNECTING, and add a test that a reconnect task running after disconnect() does not open a new socket.
|
Thanks for the thorough review, @dchankhour-godaddy . I moved the reconnect check onto the event thread as you suggested, and disconnect() now removes the old socket's listener too. Both changes are in the latest commit. I've added a note about the CONNECTING case to the description. The CHANGELOG section is intentional btw. This PR will carry the 2.4.5 release, and our release automation builds the changelog from this PR's description, so it lists changes from the other PRs in the release. I've added a note to the description to explain this. "Closes #374" stays as well, as that issue asks whether the library is still maintained, and this work and the subsequent 2.4.5 release resolves that issue. Lastly, I added a test that runs the captured reconnect task after disconnect() and checks that no second socket is created. |
|
Thanks @Keith-wright, I re-reviewed
One tiny non-blocking nit: if the reconnect task fires after The CI |
What does this PR do?
Fixes #376. Calling disconnect() while the connection was RECONNECTING left it stuck in DISCONNECTING until the ping and pong timers ran out, which takes up to activityTimeout plus pongTimeout.
In the RECONNECTING state the previous socket has already closed, so calling close() on it never produces an onClose callback. disconnect() now moves straight to DISCONNECTED in that state and cancels the pending reconnect timer, so the timer thread shuts down as well.
The reconnect task now checks the connection state on the event thread, so it can't run at the same time as disconnect() and open a new socket after the connection was closed. This race existed before this PR.
disconnect() during CONNECTING isn't changed. Closing a socket in the middle of its handshake still produces onClose, although it can be delayed if the TCP connect is blocked.
It also adds tests for these cases.
This PR will carry the 2.4.5 release. The release automation builds the changelog from this PR's description, so the CHANGELOG section lists everything in the release, including changes from other PRs.
Closes #374.
CHANGELOG