I have a working plugin that has a function called by luup.call_timer(“”,1,2) that cheerfully accesses and manipulates variables defined as local within the implementation code block.
I realised that, by using local variables to save state information, the code was not re-entrant and would fall over with multiple instances. So I placed all the variables into a table indexed by device number. Eg: vars[lul_device].ipaddr = “192.168.1.xxx”". All my functions work fine except that the one called by luup.call_timer does not see the table. It doesn’t matter whether I make the table local or global - it just gets the nil of an uninitialized variable.
Does a function called by luup.call_timer run in a different environment to the other functions? Why does it work OK with simple variables but not with a table? How do I give the timed function callback routine access to the table?
I realised that, by using local variables to save state information, the code was not re-entrant and would fall over with multiple instances
Did you observe that specific behavior somewhere? ... it doesn't sound correct.
Each Device is supposed to have it’s own LuaState context, along with 2x threads for execution (mutex’d to avoid issues), and the Plugin itself is just the code (requiring a specific Device for any state sharing).
They should be isolated from each other in this respect, and it’s certainly what I’ve seen in cases where we have multiple Devices created from a single Plugin codebase (like the Sonos)
Locals should be fine, as should globals, since each is run inside it’s own context.
Did you observe that specific behavior somewhere? ... it doesn't sound correct.
No - I didn't observe the behavior. It was my assumption based on experience with other software environments. If Luup keeps device instances in separate contexts then I can stop worrying about it. Thanks for setting me straight.
Each plugin instance has it’s own LUA context.
If a device, has a child device, they share a context. (As a result they also save memory. I believe a large part of memory is stack space for the two threads per device as well as the LUA context)
I am not sure about Scenes … I do not know if they all share a common context … (maybe the Startup context) or are parasites of the device context that initiated a trigger.
I do know that the trigger and scene level LUA are in the same context … I am not sure which context that is.
Thanks, Richard, I appreciate the extra detail. I’m not used to writing code that runs in such a protected environment. 
While I have the opportunity: I am puzzled by the visibility of lul_device. In some of my functions, it appears to behave as a global variable. In others, I have to pass it explicitly from the action context. For example:
This works fine:
local function savecodes()
local sc = serialize(codes)
luup.variable_set("urn:dcineco-com:serviceId:KiraRx1","IRCodes",sc,lul_device)
end
This one needed it passed explicitly:
local function backup(lul_device)
local fpath = '/etc/KiraRx'..lul_device..'.xml'
local outf = io.open(fpath, 'w+')
if outf ~= nil then
outf:write(serialize(codes))
outf:close()
return true
else
return false
end
end
Is there some magic in a luup.xxx call?
Your XML’ized code gets flattened into a [single] Lua code block (full of functions with specialized/generated names).
You used to be able to see this by running a URL, but MCV broke that functionality some time back (it was particularly handy for seeing where certain contexts were set)
Anyhow, there are a few cases where this codegen would “set” [global] variables for certain implicit contexts.
This will give you an idea of the “formal” ones that can be used, and which XML element they come in from:
http://wiki.micasaverde.com/index.php/Luup_Declarations
eg. In an [tt]ACTION [/tt] block, you’ll get[tt] lul_device, lul_settings[/tt]
Outside of that call, I wouldn’t rely on the value being present. It might be filled in, it might not, and there’s no documentation to say [formally] how these are passed so you might end up getting an older value.
In your examples, it might just depend upon the order in which these are called, and whether the [global] variables are set prior or not.
Thanks, @guessed, that explains it - and a few other oddities. So good programming practice is to pass the device ID to functions that use it - in case the undocumented feature goes away sometime. That’s good to know.
I did find the Luup Declarations page on the Wiki very helpful when writing my first code. Most of it makes sense but I was surprised that the timed function callback does not pass lul_device - so you have to put it into the lul_data when you call the timer. It struck me as a bit odd.
One final question, if you’d be so kind? It also concerns the flattening of code. How do you pass a value back to the arguments? The following works:
<action>
<serviceId>urn:dcineco-com:serviceId:KiraRx1</serviceId>
<name>GetIPaddr</name>
<run>
luup.variable_get("urn:dcineco-com:serviceId:KiraRx1", "IPaddr", lul_device)
</run>
</action>
But I have not been able to figure out how to pass something other than a state variable. lul_settings.argname = “something” just gives an error and return “something” does nothing. Is there a trick?
Basically, you can’t return a value.
Your only option is to set a State variable somewhere in your ACTION call, and rely upon people looking at it after you return. 
Thanks for that. At least I didn’t miss something obvious. ;D
Given the limitations and foibles of this beast, it is amazing what some plugin developers have managed to achieve! 8)