moonbridge
changeset 296:ce109800ae2d
Extend moonbridge_io.poll(...) to support waiting for child process termination
author | jbe |
---|---|
date | Sat Jun 17 22:35:44 2017 +0200 (2017-06-17) |
parents | 1eef86d3b925 |
children | dd0ec48592e7 |
files | moonbridge_io.c reference.txt |
line diff
1.1 --- a/moonbridge_io.c Sat Jun 17 20:07:03 2017 +0200 1.2 +++ b/moonbridge_io.c Sat Jun 17 22:35:44 2017 +0200 1.3 @@ -1657,6 +1657,7 @@ 1.4 static int moonbr_io_poll(lua_State *L) { 1.5 moonbr_io_handle_t *handle; 1.6 moonbr_io_listener_t *listener; 1.7 + moonbr_io_child_t *child; 1.8 int fd, isnum; 1.9 int nfds = 0; 1.10 fd_set readfds, writefds, exceptfds; 1.11 @@ -1664,6 +1665,8 @@ 1.12 int force_wakeup = 0; 1.13 int use_timeout = 0; // negative for negative timeout 1.14 int check_sigterm = 0; 1.15 + int check_child = 0; 1.16 + int childstatus; 1.17 sigset_t mask, orig_mask; 1.18 int status; 1.19 FD_ZERO(&readfds); 1.20 @@ -1693,8 +1696,20 @@ 1.21 fd = listener->fd; 1.22 if (fd < 0) luaL_error(L, "Attempt to poll a closed listener"); 1.23 } else { 1.24 - fd = lua_tointegerx(L, -2, &isnum); 1.25 - if (!isnum) luaL_error(L, "Expected integer (file descriptor), I/O handle, or listener in table key"); 1.26 + child = luaL_testudata(L, -2, MOONBR_IO_CHILD_MT_REGKEY); 1.27 + if (child) { 1.28 + check_child = 1; 1.29 + if (waitpid(child->pid, &childstatus, WNOHANG|WNOWAIT) != -1) { 1.30 + force_wakeup = 1; 1.31 + } else if (errno != ECHILD && errno != EINTR) { 1.32 + moonbr_io_prepare_errmsg(); 1.33 + luaL_error(L, "Error in waitpid call: %s", errmsg); 1.34 + } 1.35 + continue; 1.36 + } else { 1.37 + fd = lua_tointegerx(L, -2, &isnum); 1.38 + if (!isnum) luaL_error(L, "Expected integer (file descriptor), I/O handle, or listener in table key"); 1.39 + } 1.40 } 1.41 } 1.42 if (fd < 0 || fd >= FD_SETSIZE) luaL_error(L, "File descriptor out of valid range"); 1.43 @@ -1743,20 +1758,29 @@ 1.44 if (use_timeout < 0) force_wakeup = 1; 1.45 if (!lua_isnoneornil(L, 4)) luaL_checktype(L, 4, LUA_TBOOLEAN); 1.46 check_sigterm = lua_toboolean(L, 4); 1.47 - if (check_sigterm) { 1.48 + if ((check_sigterm || check_child) && !force_wakeup) { 1.49 + sigemptyset(&mask); 1.50 + if (check_sigterm) sigaddset(&mask, SIGTERM); 1.51 + if (check_child) sigaddset(&mask, SIGCHLD); 1.52 + if (sigprocmask(SIG_BLOCK, &mask, &orig_mask)) abort(); 1.53 + } 1.54 + if (check_sigterm && moonbr_io_sigterm_flag) { 1.55 if (!force_wakeup) { 1.56 - sigemptyset(&mask); 1.57 - sigaddset(&mask, SIGTERM); 1.58 - if (sigprocmask(SIG_BLOCK, &mask, &orig_mask)) abort(); 1.59 + if (sigprocmask(SIG_SETMASK, &orig_mask, NULL)) abort(); 1.60 } 1.61 - if (moonbr_io_sigterm_flag) { 1.62 - if (!force_wakeup) { 1.63 - if (sigprocmask(SIG_SETMASK, &orig_mask, NULL)) abort(); 1.64 - } 1.65 - lua_pushboolean(L, 0); 1.66 - lua_pushliteral(L, "SIGTERM received"); 1.67 - lua_pushboolean(L, 1); 1.68 - return 3; 1.69 + lua_pushboolean(L, 0); 1.70 + lua_pushliteral(L, "SIGTERM received"); 1.71 + lua_pushboolean(L, 1); 1.72 + return 3; 1.73 + } 1.74 + if (check_child && !force_wakeup) { 1.75 + if (wait3(&childstatus, WNOHANG|WNOWAIT, NULL) != -1) { 1.76 + if (sigprocmask(SIG_SETMASK, &orig_mask, NULL)) abort(); 1.77 + force_wakeup = 1; 1.78 + } else if (errno != ECHILD && errno != EINTR) { 1.79 + moonbr_io_prepare_errmsg(); 1.80 + if (sigprocmask(SIG_SETMASK, &orig_mask, NULL)) abort(); 1.81 + luaL_error(L, "Error in waitpid call: %s", errmsg); 1.82 } 1.83 } 1.84 if (use_timeout < 0) {
2.1 --- a/reference.txt Sat Jun 17 20:07:03 2017 +0200 2.2 +++ b/reference.txt Sat Jun 17 22:35:44 2017 +0200 2.3 @@ -310,7 +310,7 @@ 2.4 2.5 ### moonbridge_io.exec(command, arg1, arg2, ...) 2.6 2.7 -Executes the given command and returns a handle with three sockets named 2.8 +Executes the given command and returns a child handle with three sockets named 2.9 "stdin", "stdout", and "stderr" as well as the following methods: 2.10 2.11 - :kill(signal) 2.12 @@ -335,6 +335,9 @@ 2.13 2.14 The method :wait_yield() is an alias for :wait_call(coroutine.yield). 2.15 2.16 +It is possible to wait for process termination by including the child handle 2.17 +in the input_set of the moonbridge_io.poll(...) call. 2.18 + 2.19 moonbridge_io.exec(...) returns nil (as first return value) plus an error 2.20 message (as second return value) in case of error. 2.21 2.22 @@ -399,6 +402,9 @@ 2.23 which does evaluate to true, e.g. input_set = {[socketA] = true}. If a set is 2.24 nil, it is treated as being empty. 2.25 2.26 +The input_set may also contain listeners (to wait for incoming connections) and 2.27 +child handles (to wait for process termination). 2.28 + 2.29 If the 4th parameter (wakeup_on_sigterm) is set to true, then the function 2.30 returns immediately if the process received at least one SIGTERM signal after 2.31 moonbridge_io.catch_sigterm() has been called for the first time. Three values