Skip to content

🧵 Improve synchronization of connection_state transitions #494

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Jun 19, 2025

Conversation

nevans
Copy link
Collaborator

@nevans nevans commented Jun 19, 2025

Handles a few more connection_state synchronization issues I saw while working on #493.

While attempting to get the tests running under JRuby, #disconnect would sometimes deadlock. So, although I haven't seen that problem elsewhere and JRuby is still failing tests with another threading issue, this did get most tests passing for me (but not in CI).

nevans added 9 commits June 19, 2025 11:58
If `disconnected?` returns true, the connection state is most likely
already `logout`.  But, just in case, we'll ensure it's set every time.
Net::IMAP#disconnect will still attempt to enter the logout state first,
but it won't wait for the lock until after the socket has been shutdown.
The reciever thread should close the connection before it finishes, and
`@sock.shutdown` should've been enough to trigger that.  But this
ensures the socket is closed right away, without needing to wait on
whatever the receiver thread might be in the middle of doing.
It should always be safe to call `@sock.close` without external
synchronization.  And synchronizing here with could get stuck
waiting e.g. for a response_handler looping in the receiver thread.
This should work both for TCPSocket and for OpenSSL::SSL::SSLSocket.
Both will respond to `#to_io` with the TCPSocket.
Eventually the states will store small bits of information about each
state on the state objects, e.g: which mailbox was selected or what
caused the logout.  So for `logout`, the first attempt wins and
shouldn't be overwritten.  For other states, like `selected`, the last
attempt _should_ win.

On the other hand, state transitions should be reworked so that:
* state transitioning commands cannot run simultaneously
* all state transitions are effected _inside_ the receiver thread
This pushes `ensure state_logout!` from the receiver's `Thread.start`
down into `#receive_responses`.  As a side-effect, this also pulls
the state change inside the receiver thread's exception handling.  But
that's fine: it fits better inside `receive_responses`, and
`state_logout!` really should never raise an exception anyway.
@nevans nevans merged commit 0c89bb1 into master Jun 19, 2025
37 checks passed
@nevans nevans deleted the synchronize-state-transitions branch June 19, 2025 16:06
@nevans nevans added the bug Something isn't working label Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant