Skip to content

Commit

Permalink
main: don't break graceful shutdown on init script exit
Browse files Browse the repository at this point in the history
Graceful shutdown is done in a special fiber which is started for
example on SIGTERM. So it can run concurrently with fiber executing
Tarantool init script. On init fiber exit we break event loop to pass
control back to the Tarantool initialization code. But we fail to run
event loop a bit more to finish graceful shutdown.

The test is a bit contrived. A more real world case is when Tarantool is
termintated during lingering box.cfg().

Close tarantool#9411

NO_DOC=bugfix
  • Loading branch information
nshy authored and locker committed Nov 28, 2023
1 parent c92500a commit 786eb2a
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## bugfix/core

* Fixed graceful shutdown break on init script exit (gh-9411).
14 changes: 8 additions & 6 deletions src/main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ static double start_time;
/** A preallocated fiber to run on_shutdown triggers. */
static struct fiber *on_shutdown_fiber = NULL;
/** A flag restricting repeated execution of tarantool_exit(). */
static bool is_shutting_down = false;
static bool shutdown_started = false;
static bool shutdown_finished = false;
static int exit_code = 0;

char tarantool_path[PATH_MAX];
Expand Down Expand Up @@ -162,14 +163,15 @@ on_shutdown_f(va_list ap)
fiber_sleep(0.0);

/* Handle spurious wakeups. */
while (!is_shutting_down)
while (!shutdown_started)
fiber_yield();

if (on_shutdown_run_triggers() != 0) {
say_error("on_shutdown triggers failed");
diag_log();
diag_clear(diag_get());
}
shutdown_finished = true;
ev_break(loop(), EVBREAK_ALL);
return 0;
}
Expand All @@ -178,15 +180,15 @@ void
tarantool_exit(int code)
{
start_loop = false;
if (is_shutting_down) {
if (shutdown_started) {
/*
* We are already running on_shutdown triggers,
* and will exit as soon as they'll finish.
* Do not execute them twice.
*/
return;
}
is_shutting_down = true;
shutdown_started = true;
exit_code = code;
box_broadcast_fmt("box.shutdown", "%b", true);
fiber_wakeup(on_shutdown_fiber);
Expand Down Expand Up @@ -1074,10 +1076,10 @@ main(int argc, char **argv)
* init script, and there was neither os.exit nor SIGTERM, then call
* tarantool_exit and start an event loop to run on_shutdown triggers.
*/
if (!is_shutting_down) {
if (!shutdown_started)
tarantool_exit(exit_code);
if (!shutdown_finished)
ev_run(loop(), 0);
}
/* freeing resources */
free((void *)instance.name);
free((void *)instance.config);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
local popen = require('popen')
local t = require('luatest')

local g = t.group()

g.after_each(function()
if g.handle ~= nil then
g.handle:close()
end
g.handle = nil
end)

g.test = function()
local script = [[
local fiber = require('fiber')
box.ctl.set_on_shutdown_timeout(1)
box.ctl.on_shutdown(function()
fiber.sleep(0.2)
print('shutdown callback finished')
end, nil)
fiber.create(function()
os.exit(0)
end)
fiber.sleep(0.1)
]]
local tarantool_bin = arg[-1]
local handle, err = popen.new({tarantool_bin, '-e', script},
{stdout = popen.opts.PIPE,
stdin = popen.opts.DEVNULL,
stderr = popen.opts.DEVNULL})
assert(handle, err)
g.handle = handle
local output, err = handle:read({timeout = 3})
assert(output, err)
t.assert_equals(output, 'shutdown callback finished\n')
local status = handle:wait()
t.assert_equals(status.state, 'exited')
t.assert_equals(status.exit_code, 0)
end

0 comments on commit 786eb2a

Please sign in to comment.