My assumption is that my test app terminates before the GC has a chance to run. To test this:
- I run node with the --expose-gc option - I call global.gc() at the end of my test app In this case, the destructor of my ObjectWrap is indeed called. *So it seams the GC doesn't simply "collect everything" when the app terminates.* *Why is that?* It sounds like a pretty bad things to do: some behaviors might expect the destructor to be called in order to close a socket, etc... On Friday, November 17, 2017 at 10:27:22 AM UTC+1, Jean-Marc Le Roux wrote: > > Thanks for the great help! > I think it makes a lot of sense now. > > So I've extended ObjectWrap like so: > > class BSONObject : public node::ObjectWrap >> { >> private: >> bson_t* _bson; >> public: >> BSONObject(bson_t* bson, Local<ObjectTemplate> tpl) : >> _bson(bson) >> { >> Local<Object> obj = tpl->NewInstance(); >> initialize(v8::Isolate::GetCurrent(), obj, bson); >> Wrap(obj); >> } > > > > ~BSONObject() >> { >> std::cout << "~BSONObject()" << std::endl; >> bson_destroy(_bson); >> } > > > > static >> Local<ObjectTemplate> >> create_template(Isolate* isolate, const bson_t* document) >> { >> Local<ObjectTemplate> tpl = ObjectTemplate::New(isolate); >> tpl->SetInternalFieldCount(1); > > > And the "insert" function that I expose in JS and that instanciates the > BSONObject is written like this: > > void >> insert(const FunctionCallbackInfo<Value>& args) >> { >> Isolate* isolate = args.GetIsolate(); >> >> db->insert( >> json_stringify(isolate, args[0]), >> [&args, isolate](const bson_t* document) >> { >> if (!args[1]->IsUndefined()) >> { >> Local<Function> callback = Local<Function>::Cast(args[1]); >> Local<Object> obj = Object::New(isolate); >> *Local<Value> argv[] = { (new >> BSONObject(bson_copy(document)))->handle(isolate) };* >> callback->Call(isolate->GetCurrentContext()->Global(), 1, >> argv); >> } >> } >> ); >> } > > > It runs fine. > But the destructor is never called (I never see "~BSONObject()" on stdout). > > Did I miss something? > > Thanks ! > > 2017-11-16 22:01 GMT+01:00 J Decker <d3c...@gmail.com>: > >> You don't manage the instance, the v8 engine does. When it's no longer >> referenced in the engine, it's garbage collected which triggers the >> callback set when the perisstent object is made weak. >> >> You don't need to keep it anywhere, the object returned into the engine >> has Internal Fields ( SetInternalFieldCount(1) ) which is used to point to >> your native class instance. So when the callback is called, it knows what >> to call to destruct it. >> >> You have to 'keep it' in the javascript scrpit if you want to keep it, >> it's not 'kept' in the native code... >> >> >> --- This is some code I've done that uses setweak directly; but it's >> fairly scattered.... >> >> https://github.com/d3x0r/sack.vfs/blob/master/src/jsonParse.cc#L18 >> >> You can create an instance of your wrapped object with >> >> Local<Function> cons = Local<Function>::New( isolate, constructor ); >> cons->NewInstance( isolate->GetCurrentContext(), argc, argv >> ).ToLocalChecked() ; >> >> Hmm that's not the best example source... >> >> https://github.com/d3x0r/sack.vfs/blob/master/src/vfs_module.cc#L407 >> >> This creates a buffer, and makes it weak... >> https://github.com/d3x0r/sack.vfs/blob/master/src/vfs_module.cc#L438 >> >> Local<Object> arrayBuffer = ArrayBuffer::New( isolate, buf, len ); >> PARRAY_BUFFER_HOLDER holder = GetHolder(); >> holder->o.Reset( isolate, arrayBuffer ); >> holder->o.SetWeak<ARRAY_BUFFER_HOLDER>( holder, releaseBuffer, >> WeakCallbackType::kParameter ); >> holder->buffer = buf; >> >> PARRAY_BUFFER_HOLDER is just a type that holds the thing to be free (or >> object containing things to be released), and the persistent reference that >> has been made weak... >> >> struct arrayBufferHolder { >> void *buffer; >> Persistent<Object> o; >> }; >> >> >> >> On Thu, Nov 16, 2017 at 9:56 AM, Jean-Marc Le Roux < >> jeanmarc.ler...@aerys.in> wrote: >> >>> Node offers a C++ Clss extention ObjectWrap >>> >>> >>> Thanks ! >>> It is related to node. Yet I'm not sure how I'm supposed to use >>> ObjectWrap. >>> >>> I understand I have to create my own class that extends ObjectWrap and >>> implement a proper destructor. >>> Then I'll instanciate that class to wrap my Local<Object>. But how do I >>> manage such instance? >>> If I don't keep it somewhere, it will be destructed. >>> If I keep it around, then I'm keeping references to potentially >>> deallocated memory. >>> >>> It feels like I end up with the same problem... >>> >>> 2017-11-16 18:36 GMT+01:00 J Decker <d3c...@gmail.com>: >>> >>>> >>>> >>>> On Thu, Nov 16, 2017 at 1:24 AM, Jean-Marc Le Roux < >>>> jeanmarc.ler...@aerys.in> wrote: >>>> >>>>> I assume you're storing references to the objects as Persistent? >>>>> >>>>> >>>>> Nope. You've got the whole code up there. >>>>> So if I understand correctly: >>>>> >>>>> - the objects I need to keep track of the lifecycle should be made >>>>> Persistent ; >>>>> - all the other values can be set as Local ; >>>>> - I should set the weak callback using MakeWeak ; >>>>> - callback will be called when such object is GCed and I can free >>>>> my C/C++ memory then. >>>>> >>>>> Is that correct ? >>>>> >>>> >>>> Yes. I sthis perhaps related to Node? >>>> Node offers a C++ Clss extention ObjectWrap >>>> >>>> https://nodejs.org/api/addons.html#addons_wrapping_c_objects >>>> https://github.com/nodejs/node/blob/master/src/node_object_wrap.h >>>> >>>> which simplifies that whole process... when the object is GC'd the >>>> class destructor will get called so you can release values there. >>>> >>>> >>>> >>>>> >>>>> 2017-11-15 19:50 GMT+01:00 J Decker <d3c...@gmail.com>: >>>>> >>>>>> I assume you're storing references to the objects as Persistent? All >>>>>> you need to do then is call SetWeak() >>>>>> https://v8docs.nodesource.com/node-0.10/d2/d78/classv8_1_1_persistent.html >>>>>> void MakeWeak (void *parameters, WeakReferenceCallback callback) >>>>>> >>>>>> the callback is called when the object is GC'd. >>>>>> >>>>>> >>>>>> On Wed, Nov 15, 2017 at 8:37 AM, Jean-Marc Le Roux < >>>>>> jeanmarc.ler...@aerys.in> wrote: >>>>>> >>>>>>> So I've found a solution to make things lazy: I set an accessor >>>>>>> instead of decoding/setting the actual value. >>>>>>> >>>>>>> void >>>>>>> getter(Local<Name> property, const PropertyCallbackInfo<Value>& info) >>>>>>> { >>>>>>> Isolate* isolate = info.GetIsolate(); >>>>>>> >>>>>>> const bson_t* document = reinterpret_cast<bson_t*>( >>>>>>> info.This() >>>>>>> ->Get(String::NewFromUtf8(isolate, >>>>>>> "__bson_document_ptr")) >>>>>>> ->ToInt32()->Value() >>>>>>> ); >>>>>>> >>>>>>> char key[255]; >>>>>>> property->ToString(isolate)->WriteUtf8(key, 255); >>>>>>> >>>>>>> bson_iter_t iter; >>>>>>> bson_iter_init(&iter, document); >>>>>>> // FIXME: index the property so we don't have to find it >>>>>>> bson_iter_find(&iter, key); >>>>>>> >>>>>>> // FIXME: replace the accessor with the deserialized value >>>>>>> >>>>>>> info.GetReturnValue().Set(iterator_to_value(isolate, &iter)); >>>>>>> } >>>>>>> >>>>>>> Local<Value> >>>>>>> fill_object(Isolate* isolate, Local<Object>& obj, const bson_t* >>>>>>> document) >>>>>>> { >>>>>>> obj->Set( >>>>>>> String::NewFromUtf8(isolate, "__bson_document_ptr"), >>>>>>> Int32::New(isolate, reinterpret_cast<intptr_t>(document)) >>>>>>> ); >>>>>>> >>>>>>> bson_iter_t iter; >>>>>>> if (bson_iter_init(&iter, document)) >>>>>>> { >>>>>>> while (bson_iter_next(&iter)) >>>>>>> { >>>>>>> const char* key = bson_iter_key(&iter); >>>>>>> >>>>>>> if (!obj->Has(String::NewFromUtf8(isolate, >>>>>>> key))) >>>>>>> { >>>>>>> obj->SetAccessor( >>>>>>> isolate->GetCurrentContext(), >>>>>>> String::NewFromUtf8(isolate, key), >>>>>>> &getter >>>>>>> ); >>>>>>> } >>>>>>> } >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> The secret sauce is to : >>>>>>> >>>>>>> - keep the original data (the bson_t) allocated >>>>>>> - store the corresponding pointer in a v8::Object >>>>>>> >>>>>>> But now I have a memory leak because those pointers will never be >>>>>>> freed. >>>>>>> How can I know when the Object will be disposed by the GC? >>>>>>> >>>>>>> Thanks, >>>>>>> >>>>>>> -- >>>>>>> -- >>>>>>> v8-users mailing list >>>>>>> v8-users@googlegroups.com >>>>>>> http://groups.google.com/group/v8-users >>>>>>> --- >>>>>>> You received this message because you are subscribed to the Google >>>>>>> Groups "v8-users" group. >>>>>>> To unsubscribe from this group and stop receiving emails from it, >>>>>>> send an email to v8-users+unsubscr...@googlegroups.com. >>>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>>> >>>>>> >>>>>> -- >>>>>> -- >>>>>> v8-users mailing list >>>>>> v8-users@googlegroups.com >>>>>> http://groups.google.com/group/v8-users >>>>>> --- >>>>>> You received this message because you are subscribed to a topic in >>>>>> the Google Groups "v8-users" group. >>>>>> To unsubscribe from this topic, visit >>>>>> https://groups.google.com/d/topic/v8-users/aVeevQcHJ2c/unsubscribe. >>>>>> To unsubscribe from this group and all its topics, send an email to >>>>>> v8-users+unsubscr...@googlegroups.com. >>>>>> For more options, visit https://groups.google.com/d/optout. >>>>>> >>>>> >>>>> >>>>> >>>>> -- >>>>> *Jean-Marc Le Roux* >>>>> >>>>> >>>>> Founder and CEO of Aerys (http://aerys.in) >>>>> >>>>> Blog: http://blogs.aerys.in/jeanmarc-leroux >>>>> Cell: (+33)6 20 56 45 78 >>>>> Phone: (+33)9 72 40 17 58 >>>>> >>>>> -- >>>>> -- >>>>> v8-users mailing list >>>>> v8-users@googlegroups.com >>>>> http://groups.google.com/group/v8-users >>>>> --- >>>>> You received this message because you are subscribed to the Google >>>>> Groups "v8-users" group. >>>>> To unsubscribe from this group and stop receiving emails from it, send >>>>> an email to v8-users+unsubscr...@googlegroups.com. >>>>> For more options, visit https://groups.google.com/d/optout. >>>>> >>>> >>>> -- >>>> -- >>>> v8-users mailing list >>>> v8-users@googlegroups.com >>>> http://groups.google.com/group/v8-users >>>> --- >>>> You received this message because you are subscribed to a topic in the >>>> Google Groups "v8-users" group. >>>> To unsubscribe from this topic, visit >>>> https://groups.google.com/d/topic/v8-users/aVeevQcHJ2c/unsubscribe. >>>> To unsubscribe from this group and all its topics, send an email to >>>> v8-users+unsubscr...@googlegroups.com. >>>> For more options, visit https://groups.google.com/d/optout. >>>> >>> >>> >>> >>> -- >>> *Jean-Marc Le Roux* >>> >>> >>> Founder and CEO of Aerys (http://aerys.in) >>> >>> Blog: http://blogs.aerys.in/jeanmarc-leroux >>> Cell: (+33)6 20 56 45 78 >>> Phone: (+33)9 72 40 17 58 >>> >>> -- >>> -- >>> v8-users mailing list >>> v8-users@googlegroups.com >>> http://groups.google.com/group/v8-users >>> --- >>> You received this message because you are subscribed to the Google >>> Groups "v8-users" group. >>> To unsubscribe from this group and stop receiving emails from it, send >>> an email to v8-users+unsubscr...@googlegroups.com. >>> For more options, visit https://groups.google.com/d/optout. >>> >> >> -- >> -- >> v8-users mailing list >> v8-users@googlegroups.com >> http://groups.google.com/group/v8-users >> --- >> You received this message because you are subscribed to a topic in the >> Google Groups "v8-users" group. >> To unsubscribe from this topic, visit >> https://groups.google.com/d/topic/v8-users/aVeevQcHJ2c/unsubscribe. >> To unsubscribe from this group and all its topics, send an email to >> v8-users+unsubscr...@googlegroups.com. >> For more options, visit https://groups.google.com/d/optout. >> > > > > -- > *Jean-Marc Le Roux* > > > Founder and CEO of Aerys (http://aerys.in) > > Blog: http://blogs.aerys.in/jeanmarc-leroux > Cell: (+33)6 20 56 45 78 > Phone: (+33)9 72 40 17 58 > -- -- v8-users mailing list v8-users@googlegroups.com http://groups.google.com/group/v8-users --- You received this message because you are subscribed to the Google Groups "v8-users" group. To unsubscribe from this group and stop receiving emails from it, send an email to v8-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.