Reputation: 1044
In lua 5.2.4, when using the widely used os.execute('sleep n')
method in an infinite loop, the program can't be stopped by ^C
(Ctrl-C).
Minimal example:
while true do
print("HELLO!")
os.execute("sleep 3")
end
My questions are:
Is this expected behavior? I would have guessed the program receives the ^C
signal after returning from the os.execute
command.
Is there a "builtin" way to sleep efficiently?
Upvotes: 2
Views: 1288
Reputation: 858
Nice explanations from @pilcrow and @lhf. Based on that info, I just crafted a sleep method, what exits when sleep process is exited prematurely (killed).
function responsive_sleep(duration)
local endTime = os.time() + duration
os.execute("sleep " .. tonumber(duration))
if os.time() < endTime then
-- The sleep was interrupted, indicating the process should exit.
os.exit(125)
end
end
Upvotes: 0
Reputation: 58651
Your sleep child is killed by the terminal's SIGINT, but os.execute
ignores the signal and thus lua continues in its loop.
Your terminal driver turns Ctrl+C into a SIGINT generated for the foreground process group, which includes (at least) your lua process and its child sleep process.
(This signal is overwhelmingly likely to be generated while the lua script is inside os.execute
, since that is where your script spends the majority of its time.)
When that happens, the sleep process is killed immediately by SIGINT. The lua process, however, ignores the signal.
It ignores SIGINT because os.execute
is a wrapper around the traditional library call system
, as you can see in the source:
static int os_execute (lua_State *L) {
const char *cmd = luaL_optstring(L, 1, NULL);
int stat = system(cmd); /* <<<<<<<<<<<<<<< here <<<<<<<<<<<<< */
if (cmd != NULL)
return luaL_execresult(L, stat);
else {
lua_pushboolean(L, stat); /* true if there is a shell */
return 1;
}
}
This library call has very specific semantics, including ignoring SIGINT in the caller.
Upvotes: 3
Reputation: 72362
Control-c is most probably being caught by the shell spawned by os.execute
, not by Lua. You need to look at the code returned by os.execute
. When the command ends normally, os.execute
returns true,"exit",rc. Otherwise, it returns nil,etc. When it is interrupted with control-c, it returns nil,"signal",2 in my machine.
Bottom line, try this code:
while true do
print("HELLO!")
if not os.execute("sleep 3") then break end
end
Upvotes: 4