tc39/proposal-weakrefs

Example use case (not resource management; convenient via iterable weak set)

domenic opened this issue · 8 comments

I have a definite use case for weak references in one of my projects, jsdom. It runs primarily in Node, but also in the browser. Of note, this case isn't related to resource management.

jsdom implements, among other things, the DOM Standard. In particular, there is one line that concerns us here: when removing a node:

  1. For each NodeIterator object iterator whose root’s node document is node’s node document, run the NodeIterator pre-removing steps given node and iterator.

To correctly implement this step, you need a reference to every NodeIterator object that's ever been created, so you can update it correctly (by running the pre-removing steps).

This really requires weak references, as otherwise you end up holding a strong reference to every NodeIterator ever created, never releasing their memory, or the memory of the other objects that the NodeIterator references (e.g. the two DOM nodes it points to, root and reference node).


Of note:

  • This is not being used as any sort of backstop for cleaning things up. It's just plain required for correctness without massive memory leaks.
  • This doesn't require finalization at all.
  • What we really need is an "iterable weak set"; weak references of course can be used to build one of these (and an iterable weak set can be used to build a weak reference).

Finally, people may be amused by how we currently work around this. We have a user-configurable number, "max working NodeIterators", which we default to 10. We hold strong references to the last 10 created NodeIterators. If an 11th NodeIterator is created, we remove it from the list, then set an internal bit on it that says "sorry, this one's broken", and any methods you try to call on that NodeIterator will throw an exception, helpfully telling you to up the max working NodeIterators configuration.

We then loop over those 10 strongly-held NodeIterators whenever a node is removed from the document, and update them appropriately per the spec.

This is a great use. Thanks. Can you post a pointer to the code so that we can try enhancing it with weak ref code? I will be interested in trying the proposed weakref approach and trying it with various weak collection options.

Also, is there anything in the standard or other source of desire/requirement for making a similar cleanup step if the node is just dropped on the floor (e.g., some incidental additional value in triggering the cleanup).

+1 to "iterable weak set", as another use case I have heard is implementing the "become" operator in languages like Smalltalk.

Is it possible to change the title of an issue? If so, I suggest adding "iterable weak set" to the title here.

I got a rather simple other usecase, I've objects representing points in a 2d plane, I like to freeze them and I would like to intern them, so a simple a === b would test them for equality. For this I'd put all the points generated in a weak table with some kind of hash value to test if this object already exists before creating a new one. Similar for objects representing lines or other geometric objects.

@dtribble the code is in roughly these locations:

I'm not sure which node you'd be referring to in "is there anything in the standard or other source of desire/requirement for making a similar cleanup step if the node is just dropped on the floor", but I don't think there is any such desire/requirement.

@titzer I'm not sure why we'd call out the iterable weak set API shape specifically here; this could be accomplished with any API shape, from the proposal's weak ref, to an iterable weak map, to an iterable weak set, ...

@domenic I simply meant using "iterable weak set" in a conceptual sense to summarize the use case described here, not any particular API commitment.

Ah OK, sure, I'll do that if you think that there's a tie between a generalization of this use case and the API shape.

@mathiasbynens wrote up something related to this use case in the explainer. Is there anything else to add, or is it OK to close this issue?

Closing due to #17 (comment)