Software /
code /
prosody
Changeset
8609:9f6ab206d741
util.async: Ensure runner is left in correct state after out-of-main-loop error (+tests)
author | Matthew Wild <mwild1@gmail.com> |
---|---|
date | Fri, 16 Mar 2018 22:26:15 +0000 |
parents | 8608:a2e6caf5848d |
children | 8610:b03c7884fade |
files | spec/util_async_spec.lua util/async.lua |
diffstat | 2 files changed, 29 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/spec/util_async_spec.lua Fri Mar 16 22:19:33 2018 +0000 +++ b/spec/util_async_spec.lua Fri Mar 16 22:26:15 2018 +0000 @@ -35,9 +35,14 @@ describe("#errors", function () local last_processed_item, last_error; local r; + local wait, done; r = async.runner(function (item) if item == "error" then error({ e = "test error" }); + elseif item == "wait" then + wait, done = async.waiter(); + wait(); + error({ e = "post wait error" }); end last_processed_item = item; end, { @@ -81,6 +86,24 @@ assert.equal(r.state, "ready"); assert.equal(last_processed_item, "world"); end); + it("should work despite a #waiter", function () + -- This test covers an important case where a runner + -- throws an error while being executed outside of the + -- main loop. This happens when it was blocked ('waiting'), + -- and then released (via a call to done()). + last_error = nil; + r:run("wait"); + assert.equal(r.state, "waiting"); + done(); + -- At this point an error happens (state goes error->ready) + assert.equal(r.state, "ready"); + assert.is_table(last_error); + assert.equal(last_error.e, "post wait error"); + last_error = nil; + r:run("hello again"); + assert.equal(r.state, "ready"); + assert.equal(last_processed_item, "hello again"); + end); end); end); describe("#waiter", function()
--- a/util/async.lua Fri Mar 16 22:19:33 2018 +0000 +++ b/util/async.lua Fri Mar 16 22:26:15 2018 +0000 @@ -17,13 +17,18 @@ end local ok, state, runner = coroutine.resume(thread); if not ok then + local err = state; -- Running the coroutine failed, which means we have to find the runner manually, -- in order to inform the error handler local level = 0; while debug.getinfo(thread, level, "") do level = level + 1; end ok, runner = debug.getlocal(thread, level-1, 1); local error_handler = runner.watchers.error; - if error_handler then error_handler(runner, debug.traceback(thread, state)); end + if error_handler then error_handler(runner, debug.traceback(thread, err)); end + local ready_handler = runner.watchers.ready; + runner.state, runner.thread = "ready", nil; + if ready_handler then ready_handler(runner); end + runner.notified_state = "ready"; elseif state == "ready" then -- If state is 'ready', it is our responsibility to update runner.state from 'waiting'. -- We also have to :run(), because the queue might have further items that will not be