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:
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