-
Notifications
You must be signed in to change notification settings - Fork 673
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
Semi-circular references #1963
Comments
What about storing an object in the structure pointed by the private property? I am not sure I understand the circular part. |
Hmm, I think I follow, but just to summarize:
I've ran into this issue before too. I don't have a great answer unfortunately.
A "work-around" that we are using is to move the ownership to JS entirely. It's possible to make the internal variables inaccessible by using closures, for example:
|
@martijnthe about your "weak reference", I think it is quite similar with @grgustaf 's "hidden property", right? Only native code can access the "properties", and GC will mark those property as "visited". |
Sure, I just tried to distill the problem to the essence. Having "hidden properties" sounds like you would have to do property look-ups, which would be much more expensive compared to having a direct but weak |
Ok, so the problem is: there is a circular reference between objects, and this circular reference is caused by native handlers. Since native handlers can only create strong references, the engine keeps these objects alive. Perhaps weak references could be created by not referencing these objects in the native side. As long as they are used, they will be kept alive. When destroyed, their native free callback can drop these weak references. When the object is temporarily used for something (e.g. setting its property), just acquire / release a strong reference to it. The problem with hidden properties is that names can collide. E.g. if your |
Yes, you've all understood the issue exactly. @zherczeg if I'm understanding your last comment, I don't think it works. The object in question represents a network connection. So JS signs up for various callbacks, and then forgets about the object. The callbacks will still be called - the case I'm trying to handle is when the network connection is closed; I have no way to free things up. I'll experiment with manually releasing all the references at the time the connection is closed.
I can't just not acquire those callback functions, because I'm the only one who holds a reference to them - they would simply be freed if I didn't acquire them. |
This does not sound difficult. The C struct of the N network connection has a reference counter, and each native callback increases the reference count. It also has a status member which tells whether the connection is closed. Callbacks do different things when the connection is closed. And you only free things up when the reference counter reaches zero. Basically you don't create a reference to a JS object, instead the private C struct has a reference counter. |
They're not native callbacks, they're JS callbacks - an anonymous function only passed to my setter. So there are no other references to it but one that I make in C. If I don't acquire it, it will be immediately freed the next time the GC runs. It does appear I can solve my dilemma by releasing the callback(s) when the connection is closed. But it seems like it would be nice for me if I were able to make a weak reference to the JS callback instead, so that once my connection object drops to zero, they would both get freed together. I guess a weak reference requires directionality "obj A is referred to by obj B", while strong references are global "obj A is referred to by someone". So it would probably be something like Basically, this would give the API user the same kind of flexibility you have internally. But I guess it's just nice to have; I'm not sure if some situation truly requires it. |
I can imagine a jerryx extension for this purpose. These references could be stored in a shadow JS object, which is not accessible from JS side (only C side). A new private property type could refer to such shadow object. These objects are also marked by GC. |
@zherczeg I dont think jerryx can handle this issue, because we need to change the
@grgustaf is this what you want? |
I'm not sure if Often you already have a C struct "backing" the JS object which has a I guess you could remove it and only use get/set by string name (i.e.
How about the other way around? If
|
Yes, that is exactly what I thought. In this way, we provide a choice that users dont need to track the obj_b in native code. For the name string, we could allow it be undefined, if users decide to track obj_b in native. But that is just my preference.
No. to GC, |
A GC change increase the binary size even for those, who don't want to use this feature. As usual, I would prefer a very simple API in core, and something user friendly in jerryx. This would be my proposal: core: When object_a is marked, object_b (and its properties) are also marked. From implementation perspective this would be another private magic string. jerryx: |
How can it be implemented without changing the gc? |
The gc will be changed. But the change will be small (the binary growth should be very minimal). |
Naming the objects As an alternative, I do still wonder if it might make sense to notify when refcount drops to zero, with a second callback in the type_info. If I was notified when refcount went to zero, I could free up any hard references I hold and break these circular dependencies that way. Which of these models is easier for the programmer to grasp? The callback could be called "release_references_cb" perhaps... the time for me to stop referring to any other JS objects on behalf of this one. Is this more or less overhead to implement? |
@grgustaf I doubt the |
Pinging this issue. The discussion seems to have died away. Is that because a workaround was found and used, or a PR has landed that solved the issue (which I'm not aware of)? So, is this still a problem? |
Hi @akosthekiss ! The workaround I used involved using the context manager API. In the |
Hi @martijnthe ! Thanks for the feedback! I'm going to close this issue then as this workaround seems to be the solution. Let's have another issue opened if and when this is not enough, and another/better approach is needed. |
I ran into a problem today where an object wasn't getting freed. It turned out this was because its native pointer struct held acquired references to "listener" functions which in turn were bound to a scope object that held the original object as a property. I had to dig deeply into the GC today to learn about these things; before I had assumed that once refcounts went to 0, it would be freed. But there are essentially these "weak references" of 10 different types I see in gc_mark().
Anyway, I had been expecting when the reference count went to zero I would get my type_info's free_cb() called and that was where I was releasing these listener functions. It seems to me now that for this kind of case, there should perhaps be release_cb() in the type_info as well. I'm thinking when my refcount drops to zero I could probably release my own references like this. But maybe I'm wrong, it's a bit confusing.
I could use advice if there's another way to solve this. Previously we had implemented a "hidden property" to store an array of listeners; so these were held by "property reference" and disappeared. But this hidden property implementation was also something of a hack of JerryScript and a clever JS user could maybe have accessed it. So another good way to solve this might be for JrS to provide a way to define properties on objects that are hidden to JS and visible only from C APIs. I wonder if others have desired these same things?
The text was updated successfully, but these errors were encountered: