diff --git a/extra/exports b/extra/exports index 5f69e07309674a6b17e803c042c5a2305081fb1b..35b47ae00338eb1cea25ff3e8abd660167907369 100644 --- a/extra/exports +++ b/extra/exports @@ -69,6 +69,7 @@ say_set_log_level say_logrotate say_set_log_format tarantool_uptime +tarantool_exit log_pid space_by_id space_run_triggers diff --git a/src/lua/init.lua b/src/lua/init.lua index fa324d34c81b1c26170235f8bd555a02a846328a..9fd56f483fac969e5609d2578b3eddd19256066c 100644 --- a/src/lua/init.lua +++ b/src/lua/init.lua @@ -38,6 +38,8 @@ double tarantool_uptime(void); typedef int32_t pid_t; pid_t getpid(void); +void +tarantool_exit(int); ]] local fio = require("fio") @@ -50,6 +52,16 @@ dostring = function(s, ...) return chunk(...) end +local fiber = require("fiber") +os.exit = function(code) + code = (type(code) == 'number') and code or 0 + ffi.C.tarantool_exit(code) + -- Make sure we yield even if the code after + -- os.exit() never yields. After on_shutdown + -- fiber completes, we will never wake up again. + while true do fiber.yield() end +end + local function uptime() return tonumber(ffi.C.tarantool_uptime()); end diff --git a/src/main.cc b/src/main.cc index a3d96843d649e6ccda304c3ed0f79e50b0f70844..4edae2185238ce64e90a668fe97288144120a71f 100644 --- a/src/main.cc +++ b/src/main.cc @@ -95,6 +95,7 @@ static struct fiber *on_shutdown_fiber = NULL; static bool is_shutting_down = false; /** A trigger which will break the event loop on shutdown. */ static struct trigger break_loop_trigger; +static int exit_code = 0; double tarantool_uptime(void) @@ -134,9 +135,10 @@ on_shutdown_f(va_list ap) return 0; } -static void -tarantool_exit(void) +void +tarantool_exit(int code) { + start_loop = false; if (is_shutting_down) { /* * We are already running on_shutdown triggers, @@ -146,6 +148,7 @@ tarantool_exit(void) return; } is_shutting_down = true; + exit_code = code; fiber_call(on_shutdown_fiber); } @@ -165,8 +168,7 @@ signal_cb(ev_loop *loop, struct ev_signal *w, int revents) */ if (pid_file) say_crit("got signal %d - %s", w->signum, strsignal(w->signum)); - start_loop = false; - tarantool_exit(); + tarantool_exit(0); } static void @@ -849,5 +851,5 @@ main(int argc, char **argv) if (start_loop) say_crit("exiting the event loop"); /* freeing resources */ - return 0; + return exit_code; } diff --git a/src/main.h b/src/main.h index 221374144a03e1402a3745b3428d64f52db24a17..f509e905bfedf2add4a6de73f55167b46360fbe4 100644 --- a/src/main.h +++ b/src/main.h @@ -39,6 +39,9 @@ extern "C" { double tarantool_uptime(void); +void +tarantool_exit(int); + void load_cfg(); diff --git a/test/box/misc.result b/test/box/misc.result index 6912915c161bb74708e5e14e7bdf547597acc002..97189ecbb20fe8b2c31bde70d6c97c2b3338b7aa 100644 --- a/test/box/misc.result +++ b/test/box/misc.result @@ -1321,3 +1321,92 @@ box.space.shutdown:select{} box.space.shutdown:drop() --- ... +-- Check that os.exit invokes triggers +fiber = require("fiber") +--- +... +test_run:cmd("create server test with script='box/proxy.lua'") +--- +- true +... +test_run:cmd("start server test") +--- +- true +... +logfile = test_run:eval("test", "box.cfg.log")[1] +--- +... +test_run:cmd("stop server test") +--- +- true +... +-- clean up any leftover logs +require("fio").unlink(logfile) +--- +- true +... +test_run:cmd("start server test") +--- +- true +... +test_run:cmd("switch test") +--- +- true +... +_ = box.ctl.on_shutdown(function() print("on_shutdown 5") end) +--- +... +-- Check that we don't hang infinitely after os.exit() +-- even if the following code doesn't yield. +fiber = require("fiber") +--- +... +_ = fiber.create(function() fiber.sleep(0.05) os.exit() while true do end end) +--- +... +test_run:cmd("switch default") +--- +- true +... +fiber.sleep(0.1) +--- +... +-- The server should be already stopped by os.exit(), +-- but start doesn't work without a prior call to stop. +test_run:cmd("stop server test") +--- +- true +... +test_run:cmd("start server test") +--- +- true +... +test_run:cmd("switch test") +--- +- true +... +test_run:grep_log('test', 'on_shutdown 5', nil, {noreset=true}) +--- +- on_shutdown 5 +... +-- make sure we exited because of os.exit(), not a signal. +test_run:grep_log('test', 'signal', nil, {noreset=true}) +--- +- null +... +test_run:cmd("switch default") +--- +- true +... +test_run:cmd("stop server test") +--- +- true +... +test_run:cmd("cleanup server test") +--- +- true +... +test_run:cmd("delete server test") +--- +- true +... diff --git a/test/box/misc.test.lua b/test/box/misc.test.lua index f1c9d8e8cbbbcda7a27111180bb2cd8e41b66e27..18128b29911782cabb40485efd5ec34740602379 100644 --- a/test/box/misc.test.lua +++ b/test/box/misc.test.lua @@ -384,3 +384,33 @@ test_run:grep_log('default', 'on_shutdown 3', nil, {noreset=true}) test_run:grep_log('default', 'on_shutdown 4', nil, {noreset=true}) box.space.shutdown:select{} box.space.shutdown:drop() + +-- Check that os.exit invokes triggers +fiber = require("fiber") +test_run:cmd("create server test with script='box/proxy.lua'") +test_run:cmd("start server test") +logfile = test_run:eval("test", "box.cfg.log")[1] +test_run:cmd("stop server test") +-- clean up any leftover logs +require("fio").unlink(logfile) +test_run:cmd("start server test") +test_run:cmd("switch test") +_ = box.ctl.on_shutdown(function() print("on_shutdown 5") end) +-- Check that we don't hang infinitely after os.exit() +-- even if the following code doesn't yield. +fiber = require("fiber") +_ = fiber.create(function() fiber.sleep(0.05) os.exit() while true do end end) +test_run:cmd("switch default") +fiber.sleep(0.1) +-- The server should be already stopped by os.exit(), +-- but start doesn't work without a prior call to stop. +test_run:cmd("stop server test") +test_run:cmd("start server test") +test_run:cmd("switch test") +test_run:grep_log('test', 'on_shutdown 5', nil, {noreset=true}) +-- make sure we exited because of os.exit(), not a signal. +test_run:grep_log('test', 'signal', nil, {noreset=true}) +test_run:cmd("switch default") +test_run:cmd("stop server test") +test_run:cmd("cleanup server test") +test_run:cmd("delete server test")