lua synchronous call C asynchronous function - c

c call lua with the following code
if ( lua_pcallk(L, 0, LUA_MULTRET, 0, ctx, pcallk_continue) != 0 )
{}
the lua code is
local coroutine = coroutine
co = coroutine.create( function (...)
c.call(...)
-- [run here]
end )
c.call() is an asynchronous c function(it call lua_yieldk() at the end of the function, util the network recv the data and call lua_resume() )
return lua_yieldk(L, 0, 0, yield_continue);
it run to yield_continue function, but not run to the line of "[run here]", why?
the reference URL: http://www.lua.org/manual/5.2/manual.html#4.7

This is main idea with dispatcher.
Worker thread start async operation and switch to dispatcher thread.
Dispatcher thread polls async operations and when they done switch to
worker thread.
-- basic async operation queue
local ASYNC_RECV = {}
-- Put operation to queue and wait
local function recv()
local co = coroutine.running()
-- here we start async operation
ASYNC_RECV[co] = true
-- here we switch to dispatcher
return coroutine.yield()
end
coroutine.wrap(function()
for i = 1, 10 do print(recv()) end
end)()
-- main Loop
-- here we poll all async operation and
-- resume according coroutines
while true do
-- we have no async operation
if not next(ASYNC_RECV) then break end
for co in pairs(ASYNC_RECV) do
ASYNC_RECV[co] = nil -- mark operation is done
coroutine.resume(co, "some string") -- send result to coroutine
end
end
And here work example with libuv as dispatcher
local uv = require "lluv"
local function sleep(timeout)
local co = coroutine.running()
-- here we start async operation
uv.timer():start(timeout, function(timer)
timer:close() -- just close unused timer.
-- dispatcher(libuv) call this callback and
-- callback switch to coroutine
coroutine.resume(co)
end)
coroutine.yield()
end
coroutine.wrap(function()
for i = 1, 10 do
print("Tick")
sleep(1000)
end
end)()
uv.run()

Related

Lua : timer.cancel --> 'Attempt to index a nil value'

I'm fairly new to Lua, and one particular command in my code has been causing me some major problems.
I've been trying to cancel the timer:
currentPuddleDelay = timer.performWithDelay(puddleDelay, createPuddle);
The error that I am shown is:
Attempt to index a nil value
File: ?
stack traceback:
?: in function 'cancel'
game.lua:534: in function '?'
?: in function 'dispatchEvent'
?: in function '_saveSceneAndHide'
?: in function 'gotoScene'
game.lua:452: in function '?'
?: in function <?:182>
From what I've researched already, this problem can occur when the timer is within a function and is local, however, the timer in my code is global, so I don't think that that is the problem.
Below is the bit of code with the issue:
local function createPuddle()
local function constantDelay()
local puddle = display.newImage( sceneGroup, "images/puddle.png" )
puddle.x = puddleLane
puddle.y = -200
physics.addBody( puddle, "dynamic", {density=0, filter=puddleCollisionFilter} )
puddle:applyForce( 0, puddleSpeed, puddle.x, puddle.y )
sceneGroup:insert(3,puddle)
local function onPuddleCollision( self, event )
if ( event.phase == "began" ) then
print('puddle collision')
puddle:removeSelf( )
puddle = nil
composer.gotoScene( "menu" )
end
end
puddle.collision = onPuddleCollision
puddle:addEventListener( "collision" )
end
local constantDelayTimer = timer.performWithDelay(puddleDelay/2,constantDelay,1)
currentPuddleDelayHold = timer.performWithDelay(puddleDelay, createPuddle);
end
currentPuddleDelay = timer.performWithDelay(puddleDelay, createPuddle);
And then later on in the program:
timer.cancel(currentPuddleDelay)
Any help would be greatly appreciated.
I can only guess as you most likely did not provide all relevant code.
It obviously doesn't make sense to cancel a non-existing timer so for the start just do
if currentPuddleDelay then timer.cancel(currentPuddleDelay) end
If there is any reason why currentPuddleDelay should still exist you should find out why it is nil.

lua timing sound files

