Skip to content

Instantly share code, notes, and snippets.

@Manishearth
Last active December 14, 2015 15:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Manishearth/014c92f8d27ee6a04eee to your computer and use it in GitHub Desktop.
Save Manishearth/014c92f8d27ee6a04eee to your computer and use it in GitHub Desktop.

Uprooting

We root way too much in DOM. Let's not do that.

Magic DOM might fix a lot of problems here, and we're not certain what the performance hit of this is, so we shouldn't really do anything about this until after that. This is more of a dump of all the ideas that came up in this discussion.

Low hanging fruit: Escape analysis

There are lot of .root()s left over from the pre-dereffable-JS<T> era. These are transitively rooted and aren't being moved, so just using Deref should work. Simple escape analysis can catch these.

Interior mutability

Both the Cell (copying, no interior references) and RefCell (interior references with manual checks) models are compatible with transitive rooting/Dereffable JS<T>. (We whiteboarded this, can't find any flaw in it)

Currently MutHeap is a Cell, but it must root on .get(). This is space efficient, but introduces more unnecessary roots for transitively rooted things. A .get() -> JS<T> API is unsafe, however, and .get() -> &T introduces interior references which can cause unsafety.

For MutHeaps which can afford to pay the cost of an extra flag, we can avoid rooting in these situations.

The idea is to introduce two MutHeaps:

MutHeapCell<T>:

  • Identical to current MutHeap
  • .get() -> Root<T>, roots every time
  • .set(&T)
  • thin wrapper around T
  • basically Cell<T>+rooting

MutHeapRefCell<T>:

  • Identical to RefCell<JS<T>> (?)
  • .borrow() -> &JS<T>
  • .borrow_mut() -> &mut JS<T> (It is important to return &mut JS<T> instead of &mut T so that we can manipulate the tree. This is safe and won't invalidate other transitively rooted things since other interior references through the same path aren't possible due to the runtime check in the refcell)
  • Has extra field. Can't be used in common structs like Node.
  • Has runtime check overhead. Ew.
  • No rooting

We use the MutHeaps in different places depending on the situation.

It's very unclear if the tradeoff is worth it here.

Parsing

We root twice in parsing. As long as the tree is constructed in preorder and the Document is rooted this should be fine.

  • get_or_create in servohtmlparser.rs, called by append in parse.rs (removed by Ms2ger)
  • create_element in parse.rs

Rooting into javascript

Often we create a value, root it, and directly return it to Javascript. This isn't very optimal.

One common occurrence for this is constructors. We root in Constructor() via new(), and the root is immediately returned. Perhaps we should have separate new() and new_unrooted() (where the latter is used by constructors)? Ensuring that new_unrooted() is called last can be done by a lint.

We can also reduce rooting in methods returned to JS similarly. This can be done with an unwrap_ptr() method that converts an &JS<T> into a JS<T>, only allowed at the last position of a DOM method. We also forbid DOM methods from being called directly by Rust code without rooting.

Destructors can cause problems with this design since they run after the return value is computed.

fn SomeDOMMethod() -> JS<Foo> {
    let bomb = something_rooted;
    let x : &JS<T> = bomb.x;

    unwrap_ptr(x)
    // bomb unroots, triggers GC, GC now thinks x is unreachable.
    // boom.
}

(not sure if this can be made safer with lints. Probably.)

For constructors, NEWBI<- (example) might be useful. Alan has a clearer idea of this design.

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