Skip to content

runtime: cross-worker GC soundness — per-OS-thread heaps, but objects migrate and are shared across workers #49

Description

@brianp

Found during the documentation-validation sweep (Aug 2026). This is the one item from that sweep that looks like a potential soundness problem rather than debt.

Verified facts

  • GC state is thread-local: codegen/src/runtime/gc.rs:38-57 (thread_local! HEAP_HEAD, GC_HEAP_LO, GC_HEAP_HI). Collection is per-thread (gc_collect_inner(), gc.rs:309) with no cross-thread rendezvous.
  • Pointer validation checks the current thread's recorded heap range: is_gc_payload at gc.rs:132 (addr >= lo && addr < hi against thread-local bounds).
  • Green threads migrate between OS workers (work-stealing scheduler, codegen/src/green/scheduler.rs, crossbeam deques + global injector), and channels hand raw object pointers between green threads (codegen/src/runtime/channel.rs).
  • Safepoints exist (aster_safepoint, codegen/src/runtime/error.rs:106; preemption at PREEMPT_THRESHOLD = 1024, scheduler.rs:29, checked at scheduler.rs:637) but are not used for any GC coordination.
  • The old WALKTHROUGH.md claimed "stop-the-world GC across all workers coordinated via safepoints" and cited a test by name (stop_the_world_gc_traces_stack_roots_from_multiple_suspended_tasks). That test does not exist anywhere in the repo (grep confirms), and no STW machinery was found. The design appears to have been planned but never built.

The question

If worker B holds the only live reference to an object allocated from worker A's heap — via channel hand-off, or because a green thread migrated after allocating — does worker A's collection free it out from under B? Relatedly, is_gc_payload's thread-local range check would seem to reject valid pointers into another thread's heap during tracing, causing under-tracing.

Asks

  1. A test that allocates on one worker, hands the object off via channel, forces collection on the allocating worker, and verifies the object survives and is still traced correctly.
  2. A design decision: cross-worker stop-the-world via the existing safepoint mechanism, per-object ownership transfer on hand-off, or a shared heap with synchronized collection.

Documentation

The per-thread heap design and precise tracing are documented in docs/src/content/docs/internals/codegen.mdx (GC section); the missing STW coordination is listed in docs/src/content/docs/reference/roadmap.mdx (Runtime).

Metadata

Metadata

Assignees

No one assigned

    Labels

    codegenJIT, AOT, FIR lowering, runtimehighImportant, address soonsoundnessType system or runtime correctness issue

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions