@@ -83,8 +83,8 @@ protected boolean isPlatformSupported() {
8383 * reading - so a closed result is final and only an open/matching result needs the retry.
8484 * The same real pass also produces the closed reading on its own, so a closed-direction probe
8585 * (a negated gate reading) must be bracketed by a check that the seeded state is still in force
86- * (see {@link #rootEnumClosedWithSeedIntact(long)}) - otherwise a closed result caused by a
87- * real pass would be accepted instead of retried.
86+ * (see {@link #rootEnumClosedWithSeedIntact(long, Runnable )}) - otherwise a closed result
87+ * caused by a real pass would be accepted instead of retried.
8888 */
8989 private static boolean eventuallyTrue (String message , Runnable seed ,
9090 BooleanSupplier probe )
@@ -102,16 +102,35 @@ private static boolean eventuallyTrue(String message, Runnable seed,
102102
103103 /**
104104 * Closed-direction root-enum probe (see eventuallyTrue): a closed gate counts only while the
105- * seeded floor is still in force. A concurrent real enumeration also closes the gate (it
106- * refreshes _last_root_enum_ns to "now"), so a bare closed reading cannot tell the seeded floor
107- * apart from that unrelated close. The two JNI calls take the engine lock separately; a real
108- * pass landing between them either changes the floor (the equality fails and the probe
109- * retries) or leaves it unchanged (the closed reading is still what the seeded floor predicts),
110- * so that window can cause a retry, never a false accept.
105+ * seeded state is still in force, verified by a floor + pass-counter bracket around the gate
106+ * read. A concurrent real enumeration also closes the gate (it refreshes _last_root_enum_ns to
107+ * "now"), so a bare closed reading cannot tell the seeded floor apart from that unrelated
108+ * close - and a floor-only bracket is not enough either: a real enumeration measuring a cost
109+ * that re-derives the same floor (e.g. ~0 ms on a small graph vs the seeded 0 cost) would
110+ * reproduce the seeded floor while the closed reading was actually caused by that enumeration.
111+ *
112+ * <p>The bracket is structurally airtight for pass-driven taint: real root enumeration runs
113+ * ONLY inside a BFS pass ({@code runPassSerialized()} holds {@code _engine_lock} across the
114+ * whole {@code runPass()}), and {@code _passes_run} increments only after the manual walk
115+ * returns - so enumeration state and the pass counter flip together, and the probe's
116+ * engine-lock-acquiring gate/floor reads can never observe one without the other: a completed
117+ * pass always flips {@code p0 != p1}, and an enumeration whose measured cost shifts the floor
118+ * flips {@code f1 != expectedFloorNs}. The probe also re-seeds immediately before reading,
119+ * undoing any real enumeration that already completed and clobbered the seeded state, so only
120+ * an enumeration landing DURING the read sequence can taint it - a microsecond window instead
121+ * of the seed-to-probe gap. Premise: if a future change ever runs root enumeration outside a
122+ * pass (outside {@code runPassSerialized()}'s lock hold), the pass-counter premise breaks and
123+ * this bracket degrades to probabilistic - revisit it then.
111124 */
112- private static boolean rootEnumClosedWithSeedIntact (long expectedFloorNs ) {
113- return !ReferenceChainTestHooks .shouldRunRootEnumForTest0 (false )
114- && ReferenceChainTestHooks .rootEnumIntervalFloorNsForTest0 () == expectedFloorNs ;
125+ private static boolean rootEnumClosedWithSeedIntact (long expectedFloorNs , Runnable seed ) {
126+ seed .run (); // re-seed immediately before reading: undoes any real enumeration that already
127+ // completed and clobbered the seeded state.
128+ int p0 = ReferenceChainTestHooks .referenceChainPassesRunForTest0 ();
129+ long f0 = ReferenceChainTestHooks .rootEnumIntervalFloorNsForTest0 ();
130+ boolean closed = !ReferenceChainTestHooks .shouldRunRootEnumForTest0 (false );
131+ long f1 = ReferenceChainTestHooks .rootEnumIntervalFloorNsForTest0 ();
132+ int p1 = ReferenceChainTestHooks .referenceChainPassesRunForTest0 ();
133+ return closed && p0 == p1 && f0 == expectedFloorNs && f1 == expectedFloorNs ;
115134 }
116135
117136 /** Seed relations the root-enum tests rely on, checked on the Java mirrors themselves. */
@@ -296,10 +315,11 @@ public void rootEnumWaitsForMinimumInterval() throws InterruptedException {
296315 assertRootEnumSeedAssumptions ();
297316 ReferenceChainTestHooks .resetReferenceChainSearchForTest0 ();
298317 try {
318+ Runnable seed = () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
319+ ROOT_ENUM_BEFORE_BASE_MS , 0 );
299320 eventuallyTrue ("Re-enumeration must wait out the base interval since the last attempt" ,
300- () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
301- ROOT_ENUM_BEFORE_BASE_MS , 0 ),
302- () -> rootEnumClosedWithSeedIntact (ROOT_ENUM_MIN_INTERVAL_NS ));
321+ seed ,
322+ () -> rootEnumClosedWithSeedIntact (ROOT_ENUM_MIN_INTERVAL_NS , seed ));
303323 } finally {
304324 ReferenceChainTestHooks .resetReferenceChainSearchForTest0 ();
305325 }
@@ -353,6 +373,8 @@ public void rootEnumIntervalExtendsWithMeasuredSafepointCost() throws Interrupte
353373 assertRootEnumSeedAssumptions ();
354374 ReferenceChainTestHooks .resetReferenceChainSearchForTest0 ();
355375 try {
376+ Runnable seed = () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
377+ ROOT_ENUM_PAST_BASE_MS , ROOT_ENUM_EXTENDING_COST_MS );
356378 // Positive control: the past-base seed alone opens the gate, so the closed reading below
357379 // is attributable to the cost-extended floor.
358380 eventuallyTrue ("A past-base seed with no measured cost must open the gate" ,
@@ -369,10 +391,9 @@ public void rootEnumIntervalExtendsWithMeasuredSafepointCost() throws Interrupte
369391 == ROOT_ENUM_EXTENDING_COST_MS * ROOT_ENUM_NS_PER_COST_MS );
370392 eventuallyTrue ("Re-enumeration must wait out the cost-extended interval even though the "
371393 + "base interval has elapsed" ,
372- () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
373- ROOT_ENUM_PAST_BASE_MS , ROOT_ENUM_EXTENDING_COST_MS ),
394+ seed ,
374395 () -> rootEnumClosedWithSeedIntact (
375- ROOT_ENUM_EXTENDING_COST_MS * ROOT_ENUM_NS_PER_COST_MS ));
396+ ROOT_ENUM_EXTENDING_COST_MS * ROOT_ENUM_NS_PER_COST_MS , seed ));
376397 } finally {
377398 ReferenceChainTestHooks .resetReferenceChainSearchForTest0 ();
378399 }
@@ -385,6 +406,8 @@ public void rootEnumIntervalCapsAtMaximum() throws InterruptedException {
385406 assertRootEnumSeedAssumptions ();
386407 ReferenceChainTestHooks .resetReferenceChainSearchForTest0 ();
387408 try {
409+ Runnable seed = () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
410+ ROOT_ENUM_PAST_BASE_MS , ROOT_ENUM_CAPPING_COST_MS );
388411 // Positive control, as in rootEnumIntervalExtendsWithMeasuredSafepointCost.
389412 eventuallyTrue ("A past-base seed with no measured cost must open the gate" ,
390413 () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
@@ -398,9 +421,8 @@ public void rootEnumIntervalCapsAtMaximum() throws InterruptedException {
398421 () -> ReferenceChainTestHooks .rootEnumIntervalFloorNsForTest0 ()
399422 == ROOT_ENUM_MAX_INTERVAL_NS );
400423 eventuallyTrue ("Re-enumeration must wait out the capped interval" ,
401- () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
402- ROOT_ENUM_PAST_BASE_MS , ROOT_ENUM_CAPPING_COST_MS ),
403- () -> rootEnumClosedWithSeedIntact (ROOT_ENUM_MAX_INTERVAL_NS ));
424+ seed ,
425+ () -> rootEnumClosedWithSeedIntact (ROOT_ENUM_MAX_INTERVAL_NS , seed ));
404426 // An uncapped floor would still hold this age off.
405427 eventuallyTrue ("Re-enumeration must run once the capped interval has elapsed" ,
406428 () -> ReferenceChainTestHooks .setReferenceChainRootEnumStateForTest0 (
0 commit comments