3
0
mirror of https://github.com/britzl/monarch.git synced 2025-06-27 10:27:49 +02:00

Make sure that callbacks aren't invoked more than once

This commit is contained in:
Björn Ritzl 2021-08-19 14:35:05 +02:00
parent 76d4ca2927
commit d3799a93ff
3 changed files with 67 additions and 33 deletions

View File

@ -1,4 +1,5 @@
local callback_tracker = require "monarch.utils.callback_tracker" local callback_tracker = require "monarch.utils.callback_tracker"
local async = require "monarch.utils.async"
local M = {} local M = {}
@ -744,8 +745,9 @@ function M.show(id, options, data, cb)
pop = pop - 1 pop = pop - 1
end end
stack[#stack] = nil stack[#stack] = nil
show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) async(function(await, resume)
callbacks.yield_until_done() await(show_out, top, screen, WAIT_FOR_TRANSITION, resume)
end)
top = stack[#stack] top = stack[#stack]
end end
@ -784,8 +786,9 @@ function M.show(id, options, data, cb)
local same_screen = top and top.id == screen.id local same_screen = top and top.id == screen.id
if same_screen or (options and options.sequential) then if same_screen or (options and options.sequential) then
if top then if top then
show_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) async(function(await, resume)
callbacks.yield_until_done() await(show_out, top, screen, WAIT_FOR_TRANSITION, resume)
end)
end end
show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track()) show_in(screen, top, options and options.reload, add_to_stack, WAIT_FOR_TRANSITION, callbacks.track())
else else
@ -872,16 +875,11 @@ function M.clear(cb)
log("clear() queuing action") log("clear() queuing action")
queue_action(function(action_done, action_error) queue_action(function(action_done, action_error)
local co async(function(await, resume)
co = coroutine.create(function()
local callbacks = callback_tracker()
local top = stack[#stack] local top = stack[#stack]
while top and top.visible do while top and top.visible do
stack[#stack] = nil stack[#stack] = nil
back_out(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) await(back_out, top, screen, WAIT_FOR_TRANSITION, resume)
callbacks.yield_until_done()
top = stack[#stack] top = stack[#stack]
end end
@ -889,12 +887,9 @@ function M.clear(cb)
table.remove(stack) table.remove(stack)
end end
callbacks.when_done(function() pcallfn(cb)
pcallfn(cb) pcallfn(action_done)
pcallfn(action_done)
end)
end) end)
assert(coroutine.resume(co))
end) end)
end end
@ -907,6 +902,7 @@ function M.back(data, cb)
queue_action(function(action_done) queue_action(function(action_done)
local callbacks = callback_tracker() local callbacks = callback_tracker()
local back_cb = callbacks.track()
local screen = table.remove(stack) local screen = table.remove(stack)
if screen then if screen then
log("back()", screen.id) log("back()", screen.id)
@ -918,7 +914,7 @@ function M.back(data, cb)
if data then if data then
top.data = data top.data = data
end end
back_in(top, screen, WAIT_FOR_TRANSITION, callbacks.track()) back_in(top, screen, WAIT_FOR_TRANSITION, back_cb)
end) end)
else else
if top then if top then
@ -926,10 +922,10 @@ function M.back(data, cb)
top.data = data top.data = data
end end
back_in(top, screen, DO_NOT_WAIT_FOR_TRANSITION, function() back_in(top, screen, DO_NOT_WAIT_FOR_TRANSITION, function()
back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track()) back_out(screen, top, WAIT_FOR_TRANSITION, back_cb)
end) end)
else else
back_out(screen, top, WAIT_FOR_TRANSITION, callbacks.track()) back_out(screen, top, WAIT_FOR_TRANSITION, back_cb)
end end
end end
end end

47
monarch/utils/async.lua Normal file
View File

@ -0,0 +1,47 @@
local M = {}
local NOT_STARTED = "not_started"
local YIELDED = "yielded"
local RESUMED = "resumed"
local RUNNING = "running"
function M.async(fn)
local co = coroutine.running()
local state = NOT_STARTED
local function await(fn, ...)
state = RUNNING
fn(...)
if state ~= RUNNING then
return
end
state = YIELDED
local r = { coroutine.yield() }
return unpack(r)
end
local function resume(...)
if state ~= RUNNING then
state = RUNNING
local ok, err = coroutine.resume(co, ...)
if not ok then
print(err)
print(debug.traceback())
end
end
end
if co then
return fn(await, resume)
else
co = coroutine.create(fn)
return resume(await, resume)
end
end
return setmetatable(M, {
__call = function(t, ...)
return M.async(...)
end
})

View File

@ -6,13 +6,18 @@ function M.create()
local callback = nil local callback = nil
local callback_count = 0 local callback_count = 0
local all_callbacks_done = false
local function is_done() local function is_done()
return callback_count == 0 return callback_count == 0
end end
local function invoke_if_done() local function invoke_if_done()
if all_callbacks_done then
print("Warning: The same callback will be invoked twice from the callback tracker!", id or "")
end
if callback_count == 0 and callback then if callback_count == 0 and callback then
all_callbacks_done = true
local ok, err = pcall(callback) local ok, err = pcall(callback)
if not ok then print(err) end if not ok then print(err) end
end end
@ -41,20 +46,6 @@ function M.create()
invoke_if_done() invoke_if_done()
end end
function instance.yield_until_done()
local co = coroutine.running()
callback = function()
local ok, err = coroutine.resume(co)
if not ok then
print(err)
end
end
invoke_if_done()
if not is_done() then
coroutine.yield()
end
end
return instance return instance
end end