Tarantool fiber behavior with fiber.yield() and fiber.testcancel() - lua

I ran into an unexpected behavior while building Tarantool app based on fibers.
Simple reproducer of my code looks like this:
local log = require('log')
local fiber = require('fiber')
box.cfg{}
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
fiber.yield()
end
return 0
end
local wrapfunc = function()
local ok, resp = pcall(func)
log.info(ok)
log.info(resp)
end
for _ = 1, 100 do
local myfiber = fiber.create(wrapfunc)
fiber.sleep(0.02)
fiber.kill(myfiber)
end
and it prints to log false, fiber is cancelled. Moreover, if I use the following func:
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
pcall(fiber.yield)
end
return 0
end
it prints to log true, 1, and if I use
local func = function()
for i = 1, 100000 do
if pcall(fiber.testcancel) ~= true then
return 1
end
if pcall(fiber.yield) ~= true then
return 2
end
end
return 0
end
it prints to log true, 2.
I expected that after yielding from running myfiber, if control returns to the external fiber and it calls fiber.kill(myfiber), the next time control returns to the cancelled myfiber we will be in the end of cycle iteration and on the next iteration code will successfully return 1. However, the work of func ends with throwing error fiber is cancelled, not with return. So how the real life cycle of yielding fiber works?

Actually, there is no unexpected behaviour here. I believe it mostly documentation issue. Let me explain. I've simplified your example a bit:
#!/usr/bin/env tarantool
local fiber = require('fiber')
local f1 = function() fiber.yield() end
local f2 = function() pcall(fiber.yield) end
local func = function(fn)
fn()
if not pcall(fiber.testcancel) then
return 'fiber.testcancel() failed'
end
end
local fiber1 = fiber.create(function() print(pcall(func, f1)) end)
fiber.kill(fiber1)
local fiber2 = fiber.create(function() print(pcall(func, f2)) end)
fiber.kill(fiber2)
The output would be:
false fiber is cancelled
true fiber.testcancel() failed
When you call fiber.kill, fiber.yield() or fiber.sleep() just raises an error, so your fiber is not able to reach fiber.testcancel and just dies. When you do pcall(fiber.yield), you basically suppress this error and proceed. Then fiber.testcancel checks its fiber status and reraise an exception. But this is a silly example.
Now, with bigger chunks of code, when lots of function invocations involved, you usually want to catch those errors during yield, do some finalisation work and call fiber.testcancel() to promote error upwards (imagine multiple checks of this kind in different parts of big stacktrace). I believe it is the basic use case, fiber.testcancel was introduced for, besides discussions if its design is usable or not.
P.s. And yes, occasional exceptions of such yield calls are not documented. At least I could not find anything in the fiber page

Related

LUA using function variable outside of it

