Skip to content

feat: implement mark_sweep _branded based on approved API redesign#83

Open
shruti2522 wants to merge 7 commits intoboa-dev:mainfrom
shruti2522:prototype-impl
Open

feat: implement mark_sweep _branded based on approved API redesign#83
shruti2522 wants to merge 7 commits intoboa-dev:mainfrom
shruti2522:prototype-impl

Conversation

@shruti2522
Copy link
Copy Markdown
Contributor

Implement mark_sweep_branded GC based on the approved API redesign for oscars. This is a draft implementation, also includes changes from #82

@shruti2522 shruti2522 marked this pull request as ready for review April 23, 2026 17:57
Copy link
Copy Markdown
Member

@nekevss nekevss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some early feedback. I may try a second pass at this within a couple days.

In general, a lot of this is super interesting. Super excited to see it progress 😄

/// Converts this root into a `Gc` pointer
pub fn get<'gc>(
&self,
_cx: &crate::collectors::mark_sweep_branded::MutationContext<'id, 'gc>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: import MutationContext

/// Use `Tracer::mark` for every reachable `Gc` pointer.
pub trait Trace {
/// Marks all `Gc` pointers reachable from `self`.
fn trace(&mut self, tracer: &mut Tracer);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm, a couple questions here:

  1. Have you looked into using a Trace color vs. a Tracer? I'd prefer that approach vs. keeping Tracer around.
  2. This should still be & vs. & mut.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the TraceColor refactor slipped my mind 😅 on it now. Also switching &mut self to &self since mark bits already use interior mutability via Cell<bool> so it's overkill

let slot = self
.pool
.borrow_mut()
.try_alloc_bytes(layout)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is incorrect. It should be try_alloc not try_alloc_bytes

Is there a reason for allocating the the BumpPage here rather than the slots?

If we are using one of the bump pages here, then we should probably use one of the arena allocators

// and we ensure that `Gc` objects and pool allocations do not outlive
// the `Collector` instance
pub(crate) pool: RefCell<PoolAllocator<'static>>,
pool_entries: RefCell<Vec<PoolEntry>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need to maintain a Vec of pool entries here nor the allocation count. That should be managed by the allocator.

use core::ptr::NonNull;

/// A weak reference to a GC managed value
pub struct WeakGc<'id, T: Trace + ?Sized> {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmmm, I think we will still need an ephemeron implementation, but we could probably iterate on this

pub fn root<T: Trace + Finalize + 'gc>(&self, gc: Gc<'gc, T>) -> Root<'id, T> {
let gc_ptr = gc.ptr;

let node = Box::new(RootNode {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RootNode should ideally be allocated onto our heap. It would actually probably be really ideal to have a mempool specifically for RootNodes

pub(crate) fn iter_from_sentinel(
sentinel: NonNull<Self>,
) -> impl Iterator<Item = NonNull<Self>> {
struct Iter {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: this should probably just be a separate iterator RootLinkIter, and then we call iter that returns RootLinkIter rather than iter_from_sentinel call which is an anonymous iter

// the `Collector` instance
pub(crate) pool: RefCell<PoolAllocator<'static>>,
pool_entries: RefCell<Vec<PoolEntry>>,
pub(crate) sentinel: Pin<Box<RootLink>>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about a new type RootSentinel that can be a transparent new type.

pub struct RootSentinel(Pin<Box<RootLink>>);

/// Reachability flag set by the mark phase.
pub(crate) marked: Cell<bool>,
/// Type-erased trace function.
pub(crate) trace_fn: Option<TraceFn>,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there ever an instance where trace_fn would be None. This should be fine to just be TraceFn as it's never not set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants