Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Library authors do not benefit as much from defer #3

Open
JadenSimon opened this issue Oct 11, 2024 · 2 comments
Open

Library authors do not benefit as much from defer #3

JadenSimon opened this issue Oct 11, 2024 · 2 comments

Comments

@JadenSimon
Copy link
Owner

JadenSimon commented Oct 11, 2024

It would be much, much better if libraries could specify how a specific resource is cleaned up with no user interaction. FinalizationRegistry doesn't work here because it lacks guarantees.

Possible Solution?

Because we want to do this on the library side, we can be a little clever by offering a mechanism to turn any JS object into a smart pointer:

const ref = FinalizationRegistry.addFinalizer(obj, o => o.dispose())
// Or maybe libraries can extend a `SmartObject` class? Could be easier on engines too.

Now, this isn't just any smart pointer. Much of the challenge here involves ensuring high performance on the engine side. So calling this method (a new class might be better?) causes the current frame to be marked as containing a smart pointer. We'll call these frames "smart frames".

Smart frames will leverage a technique already employed by many engines for GC: write barriers. While a smart frame is active, we can enable these write barriers to track how smart pointers are being moved around. And when a smart frame exits, we do the following:

  • Decrement any smart pointers in the frame
  • Run finalizers for any pointers w/ a count of 0
  • Recompute the reachability of smart pointers from code
    • If code can reach a smart pointer, it should run with a smart frame
    • This is likely the trickiest part of the design, maybe it can be done incrementally i.e. whenever a smart pointer is written
  • If the return value contains a smart pointer, then the caller frame becomes a smart frame
  • When code can no longer reach a smart pointer, we need to flush any optimized code
    • Unless we can track if the optimized code references smart pointers?

We're effectively adding a mini GC to the end of specific frames. Branch prediction should allow for negligible overhead when smart pointers aren't in use.

This approach seems viable from my limited knowledge of JS engines. Is it worth it? Maybe!

Hidden cost

Having no user interaction has the downside that users of libraries would be unaware of the (possibly high) overhead of smart frames. Likewise, there's no obvious way to "opt-out" if the user prefers manual disposal. It seems unreasonable to force users into this pattern. However, things like this are always a problem with libraries.

@martinheidegger
Copy link

To me hiding the cost would be a strong argument against this. That is why I would prefer using for that: tc39/proposal-explicit-resource-management#244 (comment)

@JadenSimon
Copy link
Owner Author

Hidden costs are a big part of the language though (GC, JIT, polyfills, etc.), JS devs don't seem to mind the tradeoff for simplicity. Not adding new syntax is incredibly valuable and seems worth the hidden cost. Syntax is not easy to take back.

using can work but I think it needs to address the "closure problem", double frees, and library author concerns over enforcement. And it needs to do it all from the outset, not with additional proposals. As far as I can tell, explicit resource management is not exactly something the community is clamoring for. The phrase itself is practically opposite to the spirit of the language.

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

No branches or pull requests

2 participants