I'm a musician attempting to write a music reading programme for guitarists.
I want to time two consecutive sounds so that the first stops when the second begins. Each should last a predetermined duration (defined in this example as 72 in 60000/72). As a beginner coder I'm struggling and would really appreciate any help.
-- AUDIO 1 --
local aa = audio.loadStream(sounds/chord1.mp3)
audio.play(aa)
-- TIMER 1 --
local timeLimit = 1
local function timerDown()
timeLimit = timeLimit-1
if(timeLimit==0)then
end
end
timer.performWithDelay( 60000/72, timerDown, timeLimit )
-- TIMER 2 --
local timeLimit = 1
local function timerDown()
timeLimit = timeLimit-1
if(timeLimit==0)then
-- AUDIO 2 --
local aa = audio.loadStream(sounds/chord2.mp3])
audio.play(aa)
end
end
timer.performWithDelay( 60000/72, timerDown, timeLimit )
There are a few things to note here. Sorry for the wall of text!
Strings (text)
Must be enclosed in quotes.
local aa = audio.loadStream(sounds/chord1.mp3)
becomes:
local aa = audio.loadStream('sounds/chord1.mp3')
Magic numbers
Values which aren't explained anywhere should be avoided. They make code harder to understand and harder to maintain or modify.
timer.performWithDelay(60000/72, timerDown, timeLimit)
becomes:
-- Might be slight overkill but hopefully you get the idea!
local beatsToPlay = 10
local beatsPerMinute = 72
local millisPerMinute = 60 * 1000
local playTimeMinutes = beatsToPlay / beatsPerMinute
local playTimeMillis = playTimeMinutes * millisPerMinute
timer.performWithDelay(playTimeMillis, timerDown, timeLimit)
Corona API
It is an invaluable skill when programming to be able to read and understand documentation. Corona's API is documented here.
audio.loadStream()'s docs tell you that it returns an audio handle which you can use to play sounds which is what you've got already. It also reminds you that you should dispose of the handle when you are done so you'll need to add that in.
timer.performWithDelay()'s docs tell you that it needs the delay time in milliseconds and a listener which is what will be activated at that time, so you will need to write a listener of some description. If you follow the link to listener or if you look at the examples further down the page then you'll see that a simple function will suffice.
audio.play() is fine as it is but if you read the docs then it informs you of some more functionality which you could use to your advantage. Namely the options parameter, which includes duration and onComplete. duration is how long - in millis - to play the sound. onComplete is a listener which will be triggered when the sound has finished playing.
The result
Using timers only:
local function playAndQueue(handle, playTime, queuedHandle, queuedPlayTime)
audio.play(handle, { duration = playTime })
timer.performWithDelay(playTime, function(event)
audio.dispose(handle)
audio.play(queuedHandle, { duration = queuedPlayTime })
end)
timer.performWithDelay(playTime + queuedPlayTime, function(event)
audio.dispose(queuedHandle)
end)
end
local audioHandle1 = audio.loadStream('sounds/chord1.mp3')
local audioHandle2 = audio.loadStream('sounds/chord2.mp3')
local beatsToPlay = 10
local beatsPerMinute = 72
local millisPerMinute = 60 * 1000
local playTimeMinutes = beatsToPlay / beatsPerMinute
local playTimeMillis = playTimeMinutes * millisPerMinute
playAndQueue(audioHandle1, playTimeMillis, audioHandle2, playTimeMillis)
Using onComplete:
local function playAndQueue(handle, playTime, queuedHandle, queuedPlayTime)
-- Before we can set the 1st audio playing we have to define what happens
-- when it is done (disposes self and starts the 2nd audio).
-- Before we can start the 2nd audio we have to define what happens when
-- it is done (disposes of the 2nd audio handle)
local queuedCallback = function(event)
audio.dispose(queuedHandle)
end
local callback = function(event)
audio.dispose(handle)
local queuedOpts = {
duration = queuedPlayTime,
onComplete = queuedCallback
}
audio.play(queuedHandle, queuedOpts)
end
local opts = {
duration = playTime,
onComplete = callback
}
audio.play(handle, opts)
end
local audioHandle1 = audio.loadStream('sounds/chord1.mp3')
local audioHandle2 = audio.loadStream('sounds/chord2.mp3')
local beatsToPlay = 10
local beatsPerMinute = 72
local millisPerMinute = 60 * 1000
local playTimeMinutes = beatsToPlay / beatsPerMinute
local playTimeMillis = playTimeMinutes * millisPerMinute
playAndQueue(audioHandle1, playTimeMillis, audioHandle2, playTimeMillis)
You might find that using onComplete works out better than pure timers since you might end up disposing the audio handle just before is is done being used for playback (and causing errors). I haven't had any experience with Corona so I'm not sure how robust its timer or audio libraries are.

How to run a function during a limited time?

