You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The lifecycle fuzzer accumulated Promise-reaction completions for Thread.asyncJoin(), Atomics.waitAsync(...), and Lock.asyncHold(fn) with shared ordinary numeric += operations. Those reactions may run concurrently on different no-GIL settling or task-pump threads after their jobs settle. The counters therefore contained JavaScript program races, not engine synchronization witnesses.
The initially observed failures were both in the async-hold total and each lost exactly one unique marker:
Six immediate seed-17 reruns and a later instrumented 20-seed window did not reproduce the schedule collision. Source ordering nevertheless proves the assertion was invalid: runHoldJob releases the callback-form async hold before resolving the outer Promise, and its reaction performed the racy += afterward. A wider audit found the same invalid pattern in the async-join and wait-async reaction totals.
The no-function asyncHold() release total is different: each fulfillment reaction retains the live hold while updating its score and calls release() only afterward, so that ordinary total remains lock-protected.
Required correction
Accumulate async-join, wait-async, and callback-form async-hold sum/count pairs in a shared integer TypedArray using Atomics.add.
Keep validating each async-hold callback result against its unique expected marker.
Check every exact sum and completion count after run-loop quiescence so missing, duplicate, rejected, or wrong-value delivery remains a hard failure.
Preserve the ordinary release total and document why its live no-function hold provides synchronization.
Do not serialize engine task execution, move assertions into unrelated locks, weaken expected totals, retry failed seeds, add sleeps, or treat non-reproduction as proof.
Post-settlement shared totals contain no ordinary read-modify-write outside synchronization.
Exact atomic sum/count pairs detect missing, duplicate, and rejected async-join, wait-async, and async-hold completions; async-hold also validates every returned marker.
Seeds 17–36 pass as one 1,120-program lifecycle window.
Seeds 17 and 26 pass under suppression-free TSan.
The full 22,400-program lifecycle profile passes with zero functional failures.
The change is test-only and makes no runtime throughput or semantic claim.
Parent: #482
Related: #474, #493, #874
Root cause
The lifecycle fuzzer accumulated Promise-reaction completions for
Thread.asyncJoin(),Atomics.waitAsync(...), andLock.asyncHold(fn)with shared ordinary numeric+=operations. Those reactions may run concurrently on different no-GIL settling or task-pump threads after their jobs settle. The counters therefore contained JavaScript program races, not engine synchronization witnesses.The initially observed failures were both in the async-hold total and each lost exactly one unique marker:
923080 / 984084, delta61004(thread 2, iteration 4)1804312 / 1845315, delta41003(thread 1, iteration 3)Six immediate seed-17 reruns and a later instrumented 20-seed window did not reproduce the schedule collision. Source ordering nevertheless proves the assertion was invalid:
runHoldJobreleases the callback-form async hold before resolving the outer Promise, and its reaction performed the racy+=afterward. A wider audit found the same invalid pattern in the async-join and wait-async reaction totals.The no-function
asyncHold()release total is different: each fulfillment reaction retains the live hold while updating its score and callsrelease()only afterward, so that ordinary total remains lock-protected.Required correction
Atomics.add.Acceptance