We have moved at community.getvera.com

Author Topic: Lua environment on callbacks  (Read 192 times)

Offline GeekGoneOld

  • Jr. Member
  • **
  • Posts: 80
  • Karma: +3/-0
Lua environment on callbacks
« on: March 01, 2019, 09:19:03 pm »
I'm a bit fuzzy on the environement in which things run.

It seems to me that if I make a device, each instance runs in its own environment (please correct my terminology if wrong) and a local variables defined before any code in startup are available only to that device instance.  Not that device, but that device instance.

Now what about a local function.  It is only accessible from within that device instance environment but is it efficiently not duplicated for more device instances of that type?

Now I know I must make a callback global, but why?  As far as I know, when I'm called back (e.g. a call delay from within a device), it is running in my device instance environment.  Why, then, would it be a requirement to be global.  I realize it is being called by the OS, but the OS is obviously setting up the environment first.

Although I have everything in my device working, that's no reason to skip the understanding of how it works and why.  Any insight would be extremely helpful.

Keith

Offline rigpapa

  • Beta Testers
  • Hero Member
  • *****
  • Posts: 1121
  • Karma: +187/-3
Re: Lua environment on callbacks
« Reply #1 on: March 02, 2019, 09:33:25 am »
I'm a bit fuzzy on the environement in which things run.

It seems to me that if I make a device, each instance runs in its own environment (please correct my terminology if wrong) and a local variables defined before any code in startup are available only to that device instance.  Not that device, but that device instance.

Now what about a local function.  It is only accessible from within that device instance environment but is it efficiently not duplicated for more device instances of that type?

Now I know I must make a callback global, but why?  As far as I know, when I'm called back (e.g. a call delay from within a device), it is running in my device instance environment.  Why, then, would it be a requirement to be global.  I realize it is being called by the OS, but the OS is obviously setting up the environment first.

Although I have everything in my device working, that's no reason to skip the understanding of how it works and why.  Any insight would be extremely helpful.

Keith

There are many questions here, but they all stem from "why must callbacks be global?" The answer is that the callback is specified by name in a string, rather than a function reference. The string, as a function name, lacks the context to tell the system what context the function can be found in. So requiring it to be global context quickly answers that question.

This is an unfortunate design decision on Vera's part that has persisted for some time, and could be improved upon any time without disrupting any current plugins or user fragments, IMO. The better choice would have been to simply pass a function reference, essentially a pointer directly to the function. This is unique to each function, so it removes all ambiguity, and removes the necessity for the system to search for the function--it already has it. Here's an example:

Old (current) way:
Code: [Select]
function myGlobalCallback( data ) -- old way, this has to be global
    luup.log("callback called! I got data=" + tostring(data))
end

luup.call_delay( "myGlobalCallback", 10, data )

New way:
Code: [Select]
local function privateCallback( data ) -- declared local, does not need to be global now
    luup.log("callback called! I got data=" + tostring(data))
end

luup.call_delay( privateCallback, 10, data )

This would also facilitate using an anonymous closure as the callback:

Code: [Select]
-- Turn off light after 10 seconds
luup.call_delay( function()
    luup.call_action( "urn:upnp-org:serviceId:SwitchPower1", "SetTarget", { newTargetValue="0" }, lightDevNum )
end, 10 )

This may be a little challenging to read for new players, but it's just passing an anonymous function (defined in line as part of the call) to call_delay(). Being able to do it this way (passing function reference) also makes it possible to use upvalues without any extra or unusual caution.

Vera could make this change today without disrupting current code. All they would need to do is look at the first argument, and if it's a string data type, they do it the old way, and if it's a function, do it the new way. At some point, all they are doing is calling the function pointer anyway, the old way just does the extra work of figuring out how to get it first, while the new way passes it directly, so again, no search needed, and thus doesn't need to be global.
Author of Reactor, DelayLight, SiteSensor, Rachio, Deus Ex Machina II, Intesis WMP Gateway, Auto Virtual Thermostat and VirtualSensor plugins. Vera Plus w/100+ Z-wave devices. Vera3, Lite. Hassio, Slapdash.

Offline therealdb

  • Sr. Member
  • ****
  • Posts: 258
  • Karma: +6/-0
  • Automate all the things!
Re: Lua environment on callbacks
« Reply #2 on: March 02, 2019, 11:42:53 am »
I second that. Coming from C#, anonymous functions are better in terms of readability too.
Vera Edge EU, Fibaro FGRM 222 (14), Fibaro FGS 223 (26), Fibaro FGS 222 (6), Fibaro UBS (2), Fibaro+N Plug (3), NeoCoolCam Door Sensor (3), PIR (2) & Plugs (2), Nest (3), Home Server running my own integrations, Harmony Hub, OpenSprinkler, Personal Weather Station, Sonoff TH & more

Offline GeekGoneOld

  • Jr. Member
  • **
  • Posts: 80
  • Karma: +3/-0
Re: Lua environment on callbacks
« Reply #3 on: March 02, 2019, 01:39:44 pm »
There are many questions here, but they all stem from "why must callbacks be global?" The answer is that the callback is specified by name in a string, rather than a function reference. The string, as a function name, lacks the context to tell the system what context the function can be found in. So requiring it to be global context quickly answers that question.
Very interesting.  I certainly didn't know that!

I like your proposal but as a programmer I can't tell you how many times I've seen a change in one part of code affect something in a completely unrelated area.  Also as a user I would expect full testing prior to release.  All of that takes manpower and there are higher priority issues.

Do you have any comments about the other issues such as whether the functions of a device instance are efficiently not recompiled (i.e. only exist once for the device type).  Specifically, the global functions are redefined (identically) for each device instance.  In most programming languages that is a no-no, but lua is different.  Then there are the local functions.  Are they recompiled for each instance?  Looking at squeezing as much efficiency as possible...

Offline akbooer

  • Beta Testers
  • Master Member
  • *****
  • Posts: 6387
  • Karma: +290/-70
  • "Less is more"
Re: Lua environment on callbacks
« Reply #4 on: March 02, 2019, 01:56:57 pm »
Do you have any comments about the other issues such as whether the functions of a device instance are efficiently not recompiled (i.e. only exist once for the device type).  Specifically, the global functions are redefined (identically) for each device instance.  In most programming languages that is a no-no, but lua is different.  Then there are the local functions.  Are they recompiled for each instance?  Looking at squeezing as much efficiency as possible...

I don't think that there is anything you should be worrying about here.  Lua byte code is incredibly compact.  Much worse is Vera's profligate use of memory.  The latter arises from each plugin having its own Lua instance, and each instance is quite separate from every other.  Within any one, the Lua interpreter is really rather efficient in sharing objects, particularly because of its lexical scoping and use of closures.

Strings, of course, are immutable and always shared, but you should be careful with concatenation operators, always preferring the use of table.concat() over multiple uses of '..'
3x Vera Lite-UI5/Edge-UI7, 25x Fibaro, 23x TKB, 9x MiniMote, 2x NorthQ Power, 2x Netatmo, 1x Foscam FI9831P, 9x Philips Hue,
Razberry, MySensors Arduino, HomeWave, AltUI, AltHue, DataYours, Grafana, openLuup, ZWay, ZeroBrane Studio.