Skip to content

Commit

Permalink
core: properly handle fiber cancellation for fiber.cond
Browse files Browse the repository at this point in the history
Before this patch fiber.cond():wait() just returns for cancelled
fiber. In contrast fiber.channel():get() threw "fiber is
canceled" error.
This patch unify behaviour of channels and condvars and also fixes
related net.box module problem - it was impossible to interrupt
net.box call with fiber.cancel because it used fiber.cond under
the hood. Test cases for both bugs are added.

Closes #4834
Closes #5013

@TarantoolBot document
Title: fiber.cond():wait() throws if fiber is cancelled

Currently fiber.cond():wait() throws an error if waiting fiber is
cancelled like in case with fiber.channel():get().
  • Loading branch information
olegrok committed Sep 6, 2020
1 parent 71a24b9 commit e7a5120
Show file tree
Hide file tree
Showing 5 changed files with 161 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/lib/core/fiber_cond.c
Expand Up @@ -108,6 +108,10 @@ fiber_cond_wait_timeout(struct fiber_cond *c, double timeout)
diag_set(TimedOut);
return -1;
}
if (fiber_is_cancelled()) {
diag_set(FiberIsCancelled);
return -1;
}
return 0;
}

Expand Down
46 changes: 46 additions & 0 deletions test/app/gh-5013-fiber_cond-cancel.result
@@ -0,0 +1,46 @@
-- test-run result file version 2
fiber = require('fiber')
| ---
| ...
test_run = require('test_run').new()
| ---
| ...

err = nil
| ---
| ...

test_run:cmd("setopt delimiter ';'")
| ---
| - true
| ...
function test()
_, err = pcall(function() fiber.cond():wait() end)
end;
| ---
| ...
test_run:cmd("setopt delimiter ''");
| ---
| - true
| ...

f = fiber.new(test)
| ---
| ...
fiber.yield()
| ---
| ...
f:cancel()
| ---
| ...
fiber.yield()
| ---
| ...
f
| ---
| - the fiber is dead
| ...
err
| ---
| - fiber is cancelled
| ...
17 changes: 17 additions & 0 deletions test/app/gh-5013-fiber_cond-cancel.test.lua
@@ -0,0 +1,17 @@
fiber = require('fiber')
test_run = require('test_run').new()

err = nil

test_run:cmd("setopt delimiter ';'")
function test()
_, err = pcall(function() fiber.cond():wait() end)
end;
test_run:cmd("setopt delimiter ''");

f = fiber.new(test)
fiber.yield()
f:cancel()
fiber.yield()
f
err
65 changes: 65 additions & 0 deletions test/box/net.box_fiber_cancel_gh-4834.result
@@ -0,0 +1,65 @@
-- test-run result file version 2
remote = require 'net.box'
| ---
| ...
fiber = require 'fiber'
| ---
| ...
test_run = require('test_run').new()
| ---
| ...

-- #4834: Cancelling fiber doesn't interrupt netbox operations
function infinite_call() fiber.channel(1):get() end
| ---
| ...
box.schema.func.create('infinite_call')
| ---
| ...
box.schema.user.grant('guest', 'execute', 'function', 'infinite_call')
| ---
| ...

error_msg = nil
| ---
| ...
test_run:cmd("setopt delimiter ';'")
| ---
| - true
| ...
function gh4834()
local cn = remote.connect(box.cfg.listen)
local f = fiber.new(function()
_, error_msg = pcall(cn.call, cn, 'infinite_call')
end)
f:set_joinable(true)
fiber.yield()
f:cancel()
f:join()
cn:close()
end;
| ---
| ...
test_run:cmd("setopt delimiter ''");
| ---
| - true
| ...
gh4834()
| ---
| ...
error_msg
| ---
| - fiber is cancelled
| ...
box.schema.func.drop('infinite_call')
| ---
| ...
infinite_call = nil
| ---
| ...
channel = nil
| ---
| ...
error_msg = nil
| ---
| ...
29 changes: 29 additions & 0 deletions test/box/net.box_fiber_cancel_gh-4834.test.lua
@@ -0,0 +1,29 @@
remote = require 'net.box'
fiber = require 'fiber'
test_run = require('test_run').new()

-- #4834: Cancelling fiber doesn't interrupt netbox operations
function infinite_call() fiber.channel(1):get() end
box.schema.func.create('infinite_call')
box.schema.user.grant('guest', 'execute', 'function', 'infinite_call')

error_msg = nil
test_run:cmd("setopt delimiter ';'")
function gh4834()
local cn = remote.connect(box.cfg.listen)
local f = fiber.new(function()
_, error_msg = pcall(cn.call, cn, 'infinite_call')
end)
f:set_joinable(true)
fiber.yield()
f:cancel()
f:join()
cn:close()
end;
test_run:cmd("setopt delimiter ''");
gh4834()
error_msg
box.schema.func.drop('infinite_call')
infinite_call = nil
channel = nil
error_msg = nil

0 comments on commit e7a5120

Please sign in to comment.