Repository navigation
Sockets: fixes to ClientSocket_POSIX - #1783
Merged
Merged
Conversation
Mysticial
force-pushed
the
sockets-posix-disconnect
branch
2 times, most recently
from
October 31, 2026 12:43
df7d0e6 to
51484f3
Compare
Ports the same state-machine fixes as the WinSocket PR to the POSIX implementation: - Detect connection loss: recv()==0 (graceful close) and fatal recv() errors (ECONNRESET, ENOTCONN, ...) now fire on_disconnect() and return the socket to NOT_RUNNING instead of exiting the thread silently with the state stuck at CONNECTED. - Close connections properly: close() now joins the receive thread, closes the socket, and returns to NOT_RUNNING so connect() can be called again. It is safe to call multiple times. - New sockets per connect(): a socket whose previous connection ended cannot be reconnected, so connect() always starts from a fresh socket. m_socket is now std::atomic since the receive thread and send() share it. - Non-blocking connect done correctly: only one connect() call; while in progress, wait with poll() for the socket to become writable and check SO_ERROR for the result. (Calling connect() again while in progress returns EISCONN on Linux even when the attempt is still pending or will fail, which the old retry loop treated as success - connecting to a dead port marked the socket CONNECTED with an empty error.) - Connect failures (refused, unreachable, ...) now report the error through on_connect_finished() and return to NOT_RUNNING.
Now that dispatch() spawns threads as needed, the receive thread no longer needs dispatch_now_blocking(). The blocking variant can also deadlock if connect() is called from a thread that is itself running on a capped pool, since it blocks until a pool thread is free.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the socket state-machine fixes; the
on_disconnect()hook landed in #1780.What was wrong
recv()==0(graceful close) was unhandled, and fatalrecv()errors (ECONNRESET,ENOTCONN, ...) just exited the receive thread with the state stuck atCONNECTED. The UI stayed green andsend()silently no-oped until the object was destroyed.close()was a flag flip. It setDESTRUCTINGand returned without joining the receive thread or closing the fd; the state could never get back to a reusable value.connect()every 10 ms and treatedEISCONNas "connected". On Linux, callingconnect()while an attempt is in progress returnsEISCONNeven if the attempt is still pending or about to fail, so connecting to a dead port marked the socketCONNECTEDwith an empty error (verified against the kernel directly).const. A socket whose connection ended cannot be reconnected, but there was no way to get a new one.What this does
recv()==0and fatalrecv()errors now fireon_disconnect(error_message)and return the socket toNOT_RUNNINGsoconnect()can be called again.close()now joins the receive thread, closes the socket, and returns toNOT_RUNNING. It is safe to call multiple times and must not be called from within a listener callback (same contract as Sockets: fixes to ClientSocket_WinSocket #1774).connect()always starts from a fresh socket;m_socketisstd::atomic<int>since the receive thread andsend()share it.connect()call, thenpoll()for writability andgetsockopt(SO_ERROR)for the result. Refused/unreachable/etc. report throughon_connect_finished(error_message).Testing
Full functional test against a real TCP server (loopback): connect, send, receive (including a >4 KB burst), graceful close, RST, refused connect, reconnect without
close(),close()+ reconnect, and 100 rapid connect/disconnect cycles - all pass.pa-build+pa-testpass on Linux.