Skip to content

Commit f8093f9

Browse files
authored
revision: Add more comments around Nova engine code (#55)
1 parent bcab23f commit f8093f9

File tree

1 file changed

+44
-3
lines changed

1 file changed

+44
-3
lines changed

pages/blog/garbage-collection-is-contrarian.md

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -291,25 +291,33 @@ pub(crate) fn set<'a>(
291291
throw: bool,
292292
mut gc: GcScope<'a, '_>,
293293
) -> JsResult<'a, ()> {
294+
// By convention, always bind all parameters at function entry for safety.
294295
let nogc = gc.nogc();
295296
let o = o.bind(nogc);
296297
let p = p.bind(nogc);
297298
let v = v.bind(nogc);
299+
300+
// Scope p for use after possible garbage collection safepoint.
298301
let scoped_p = p.scope(agent, nogc);
302+
303+
// Actual function work starts.
299304
let success = o
300305
.unbind()
301306
.internal_set(agent, p.unbind(), v.unbind(), o.unbind().into(), gc.reborrow())
302307
.unbind()?;
308+
309+
// Transition to a proven "no GC past this point" regime.
303310
let gc = gc.into_nogc();
304-
let p = scoped_p.get(agent).bind(gc);
305311
if !success && throw {
312+
let p = scoped_p.get(agent).bind(gc);
306313
return throw_set_error(agent, p, gc).into();
307314
}
308315
Ok(())
309316
}
310317

311318
/// The unbind() and bind() functions come from this trait.
312319
unsafe trait Bindable {
320+
/// This is always Self<'a>;
313321
type Of<'a>;
314322

315323
fn unbind(self) -> Self::Of<'static>;
@@ -344,23 +352,56 @@ pub(crate) fn set<'a>(
344352
throw: bool,
345353
mut gc: GcScope<'a>,
346354
) -> JsResult<'a, ()> {
355+
// We should still "bind" all parameters at function entry for safety.
347356
let nogc = gc.nogc();
348357
let o = o.local();
349358
nogc.join(o);
350359
let p = p.local();
351360
nogc.join(p);
352361
let v = v.local();
353362
nogc.join(v);
363+
364+
// We still have to scope p.
354365
let scoped_p = p.scope(agent, nogc);
366+
367+
// Actual function work starts.
355368
let success = o.internal_set(agent, p, v, o.into(), gc.reborrow())?;
369+
370+
// It's still useful to mark "no GC" regimes explicitly.
356371
let gc = gc.into_nogc();
357-
let p = scoped_p.get(agent);
358-
gc.join(p);
359372
if !success && throw {
373+
let p = scoped_p.get(agent);
374+
gc.join(p);
360375
return Err(throw_set_error(agent, p, gc));
361376
}
362377
Ok(())
363378
}
379+
380+
/// Helper functions
381+
trait Handle {
382+
/// This is always Self<'a>;
383+
type Of<'a>;
384+
385+
/// Create a local copy of Self: note that for contravariant lifetimes this
386+
/// is fundamentally an unsafe operation.
387+
fn local<'a>(&'a self) -> Self::Of<'a>;
388+
}
389+
390+
impl<'gc, 'scope> GcScope<'gc, 'scope> {
391+
/// Join a handle's lifetime together with a shared borrow of the
392+
/// (guaranteed unique) GcScope.
393+
#[inline(always)]
394+
fn join<'a, T: Handle>(&'a self, handle: T::Of<'a>) {}
395+
}
396+
397+
impl<'gc, 'scope> NoGcScope<'gc, 'scope> {
398+
/// Join a handle's lifetime together with the GC lifetime of a GcScope.
399+
/// Note that NoGcScope is a Copy type created from a shared borrow of the
400+
/// (guaranteed unique) GcScope, meaning that this joins the handle's
401+
/// lifetime together with a shared borrow of that GcScope.
402+
#[inline(always)]
403+
fn join<T: Handle>(self, handle: T::Of<'gc>) {}
404+
}
364405
```
365406

366407
The most important change here is the actual `internal_set` call: the

0 commit comments

Comments
 (0)