Mark, yes, I needed that persistence for the HTTP and VFS classes, but I also needed to be able to couple a dynamic C++ instance with a JS object and have a mechanism to prevent garbage collection while the C++ side is still in use. If the C++ side is no longer needed, the JS finalizer also needs to imply the C++ instance can be deleted. That is all implemented by DuktapeObject. DuktapeObject also provides JS method invocation on the coupled JS object and a mutex for concurrency protection. We probably need some more framework documentation than the header comments (applies to all of our framework classes…): /*************************************************************************************************** * DuktapeObject: coupled C++ / JS object * * Intended for API methods to attach internal API state to a JS object and provide * a standard callback invocation interface for JS objects in local scopes. * * - Override CallMethod() to implement specific method calls * - Override Finalize() for specific destruction in JS context (garbage collection) * - call Register() to prevent normal garbage collection (but not heap destruction) * - call Ref() to protect against deletion (reference count) * - call Lock() to protect concurrent access (recursive mutex) * * - GetInstance() retrieves the DuktapeObject associated with a JS object if any * - Push() pushes the JS object onto the Duktape stack * * Note: the DuktapeObject may persist after the JS object has been finalized, e.g. * if some callbacks are pending after the Duktape heap has been destroyed. * Use IsCoupled() to check if the JS object is still available. * * Ref/Unref: * Normal life cycle is from construction to finalization. Pending callbacks extend * the life until the last callback has been processed. A subclass may extend the life * by calling Ref(), which increases the reference count. Unref() deletes the instance * if no references are left. */ You normally just need to use Register/Deregister & Ref/Unref, and to implement the constructor and CallMethod. Coupling of the instances normally is done on construction, as a JS object is normally already needed for the parameters and can simply be attached to. Have a look at DuktapeHTTPRequest, DuktapeVFSLoad and DuktapeVFSSave, these are the current subclasses using this. For the command registration I would probably couple the OvmsCommand instance with a JS command object providing an execution method. Tell me if you need more info. Regards, Michael Am 15.07.20 um 08:12 schrieb Mark Webb-Johnson:
@Michael this is probably for you.
I am trying to implement javascript command registration. The idea is that a javascript module can call something like:
OvmsCommand.Register(basecommand, name, title, callbackfn, usage, min, max)
Then we reflect that into MyCommandApp.RegisterCommand, and keep a track of which command is for which javascript callbackfn. When the command is executed, we pass it into duktape.
I also have tracking for javascript module loading and unloading, so I can DeregisterCommand() if duktape is reloaded (and also protected against commands being registered in short-lived scripts run from the command line).
To implement this, I need to store the callbackfn as a persistent reference to a duktape javascript function.
The issue with callback function references in duktape is summarised here:
https://wiki.duktape.org/howtonativepersistentreferences
/When a Duktape/C function is called, Duktape places the call arguments on the value stack. While the arguments are on the value stack, they're guaranteed to be reachable and the Duktape/C function can safely work with the arguments.
However, when the Duktape/C function returns, the value stack is unwound and references in the function's value stack frame are lost. If the last reference to a particular value was in the function's value stack frame, the value will be garbage collected when the function return is processed./
The standard approach is to store the reference back in the duktape duk_push_global_stash so it won’t get garbage-collected. But, that seems messy.
I see that Michael has already implemented something that seems similar in ovms_script.{h, cpp}, for the async http callbacks. Presumably to avoid this issue. But, the approach seems very different, and I am not sure if it is stopping _all_ garbage collection for the duration of the async query, or just that particular object being garbage collected. The work seems extensive (quite a few objects involved).
So @Michael, any suggestions for this? I don’t want to reinvent the wheel...
Regards, Mark.
_______________________________________________ OvmsDev mailing list OvmsDev@lists.openvehicles.com http://lists.openvehicles.com/mailman/listinfo/ovmsdev
-- Michael Balzer * Helkenberger Weg 9 * D-58256 Ennepetal Fon 02333 / 833 5735 * Handy 0176 / 206 989 26