Storing a weak reference to a function, or otherwise avoiding a memory leak

152 views
Skip to first unread message

Zach Bjornson

unread,
Sep 15, 2017, 9:17:54 PM9/15/17
to v8-users
Hello,

I'm trying to implement this type of interface:

var myObj = new MyObj();
myObj
.onevent = function () {
 
// might be a reference to myObj here
}
myObj
.doSomethingAsync(); // causes `onevent` to fire; can be called more than once

`onevent` (a C++ setter) currently stores the callback function in a Persistent<Function>. However, if the callback has a reference to `myObj`, then `myObj` will never be GC'ed.

What's the correct way to implement this? 

I thought of (1) storing a weak reference to the function when it's set, (b) creating a Persistent handle to the function when `doSomethingAsync()` is called, (c) resetting that Persistent when the event is fired. However, I'm not sure how to store a weak reference to the function in the first place.

Thanks,
Zach

Zach Bjornson

unread,
Sep 15, 2017, 11:21:33 PM9/15/17
to v8-users
No, storing a weak reference on the object itself won't work.

Maybe a map of Persistent<Function>, not associated with the MyObj instances, but keyed on the MyObj instance? SetOnevent stores to that map; firing an event involves a lookup; MyObj's destructor deletes from the map?

Thanks,
Zach

Ben Noordhuis

unread,
Sep 18, 2017, 4:03:44 AM9/18/17
to v8-users
`myObj.onevent()` invokes `onevent()` with `this === myObj`. Unless
you want to deviate significantly from normal JS semantics, you would
need to maintain a reference to `myObj` anyway.

In other words, don't worry about it!

Zach Bjornson

unread,
Sep 18, 2017, 12:13:45 PM9/18/17
to v8-users
`myObj.onevent()` invokes `onevent()` with `this === myObj`.  Unless
you want to deviate significantly from normal JS semantics, you would
need to maintain a reference to `myObj` anyway.

(The spec I'm emulating actually states that `onevent()` is invoked with the global context.) Sorry, I don't think I described the issue well.

The issue is not `onevent` needing to maintain a contextual ref to `myObj`, but rather that if `onevent` references `myObj` in its JS function body, then `myObj` will not get GC'ed because `onevent`'s function is stored in a Persistent handle.

JS:

var myObj = new MyObj();

myObj
.onevent = function () { // (a C++ setter)
  console
.log(myObj); // reference to myObj here
}
myObj
.doSomethingAsync(); // causes `onevent` to fire eventually

C++ (pseudo)

class MyObj : ObjectWrap {
 
Persistent<Function> _onevent;
 
void setOnEvent(Local<String> prop, const PropertyCallbackInfo<Value>& info); // stores fn arg in _onevent
}

A work-around is to not use C++ for the `onevent` setter, but I'm still curious how this would be done in C++. Would the class member be instead a Local<Function>, and have to be set with `EscapeableHandleScope scope(isolate); _onevent = scope.Escape(functionArg);` (ish)?

Thanks,
Zach

Ben Noordhuis

unread,
Sep 18, 2017, 12:32:22 PM9/18/17
to v8-users
myObj.onevent is always going to be a strong reference but what you
can do is make myObj strong when myObj.doSomethingAsync() is called,
and weak again when myObj.onevent() is invoked.

Once it's weak again (or when you reset the persistent handle), the
myObj.onevent -> myObj cycle no longer matters, the object will be
eligible for reclamation.
Reply all
Reply to author
Forward
0 new messages