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