Skip to content
This repository has been archived by the owner on Jan 25, 2022. It is now read-only.

Document expectations of WeakRef behavior in practice #192

Open
tjcrowder opened this issue Mar 3, 2020 · 3 comments
Open

Document expectations of WeakRef behavior in practice #192

tjcrowder opened this issue Mar 3, 2020 · 3 comments

Comments

@tjcrowder
Copy link
Contributor

tjcrowder commented Mar 3, 2020

TL;DR: Other than with certain caveats (see below), can developers rely on receiving finalizer calls or not? (For instance, to clean up other things related to reclaimed objects, like cache entries, WeakRefs, wasm objects...) I'm confused because some aspects of the explainer seem to suggest they can, but others (and the proposed spec text) suggest they can't. Does this need firming up and/or clarification?


Details:

I get that finalizer calls have these caveats at a minimum:

  • The specification doesn't require implementations to do garbage collection at all (some embedded implementations, for instance). No GC means no finalizer calls.
  • The explainer gives two other times finalizers reasonably wouldn't be called:
    • When the entire process is going away.
    • When the FinalizationRegistry the objects were in is, itself, unreachable.

Other than that, though, can developers reasonably rely on getting finalizer calls for cleanup purposes?

Evidence suggesting "yes, they can:"

  • The explainer says (my emphasis):

    Whenever you have a JavaScript object that is backed by something in WebAssembly, you might want to run custom cleanup code (in WebAssembly or JavaScript) when the object goes away. A previous proposal exposed a collection of weak references, with the idea that finalization actions could be taken by periodically checking if they are still alive. This proposal includes a first-class concept of finalizers in order to give developers a way to avoid that repeated scanning.

    (Granted, "a way" doesn't necessarily mean they can always avoid it. You could read that as "a way to avoid that repeated scanning if you've ever seen a finalizer call, but you still have to have the code for repeated scanning in case you never see a finalizer call.")

  • The "Fixed version that doesn't leak memory" of the explainer's makeWeakCached example relies on finalizers to avoid leaking memory.

Evidence suggesting "no, they can't:"

  • The explainer says finalizers can be omitted by an implementation "for any reason or no reason."
  • The spec explicitly makes calling HostCleanupFinalizationRegistry optional.
  • Step 7 of the next method of the finalization cleanup interator says an implementation "may" return a result object containing the held value of a cell with an empty [[WeakRefTarget]], not that it will.
  • The Execution section of the spec says an implementation "may" mark [[WeakRefTarget]]s empty, not that it will. This means even if userland code proactively calls cleanupSome, it won't do anything if the implementation has never set [[WeakRegTarget]]s empty.

If the intention is that finalizers should be called "at some point in the future" by implementations that do perform garbage collection except in some limited situations, should the requirement be firmer/clearer? Or is it something developers can't reasonably rely on?

Thanks!


I'm curious about the intention re specified behavior/requirements. I do see that the signals are good that implementers of major engines will call finalizers; V8 and SpiderMonkey both do (behind flags at present). (I can't find any public signals for finalizers from Apple — for instance, here or here — but I haven't trawled the notes, and if V8 and SpiderMonkey both support them, it's hard to see JavaScriptCore not.)

@tjcrowder tjcrowder changed the title Avoiding scanning for dead WeakRefs? (Or: Are finalizers really completely optional?) Are finalizers really completely optional? Mar 3, 2020
@littledan
Copy link
Member

I'd expect that each engine will have certain cases where objects are not live but, nevertheless, a finalizer is significantly delayed or never runs at all. So I don't think it's possible to specify anything more than we have.

At the same time, it could be good to document shared guidelines/intention here. I believe everyone intends that some things will sometimes be called, at least.

I'm not sure if anyone will take up that work item, and I don't think it blocks Stage 4. I'd put this at a lower priority than good reference documentation #170 .

@kmiller68 can clarify the JSC status. I believe there's an implementation in progress, but I'm not sure if there's a tracking issue or anything.

@littledan littledan changed the title Are finalizers really completely optional? Document expectations of WeakRef behavior in practice Mar 4, 2020
@tjcrowder
Copy link
Contributor Author

@littledan - Thanks. What would that documentation say, though? A) That you do need to handle never getting finalizer calls (my more complicated version of makeWeakCache here for instance, but with deref() === undefined instead of isReclaimed()), or B) that while implementations are allowed to not make the calls, it's likely to be in edge cases where it's okay to just leak memory (for things like that cache), or cases where it's irrelevant (process shutdown)? Or something else? :-)

@littledan
Copy link
Member

I'm not sure what this document would say exactly. I haven't been able to find similar documents for other languages. It'll be a lot of work to figure out exactly what it'd be OK to say here, I think.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

2 participants