[added] stall support, timers and intervals to async

This commit is contained in:
Max Cahill 2020-03-16 20:17:48 +11:00
parent 3d3ccd921c
commit bb35fc2f57

View File

@ -1,6 +1,11 @@
--[[ --[[
simple kernel for async tasks running in the background simple kernel for async tasks running in the background
can "stall" a task by yielding the string "stall"
this will suspend the coroutine until the rest of
the queue has been processed or stalled
and can early-out update_for_time
todo: todo:
multiple types of callbacks multiple types of callbacks
finish, error, step finish, error, step
@ -15,6 +20,7 @@ async._mt = {__index = async}
function async:new() function async:new()
return setmetatable({ return setmetatable({
tasks = {}, tasks = {},
tasks_stalled = {},
}, self._mt) }, self._mt)
end end
@ -33,7 +39,14 @@ function async:update()
--grab task definition --grab task definition
local td = table.remove(self.tasks, 1) local td = table.remove(self.tasks, 1)
if not td then if not td then
return false --have we got stalled tasks to re-try?
if #self.tasks_stalled > 0 then
--swap queues rather than churning elements
self.tasks_stalled, self.tasks = self.tasks, self.tasks_stalled
td = table.remove(self.tasks, 1)
else
return false
end
end end
--run a step --run a step
local co, args, cb, error_cb = td[1], td[2], td[3], td[4] local co, args, cb, error_cb = td[1], td[2], td[3], td[4]
@ -54,23 +67,61 @@ function async:update()
--check done --check done
if coroutine.status(co) == "dead" then if coroutine.status(co) == "dead" then
--done? run callback with result --done? run callback with result
cb(a, b, c, d, e, f, g, h) if cb then
cb(a, b, c, d, e, f, g, h)
end
else else
--if not done, re-add --if not completed, re-add to the appropriate queue
table.insert(self.tasks, td) if a == "stall" then
--add to stalled queue as signalled stall
table.insert(self.tasks_stalled, td)
else
table.insert(self.tasks, td)
end
end end
return true return true
end end
--update tasks for some amount of time --update tasks for some amount of time
function async:update_for_time(t) function async:update_for_time(t, early_out_stalls)
local now = love.timer.getTime() local now = love.timer.getTime()
while love.timer.getTime() - now < t do while love.timer.getTime() - now < t do
if not self:update() then if not self:update() then
break break
end end
--all stalled?
if early_out_stalls and #self.tasks == 0 then
break
end
end end
end end
--add a function to run after a certain delay (in seconds)
function async:add_timeout(f, delay)
local trigger_time = love.timer.getTime() + delay
self:call(function()
while love.timer.getTime() < trigger_time do
coroutine.yield("stall")
end
f()
end)
end
--add a function to run repeatedly every delay (in seconds)
--note: not super useful currently unless you plan to destroy the async object
-- as there's no way to remove tasks :)
function async:add_interval(f, delay)
local trigger_time = love.timer.getTime() + delay
self:call(function()
while true do
while love.timer.getTime() < trigger_time do
coroutine.yield("stall")
end
f()
trigger_time = trigger_time + delay
end
end)
end
return async return async