[Ovmsdev] Duktape Persistent Function References
Michael Balzer
dexter at expeedo.de
Wed Sep 2 01:52:12 HKT 2020
Mark,
I'll have a look.
Regards,
Michael
Am 01.09.20 um 07:30 schrieb Mark Webb-Johnson:
> Michael,
>
> Still struggling with this. It seems like your DuktapeObject will do
> this, but I can’t work out how it works.
>
> Here are some notes one what I have done so far:
>
> 1. Created a stub DuktapeConsoleCommand (derived from DuktapeObject)
> in ovms_duktape.{h,cpp}. This should hold enough to be able to
> call the javascript callback method for that object. It also
> stores the module filename (so the registration can be removed
> when the module is unloaded).
>
> 2. Provide a DuktapeCommandMap m_cmdmap in
> ovms_duktape.{h,cpp} OvmsDuktape class that stores a mapping from
> OvmsCommand to DuktapeConsoleCommand.
>
> 3. Created a OvmsDuktape::RegisterDuktapeConsoleCommand in
> ovms_duktape.{h,cpp) that (a) creates the OvmsCommand() object,
> (b) registers it, (c) creates the DuktapeConsoleCommand() object,
> and (d) updates a map from OvmsCommand->DuktapeConsoleCommand.
> There Is also a single callback DukOvmsCommandRegisterRun designed
> to be run by all.
>
> 4. Created hooks NotifyDuktapeModuleLoad, NotifyDuktapeModuleUnload,
> and NotifyDuktapeModuleUnloadAll in OvmsDuktape. The javascript
> module is identified by filename (path to module or script on vfs,
> usually, but may also be an internal module). The Unload functions
> look through the m_cmdmap and unregister commands for javascript
> modules being unloaded.
>
> 5. Provide an implementation for ovms_command DukOvmsCommandRegister
> to support registering commands from Javascript modules. This
> should extract the details, and then call
> OvmsDuktape::RegisterDuktapeConsoleCommand to do the actual
> registration. This has been implemented, except for the callback
> method (and somehow passing that method from Javascript in the
> OvmsCommand.Register javascript call).
>
> 6. Provide a stub implementation for DukOvmsCommandRegisterRun. This
> uses m_cmdmap to lookup the DuktapeConsoleCommand object for
> the command to be run. It should execute the callback method (but
> that part is not yet implemented).
>
>
>
> I still need help with #5 and #6. What needs to be implemented
> in DuktapeConsoleCommand, and how is the parameter
> in OvmsCommand.Register used to store the callback (#5)? Then how to
> callback the command method from DukOvmsCommandRegisterRun (#6)? If
> you have time, it is probably much quicker for you to simply make
> those changes.
>
> An alternative implementation would be to do something like the pubsub
> framework, where the mapping command->callback is done from within a
> javascript module. That I could do, but it seems your DuktapeObject
> can do it better.
>
> Thanks, Mark.
>
>> On 15 Jul 2020, at 3:34 PM, Michael Balzer <dexter at expeedo.de
>> <mailto:dexter at expeedo.de>> wrote:
>>
>> 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
>> _______________________________________________
>> OvmsDev mailing list
>> OvmsDev at lists.openvehicles.com <mailto:OvmsDev at lists.openvehicles.com>
>> http://lists.openvehicles.com/mailman/listinfo/ovmsdev
>
>
> _______________________________________________
> 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/20200901/aa322f53/attachment.htm>
More information about the OvmsDev
mailing list