Skip to content

Commit fee9903

Browse files
committed
test(client): fix closeStarted race in closed-mid-creation lock-probe test
closedMidCreationTeardownRunsOutsideThePoolLock started the pool-closer thread and the idempotent-re-close lock probe with nothing ordering their entry into close(). On a slow scheduler (Windows CI, build 251972) the probe won the closeStarted race, became the primary closer and legitimately parked in the new bounded 5s in-flight-creation wait -- the parked teardown deliberately holds inFlightCreations at 1 -- so probe.join(5s) expired and the lock-free assertion failed even though the pool lock was free the whole time. Wait on the hasCreationWaiterForTesting() seam until the pool-closer has claimed closeStarted and parked in the creation wait (releasing the lock) before starting the probe, making the probe a true re-close. Also swap the reflective markClosing() invoke for the markClosingForTesting() seam. Reproduced by delaying the closer thread 400ms: the old test fails with the exact CI assertion in 5.1s; the fixed test passes in 0.5s under the same delay.
1 parent a167e65 commit fee9903

1 file changed

Lines changed: 16 additions & 3 deletions

File tree

core/src/test/java/io/questdb/client/test/impl/SenderPoolCloseLifecycleTest.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,16 +288,29 @@ public void closedMidCreationTeardownRunsOutsideThePoolLock() throws Exception {
288288

289289
// Raise the same early shutdown signal used by QuestDBImpl so
290290
// the creation deterministically takes the teardown branch.
291-
Method markClosing = SenderPool.class.getDeclaredMethod("markClosing");
292-
markClosing.setAccessible(true);
293-
markClosing.invoke(pool);
291+
pool.markClosingForTesting();
294292

295293
// close() now owns the in-flight creation reservation until
296294
// its closed-mid-creation teardown completes, so run it on a
297295
// separate thread while the creation remains parked.
298296
Thread poolCloser = new Thread(pool::close, "pool-closer");
299297
poolCloser.start();
300298

299+
// Make poolCloser the primary closer DETERMINISTICALLY before
300+
// anything else moves: wait until it has claimed closeStarted
301+
// and parked in the bounded in-flight-creation wait (which
302+
// releases the lock). Without this the lock-probe close()
303+
// below can win the closeStarted race, become the primary
304+
// closer itself and legitimately block in that same 5s wait
305+
// (the parked teardown holds inFlightCreations at 1) -- a
306+
// false "lock held" failure under slow thread scheduling.
307+
long closerParkedDeadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(10);
308+
while (!pool.hasCreationWaiterForTesting()) {
309+
Assert.assertTrue("pool-closer never parked in the in-flight-creation wait",
310+
System.nanoTime() < closerParkedDeadline);
311+
Thread.sleep(1);
312+
}
313+
301314
// Let the creation finish: the borrower re-locks, observes the
302315
// closed pool and starts the delegate teardown, which parks.
303316
releaseCreate.countDown();

0 commit comments

Comments
 (0)