I am trying to edit a script in LUA but I couldn't get access of a local defined in a function
LUA Code
function getSafeMoney()
local SafeMoney = nil
QBCore.Functions.ExecuteSql(false, 'SELECT * FROM `moneysafes` WHERE `safe` = "mechanic"', function(result)
SafeMoney = json.decode(json.encode(result[1])).money;
end)
return SafeMoney
end
print(getSafeMoney())
Result :
nil
here's the sql function as sysdevs asked
QBCore.Functions.ExecuteSql = function(wait, query, cb)
local rtndata = {}
local waiting = true
exports['ghmattimysql']:execute(query, {}, function(data)
if cb ~= nil and wait == false then
cb(data)
end
rtndata = data
waiting = false
end)
if wait then
while waiting do
Citizen.Wait(5)
end
if cb ~= nil and wait == true then
cb(rtndata)
end
end
return rtndata
end
Your second function is not holding the main function I assume, I am bad at lua aswell, but I think that changing
QBCore.Functions.ExecuteSql(false, ............
to
QBCore.Functions.ExecuteSql(true, ............
This will probably fix your problem, still your code is ambiguous and needs concentration if you could provide more information I might be able to help more

How to stop a function in another function in corona sdk

I have two functions:
local function first()
transition.to(ball, {x=100, y=200, time = 200}
end
local function stop()
if(score == 0)then
--stop the first function
end
end
How can I stop the first function in another function?
the transition function returns a reference to the transition, that you can later pass to transition.cancel to cancel the transition.
local currentTransition = nil
local function first()
currentTransition = transition.to(ball, {x=100, y=200, time = 200}
end
local function stop()
if (score == 0 and currentTransition ~= nil) then
transition.cancel(currentTransition)
end
end
more details are here
edit -
to handle this manually in functions you implement, you would need to have any function that supports some type of cancellation check for some flag or state to determine whether or not to continue operating. this is how multi-threaded apps support cancellation now, you create a cancellation token up front and pass it around to anything doing long/intensive work and that code occasionally checks the flag and stops if a cancellation has occurred. since pure lua doesn't support multi-threading, here is a basic and contrived example:
local token = { cancelled = false }
local function bar(cancellationToken)
print("Hi, from bar!")
-- simulate user cancellation
cancellationToken.cancelled = true
end
local function foo(cancellationToken)
for i=0, 10 do
if (cancellationToken.cancelled) then
print("Cancelling operation...")
return
end
print(i)
bar(cancellationToken)
end
end
foo(token)

Lua Set Functions in ComputerCraft

I have a ComputerCraft program set to turn on a siren when any non-whitelisted players are near:
sensor = peripheral.wrap("top")
function arraysubset(a, b)
local s = set(b)
for _, el in pairs(a)
if not s[el] then
return false
end
end
return true
end
function sirenOn() rs.setBundledOutput("back",colors.blue) end
function sirenOff() rs.setBundledOutput("back",0) end
while 1 do
playersNear = sensor.getPlayerNames()
allowedPlayers = {"VirtualDXS","jettrom","Shad0wlurker16","Demonicmobster","FireFang0113","riggs135","DaisySnow123","MasterAlex930"}
if playersNear[1] ~= nil then
if arraysubset(playersNear,allowedPlayers) then sirenOff() else sirenOn() end
else sirenOff() end
end
However, on line 3 I get an attempt to call nil. This makes me think that the set() function is not present on computercraft. I'm wondering:
Is there another (maybe better) way to find if array a is a subset of array b and
If not where can I get an API with the set() function?
Rereading the source for the subset() code, I am seeing that the code I used required more code from earlier in the answer:
function set(list)
local t = {}
for _, item in pairs(list) do
t[item] = true
end
return t
end

Different methods for determining whether a number is included in a given list

Is it faster to check whether a number is equivalent to another, or to look up the number in a table?
I have a program where commands sent from a server are received as numbers. Commands are sent frequently: anywhere from 1/second to 30+/second. Certain commands are ignored while others trigger various events. Would it be better to determine which event to trigger by doing this:
function incoming_integer(n)
if n == 33 then
print('a')
elseif n == 44 then
print('b')
elseif n == 567 then
print('c')
... (the actual list is occasionally pretty long: upwards of ten valid numbers)
end
end
or this:
functions = {
[33] = function() print('a') end,
[44] = function() print('b') end,
[567] = function() print('c') end,
...
}
relevant_commands = {[33]= true, [44]=true, [567]=true ...}
function incoming_integer(n)
if relevant_commands[n] then
functions[n]()
end
end
Is there a point at which one method becomes more efficient?
What if the commands were instead sent as strings?
For your throughput, I would suggest that the overhead for either mechanism is likely to be negligible.
I ran very quick and dirty test of the overhead of the table lookup for both string and numeric keys. See below. Even with a large number of keys and a high hit rate, I struggled to get >1us overhead even on the slowest box I could find.
f = {}
function incoming_integer(n)
local t = f[n]
return t and t()
end
SIZE = 10000
RAN = 20000
REPEAT = 10000000
for i =1,SIZE do
--f[math.random(RAN)] = function() return "z" end
f[tostring(math.random(RAN))] = function() return "z" end
end
tm = os.time()
for i = 1,REPEAT do
--math.random(RAN)
tostring(math.random(RAN))
end
print(os.difftime(os.time(),tm))
tm = os.time()
for i = 1,REPEAT do
--incoming_integer(math.random(RAN))
incoming_integer(tostring(math.random(RAN)))
end
print(os.difftime(os.time(),tm))
If you want to trigger different events with the returned numbers, I would use this way...
function trigger( value )
if ( value == "..." ) then
triggerThis()
elseif ( value == "..." ) then
triggerElse()
-- and so on
end
end
function incoming_integer(n)
if ( n ) then -- check if variable is incoming to prevent errors
trigger( n )
else
alert( "noop, there is nothing here" )
end
end
Maybe you could try keys. Didn't tested it just an example.
http://lua-users.org/wiki/TablesTutorial
table = { [33]="a", [44]="b", [567]="c" }
function onCall( value ) -- if 33
if ( table[value] ) then
return( table[value] ) -- then if exists return a
end
end
onCall( 33 ) -- now it will search in table and get key
EDIT: You could name the triggered functions like the number, so you can directly trigger them. This would save a lot of time!
functions = {[33] = function() print('a') end, etc...}
setmetatable(functions, {__index = function() return function() end end}) -- return empty function if no defined function is found
Usage:
functions[32]() -- ignores
functions[33]() -- does defined function

Lua stack overflow in recursive function in corona sdk

For some reason the code below throws a stack overflow error if the else statement gets executed too many times. I am trying to have the scene.targeting function select a target from the objTable passed in the params, but only targets with a .tgtFlag == false are valid selections. If the function selects a target that has a .tgtFlag == true, it recalls the scene.targeting function passing in the same set of params.
The line that breaks is local theTarget = params.objTable[math.random( 1, #params.objTable )] but only after else scene.targeting(params) end is called several times.
Any help would be greatly appreciated.
function scene.targeting( params ) -- Targeting functions
function animateTarget( target )
if target.savedFlag == false then
transition.to( target, {time = 100, y = target.y - 15} )
transition.to( target, {time = 100, delay = 150, y = target.y, onComplete = animateTarget} )
end
end
local theTarget = params.objTable[math.random( 1, #params.objTable )]
if theTarget.tgtFlag == false then
theTarget.tgtFlag = true
animateTarget(theTarget)
else
scene.targeting(params)
end
end
Referring to Programming in Lua:
A tail call is a kind of goto dressed as a call. A tail call happens when a function calls another as its last action, so it has nothing else to do.
In your example, animateTarget obviously isn't called in such a way, and can and will suffer from stack overflows. Either rewrite it to make use of TCO or change it to non-recursive version.

Resources