ReviewOS

zig-utils/zig-js

GC: cancel a queued concurrent marker at the finish boundary

#1034
Closed chris opened this 8 days ago ·
8 days ago

Parent: #502 Related: #1032, #1033

Executed reproduction

On exact main at f26b9688f492b961c4c815ddbb72c490ca8f9117, a focused Context test begins a real concurrent mark, pauses the public runnable scheduler at zero, spawns the production typed concurrent_gc_marker, confirms its initial start is queued, and invokes finishConcurrentGCIfActive on the Context owner thread.

The bounded observer shows that the finish boundary cannot return until runnable capacity is restored:

context.test.concurrent GC marker finish exits while its start is scheduler-paused...FAIL (TestUnexpectedResult)
1 passed; 0 skipped; 1 failed

finishConcurrentGCIfActive publishes gc_marker_stop and immediately joins the marker. A marker that has not entered gcMarkerLoop cannot observe that stop flag because it is blocked inside the process-wide initial-slot queue, so the Context boundary and scheduler policy wait on one another.

Required change

  • Give the per-cycle concurrent marker a typed start-cancellation handle.
  • At every concurrent-GC finish boundary, cancel only a marker that is still waiting for its initial slot before joining it.
  • A concurrently granted or running marker must retain the existing stop/join and synchronous finish semantics.
  • Reset the per-cycle cancellation state only after the previous marker is joined.
  • Preserve exact marker thread, runnable/blocked, stack, queue, and GC-cycle accounting under grant-versus-cancel races.

Definition of done

  • The zero-capacity finish reproduction completes before capacity is restored.
  • Canceled starts leave the concurrent mark to the existing synchronous finish path and balance all coordinator counters.
  • Deterministic queued cancellation, running-marker, repeated-cycle, and grant-race tests pass normally and under unsuppressed TSan.
  • Focused concurrent-GC, full unit, runtime-thread audit, and documentation gates pass.
  • #502 records the landed commit and exact evidence.

Guardrails

Do not raise runnable capacity, run marking outside a scheduler slot, abandon an active concurrent cycle, poll/sleep in production, or weaken the safety-priority policy.

8 days ago

Landed on main in 1cb677d.

Each concurrent-GC marker cycle now owns a typed initial-start cancellation edge. A finish boundary removes a still-queued safety waiter before joining, then closes the active mark through the existing synchronous finish path. A marker that already won a slot retains the existing stop, join, and finish ordering.

Validation:

  • pre-fix bounded reproduction: failed until runnable capacity was restored
  • post-fix zero-capacity reproduction: passes before capacity restoration
  • eight repeated queued cycles plus a running-marker cycle: exact live/runnable/blocked/stack/waiter/cancellation accounting
  • focused normal and unsuppressed TSan marker tests: pass
  • broader production concurrent-GC normal and unsuppressed TSan groups: 7/7 pass
  • full unit suite: 2,575 passed; 1 skipped; 0 failed; 0 leaked
  • zig build -j1: pass
  • runtime-thread audit: 6 production sites, 153 reviewed test-only spawns
  • docs link check: 378 files pass
  • test262 flips: not measured because the pinned corpus is unavailable in this checkout

Sign in to comment on this issue.