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

node: store callbacks in map #3772

Open
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

FloVanGH
Copy link
Member

@tronical does this go in the direction you have suggested?

@tronical
Copy link
Member

For #3706 ? No, I think the callbacks need to be stored in the instance itself, as properties. We could have a "__callbacks" property that points to an Array, but that would be externally accessible (bad). The solution to having these kind of "private" properties is to use Symbols as key, instead of a string.

@FloVanGH
Copy link
Member Author

For #3706 ? No, I think the callbacks need to be stored in the instance itself, as properties. We could have a "__callbacks" property that points to an Array, but that would be externally accessible (bad). The solution to having these kind of "private" properties is to use Symbols as key, instead of a string.

Yes I mean this issue. I have done an commit. Do mean more something more like this?

@tronical
Copy link
Member

Yes, that looks like a good direction. The main issue I have is that this only works for the wrapper. If we make the interpreter public, then users of that will still have memory leaks.

Does it pass the test? If yes, can you add this also to the CI?

@FloVanGH
Copy link
Member Author

Yes, that looks like a good direction. The main issue I have is that this only works for the wrapper. If we make the interpreter public, then users of that will still have memory leaks.

Does it pass the test? If yes, can you add this also to the CI?

Yes that's true it won't fix it for the ComponentInstance it self. What could work is do write an wrapper for the ComponentInstance that implements the corresponding behavior. So it would work for both. What do you think?

Yes it works with the tests.

@ogoffart
Copy link
Member

I don't think this will work.
The problem is not in the .ts, but it is in the .rs.

the set_callback function will move a RefCountedReference to the binding, so we have this cyclic reference:
RefCountedReference -> JS function -> instance -> native component -> binding -> RefCountedReference -> ...

We need to break the cycle somehow.

I'm not familiar about napi, but could this help us? https://docs.rs/napi/latest/napi/bindgen_prelude/struct.WeakReference.html
otherwise, we should have the object as a property exposed to JS and use that. So it would look like

native component -> array -> user js function -> instance -> native component.

And that cycle is ok because everything is managed by the garbage collector, there is no root there. (while the RefCountedReference introduces a root)

@tronical
Copy link
Member

I don't think this will work. The problem is not in the .ts, but it is in the .rs.

the set_callback function will move a RefCountedReference to the binding, so we have this cyclic reference: RefCountedReference -> JS function -> instance -> native component -> binding -> RefCountedReference -> ...

Florian's patch does indeed create a cyclic reference, because it keeps a strong reference to the (new) closure that however still captures the outer environments that hold the instance.

I'm convinced that the Symbol approach is the right way to create a "private" property. I do agree that this needs to be solved in .rs. What I'd do is (in Rust code, using napi API):

(1) Create a JS array that has N entries where N is the number of callbacks (it's a fixed number). Every callback shall have a corresponding index in the array.
(2) Create a Symbol.
(3) Store the array as property in the instance JS object with the symbol as key.
(4) In set callback, determine the index for the callback (given by name). Store the provided JS closure at the given index in the JS array. Capture the index in the Rust closure installed.
(5) When the Rust closure gets invoked, fetch the array, fetch the JS closure at the captured index, invoke it.

That way the JS GC has a full view over the cyclic reference and can collect appropriately.

tronical added a commit that referenced this pull request Nov 6, 2023
tronical added a commit that referenced this pull request Nov 28, 2023
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

Successfully merging this pull request may close these issues.

None yet

3 participants