Software /
code /
prosody
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 |