Repository navigation
Sockets: make the Qt server's close() and close_connection() cancel in-flight sends - #1832
Merged
Merged
Conversation
pa-devbot
force-pushed
the
qt-server-close-cancel
branch
from
November 3, 2026 09:57
448d16d to
10eaada
Compare
…n-flight sends close() and close_connection() now cancel the in-flight send() calls instead of waiting for them to finish: their callers return promptly with the number of bytes actually written so far (best effort), and only then does close() destroy the QTcpServer (that destruction still has to run on the event thread, so close() itself blocks until the server is destroyed - same as before). close_connection() keeps its 5 s bound on the queued close itself: that bound is about reaching the event thread for state consistency, not about the sends. How it works: - In-flight SendData objects are tracked in a list under m_lock (replacing the old send-in-flight counter). send() adds its entry before emitting the queued write, re-checking under the same lock that the connection is still attached: the packet copy can take a while for large payloads, and without the re-check a send could register after close() already cancelled the (empty) list and queued the server's destruction - its queued write would then be posted after the destruction event, Qt would silently discard it, and the infinite-timeout wait would never end. With the re-check, a send either registers before the cancel (and is cancelled, waking the wait) or sees the detached socket and returns 0 without queueing anything. - close() and close_connection() claim the list and mark every entry done + cancelled, waking the send() callers. The queued write loops see the flag and finish without writing. - The infinite default no longer carries the 5 s safety bound: "done" is now guaranteed to be set by either the event-thread write loop (deadline / socket error / Qt's userspace acceptance) or the cancel, so the wait is always ended. Finite timeouts keep their "timeout + 1 s" queueing margin. - The write loop now reports a running byte count, so a close mid-write reports the bytes written so far instead of always 0. Also adds a test: with the server's event thread blocked for 6 s on the first received data, a send() started on the test thread queues behind the stall, and a close_connection() from a worker thread must wake the send within a few milliseconds of starting. Without the cancel, the send would only wake when the stall ends and the queued write finally runs.
pa-devbot
force-pushed
the
qt-server-close-cancel
branch
from
November 4, 2026 04:01
10eaada to
1bdff22
Compare
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.
Task #4 from the close()-cancel plan: proper refactor of the Qt server so
close()andclose_connection()cancel in-flight sends, per the contract specified in #1830.Behavior
close()andclose_connection()no longer wait for in-flightsend()calls to finish. They cancel them: their callers return promptly with the number of bytes actually written so far (best effort).close()then destroys theQTcpServeron the event thread as before (that destruction is inherent to the object's lifetime, soclose()still blocks until it happens).close_connection()keeps its 5 s bound on the queued close itself: that bound is about reaching the event thread for state consistency (pinned by the completion-lifetime test), not about the sends.send()no longer carries the 5 s safety bound:doneis now guaranteed to be set by either the event-thread write loop (deadline / socket error / Qt's userspace acceptance) or the cancel, so the wait is always ended. Finite timeouts keep theirtimeout + 1 squeueing margin.How it works
SendDataobjects are tracked in a list underm_lock(replaces the old in-flight counter).send()adds its entry before emitting the queued write.close()andclose_connection()claim the list and mark every entrydone+cancelled, waking thesend()callers. The queued write loops see the flag and finish without writing.QSharedPointerkeeps each packet alive until its queued call runs (or is discarded with the server); the event loop is FIFO, so a queued write still runs before the destruction and just no-ops.Test
New Qt-only test: with the server's event thread blocked for 6 s on the first received data (a 256 KB trigger from a client), a
send()started on the test thread queues behind the stall, and aclose_connection()from a worker thread must wake the send within a few milliseconds of starting (it asserts the lag betweenclose_connection()starting and the send returning). Without the cancel, the send would only wake when the 6 s stall ends and the queued write finally runs. The test then polls for the resulting LISTENING state within the shared 60 s budget, sinceclose_connection()may return with the close still queued behind the stall (its 5 s bound).Note for the merge: this touches
Sockets_Tests.cppin the same region as #1831 (the test registrations are adjacent), so it may need a rebase onto main if #1831 merges first. The production-code changes are disjoint from #1831.Full local suite green (20,330 / 0 / 3). No changes to the POSIX/Win backends.