Conversation
* Current coreRequestsClose (the kC4NoFraming requestClose callback) skipped closeRemote whenever state was already CLOSING. On a remote-initiated close, remoteRequestsClose had already made that transition, so the close echo was never sent and HTTPListener::stopTasks() hung. * Add StateMachine.enterState() which will simply return true if already in the state or if it can legally transition to the state. * Use ensureState in coreRequestsClose so the close frame is sent both when we initiate the close (local) and when echoing a close the remote initiated.
|
With the fix, when running URLEndpointListenerTest.testCloseDbWithActiveListeners, we can see the following now: Log : https://gist.github.com/pasin/c6240fd253381e8bde315da3e725d19f |
There was a problem hiding this comment.
Pull request overview
Fixes a WebSocket close-handshake edge case where a remote-initiated close could prevent sending the required close-echo frame, potentially causing HTTPListener::stopTasks() to hang. The PR introduces an idempotent state transition helper and uses it in the close request path so coreRequestsClose still sends the close frame when the socket is already CLOSING.
Changes:
- Added
StateMachine.enterState(...)to support idempotent transitions (no-op if already in target state). - Updated
AbstractCBLWebSocket.coreRequestsClose(...)to use an idempotent transition (ensureState(CLOSING)) so remote-initiated closes still echo a close frame. - Expanded
coreRequestsClosedocumentation to describe local vs remote close flows.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| common/main/java/com/couchbase/lite/internal/utils/StateMachine.java | Adds an idempotent enterState transition helper used to avoid “already in state” failures/logging. |
| common/main/java/com/couchbase/lite/internal/replicator/AbstractCBLWebSocket.java | Ensures coreRequestsClose proceeds when already CLOSING, enabling close-echo on remote-initiated close. |
Comments suppressed due to low confidence (1)
common/main/java/com/couchbase/lite/internal/utils/StateMachine.java:165
- The Javadoc for setState() describes returning the previous state / null, but the method actually returns a boolean. This mismatch is misleading (and now that enterState() references setState(), it’s more likely to be consulted). Update the Javadoc to reflect the boolean return semantics (success/failure).
* Set the new state.
* If it is legal to transition to the new state, from the current state, do so,
* returning the now-previous state. If the transition is illegal do nothing and return null.
*
* @param nextState the requested new state
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Current coreRequestsClose (the kC4NoFraming requestClose callback) skipped closeRemote whenever state was already CLOSING. On a remote-initiated close, remoteRequestsClose had already made that transition, so the close echo was never sent and HTTPListener::stopTasks() hung.
Add StateMachine.enterState() which will simply return true if already in the state or if it can legally transition to the state.
Use ensureState in coreRequestsClose so the close frame is sent both when we initiate the close (local) and when echoing a close the remote initiated.