aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--spec/util_async_spec.lua23
-rw-r--r--util/async.lua7
2 files changed, 29 insertions, 1 deletions
diff --git a/spec/util_async_spec.lua b/spec/util_async_spec.lua
index 67438595..bc6e04f8 100644
--- a/spec/util_async_spec.lua
+++ b/spec/util_async_spec.lua
@@ -35,9 +35,14 @@ describe("util.async", function()
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 @@ describe("util.async", function()
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()
diff --git a/util/async.lua b/util/async.lua
index 25236b11..3a75e1c1 100644
--- a/util/async.lua
+++ b/util/async.lua
@@ -17,13 +17,18 @@ local function runner_continue(thread)
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