I've a function and would like to call here each 2 seconds during 3 seconds.
I tried timer.performwithDelay() but it doesn't answer to my question.
Here is the function I want to call each 2 secondes during 3 seconds :
function FuelManage(event)
if lives > 0 and pressed==true then
lifeBar[lives].isVisible=false
lives = lives - 1
-- print( lifeBar[lives].x )
livesValue.text = string.format("%d", lives)
end
end
How can I use timer.performwithDelay(2000, callback, 1) to call my function FuelManage(event) ?
So it looks like what you are actually after is to start a few check 2 seconds from "now", for a duration of 3 seconds. You can schedule registering and unregistering for the enterFrame events. Using this will call your FuelManage function every time step during the period of interest:
function cancelCheckFuel(event)
Runtime:removeListener('enterFrame', FuelManager)
end
function FuelManage(event)
if lives > 0 and pressed==true then
lifeBar[lives].isVisible=false
lives = lives - 1
-- print( lifeBar[lives].x )
livesValue.text = string.format("%d", lives)
end
end
-- fuel management:
local startFuelCheckMS = 2000 -- start checking for fuel in 2 seconds
local fuelCheckDurationMS = 3000 -- check for 3 seconds
local stopFuelCheckMS = startFuelCheckMS + fuelCheckDurationMS
timer.performWithDelay(
startFuelCheckMS,
function() Runtime:addEventListener('enterFrame', FuelManager) end,
1)
timer.performWithDelay(
stopFuelCheckMS,
function() Runtime:removeEventListener('enterFrame', FuelManager) end,
1)
If this is too high frequency, then you'll want to use a timer, and keep track of time:
local fuelCheckDurationMS = 3000 -- check for 3 seconds
local timeBetweenChecksMS = 200 -- check every 200 ms
local totalCheckTimeMS = 0
local startedChecking = false
function FuelManage(event)
if lives > 0 and pressed==true then
lifeBar[lives].isVisible=false
lives = lives - 1
-- print( lifeBar[lives].x )
livesValue.text = string.format("%d", lives)
end
if totalCheckTimeMS < 3000 then
timer.performWithDelay(timeBetweenChecksMS, FuelManage, 1)
if startedChecking then
totalCheckTimeMS = totalCheckTimeMS + timeBetweenChecksMS
end
startedChecking = true
end
end
-- fuel management:
local startFuelCheckMS = 2000 -- start checking for fuel in 2 seconds
timer.performWithDelay(startFuelCheckMS, FuelManage, 1)
Set a timer inside a timer like this:
function FuelManage(event)
if lives > 0 and pressed==true then
lifeBar[lives].isVisible=false
lives = lives - 1
-- print( lifeBar[lives].x )
livesValue.text = string.format("%d", lives)
end
end
-- Main timer, called every 2 seconds
timer.performwithDelay(2000, function()
-- Sub-timer, called every second for 3 seconds
timer.performwithDelay(1000, FuelManage, 3)
end, 1)
Be careful though because the way it's setup know you will have an infinite number of timer running very soon... Since the first timer has a lower lifetime than the second one. So you might think if you would like to secure the second timer by making sure it's cancelled first before calling it again, this kind of thing.

Construct dynamic timer with lua

I have created a timer called "timer", but I'm trying to create a function that will arm or disarm timer which is specified in it parameters
timer = sys.timer.create()
function MainTimer(timerName, action, time)
if action == "arm" then
timerName:arm(time)
else
timerName:disarm()
end
end
MainTimer("timer", "arm", 30)
but I'm getting an error from lua saying lua:272: attempt to call method 'arm' (a nil value)
where you think I did a mistake.
Thank you
Extra quotes :-)
MainTimer(timer, "arm", 30)

Creating a timer using Lua

I would like to create a timer using Lua, in a way that I could specify a callback function to be triggered after X seconds have passed.
What would be the best way to achieve this? ( I need to download some data from a webserver that will be parsed once or twice an hour )
Cheers.
If milisecond accuracy is not needed, you could just go for a coroutine solution, which you resume periodically, like at the end of your main loop, Like this:
require 'socket' -- for having a sleep function ( could also use os.execute(sleep 10))
timer = function (time)
local init = os.time()
local diff=os.difftime(os.time(),init)
while diff<time do
coroutine.yield(diff)
diff=os.difftime(os.time(),init)
end
print( 'Timer timed out at '..time..' seconds!')
end
co=coroutine.create(timer)
coroutine.resume(co,30) -- timer starts here!
while coroutine.status(co)~="dead" do
print("time passed",select(2,coroutine.resume(co)))
print('',coroutine.status(co))
socket.sleep(5)
end
This uses the sleep function in LuaSocket, you could use any other of the alternatives suggested on the Lua-users Wiki
Try lalarm, here:
http://www.tecgraf.puc-rio.br/~lhf/ftp/lua/
Example (based on src/test.lua):
-- alarm([secs,[func]])
alarm(1, function() print(2) end); print(1)
Output:
1
2
If it's acceptable for you, you can try LuaNode. The following code sets a timer:
setInterval(function()
console.log("I run once a minute")
end, 60000)
process:loop()
use Script.SetTimer(interval, callbackFunction)
After reading this thread and others I decided to go with Luv lib. Here is my solution:
uv = require('luv') --luarocks install luv
function set_timeout(timeout, callback)
local timer = uv.new_timer()
local function ontimeout()
uv.timer_stop(timer)
uv.close(timer)
callback()
end
uv.timer_start(timer, timeout, 0, ontimeout)
return timer
end
set_timeout(1000, function() print('ok') end) -- time in ms
uv.run() --it will hold at this point until every timer have finished
On my Debian I've install lua-lgi packet to get access to the GObject based libraries.
The following code show you an usage demonstrating that you can use few asynchronuous callbacks:
local lgi = require 'lgi'
local GLib = lgi.GLib
-- Get the main loop object that handles all the events
local main_loop = GLib.MainLoop()
cnt = 0
function tictac()
cnt = cnt + 1
print("tic")
-- This callback will be called until the condition is true
return cnt < 10
end
-- Call tictac function every 2 senconds
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 2, tictac)
-- You can also use an anonymous function like that
GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 1,
function()
print( "There have been ", cnt, "tic")
-- This callback will never stop
return true
end)
-- Once everything is setup, you can start the main loop
main_loop:run()
-- Next instructions will be still interpreted
print("Main loop is running")
You can find more documentation about LGI here

Resources