[Ovmsdev] Duktape Persistent Function References

Michael Balzer dexter at expeedo.de
Wed Jul 15 15:34:11 HKT 2020


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

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.openvehicles.com/pipermail/ovmsdev/attachments/20200715/644f16c7/attachment.html>


More information about the OvmsDev mailing list