diff options
Diffstat (limited to 'src/process.c')
| -rw-r--r-- | src/process.c | 130 |
1 files changed, 129 insertions, 1 deletions
diff --git a/src/process.c b/src/process.c index dac7d0440fa..1df4ed9ce03 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -283,6 +283,18 @@ static int max_desc; | |||
| 283 | the file descriptor of a socket that is already bound. */ | 283 | the file descriptor of a socket that is already bound. */ |
| 284 | static int external_sock_fd; | 284 | static int external_sock_fd; |
| 285 | 285 | ||
| 286 | /* File descriptor that becomes readable when we receive SIGCHLD. */ | ||
| 287 | static int child_signal_read_fd = -1; | ||
| 288 | /* The write end thereof. The SIGCHLD handler writes to this file | ||
| 289 | descriptor to notify `wait_reading_process_output' of process | ||
| 290 | status changes. */ | ||
| 291 | static int child_signal_write_fd = -1; | ||
| 292 | static void child_signal_init (void); | ||
| 293 | #ifndef WINDOWSNT | ||
| 294 | static void child_signal_read (int, void *); | ||
| 295 | #endif | ||
| 296 | static void child_signal_notify (void); | ||
| 297 | |||
| 286 | /* Indexed by descriptor, gives the process (if any) for that descriptor. */ | 298 | /* Indexed by descriptor, gives the process (if any) for that descriptor. */ |
| 287 | static Lisp_Object chan_process[FD_SETSIZE]; | 299 | static Lisp_Object chan_process[FD_SETSIZE]; |
| 288 | static void wait_for_socket_fds (Lisp_Object, char const *); | 300 | static void wait_for_socket_fds (Lisp_Object, char const *); |
| @@ -2060,6 +2072,10 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 2060 | Lisp_Object lisp_pty_name = Qnil; | 2072 | Lisp_Object lisp_pty_name = Qnil; |
| 2061 | sigset_t oldset; | 2073 | sigset_t oldset; |
| 2062 | 2074 | ||
| 2075 | /* Ensure that the SIGCHLD handler can notify | ||
| 2076 | `wait_reading_process_output'. */ | ||
| 2077 | child_signal_init (); | ||
| 2078 | |||
| 2063 | inchannel = outchannel = -1; | 2079 | inchannel = outchannel = -1; |
| 2064 | 2080 | ||
| 2065 | if (p->pty_flag) | 2081 | if (p->pty_flag) |
| @@ -5309,6 +5325,15 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 5309 | compute_input_wait_mask (&Atemp); | 5325 | compute_input_wait_mask (&Atemp); |
| 5310 | compute_write_mask (&Ctemp); | 5326 | compute_write_mask (&Ctemp); |
| 5311 | 5327 | ||
| 5328 | /* If a process status has changed, the child signal pipe | ||
| 5329 | will likely be readable. We want to ignore it for now, | ||
| 5330 | because otherwise we wouldn't run into a timeout | ||
| 5331 | below. */ | ||
| 5332 | int fd = child_signal_read_fd; | ||
| 5333 | eassert (fd < FD_SETSIZE); | ||
| 5334 | if (0 <= fd) | ||
| 5335 | FD_CLR (fd, &Atemp); | ||
| 5336 | |||
| 5312 | timeout = make_timespec (0, 0); | 5337 | timeout = make_timespec (0, 0); |
| 5313 | if ((thread_select (pselect, max_desc + 1, | 5338 | if ((thread_select (pselect, max_desc + 1, |
| 5314 | &Atemp, | 5339 | &Atemp, |
| @@ -5395,6 +5420,14 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, | |||
| 5395 | check_write = true; | 5420 | check_write = true; |
| 5396 | } | 5421 | } |
| 5397 | 5422 | ||
| 5423 | /* We have to be informed when we receive a SIGCHLD signal for | ||
| 5424 | an asynchronous process. Otherwise this might deadlock if we | ||
| 5425 | receive a SIGCHLD during `pselect'. */ | ||
| 5426 | int child_fd = child_signal_read_fd; | ||
| 5427 | eassert (child_fd < FD_SETSIZE); | ||
| 5428 | if (0 <= child_fd) | ||
| 5429 | FD_SET (child_fd, &Available); | ||
| 5430 | |||
| 5398 | /* If frame size has changed or the window is newly mapped, | 5431 | /* If frame size has changed or the window is newly mapped, |
| 5399 | redisplay now, before we start to wait. There is a race | 5432 | redisplay now, before we start to wait. There is a race |
| 5400 | condition here; if a SIGIO arrives between now and the select | 5433 | condition here; if a SIGIO arrives between now and the select |
| @@ -7114,7 +7147,95 @@ process has been transmitted to the serial port. */) | |||
| 7114 | subprocesses which the main thread should not reap. For example, | 7147 | subprocesses which the main thread should not reap. For example, |
| 7115 | if the main thread attempted to reap an already-reaped child, it | 7148 | if the main thread attempted to reap an already-reaped child, it |
| 7116 | might inadvertently reap a GTK-created process that happened to | 7149 | might inadvertently reap a GTK-created process that happened to |
| 7117 | have the same process ID. */ | 7150 | have the same process ID. |
| 7151 | |||
| 7152 | To avoid a deadlock when receiving SIGCHLD while | ||
| 7153 | 'wait_reading_process_output' is in 'pselect', the SIGCHLD handler | ||
| 7154 | will notify the `pselect' using a self-pipe. The deadlock could | ||
| 7155 | occur if SIGCHLD is delivered outside of the 'pselect' call, in | ||
| 7156 | which case 'pselect' will not be interrupted by the signal, and | ||
| 7157 | will therefore wait on the process's output descriptor for the | ||
| 7158 | output that will never come. | ||
| 7159 | |||
| 7160 | WINDOWSNT doesn't need this facility because its 'pselect' | ||
| 7161 | emulation (see 'sys_select' in w32proc.c) waits on a subprocess | ||
| 7162 | handle, which becomes signaled when the process exits, and also | ||
| 7163 | because that emulation delays the delivery of the simulated SIGCHLD | ||
| 7164 | until all the output from the subprocess has been consumed. */ | ||
| 7165 | |||
| 7166 | /* FIXME: On Unix-like systems that have a proper 'pselect' | ||
| 7167 | (HAVE_PSELECT), we should block SIGCHLD in | ||
| 7168 | 'wait_reading_process_output' and pass a non-NULL signal mask to | ||
| 7169 | 'pselect' to avoid the need for the self-pipe. */ | ||
| 7170 | |||
| 7171 | /* Set up `child_signal_read_fd' and `child_signal_write_fd'. */ | ||
| 7172 | |||
| 7173 | static void | ||
| 7174 | child_signal_init (void) | ||
| 7175 | { | ||
| 7176 | /* Either both are initialized, or both are uninitialized. */ | ||
| 7177 | eassert ((child_signal_read_fd < 0) == (child_signal_write_fd < 0)); | ||
| 7178 | |||
| 7179 | #ifndef WINDOWSNT | ||
| 7180 | if (0 <= child_signal_read_fd) | ||
| 7181 | return; /* already done */ | ||
| 7182 | |||
| 7183 | int fds[2]; | ||
| 7184 | if (emacs_pipe (fds) < 0) | ||
| 7185 | report_file_error ("Creating pipe for child signal", Qnil); | ||
| 7186 | if (FD_SETSIZE <= fds[0]) | ||
| 7187 | { | ||
| 7188 | /* Since we need to `pselect' on the read end, it has to fit | ||
| 7189 | into an `fd_set'. */ | ||
| 7190 | emacs_close (fds[0]); | ||
| 7191 | emacs_close (fds[1]); | ||
| 7192 | report_file_errno ("Creating pipe for child signal", Qnil, | ||
| 7193 | EMFILE); | ||
| 7194 | } | ||
| 7195 | |||
| 7196 | /* We leave the file descriptors open until the Emacs process | ||
| 7197 | exits. */ | ||
| 7198 | eassert (0 <= fds[0]); | ||
| 7199 | eassert (0 <= fds[1]); | ||
| 7200 | if (fcntl (fds[0], F_SETFL, O_NONBLOCK) != 0) | ||
| 7201 | emacs_perror ("fcntl"); | ||
| 7202 | if (fcntl (fds[1], F_SETFL, O_NONBLOCK) != 0) | ||
| 7203 | emacs_perror ("fcntl"); | ||
| 7204 | add_read_fd (fds[0], child_signal_read, NULL); | ||
| 7205 | fd_callback_info[fds[0]].flags &= ~KEYBOARD_FD; | ||
| 7206 | child_signal_read_fd = fds[0]; | ||
| 7207 | child_signal_write_fd = fds[1]; | ||
| 7208 | #endif /* !WINDOWSNT */ | ||
| 7209 | } | ||
| 7210 | |||
| 7211 | #ifndef WINDOWSNT | ||
| 7212 | /* Consume a process status change. */ | ||
| 7213 | |||
| 7214 | static void | ||
| 7215 | child_signal_read (int fd, void *data) | ||
| 7216 | { | ||
| 7217 | eassert (0 <= fd); | ||
| 7218 | eassert (fd == child_signal_read_fd); | ||
| 7219 | char dummy; | ||
| 7220 | if (emacs_read (fd, &dummy, 1) < 0) | ||
| 7221 | emacs_perror ("reading from child signal FD"); | ||
| 7222 | } | ||
| 7223 | #endif /* !WINDOWSNT */ | ||
| 7224 | |||
| 7225 | /* Notify `wait_reading_process_output' of a process status | ||
| 7226 | change. */ | ||
| 7227 | |||
| 7228 | static void | ||
| 7229 | child_signal_notify (void) | ||
| 7230 | { | ||
| 7231 | #ifndef WINDOWSNT | ||
| 7232 | int fd = child_signal_write_fd; | ||
| 7233 | eassert (0 <= fd); | ||
| 7234 | char dummy = 0; | ||
| 7235 | if (emacs_write (fd, &dummy, 1) != 1) | ||
| 7236 | emacs_perror ("writing to child signal FD"); | ||
| 7237 | #endif | ||
| 7238 | } | ||
| 7118 | 7239 | ||
| 7119 | /* LIB_CHILD_HANDLER is a SIGCHLD handler that Emacs calls while doing | 7240 | /* LIB_CHILD_HANDLER is a SIGCHLD handler that Emacs calls while doing |
| 7120 | its own SIGCHLD handling. On POSIXish systems, glib needs this to | 7241 | its own SIGCHLD handling. On POSIXish systems, glib needs this to |
| @@ -7152,6 +7273,7 @@ static void | |||
| 7152 | handle_child_signal (int sig) | 7273 | handle_child_signal (int sig) |
| 7153 | { | 7274 | { |
| 7154 | Lisp_Object tail, proc; | 7275 | Lisp_Object tail, proc; |
| 7276 | bool changed = false; | ||
| 7155 | 7277 | ||
| 7156 | /* Find the process that signaled us, and record its status. */ | 7278 | /* Find the process that signaled us, and record its status. */ |
| 7157 | 7279 | ||
| @@ -7174,6 +7296,7 @@ handle_child_signal (int sig) | |||
| 7174 | eassert (ok); | 7296 | eassert (ok); |
| 7175 | if (child_status_changed (deleted_pid, 0, 0)) | 7297 | if (child_status_changed (deleted_pid, 0, 0)) |
| 7176 | { | 7298 | { |
| 7299 | changed = true; | ||
| 7177 | if (STRINGP (XCDR (head))) | 7300 | if (STRINGP (XCDR (head))) |
| 7178 | unlink (SSDATA (XCDR (head))); | 7301 | unlink (SSDATA (XCDR (head))); |
| 7179 | XSETCAR (tail, Qnil); | 7302 | XSETCAR (tail, Qnil); |
| @@ -7191,6 +7314,7 @@ handle_child_signal (int sig) | |||
| 7191 | && child_status_changed (p->pid, &status, WUNTRACED | WCONTINUED)) | 7314 | && child_status_changed (p->pid, &status, WUNTRACED | WCONTINUED)) |
| 7192 | { | 7315 | { |
| 7193 | /* Change the status of the process that was found. */ | 7316 | /* Change the status of the process that was found. */ |
| 7317 | changed = true; | ||
| 7194 | p->tick = ++process_tick; | 7318 | p->tick = ++process_tick; |
| 7195 | p->raw_status = status; | 7319 | p->raw_status = status; |
| 7196 | p->raw_status_new = 1; | 7320 | p->raw_status_new = 1; |
| @@ -7210,6 +7334,10 @@ handle_child_signal (int sig) | |||
| 7210 | } | 7334 | } |
| 7211 | } | 7335 | } |
| 7212 | 7336 | ||
| 7337 | if (changed) | ||
| 7338 | /* Wake up `wait_reading_process_output'. */ | ||
| 7339 | child_signal_notify (); | ||
| 7340 | |||
| 7213 | lib_child_handler (sig); | 7341 | lib_child_handler (sig); |
| 7214 | #ifdef NS_IMPL_GNUSTEP | 7342 | #ifdef NS_IMPL_GNUSTEP |
| 7215 | /* NSTask in GNUstep sets its child handler each time it is called. | 7343 | /* NSTask in GNUstep sets its child handler each time it is called. |