From c942e0e5957fbbe38dedb09c968b795c8dae7936 Mon Sep 17 00:00:00 2001 From: Flamki <9833ayush@gmail.com> Date: Thu, 19 Mar 2026 20:22:48 +0530 Subject: [PATCH 1/5] docs(gc): add precise-tracing API plan post-gc_allocator --- README.md | 4 + notes/precise_tracing_api_plan.md | 158 ++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+) create mode 100644 notes/precise_tracing_api_plan.md diff --git a/README.md b/README.md index b8d0c6b..9dec4ba 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,10 @@ management and garbage collection experiments for Boa. The current API model investigation for Boa issue #2631 is documented in [`notes/gc_api_models.md`](./notes/gc_api_models.md). +The current implementation-focused follow-up for a precise-tracing API (post +`gc_allocator`) is documented in +[`notes/precise_tracing_api_plan.md`](./notes/precise_tracing_api_plan.md). + ## Project structure The current project structure is as follows. diff --git a/notes/precise_tracing_api_plan.md b/notes/precise_tracing_api_plan.md new file mode 100644 index 0000000..3550c4a --- /dev/null +++ b/notes/precise_tracing_api_plan.md @@ -0,0 +1,158 @@ +# Precise-Tracing API Plan (Post `gc_allocator`) + +Date: 2026-03-19 + +## Context + +This note proposes a concrete API-design and implementation plan for the +GC redesign track after removing the `Collector: Allocator` experiment +(`#54`). + +It builds on: + +- `notes/gc_api_models.md` (model-family investigation for boa#2631) +- `docs/boa_gc_api_surface.md` (current Boa-facing GC contract) +- Oscars integration tracker issues (`#26`, `#27`, `#28`, `#30`) + +The objective is to design a GC API that does **not** rely on root/reference +counting for liveness, while preserving precise tracing semantics and providing +an incremental integration path into Boa. + +## Problem statement + +Today, root/reference counts are coupled with public pointer ergonomics and +collector internals. This coupling increases implementation complexity and +makes it harder to evolve collector strategy. + +For a redesign to be integration-ready, we need: + +1. A pointer/rooting API where liveness is derived from precise tracing. +2. Explicit invariants for weak/ephemeron/finalizer behavior. +3. A staged migration path that can run behind feature gates in Boa. + +## Design goals + +1. Precise tracing as the source of truth for liveness. +2. Context-safe pointer model (no accidental cross-context sharing). +3. Weak and ephemeron behavior parity with current Boa semantics. +4. Finalization semantics that are explicit and testable. +5. Incremental adoption (no big-bang replacement). + +## Non-goals (for this phase) + +1. Generational or incremental collector algorithm rollout. +2. Concurrent collector implementation. +3. Full allocator-framework redesign in the same milestone. +4. Public engine-wide API churn in one PR. + +## Core invariants + +### I1. Tracing invariant + +Any object reachable from roots through strong edges must be marked alive in the +same collection cycle. + +### I2. Rooting invariant + +Rooting API must make temporary and long-lived roots explicit, and prevent +silent loss of roots due to API misuse. + +### I3. Weak invariant + +Weak handles do not keep referents alive. Upgrades succeed only if referent is +alive at upgrade time. + +### I4. Ephemeron invariant + +Ephemeron value is considered reachable only when key is independently +reachable by strong tracing rules. + +### I5. Finalizer invariant + +Finalization happens before drop/reclaim. If finalization can make objects +reachable again, collection ordering must prevent use-after-free. + +### I6. Teardown invariant + +Collector-drop paths must preserve safety ordering and avoid freeing objects +that may still be referenced by finalizer-triggered graph activity. + +## Proposed API direction (high-level) + +1. Keep user-facing `Gc` ergonomics close to current behavior where possible. +2. Shift internal liveness authority to tracing rather than root/refcount + arithmetic. +3. Make rooting scopes/handles explicit in API boundaries where ambiguity + exists. +4. Keep weak and ephemeron APIs stable at surface level while tightening + internal reachability contracts. + +This allows incremental migration while reducing dependence on reference-count +based root detection. + +## Implementation phases + +### Phase 1: API contract and invariant harness + +1. Add a dedicated invariant checklist and edge-case test matrix. +2. Expand coverage for rooting misuse, weak upgrades, ephemeron pruning, and + finalizer/resurrection-sensitive flows. +3. Define acceptance criteria for parity against `docs/boa_gc_api_surface.md`. + +Deliverable: + +- Invariant-driven test suite and API-diff checklist. + +### Phase 2: Oscars prototype changes + +1. Prototype precise-tracing-first liveness flow in Oscars internals. +2. Keep changes in reviewable slices (pointer semantics, tracing hooks, weak/ + ephemeron behavior). +3. Ensure Miri-clean behavior throughout. + +Deliverable: + +- Prototype branch with passing invariants and stress tests. + +### Phase 3: Boa integration path (feature-gated) + +1. Map prototype API to Boa `core/gc` surface. +2. Integrate behind unstable gate with fallback to existing path. +3. Validate on Boa-relevant workloads, not only microbenchmarks. + +Deliverable: + +- Gated integration slices plus benchmark and migration notes. + +## Validation strategy + +1. Correctness + - Targeted unit/regression tests for each invariant. + - Workspace test suites pass. +2. Safety + - Miri coverage for critical paths. +3. Performance + - Boa workload benchmarks plus focused GC stress tests. +4. Integration confidence + - API parity matrix against `docs/boa_gc_api_surface.md`. + +## Risk management + +1. Scope creep + - Keep non-goals explicit and enforce slice-based PRs. +2. Unsoundness regressions + - Require invariant tests before merging behavioral changes. +3. Integration disruption + - Use feature-gated rollout and preserve fallback path. + +## Relationship to active tracker work + +This plan is intended to feed directly into: + +- `#26` Tracking issue for Boa integration +- `#27` Coverage of `boa_gc` API surface area +- `#28` Integration into Boa +- `#30` Benchmark MarkSweepGarbageCollector in Boa with `arena3` allocator + +And it is explicitly aligned with the `#54` direction to remove the +`gc_allocator` supertrait experiment. From 90959bf8e4b2f57e4b48e57d8440e12898aa6523 Mon Sep 17 00:00:00 2001 From: Flamki <9833ayush@gmail.com> Date: Fri, 27 Mar 2026 12:03:21 +0530 Subject: [PATCH 2/5] docs(gc): turn precise-tracing note into concrete API proposal --- README.md | 4 +- notes/precise_tracing_api_plan.md | 244 ++++++++++++++++++------------ 2 files changed, 148 insertions(+), 100 deletions(-) diff --git a/README.md b/README.md index 9dec4ba..f05f5cc 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ management and garbage collection experiments for Boa. The current API model investigation for Boa issue #2631 is documented in [`notes/gc_api_models.md`](./notes/gc_api_models.md). -The current implementation-focused follow-up for a precise-tracing API (post -`gc_allocator`) is documented in +The current precise-tracing API redesign proposal (post `gc_allocator`) is +documented in [`notes/precise_tracing_api_plan.md`](./notes/precise_tracing_api_plan.md). ## Project structure diff --git a/notes/precise_tracing_api_plan.md b/notes/precise_tracing_api_plan.md index 3550c4a..02d999e 100644 --- a/notes/precise_tracing_api_plan.md +++ b/notes/precise_tracing_api_plan.md @@ -1,158 +1,206 @@ -# Precise-Tracing API Plan (Post `gc_allocator`) +# Precise-Tracing API Redesign Proposal (Post `gc_allocator`) Date: 2026-03-19 ## Context -This note proposes a concrete API-design and implementation plan for the -GC redesign track after removing the `Collector: Allocator` experiment -(`#54`). +After removing the `Collector: Allocator` experiment (`#54`), the next step is +to propose a concrete API redesign shape that can be discussed and tested. -It builds on: +This note builds on: - `notes/gc_api_models.md` (model-family investigation for boa#2631) - `docs/boa_gc_api_surface.md` (current Boa-facing GC contract) -- Oscars integration tracker issues (`#26`, `#27`, `#28`, `#30`) +- Tracker issues `#26`, `#27`, `#28`, `#30` -The objective is to design a GC API that does **not** rely on root/reference -counting for liveness, while preserving precise tracing semantics and providing -an incremental integration path into Boa. +The core target is a precise-tracing API that does not use root/reference +count arithmetic as the liveness authority. ## Problem statement -Today, root/reference counts are coupled with public pointer ergonomics and -collector internals. This coupling increases implementation complexity and -makes it harder to evolve collector strategy. +Today, `Gc` ergonomics and liveness accounting are tightly coupled to root +counting. That gives simple usage but makes collector internals harder to +evolve and reason about. -For a redesign to be integration-ready, we need: +For redesign work to be useful to Boa, we need an API that: -1. A pointer/rooting API where liveness is derived from precise tracing. -2. Explicit invariants for weak/ephemeron/finalizer behavior. -3. A staged migration path that can run behind feature gates in Boa. +1. uses tracing as the single source of liveness truth, +2. keeps weak/ephemeron/finalizer semantics explicit, +3. remains adoptable against the current Boa-facing API surface. -## Design goals +## Proposed API (draft v0) -1. Precise tracing as the source of truth for liveness. -2. Context-safe pointer model (no accidental cross-context sharing). -3. Weak and ephemeron behavior parity with current Boa semantics. -4. Finalization semantics that are explicit and testable. -5. Incremental adoption (no big-bang replacement). +This proposal uses an explicit root-table model with scope handles. -## Non-goals (for this phase) +### Core types -1. Generational or incremental collector algorithm rollout. -2. Concurrent collector implementation. -3. Full allocator-framework redesign in the same milestone. -4. Public engine-wide API churn in one PR. +```rust +pub struct Gc { + ptr: GcErasedPointer, + _marker: core::marker::PhantomData, +} -## Core invariants +pub struct Root { + ptr: Gc, + slot: RootSlotId, +} -### I1. Tracing invariant +pub struct WeakGc { + ptr: GcErasedPointer, + _marker: core::marker::PhantomData, +} -Any object reachable from roots through strong edges must be marked alive in the -same collection cycle. +pub struct GcContext { + /* collector state + root table + weak queues */ +} -### I2. Rooting invariant +pub struct Scope<'gc> { + cx: &'gc mut GcContext, +} +``` -Rooting API must make temporary and long-lived roots explicit, and prevent -silent loss of roots due to API misuse. +### Allocation and rooting -### I3. Weak invariant +```rust +impl GcContext { + pub fn scope(&mut self, f: impl for<'gc> FnOnce(Scope<'gc>) -> R) -> R; + pub fn collect(&mut self); +} -Weak handles do not keep referents alive. Upgrades succeed only if referent is -alive at upgrade time. +impl<'gc> Scope<'gc> { + pub fn alloc(&mut self, value: T) -> Gc; + pub fn root(&mut self, value: &Gc) -> Root; + pub fn downgrade(&self, value: &Gc) -> WeakGc; +} -### I4. Ephemeron invariant +impl Root { + pub fn gc(&self) -> &Gc; +} +``` -Ephemeron value is considered reachable only when key is independently -reachable by strong tracing rules. +### Pointer identity and casts (parity-preserving) -### I5. Finalizer invariant +```rust +impl Gc { + pub fn ptr_eq(a: &Gc, b: &Gc) -> bool; + pub fn into_raw(this: Gc) -> GcRaw; + pub unsafe fn from_raw(raw: GcRaw) -> Gc; -Finalization happens before drop/reclaim. If finalization can make objects -reachable again, collection ordering must prevent use-after-free. + pub fn downcast(this: Gc) -> Option>; + pub unsafe fn cast_unchecked(this: Gc) -> Gc; + pub unsafe fn cast_ref_unchecked(this: &Gc) -> &Gc; +} +``` -### I6. Teardown invariant +### Weak behavior -Collector-drop paths must preserve safety ordering and avoid freeing objects -that may still be referenced by finalizer-triggered graph activity. +```rust +impl WeakGc { + pub fn new(value: &Gc) -> WeakGc; + pub fn upgrade(&self) -> Option>; +} +``` -## Proposed API direction (high-level) +### Runtime helpers -1. Keep user-facing `Gc` ergonomics close to current behavior where possible. -2. Shift internal liveness authority to tracing rather than root/refcount - arithmetic. -3. Make rooting scopes/handles explicit in API boundaries where ambiguity - exists. -4. Keep weak and ephemeron APIs stable at surface level while tightening - internal reachability contracts. +```rust +impl GcContext { + pub fn finalizer_safe(&self) -> bool; +} +``` -This allows incremental migration while reducing dependence on reference-count -based root detection. +`force_collect()` compatibility can be provided by wiring to +`MarkSweepGarbageCollector::collect` in integration mode. -## Implementation phases +## Semantics and invariants -### Phase 1: API contract and invariant harness +### I1. Tracing is authoritative -1. Add a dedicated invariant checklist and edge-case test matrix. -2. Expand coverage for rooting misuse, weak upgrades, ephemeron pruning, and - finalizer/resurrection-sensitive flows. -3. Define acceptance criteria for parity against `docs/boa_gc_api_surface.md`. +Reachability is determined only by tracing from root slots and strong graph +edges in the same cycle. -Deliverable: +### I2. Root slots replace root counts -- Invariant-driven test suite and API-diff checklist. +A value is rooted when at least one root slot references it. Slot lifetime is +explicit (`Root` drop unregisters slot). No per-object root/refcount math is +used for liveness. -### Phase 2: Oscars prototype changes +### I3. Weak upgrade semantics -1. Prototype precise-tracing-first liveness flow in Oscars internals. -2. Keep changes in reviewable slices (pointer semantics, tracing hooks, weak/ - ephemeron behavior). -3. Ensure Miri-clean behavior throughout. +`WeakGc::upgrade` succeeds only if the referent is marked live in the current +collector state. -Deliverable: +### I4. Ephemeron semantics -- Prototype branch with passing invariants and stress tests. +Ephemeron values are traced only when keys are independently reachable via +strong edges. -### Phase 3: Boa integration path (feature-gated) +### I5. Finalizer ordering -1. Map prototype API to Boa `core/gc` surface. -2. Integrate behind unstable gate with fallback to existing path. -3. Validate on Boa-relevant workloads, not only microbenchmarks. +Finalize-before-drop ordering is preserved, and collector teardown runs +finalizers before destructors for tracked live values. -Deliverable: +### I6. Teardown safety -- Gated integration slices plus benchmark and migration notes. +Collector drop does not free values in an order that can cause UAF via +finalizer-triggered graph activity. -## Validation strategy +## Feasibility and adoption path -1. Correctness - - Targeted unit/regression tests for each invariant. - - Workspace test suites pass. -2. Safety - - Miri coverage for critical paths. -3. Performance - - Boa workload benchmarks plus focused GC stress tests. -4. Integration confidence - - API parity matrix against `docs/boa_gc_api_surface.md`. +This proposal is intentionally structured in two layers: -## Risk management +1. Collector-native API in Oscars (`GcContext`, `Scope`, `Root`). +2. Boa-compat layer that preserves current surface where required. -1. Scope creep - - Keep non-goals explicit and enforce slice-based PRs. -2. Unsoundness regressions - - Require invariant tests before merging behavioral changes. -3. Integration disruption - - Use feature-gated rollout and preserve fallback path. +### Boa compatibility mapping -## Relationship to active tracker work +1. `Gc::new(value)`: + - compatibility shim calls `with_gc_context(|cx| cx.scope(|s| s.alloc(value)))`. +2. `WeakGc::new/upgrade`, raw-pointer helpers, and cast helpers: + - keep the same signatures. +3. `force_collect()`: + - routed to collector `collect()`. +4. `finalizer_safe()`: + - routed to collector phase state. -This plan is intended to feed directly into: +This keeps migration incremental and avoids an all-at-once engine rewrite. + +## What this proposal deliberately does not include + +1. A new incremental/generational/concurrent algorithm. +2. Full Boa integration in one milestone. +3. Allocator-framework redesign in the same proposal. + +## Validation plan for this API proposal + +1. Contract tests: + - rooting slot lifetime and misuse resistance, + - weak upgrade behavior across collections, + - ephemeron key/value reachability behavior. +2. Safety checks: + - Miri on root registration/unregistration paths and raw round-trips. +3. Compatibility checks: + - parity checklist against `docs/boa_gc_api_surface.md`. +4. Benchmarks: + - Boa workloads plus targeted GC stress cases. + +## Open review questions + +1. Should `Scope<'gc>` be mandatory for all allocations, or should we keep a + global-context fallback for Boa compatibility? +2. Should `Root` be cloneable (multiple slots) or explicitly unique? +3. Is `cast_ref_unchecked` still desirable, or should compatibility rely on + value-consuming casts only? +4. Which minimal Boa integration slice gives the best signal first: + pointer API parity, weak semantics parity, or runtime helpers? + +## Relationship to tracker work + +This proposal is intended to feed directly into: - `#26` Tracking issue for Boa integration - `#27` Coverage of `boa_gc` API surface area - `#28` Integration into Boa - `#30` Benchmark MarkSweepGarbageCollector in Boa with `arena3` allocator -And it is explicitly aligned with the `#54` direction to remove the -`gc_allocator` supertrait experiment. +And it is aligned with `#54` (remove `gc_allocator` supertrait direction). From bb4d5d7e4fc8e80f9e61e8fb173d31a5b0293ac3 Mon Sep 17 00:00:00 2001 From: Flamki <9833ayush@gmail.com> Date: Fri, 27 Mar 2026 22:54:12 +0530 Subject: [PATCH 3/5] docs(gc): tie proposed Root handles to gc context lifetime --- notes/precise_tracing_api_plan.md | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/notes/precise_tracing_api_plan.md b/notes/precise_tracing_api_plan.md index 02d999e..c3e0beb 100644 --- a/notes/precise_tracing_api_plan.md +++ b/notes/precise_tracing_api_plan.md @@ -40,9 +40,10 @@ pub struct Gc { _marker: core::marker::PhantomData, } -pub struct Root { +pub struct Root<'gc, T: Trace + ?Sized> { ptr: Gc, slot: RootSlotId, + _ctx: core::marker::PhantomData<&'gc mut GcContext>, } pub struct WeakGc { @@ -69,15 +70,18 @@ impl GcContext { impl<'gc> Scope<'gc> { pub fn alloc(&mut self, value: T) -> Gc; - pub fn root(&mut self, value: &Gc) -> Root; + pub fn root(&'gc mut self, value: &Gc) -> Root<'gc, T>; pub fn downgrade(&self, value: &Gc) -> WeakGc; } -impl Root { +impl<'gc, T: Trace + ?Sized> Root<'gc, T> { pub fn gc(&self) -> &Gc; } ``` +Safety note: `Root<'gc, T>` is lifetime-branded to the same `GcContext` scope, +so safe code cannot hold rooted references after the context/scope is dropped. + ### Pointer identity and casts (parity-preserving) ```rust @@ -122,8 +126,9 @@ edges in the same cycle. ### I2. Root slots replace root counts A value is rooted when at least one root slot references it. Slot lifetime is -explicit (`Root` drop unregisters slot). No per-object root/refcount math is -used for liveness. +explicit (`Root<'gc, T>` drop unregisters slot), and root handles are branded by +the context/scope lifetime so they cannot outlive the owning GC context. No +per-object root/refcount math is used for liveness. ### I3. Weak upgrade semantics @@ -149,7 +154,7 @@ finalizer-triggered graph activity. This proposal is intentionally structured in two layers: -1. Collector-native API in Oscars (`GcContext`, `Scope`, `Root`). +1. Collector-native API in Oscars (`GcContext`, `Scope`, `Root<'gc, T>`). 2. Boa-compat layer that preserves current surface where required. ### Boa compatibility mapping From 9d3b8dd072ab643075c93b8705669dacb50b707e Mon Sep 17 00:00:00 2001 From: Flamki <9833ayush@gmail.com> Date: Sat, 28 Mar 2026 02:54:09 +0530 Subject: [PATCH 4/5] docs(gc): refine root lifetime model and proposal edge cases --- notes/precise_tracing_api_plan.md | 36 ++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/notes/precise_tracing_api_plan.md b/notes/precise_tracing_api_plan.md index c3e0beb..a73948a 100644 --- a/notes/precise_tracing_api_plan.md +++ b/notes/precise_tracing_api_plan.md @@ -40,10 +40,10 @@ pub struct Gc { _marker: core::marker::PhantomData, } -pub struct Root<'gc, T: Trace + ?Sized> { +pub struct Root<'scope, 'gc, T: Trace + ?Sized> { ptr: Gc, slot: RootSlotId, - _ctx: core::marker::PhantomData<&'gc mut GcContext>, + _scope: core::marker::PhantomData<&'scope Scope<'gc>>, } pub struct WeakGc { @@ -70,17 +70,25 @@ impl GcContext { impl<'gc> Scope<'gc> { pub fn alloc(&mut self, value: T) -> Gc; - pub fn root(&'gc mut self, value: &Gc) -> Root<'gc, T>; + pub fn root<'scope, T: Trace + 'static>( + &'scope self, + value: &Gc, + ) -> Root<'scope, 'gc, T>; pub fn downgrade(&self, value: &Gc) -> WeakGc; } -impl<'gc, T: Trace + ?Sized> Root<'gc, T> { +impl<'scope, 'gc, T: Trace + ?Sized> Root<'scope, 'gc, T> { pub fn gc(&self) -> &Gc; } ``` -Safety note: `Root<'gc, T>` is lifetime-branded to the same `GcContext` scope, -so safe code cannot hold rooted references after the context/scope is dropped. +Safety note: `Root<'scope, 'gc, T>` is lifetime-branded to the borrowed scope, +so safe code cannot hold rooted references after the owning scope/context is +dropped. + +Implementation note: `Scope::root(&self, ...)` assumes root-slot registration is +handled via internal mutability in collector internals, so multiple roots can +coexist without requiring an exclusive borrow of `Scope`. ### Pointer identity and casts (parity-preserving) @@ -126,9 +134,9 @@ edges in the same cycle. ### I2. Root slots replace root counts A value is rooted when at least one root slot references it. Slot lifetime is -explicit (`Root<'gc, T>` drop unregisters slot), and root handles are branded by -the context/scope lifetime so they cannot outlive the owning GC context. No -per-object root/refcount math is used for liveness. +explicit (`Root<'scope, 'gc, T>` drop unregisters slot), and root handles are +branded by the scope/context lifetime so they cannot outlive the owning GC +context. No per-object root/refcount math is used for liveness. ### I3. Weak upgrade semantics @@ -154,13 +162,14 @@ finalizer-triggered graph activity. This proposal is intentionally structured in two layers: -1. Collector-native API in Oscars (`GcContext`, `Scope`, `Root<'gc, T>`). +1. Collector-native API in Oscars (`GcContext`, `Scope`, `Root<'scope, 'gc, T>`). 2. Boa-compat layer that preserves current surface where required. ### Boa compatibility mapping 1. `Gc::new(value)`: - - compatibility shim calls `with_gc_context(|cx| cx.scope(|s| s.alloc(value)))`. + - compatibility shim calls + `with_gc_context(|cx| cx.scope(|mut s| s.alloc(value)))`. 2. `WeakGc::new/upgrade`, raw-pointer helpers, and cast helpers: - keep the same signatures. 3. `force_collect()`: @@ -193,11 +202,14 @@ This keeps migration incremental and avoids an all-at-once engine rewrite. 1. Should `Scope<'gc>` be mandatory for all allocations, or should we keep a global-context fallback for Boa compatibility? -2. Should `Root` be cloneable (multiple slots) or explicitly unique? +2. Should `Root<'scope, 'gc, T>` be cloneable (multiple slots) or explicitly + unique? 3. Is `cast_ref_unchecked` still desirable, or should compatibility rely on value-consuming casts only? 4. Which minimal Boa integration slice gives the best signal first: pointer API parity, weak semantics parity, or runtime helpers? +5. Should reading `T` from `Gc` require an explicit scope/root token, so + stale handles cannot be dereferenced after collection in safe code? ## Relationship to tracker work From 856134541ca9fb4c6ee198d27a6ab6e92a0578fb Mon Sep 17 00:00:00 2001 From: Flamki <9833ayush@gmail.com> Date: Sat, 28 Mar 2026 09:46:28 +0530 Subject: [PATCH 5/5] docs(gc): use shared gc context scope in API proposal --- notes/precise_tracing_api_plan.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/notes/precise_tracing_api_plan.md b/notes/precise_tracing_api_plan.md index a73948a..1b7e0fe 100644 --- a/notes/precise_tracing_api_plan.md +++ b/notes/precise_tracing_api_plan.md @@ -52,11 +52,11 @@ pub struct WeakGc { } pub struct GcContext { - /* collector state + root table + weak queues */ + /* collector state + root table + weak queues (interior mutability) */ } pub struct Scope<'gc> { - cx: &'gc mut GcContext, + cx: &'gc GcContext, } ``` @@ -64,12 +64,12 @@ pub struct Scope<'gc> { ```rust impl GcContext { - pub fn scope(&mut self, f: impl for<'gc> FnOnce(Scope<'gc>) -> R) -> R; - pub fn collect(&mut self); + pub fn scope(&self, f: impl for<'gc> FnOnce(Scope<'gc>) -> R) -> R; + pub fn collect(&self); } impl<'gc> Scope<'gc> { - pub fn alloc(&mut self, value: T) -> Gc; + pub fn alloc(&self, value: T) -> Gc; pub fn root<'scope, T: Trace + 'static>( &'scope self, value: &Gc, @@ -90,6 +90,10 @@ Implementation note: `Scope::root(&self, ...)` assumes root-slot registration is handled via internal mutability in collector internals, so multiple roots can coexist without requiring an exclusive borrow of `Scope`. +Collection note: `collect(&self)` here represents a high-level API shape for a +single-threaded runtime. Implementations should guard collection phase entry so +collection cannot race with active mutable interior borrows. + ### Pointer identity and casts (parity-preserving) ```rust @@ -169,7 +173,7 @@ This proposal is intentionally structured in two layers: 1. `Gc::new(value)`: - compatibility shim calls - `with_gc_context(|cx| cx.scope(|mut s| s.alloc(value)))`. + `with_gc_context(|cx| cx.scope(|s| s.alloc(value)))`. 2. `WeakGc::new/upgrade`, raw-pointer helpers, and cast helpers: - keep the same signatures. 3. `force_collect()`: @@ -204,11 +208,13 @@ This keeps migration incremental and avoids an all-at-once engine rewrite. global-context fallback for Boa compatibility? 2. Should `Root<'scope, 'gc, T>` be cloneable (multiple slots) or explicitly unique? -3. Is `cast_ref_unchecked` still desirable, or should compatibility rely on +3. Should scope entry be closure-only (`GcContext::scope`) or should we also + support an explicit long-lived scope handle for ergonomics/integration? +4. Is `cast_ref_unchecked` still desirable, or should compatibility rely on value-consuming casts only? -4. Which minimal Boa integration slice gives the best signal first: +5. Which minimal Boa integration slice gives the best signal first: pointer API parity, weak semantics parity, or runtime helpers? -5. Should reading `T` from `Gc` require an explicit scope/root token, so +6. Should reading `T` from `Gc` require an explicit scope/root token, so stale handles cannot be dereferenced after collection in safe code? ## Relationship to tracker work