clemisch
clemisch

Reputation: 1044

Lua: os.execute("sleep n") in infinite loop can't be stopped by ^C

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:

  1. Is this expected behavior? I would have guessed the program receives the ^C signal after returning from the os.execute command.

  2. Is there a "builtin" way to sleep efficiently?

Upvotes: 2

Views: 1288

Answers (3)

oliver nadj
oliver nadj

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

pilcrow
pilcrow

Reputation: 58651

Short Answer

Your sleep child is killed by the terminal's SIGINT, but os.execute ignores the signal and thus lua continues in its loop.

Longer Answer

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

lhf
lhf

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

Related Questions