Comparison

util/async.lua @ 8627:24b59a62acce

util.async: Split runner_continue into smaller functions for easier testing and safety
author Matthew Wild <mwild1@gmail.com>
date Sun, 18 Mar 2018 12:05:38 +0000
parent 8625:08bf4df6fdb7
child 8629:ddb04cacb8b1
comparison
equal deleted inserted replaced
8626:20532f191f8d 8627:24b59a62acce
5 local thread, main = coroutine.running(); 5 local thread, main = coroutine.running();
6 if not thread or main then 6 if not thread or main then
7 error("Not running in an async context, see https://prosody.im/doc/developers/util/async"); 7 error("Not running in an async context, see https://prosody.im/doc/developers/util/async");
8 end 8 end
9 return thread; 9 return thread;
10 end
11
12 local function runner_from_thread(thread)
13 local level = 0;
14 -- Find the 'level' of the top-most function (0 == current level, 1 == caller, ...)
15 while debug.getinfo(thread, level, "") do level = level + 1; end
16 local name, runner = debug.getlocal(thread, level-1, 1);
17 if name ~= "self" or type(runner) ~= "table" or runner.thread ~= thread then
18 return nil;
19 end
20 return runner;
21 end
22
23 local function call_watcher(runner, watcher_name, ...)
24 local watcher = runner.watchers[watcher_name];
25 if not watcher then
26 return false;
27 end
28 runner:log("debug", "Calling '%s' watcher", watcher_name);
29 local ok, err = pcall(watcher, runner, ...); -- COMPAT: Switch to xpcall after Lua 5.1
30 if not ok then
31 runner:log("error", "Error in '%s' watcher: %s", watcher_name, err);
32 return nil, err;
33 end
34 return true;
10 end 35 end
11 36
12 local function runner_continue(thread) 37 local function runner_continue(thread)
13 -- ASSUMPTION: runner is in 'waiting' state (but we don't have the runner to know for sure) 38 -- ASSUMPTION: runner is in 'waiting' state (but we don't have the runner to know for sure)
14 if coroutine.status(thread) ~= "suspended" then -- This should suffice 39 if coroutine.status(thread) ~= "suspended" then -- This should suffice
18 local ok, state, runner = coroutine.resume(thread); 43 local ok, state, runner = coroutine.resume(thread);
19 if not ok then 44 if not ok then
20 local err = state; 45 local err = state;
21 -- Running the coroutine failed, which means we have to find the runner manually, 46 -- Running the coroutine failed, which means we have to find the runner manually,
22 -- in order to inform the error handler 47 -- in order to inform the error handler
23 local level = 0; 48 runner = runner_from_thread(thread);
24 -- Find the 'level' of the top-most function (0 == current level, 1 == caller, ...) 49 if not runner then
25 while debug.getinfo(thread, level, "") do level = level + 1; end 50 log("warn", "unexpected async state: unable to locate runner during error handling");
26 ok, runner = debug.getlocal(thread, level-1, 1);
27 if ok ~= "self" or runner.thread ~= thread then
28 log("warn", "unexpected async state: unable to locate runner during error handling, got %s", ok);
29 return false; 51 return false;
30 end 52 end
31 local error_handler = runner.watchers.error; 53 call_watcher(runner, "error", debug.traceback(thread, err));
32 if error_handler then error_handler(runner, debug.traceback(thread, err)); end
33 runner.state, runner.thread = "ready", nil; 54 runner.state, runner.thread = "ready", nil;
34 return runner:run(); 55 return runner:run();
35 elseif state == "ready" then 56 elseif state == "ready" then
36 -- If state is 'ready', it is our responsibility to update runner.state from 'waiting'. 57 -- If state is 'ready', it is our responsibility to update runner.state from 'waiting'.
37 -- We also have to :run(), because the queue might have further items that will not be 58 -- We also have to :run(), because the queue might have further items that will not be