Commit 2e0719d
committed
fix(qwp): I/O thread disposes its client on exit; a connect racing close() discards instead of installing (C5)
Root cause of the C5 leak: ownership hand-off between close() and an
in-flight connect attempt was broken. close() swallows the
InterruptedException from shutdownLatch.await() (routine under
BackgroundDrainerPool.close()'s shutdownNow escalation, now that
Invariant-B drainers retry outages indefinitely) and returns while the
I/O thread is still inside a blocking native connect (connect_timeout
defaults to 0 = OS timeout; neither unpark nor interrupt cancels
connect(2)). Its client read saw the pre-connect field; a subsequently
succeeding reconnect() then installed a live client via swapClient into
the abandoned loop, and no code path ever closed it -- native socket,
fds and buffers leaked for the life of the process.
Fix, in two layers, both on the I/O thread (close() stays
interrupt-responsive; the re-await alternative would block user close()
calls for up to the OS connect timeout):
1. connectLoop: a client obtained after running flipped false is closed
and discarded instead of installed. This also stops the routine
post-teardown engine access via swapClient -> positionCursorAt ->
Unsafe reads against segments the drainer's finally may have already
unmapped (the SEGV half of C5).
2. ioLoop's finally: the I/O thread disposes whatever client it holds
before counting down the shutdown latch -- the airtight backstop for
running flipping false between connectLoop's check and swapClient
completing. Duplicate closes (loop.close()'s own, owners' stale
references) remain no-ops: WebSocketClient.close() is idempotent.
Turns CursorWebSocketSendLoopInterruptedCloseLeakTest green. Regression
net: sf/cursor (309), qwp/client (895), impl + http/client (146) -- all
green.
Residual (out of scope here): the drainer-side teardown-overlap window
-- interrupt landing in the nanoseconds between the running check and
swapClient while the engine unmaps -- needs the ioThreadStopped-style
guard in BackgroundDrainer plus a close() that signals a failed stop;
tracked as the SEGV half of the C5 review finding.1 parent f6dfb32 commit 2e0719d
1 file changed
Lines changed: 45 additions & 3 deletions
File tree
- core/src/main/java/io/questdb/client/cutlass/qwp/client/sf/cursor
Lines changed: 45 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
586 | 586 | | |
587 | 587 | | |
588 | 588 | | |
589 | | - | |
590 | | - | |
591 | | - | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
592 | 594 | | |
593 | 595 | | |
594 | 596 | | |
| |||
898 | 900 | | |
899 | 901 | | |
900 | 902 | | |
| 903 | + | |
| 904 | + | |
| 905 | + | |
| 906 | + | |
| 907 | + | |
| 908 | + | |
| 909 | + | |
| 910 | + | |
| 911 | + | |
| 912 | + | |
| 913 | + | |
| 914 | + | |
| 915 | + | |
| 916 | + | |
| 917 | + | |
| 918 | + | |
| 919 | + | |
| 920 | + | |
| 921 | + | |
| 922 | + | |
| 923 | + | |
901 | 924 | | |
902 | 925 | | |
903 | 926 | | |
| |||
1189 | 1212 | | |
1190 | 1213 | | |
1191 | 1214 | | |
| 1215 | + | |
| 1216 | + | |
| 1217 | + | |
| 1218 | + | |
| 1219 | + | |
| 1220 | + | |
| 1221 | + | |
| 1222 | + | |
| 1223 | + | |
| 1224 | + | |
| 1225 | + | |
| 1226 | + | |
| 1227 | + | |
| 1228 | + | |
| 1229 | + | |
| 1230 | + | |
| 1231 | + | |
| 1232 | + | |
| 1233 | + | |
1192 | 1234 | | |
1193 | 1235 | | |
1194 | 1236 | | |
| |||
0 commit comments