ReviewOS

zig-utils/zig-js

Runtime: enforce a shared runnable CPU-slot budget

#982
Closed chris opened this 9 days ago ·
9 days ago

Parent: #502 Depends on: #978, #979, #980, #981

Goal

Enforce a process-wide configurable runnable CPU-slot budget across all engine-owned OS-thread resource classes without spin/sleep admission or deadlock when a running thread blocks.

Scope

  • Add a process-wide max_runnable_threads policy with an unlimited default.
  • Acquire a shared slot before typed thread entry and before resuming from an outer blocking scope.
  • Release the slot on an outer blocking transition and final thread completion.
  • Park excess runtime threads on a real condition variable; wake one on release and all when policy capacity increases.
  • Expose coherent schema telemetry for active slots, queued slot waiters, peak pressure, waits, and the configured limit.
  • Preserve per-resource live = runnable + blocked and add the global invariant active_slots = sum(resource.runnable).
  • Add deterministic oversubscription and dynamic-limit tests under normal and TSan builds.

Policy

  • Existing runnable threads are never interrupted when a limit is lowered below current use; new entries/resumes wait until active use falls below the new limit.
  • max_runnable_threads = 0 pauses new entries and resumes until the embedder raises the limit.
  • This issue establishes neutral shared-slot enforcement. Priority classes, automatic host sizing, compiler queues, and cancellation remain in #502.
9 days ago

Implemented in d2e6383 and pushed to main.

The runtime now enforces one process-wide condition-backed runnable-slot budget across all six typed engine thread classes. Threads acquire a slot before entry/resume, release it at their outer blocking transition and completion, and park on the coordinator condition when capacity is unavailable. Condition and GIL wait paths release their wait mutex before queueing for a slot, preventing slot/mutex inversion. Existing runnable threads are grandfathered when the limit is lowered; zero pauses new entry/resume until the limit is raised.

Schema-v4 telemetry adds active/peak slots, current/peak waiters, cumulative waits, and the invariant active_slots = sum(resource.runnable). Public Zig controls and the thread resource documentation/inventory were updated.

Validation:

  • full units: 2,502 passed, 1 skipped, 0 failed, 0 leaked (2,503 total)
  • focused normal and TSan one-slot JavaScript Thread and Worker handoff tests passed
  • deterministic threadfuzz: 40/40 programs, 0 failures
  • runtime-thread audit: 6 production call sites across 6 resource classes
  • C API audit: 117/117 declarations, 460 private profile exports
  • docs: 51 pages, manifest stable
  • local ReleaseFast diagnostic: median paired lifecycle ratio 0.994 over 9 interleaved samples x 3,000 sequential typed thread lifecycles; diagnostic only, not publication evidence

Test262 flips were not measured because the corpus is absent in this checkout.

Sign in to comment on this issue.