diff options
| author | Paul Eggert | 2012-11-23 14:20:31 -0800 |
|---|---|---|
| committer | Paul Eggert | 2012-11-23 14:20:31 -0800 |
| commit | 6d4e8f62e93b575a1da2cd2b4abeb9dce56e1e52 (patch) | |
| tree | 2d3564ca26405db0e887bea037dcc175d85cc753 /src | |
| parent | 002c019c34eeb1cad4ce8f5ae721b1cdf22f0946 (diff) | |
| download | emacs-6d4e8f62e93b575a1da2cd2b4abeb9dce56e1e52.tar.gz emacs-6d4e8f62e93b575a1da2cd2b4abeb9dce56e1e52.zip | |
Fix a race condition with glib (Bug#8855).
This is a backport from the trunk, consisting of:
2012-11-17 Eli Zaretskii <eliz@gnu.org>
* nt/inc/sys/wait.h: New file, with prototype of waitpid and
definitions of macros it needs.
* nt/inc/ms-w32.h (wait): Don't define, 'wait' is not used anymore.
(sys_wait): Remove prototype.
* nt/config.nt (HAVE_SYS_WAIT_H): Define to 1.
* src/w32proc.c (create_child): Don't clip the PID of the child
process to fit into an Emacs integer, as this is no longer a
restriction.
(waitpid): Rename from sys_wait. Emulate a Posix 'waitpid' by
reaping only the process specified by PID argument, if that is
positive. Use PID instead of dead_child to know which process to
reap. Wait for the child to die only if WNOHANG is not in
OPTIONS.
(sys_select): Don't set dead_child.
* src/sysdep.c (wait_for_termination_1): Remove the WINDOWSNT portion,
as it is no longer needed.
* src/process.c (waitpid, WUNTRACED) [!WNOHANG]: Remove definitions,
no longer needed.
(record_child_status_change): Remove the setting of
record_at_most_one_child for the !WNOHANG case.
2012-11-03 Paul Eggert <eggert@cs.ucla.edu>
Fix a race condition that causes Emacs to mess up glib (Bug#8855).
This is a backport from the trunk.
The symptom is a diagnostic "GLib-WARNING **: In call to
g_spawn_sync(), exit status of a child process was requested but
SIGCHLD action was set to SIG_IGN and ECHILD was received by
waitpid(), so exit status can't be returned." The diagnostic
is partly wrong, as the SIGCHLD action is not set to SIG_IGN.
The real bug is a race condition between Emacs and glib: Emacs
does a waitpid (-1, ...) and reaps glib's subprocess by mistake,
so that glib can't find it. Work around the bug by invoking
waitpid only on subprocesses that Emacs itself creates.
* src/process.c (create_process, record_child_status_change):
Don't use special value -1 in pid field, as the caller now must
know the pid rather than having the callee infer it. The
inference was sometimes incorrect anyway, due to another race.
(create_process): Set new 'alive' member if child is created.
(process_status_retrieved): New function.
(record_child_status_change): Use it.
Accept negative 1st argument, which means to wait for the
processes that Emacs already knows about. Move special-case code
for DOS_NT (which lacks WNOHANG) here, from caller. Keep track of
processes that have already been waited for, by testing and
clearing new 'alive' member.
(CAN_HANDLE_MULTIPLE_CHILDREN): Remove, as record_child_status_change
now does this internally.
(handle_child_signal): Let record_child_status_change do all
the work, since we do not want to reap all exited child processes,
only the child processes that Emacs itself created.
* src/process.h (Lisp_Process): New boolean member 'alive'.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 57 | ||||
| -rw-r--r-- | src/process.c | 191 | ||||
| -rw-r--r-- | src/process.h | 3 | ||||
| -rw-r--r-- | src/sysdep.c | 7 | ||||
| -rw-r--r-- | src/w32proc.c | 127 |
5 files changed, 240 insertions, 145 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index 88fbf7a99f2..655eb1595c0 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,60 @@ | |||
| 1 | 2012-11-23 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | Fix a race condition with glib (Bug#8855). | ||
| 4 | This is a backport from the trunk, consisting of: | ||
| 5 | |||
| 6 | 2012-11-17 Eli Zaretskii <eliz@gnu.org> | ||
| 7 | |||
| 8 | * w32proc.c (create_child): Don't clip the PID of the child | ||
| 9 | process to fit into an Emacs integer, as this is no longer a | ||
| 10 | restriction. | ||
| 11 | (waitpid): Rename from sys_wait. Emulate a Posix 'waitpid' by | ||
| 12 | reaping only the process specified by PID argument, if that is | ||
| 13 | positive. Use PID instead of dead_child to know which process to | ||
| 14 | reap. Wait for the child to die only if WNOHANG is not in | ||
| 15 | OPTIONS. | ||
| 16 | (sys_select): Don't set dead_child. | ||
| 17 | |||
| 18 | * sysdep.c (wait_for_termination_1): Remove the WINDOWSNT portion, | ||
| 19 | as it is no longer needed. | ||
| 20 | |||
| 21 | * process.c (waitpid, WUNTRACED) [!WNOHANG]: Remove definitions, | ||
| 22 | no longer needed. | ||
| 23 | (record_child_status_change): Remove the setting of | ||
| 24 | record_at_most_one_child for the !WNOHANG case. | ||
| 25 | |||
| 26 | 2012-11-03 Paul Eggert <eggert@cs.ucla.edu> | ||
| 27 | |||
| 28 | Fix a race condition that causes Emacs to mess up glib (Bug#8855). | ||
| 29 | This is a backport from the trunk. | ||
| 30 | The symptom is a diagnostic "GLib-WARNING **: In call to | ||
| 31 | g_spawn_sync(), exit status of a child process was requested but | ||
| 32 | SIGCHLD action was set to SIG_IGN and ECHILD was received by | ||
| 33 | waitpid(), so exit status can't be returned." The diagnostic | ||
| 34 | is partly wrong, as the SIGCHLD action is not set to SIG_IGN. | ||
| 35 | The real bug is a race condition between Emacs and glib: Emacs | ||
| 36 | does a waitpid (-1, ...) and reaps glib's subprocess by mistake, | ||
| 37 | so that glib can't find it. Work around the bug by invoking | ||
| 38 | waitpid only on subprocesses that Emacs itself creates. | ||
| 39 | * process.c (create_process, record_child_status_change): | ||
| 40 | Don't use special value -1 in pid field, as the caller now must | ||
| 41 | know the pid rather than having the callee infer it. The | ||
| 42 | inference was sometimes incorrect anyway, due to another race. | ||
| 43 | (create_process): Set new 'alive' member if child is created. | ||
| 44 | (process_status_retrieved): New function. | ||
| 45 | (record_child_status_change): Use it. | ||
| 46 | Accept negative 1st argument, which means to wait for the | ||
| 47 | processes that Emacs already knows about. Move special-case code | ||
| 48 | for DOS_NT (which lacks WNOHANG) here, from caller. Keep track of | ||
| 49 | processes that have already been waited for, by testing and | ||
| 50 | clearing new 'alive' member. | ||
| 51 | (CAN_HANDLE_MULTIPLE_CHILDREN): Remove, as record_child_status_change | ||
| 52 | now does this internally. | ||
| 53 | (handle_child_signal): Let record_child_status_change do all | ||
| 54 | the work, since we do not want to reap all exited child processes, | ||
| 55 | only the child processes that Emacs itself created. | ||
| 56 | * process.h (Lisp_Process): New boolean member 'alive'. | ||
| 57 | |||
| 1 | 2012-11-23 Eli Zaretskii <eliz@gnu.org> | 58 | 2012-11-23 Eli Zaretskii <eliz@gnu.org> |
| 2 | 59 | ||
| 3 | * xdisp.c (set_cursor_from_row): Skip step 2 only if point is not | 60 | * xdisp.c (set_cursor_from_row): Skip step 2 only if point is not |
diff --git a/src/process.c b/src/process.c index 77e99ead01f..c095d13293b 100644 --- a/src/process.c +++ b/src/process.c | |||
| @@ -130,14 +130,6 @@ extern int sys_select (int, SELECT_TYPE *, SELECT_TYPE *, SELECT_TYPE *, | |||
| 130 | EMACS_TIME *, void *); | 130 | EMACS_TIME *, void *); |
| 131 | #endif | 131 | #endif |
| 132 | 132 | ||
| 133 | #ifndef WNOHANG | ||
| 134 | # undef waitpid | ||
| 135 | # define waitpid(pid, status, options) wait (status) | ||
| 136 | #endif | ||
| 137 | #ifndef WUNTRACED | ||
| 138 | # define WUNTRACED 0 | ||
| 139 | #endif | ||
| 140 | |||
| 141 | /* Work around GCC 4.7.0 bug with strict overflow checking; see | 133 | /* Work around GCC 4.7.0 bug with strict overflow checking; see |
| 142 | <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>. | 134 | <http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52904>. |
| 143 | These lines can be removed once the GCC bug is fixed. */ | 135 | These lines can be removed once the GCC bug is fixed. */ |
| @@ -795,9 +787,8 @@ get_process (register Lisp_Object name) | |||
| 795 | #ifdef SIGCHLD | 787 | #ifdef SIGCHLD |
| 796 | /* Fdelete_process promises to immediately forget about the process, but in | 788 | /* Fdelete_process promises to immediately forget about the process, but in |
| 797 | reality, Emacs needs to remember those processes until they have been | 789 | reality, Emacs needs to remember those processes until they have been |
| 798 | treated by the SIGCHLD handler; otherwise this handler would consider the | 790 | treated by the SIGCHLD handler and waitpid has been invoked on them; |
| 799 | process as being synchronous and say that the synchronous process is | 791 | otherwise they might fill up the kernel's process table. */ |
| 800 | dead. */ | ||
| 801 | static Lisp_Object deleted_pid_list; | 792 | static Lisp_Object deleted_pid_list; |
| 802 | #endif | 793 | #endif |
| 803 | 794 | ||
| @@ -1704,16 +1695,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 1704 | if (inchannel > max_process_desc) | 1695 | if (inchannel > max_process_desc) |
| 1705 | max_process_desc = inchannel; | 1696 | max_process_desc = inchannel; |
| 1706 | 1697 | ||
| 1707 | /* Until we store the proper pid, enable the SIGCHLD handler | 1698 | /* This may signal an error. */ |
| 1708 | to recognize an unknown pid as standing for this process. | ||
| 1709 | It is very important not to let this `marker' value stay | ||
| 1710 | in the table after this function has returned; if it does | ||
| 1711 | it might cause call-process to hang and subsequent asynchronous | ||
| 1712 | processes to get their return values scrambled. */ | ||
| 1713 | XPROCESS (process)->pid = -1; | ||
| 1714 | |||
| 1715 | /* This must be called after the above line because it may signal an | ||
| 1716 | error. */ | ||
| 1717 | setup_process_coding_systems (process); | 1699 | setup_process_coding_systems (process); |
| 1718 | 1700 | ||
| 1719 | encoded_current_dir = ENCODE_FILE (current_dir); | 1701 | encoded_current_dir = ENCODE_FILE (current_dir); |
| @@ -1880,6 +1862,8 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir) | |||
| 1880 | #endif | 1862 | #endif |
| 1881 | 1863 | ||
| 1882 | XPROCESS (process)->pid = pid; | 1864 | XPROCESS (process)->pid = pid; |
| 1865 | if (0 <= pid) | ||
| 1866 | XPROCESS (process)->alive = 1; | ||
| 1883 | 1867 | ||
| 1884 | /* Stop blocking signals in the parent. */ | 1868 | /* Stop blocking signals in the parent. */ |
| 1885 | #ifdef SIGCHLD | 1869 | #ifdef SIGCHLD |
| @@ -6273,9 +6257,35 @@ process has been transmitted to the serial port. */) | |||
| 6273 | return process; | 6257 | return process; |
| 6274 | } | 6258 | } |
| 6275 | 6259 | ||
| 6276 | /* On receipt of a signal that a child status has changed, loop asking | 6260 | /* If the status of the process DESIRED has changed, return true and |
| 6277 | about children with changed statuses until the system says there | 6261 | set *STATUS to its exit status; otherwise, return false. |
| 6278 | are no more. | 6262 | If HAVE is nonnegative, assume that HAVE = waitpid (HAVE, STATUS, ...) |
| 6263 | has already been invoked, and do not invoke waitpid again. */ | ||
| 6264 | |||
| 6265 | static bool | ||
| 6266 | process_status_retrieved (pid_t desired, pid_t have, int *status) | ||
| 6267 | { | ||
| 6268 | if (have < 0) | ||
| 6269 | { | ||
| 6270 | /* Invoke waitpid only with a known process ID; do not invoke | ||
| 6271 | waitpid with a nonpositive argument. Otherwise, Emacs might | ||
| 6272 | reap an unwanted process by mistake. For example, invoking | ||
| 6273 | waitpid (-1, ...) can mess up glib by reaping glib's subprocesses, | ||
| 6274 | so that another thread running glib won't find them. */ | ||
| 6275 | do | ||
| 6276 | have = waitpid (desired, status, WNOHANG | WUNTRACED); | ||
| 6277 | while (have < 0 && errno == EINTR); | ||
| 6278 | } | ||
| 6279 | |||
| 6280 | return have == desired; | ||
| 6281 | } | ||
| 6282 | |||
| 6283 | /* If PID is nonnegative, the child process PID with wait status W has | ||
| 6284 | changed its status; record this and return true. | ||
| 6285 | |||
| 6286 | If PID is negative, ignore W, and look for known child processes | ||
| 6287 | of Emacs whose status have changed. For each one found, record its new | ||
| 6288 | status. | ||
| 6279 | 6289 | ||
| 6280 | All we do is change the status; we do not run sentinels or print | 6290 | All we do is change the status; we do not run sentinels or print |
| 6281 | notifications. That is saved for the next time keyboard input is | 6291 | notifications. That is saved for the next time keyboard input is |
| @@ -6298,13 +6308,15 @@ process has been transmitted to the serial port. */) | |||
| 6298 | ** Malloc WARNING: This should never call malloc either directly or | 6308 | ** Malloc WARNING: This should never call malloc either directly or |
| 6299 | indirectly; if it does, that is a bug */ | 6309 | indirectly; if it does, that is a bug */ |
| 6300 | 6310 | ||
| 6301 | /* Record the changed status of the child process PID with wait status W. */ | ||
| 6302 | void | 6311 | void |
| 6303 | record_child_status_change (pid_t pid, int w) | 6312 | record_child_status_change (pid_t pid, int w) |
| 6304 | { | 6313 | { |
| 6305 | #ifdef SIGCHLD | 6314 | #ifdef SIGCHLD |
| 6306 | Lisp_Object proc; | 6315 | |
| 6307 | struct Lisp_Process *p; | 6316 | /* On POSIXish hosts, record at most one child only if we already |
| 6317 | know one child that has exited. */ | ||
| 6318 | bool record_at_most_one_child = 0 <= pid; | ||
| 6319 | |||
| 6308 | Lisp_Object tail; | 6320 | Lisp_Object tail; |
| 6309 | 6321 | ||
| 6310 | /* Find the process that signaled us, and record its status. */ | 6322 | /* Find the process that signaled us, and record its status. */ |
| @@ -6312,68 +6324,69 @@ record_child_status_change (pid_t pid, int w) | |||
| 6312 | /* The process can have been deleted by Fdelete_process. */ | 6324 | /* The process can have been deleted by Fdelete_process. */ |
| 6313 | for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail)) | 6325 | for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail)) |
| 6314 | { | 6326 | { |
| 6327 | bool all_pids_are_fixnums | ||
| 6328 | = (MOST_NEGATIVE_FIXNUM <= TYPE_MINIMUM (pid_t) | ||
| 6329 | && TYPE_MAXIMUM (pid_t) <= MOST_POSITIVE_FIXNUM); | ||
| 6315 | Lisp_Object xpid = XCAR (tail); | 6330 | Lisp_Object xpid = XCAR (tail); |
| 6316 | if ((INTEGERP (xpid) && pid == XINT (xpid)) | 6331 | if (all_pids_are_fixnums ? INTEGERP (xpid) : NUMBERP (xpid)) |
| 6317 | || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid))) | ||
| 6318 | { | 6332 | { |
| 6319 | XSETCAR (tail, Qnil); | 6333 | pid_t deleted_pid; |
| 6320 | return; | 6334 | if (INTEGERP (xpid)) |
| 6335 | deleted_pid = XINT (xpid); | ||
| 6336 | else | ||
| 6337 | deleted_pid = XFLOAT_DATA (xpid); | ||
| 6338 | if (process_status_retrieved (deleted_pid, pid, &w)) | ||
| 6339 | { | ||
| 6340 | XSETCAR (tail, Qnil); | ||
| 6341 | if (record_at_most_one_child) | ||
| 6342 | return; | ||
| 6343 | } | ||
| 6321 | } | 6344 | } |
| 6322 | } | 6345 | } |
| 6323 | 6346 | ||
| 6324 | /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */ | 6347 | /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */ |
| 6325 | p = 0; | ||
| 6326 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) | 6348 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) |
| 6327 | { | 6349 | { |
| 6328 | proc = XCDR (XCAR (tail)); | 6350 | Lisp_Object proc = XCDR (XCAR (tail)); |
| 6329 | p = XPROCESS (proc); | 6351 | struct Lisp_Process *p = XPROCESS (proc); |
| 6330 | if (EQ (p->type, Qreal) && p->pid == pid) | 6352 | if (p->alive && process_status_retrieved (p->pid, pid, &w)) |
| 6331 | break; | 6353 | { |
| 6332 | p = 0; | 6354 | /* Change the status of the process that was found. */ |
| 6333 | } | 6355 | p->tick = ++process_tick; |
| 6334 | 6356 | p->raw_status = w; | |
| 6335 | /* Look for an asynchronous process whose pid hasn't been filled | 6357 | p->raw_status_new = 1; |
| 6336 | in yet. */ | ||
| 6337 | if (! p) | ||
| 6338 | for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) | ||
| 6339 | { | ||
| 6340 | proc = XCDR (XCAR (tail)); | ||
| 6341 | p = XPROCESS (proc); | ||
| 6342 | if (p->pid == -1) | ||
| 6343 | break; | ||
| 6344 | p = 0; | ||
| 6345 | } | ||
| 6346 | 6358 | ||
| 6347 | /* Change the status of the process that was found. */ | 6359 | /* If process has terminated, stop waiting for its output. */ |
| 6348 | if (p) | 6360 | if (WIFSIGNALED (w) || WIFEXITED (w)) |
| 6349 | { | 6361 | { |
| 6350 | int clear_desc_flag = 0; | 6362 | int clear_desc_flag = 0; |
| 6363 | p->alive = 0; | ||
| 6364 | if (p->infd >= 0) | ||
| 6365 | clear_desc_flag = 1; | ||
| 6351 | 6366 | ||
| 6352 | p->tick = ++process_tick; | 6367 | /* clear_desc_flag avoids a compiler bug in Microsoft C. */ |
| 6353 | p->raw_status = w; | 6368 | if (clear_desc_flag) |
| 6354 | p->raw_status_new = 1; | 6369 | { |
| 6370 | FD_CLR (p->infd, &input_wait_mask); | ||
| 6371 | FD_CLR (p->infd, &non_keyboard_wait_mask); | ||
| 6372 | } | ||
| 6373 | } | ||
| 6355 | 6374 | ||
| 6356 | /* If process has terminated, stop waiting for its output. */ | 6375 | /* Tell wait_reading_process_output that it needs to wake up and |
| 6357 | if ((WIFSIGNALED (w) || WIFEXITED (w)) | 6376 | look around. */ |
| 6358 | && p->infd >= 0) | 6377 | if (input_available_clear_time) |
| 6359 | clear_desc_flag = 1; | 6378 | *input_available_clear_time = make_emacs_time (0, 0); |
| 6360 | 6379 | ||
| 6361 | /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */ | 6380 | if (record_at_most_one_child) |
| 6362 | if (clear_desc_flag) | 6381 | return; |
| 6363 | { | ||
| 6364 | FD_CLR (p->infd, &input_wait_mask); | ||
| 6365 | FD_CLR (p->infd, &non_keyboard_wait_mask); | ||
| 6366 | } | 6382 | } |
| 6367 | |||
| 6368 | /* Tell wait_reading_process_output that it needs to wake up and | ||
| 6369 | look around. */ | ||
| 6370 | if (input_available_clear_time) | ||
| 6371 | *input_available_clear_time = make_emacs_time (0, 0); | ||
| 6372 | } | 6383 | } |
| 6373 | /* There was no asynchronous process found for that pid: we have | 6384 | |
| 6374 | a synchronous process. */ | 6385 | if (0 <= pid) |
| 6375 | else | ||
| 6376 | { | 6386 | { |
| 6387 | /* The caller successfully waited for a pid but no asynchronous | ||
| 6388 | process was found for it, so this is a synchronous process. */ | ||
| 6389 | |||
| 6377 | synch_process_alive = 0; | 6390 | synch_process_alive = 0; |
| 6378 | 6391 | ||
| 6379 | /* Report the status of the synchronous process. */ | 6392 | /* Report the status of the synchronous process. */ |
| @@ -6392,38 +6405,10 @@ record_child_status_change (pid_t pid, int w) | |||
| 6392 | 6405 | ||
| 6393 | #ifdef SIGCHLD | 6406 | #ifdef SIGCHLD |
| 6394 | 6407 | ||
| 6395 | /* On some systems, the SIGCHLD handler must return right away. If | ||
| 6396 | any more processes want to signal us, we will get another signal. | ||
| 6397 | Otherwise, loop around to use up all the processes that have | ||
| 6398 | something to tell us. */ | ||
| 6399 | #if (defined WINDOWSNT \ | ||
| 6400 | || (defined USG && !defined GNU_LINUX \ | ||
| 6401 | && !(defined HPUX && defined WNOHANG))) | ||
| 6402 | enum { CAN_HANDLE_MULTIPLE_CHILDREN = 0 }; | ||
| 6403 | #else | ||
| 6404 | enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 }; | ||
| 6405 | #endif | ||
| 6406 | |||
| 6407 | static void | 6408 | static void |
| 6408 | handle_child_signal (int sig) | 6409 | handle_child_signal (int sig) |
| 6409 | { | 6410 | { |
| 6410 | do | 6411 | record_child_status_change (-1, 0); |
| 6411 | { | ||
| 6412 | pid_t pid; | ||
| 6413 | int status; | ||
| 6414 | |||
| 6415 | do | ||
| 6416 | pid = waitpid (-1, &status, WNOHANG | WUNTRACED); | ||
| 6417 | while (pid < 0 && errno == EINTR); | ||
| 6418 | |||
| 6419 | /* PID == 0 means no processes found, PID == -1 means a real failure. | ||
| 6420 | Either way, we have done all our job. */ | ||
| 6421 | if (pid <= 0) | ||
| 6422 | break; | ||
| 6423 | |||
| 6424 | record_child_status_change (pid, status); | ||
| 6425 | } | ||
| 6426 | while (CAN_HANDLE_MULTIPLE_CHILDREN); | ||
| 6427 | } | 6412 | } |
| 6428 | 6413 | ||
| 6429 | static void | 6414 | static void |
diff --git a/src/process.h b/src/process.h index ce3d2e702cc..74d1a124060 100644 --- a/src/process.h +++ b/src/process.h | |||
| @@ -142,6 +142,9 @@ struct Lisp_Process | |||
| 142 | /* Flag to set coding-system of the process buffer from the | 142 | /* Flag to set coding-system of the process buffer from the |
| 143 | coding_system used to decode process output. */ | 143 | coding_system used to decode process output. */ |
| 144 | unsigned int inherit_coding_system_flag : 1; | 144 | unsigned int inherit_coding_system_flag : 1; |
| 145 | /* Whether the process is alive, i.e., can be waited for. Running | ||
| 146 | processes can be waited for, but exited and fake processes cannot. */ | ||
| 147 | unsigned int alive : 1; | ||
| 145 | /* Record the process status in the raw form in which it comes from `wait'. | 148 | /* Record the process status in the raw form in which it comes from `wait'. |
| 146 | This is to avoid consing in a signal handler. The `raw_status_new' | 149 | This is to avoid consing in a signal handler. The `raw_status_new' |
| 147 | flag indicates that `raw_status' contains a new status that still | 150 | flag indicates that `raw_status' contains a new status that still |
diff --git a/src/sysdep.c b/src/sysdep.c index 63eac5d9e09..bb81353847b 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -289,10 +289,6 @@ wait_for_termination_1 (pid_t pid, int interruptible) | |||
| 289 | { | 289 | { |
| 290 | while (1) | 290 | while (1) |
| 291 | { | 291 | { |
| 292 | #ifdef WINDOWSNT | ||
| 293 | wait (0); | ||
| 294 | break; | ||
| 295 | #else /* not WINDOWSNT */ | ||
| 296 | int status; | 292 | int status; |
| 297 | int wait_result = waitpid (pid, &status, 0); | 293 | int wait_result = waitpid (pid, &status, 0); |
| 298 | if (wait_result < 0) | 294 | if (wait_result < 0) |
| @@ -306,7 +302,8 @@ wait_for_termination_1 (pid_t pid, int interruptible) | |||
| 306 | break; | 302 | break; |
| 307 | } | 303 | } |
| 308 | 304 | ||
| 309 | #endif /* not WINDOWSNT */ | 305 | /* Note: the MS-Windows emulation of waitpid calls QUIT |
| 306 | internally. */ | ||
| 310 | if (interruptible) | 307 | if (interruptible) |
| 311 | QUIT; | 308 | QUIT; |
| 312 | } | 309 | } |
diff --git a/src/w32proc.c b/src/w32proc.c index e3c54fe5460..b4f2099f06a 100644 --- a/src/w32proc.c +++ b/src/w32proc.c | |||
| @@ -783,7 +783,6 @@ alarm (int seconds) | |||
| 783 | /* Child process management list. */ | 783 | /* Child process management list. */ |
| 784 | int child_proc_count = 0; | 784 | int child_proc_count = 0; |
| 785 | child_process child_procs[ MAX_CHILDREN ]; | 785 | child_process child_procs[ MAX_CHILDREN ]; |
| 786 | child_process *dead_child = NULL; | ||
| 787 | 786 | ||
| 788 | static DWORD WINAPI reader_thread (void *arg); | 787 | static DWORD WINAPI reader_thread (void *arg); |
| 789 | 788 | ||
| @@ -1036,9 +1035,6 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app, | |||
| 1036 | if (cp->pid < 0) | 1035 | if (cp->pid < 0) |
| 1037 | cp->pid = -cp->pid; | 1036 | cp->pid = -cp->pid; |
| 1038 | 1037 | ||
| 1039 | /* pid must fit in a Lisp_Int */ | ||
| 1040 | cp->pid = cp->pid & INTMASK; | ||
| 1041 | |||
| 1042 | *pPid = cp->pid; | 1038 | *pPid = cp->pid; |
| 1043 | 1039 | ||
| 1044 | return TRUE; | 1040 | return TRUE; |
| @@ -1114,55 +1110,110 @@ reap_subprocess (child_process *cp) | |||
| 1114 | delete_child (cp); | 1110 | delete_child (cp); |
| 1115 | } | 1111 | } |
| 1116 | 1112 | ||
| 1117 | /* Wait for any of our existing child processes to die | 1113 | /* Wait for a child process specified by PID, or for any of our |
| 1118 | When it does, close its handle | 1114 | existing child processes (if PID is nonpositive) to die. When it |
| 1119 | Return the pid and fill in the status if non-NULL. */ | 1115 | does, close its handle. Return the pid of the process that died |
| 1116 | and fill in STATUS if non-NULL. */ | ||
| 1120 | 1117 | ||
| 1121 | int | 1118 | pid_t |
| 1122 | sys_wait (int *status) | 1119 | waitpid (pid_t pid, int *status, int options) |
| 1123 | { | 1120 | { |
| 1124 | DWORD active, retval; | 1121 | DWORD active, retval; |
| 1125 | int nh; | 1122 | int nh; |
| 1126 | int pid; | ||
| 1127 | child_process *cp, *cps[MAX_CHILDREN]; | 1123 | child_process *cp, *cps[MAX_CHILDREN]; |
| 1128 | HANDLE wait_hnd[MAX_CHILDREN]; | 1124 | HANDLE wait_hnd[MAX_CHILDREN]; |
| 1125 | DWORD timeout_ms; | ||
| 1126 | int dont_wait = (options & WNOHANG) != 0; | ||
| 1129 | 1127 | ||
| 1130 | nh = 0; | 1128 | nh = 0; |
| 1131 | if (dead_child != NULL) | 1129 | /* According to Posix: |
| 1130 | |||
| 1131 | PID = -1 means status is requested for any child process. | ||
| 1132 | |||
| 1133 | PID > 0 means status is requested for a single child process | ||
| 1134 | whose pid is PID. | ||
| 1135 | |||
| 1136 | PID = 0 means status is requested for any child process whose | ||
| 1137 | process group ID is equal to that of the calling process. But | ||
| 1138 | since Windows has only a limited support for process groups (only | ||
| 1139 | for console processes and only for the purposes of passing | ||
| 1140 | Ctrl-BREAK signal to them), and since we have no documented way | ||
| 1141 | of determining whether a given process belongs to our group, we | ||
| 1142 | treat 0 as -1. | ||
| 1143 | |||
| 1144 | PID < -1 means status is requested for any child process whose | ||
| 1145 | process group ID is equal to the absolute value of PID. Again, | ||
| 1146 | since we don't support process groups, we treat that as -1. */ | ||
| 1147 | if (pid > 0) | ||
| 1132 | { | 1148 | { |
| 1133 | /* We want to wait for a specific child */ | 1149 | int our_child = 0; |
| 1134 | wait_hnd[nh] = dead_child->procinfo.hProcess; | 1150 | |
| 1135 | cps[nh] = dead_child; | 1151 | /* We are requested to wait for a specific child. */ |
| 1136 | if (!wait_hnd[nh]) emacs_abort (); | 1152 | for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--) |
| 1137 | nh++; | 1153 | { |
| 1138 | active = 0; | 1154 | /* Some child_procs might be sockets; ignore them. Also |
| 1139 | goto get_result; | 1155 | ignore subprocesses whose output is not yet completely |
| 1156 | read. */ | ||
| 1157 | if (CHILD_ACTIVE (cp) | ||
| 1158 | && cp->procinfo.hProcess | ||
| 1159 | && cp->pid == pid) | ||
| 1160 | { | ||
| 1161 | our_child = 1; | ||
| 1162 | break; | ||
| 1163 | } | ||
| 1164 | } | ||
| 1165 | if (our_child) | ||
| 1166 | { | ||
| 1167 | if (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0) | ||
| 1168 | { | ||
| 1169 | wait_hnd[nh] = cp->procinfo.hProcess; | ||
| 1170 | cps[nh] = cp; | ||
| 1171 | nh++; | ||
| 1172 | } | ||
| 1173 | else if (dont_wait) | ||
| 1174 | { | ||
| 1175 | /* PID specifies our subprocess, but its status is not | ||
| 1176 | yet available. */ | ||
| 1177 | return 0; | ||
| 1178 | } | ||
| 1179 | } | ||
| 1180 | if (nh == 0) | ||
| 1181 | { | ||
| 1182 | /* No such child process, or nothing to wait for, so fail. */ | ||
| 1183 | errno = ECHILD; | ||
| 1184 | return -1; | ||
| 1185 | } | ||
| 1140 | } | 1186 | } |
| 1141 | else | 1187 | else |
| 1142 | { | 1188 | { |
| 1143 | for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--) | 1189 | for (cp = child_procs + (child_proc_count-1); cp >= child_procs; cp--) |
| 1144 | /* some child_procs might be sockets; ignore them */ | 1190 | { |
| 1145 | if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess | 1191 | if (CHILD_ACTIVE (cp) |
| 1146 | && (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0)) | 1192 | && cp->procinfo.hProcess |
| 1147 | { | 1193 | && (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0)) |
| 1148 | wait_hnd[nh] = cp->procinfo.hProcess; | 1194 | { |
| 1149 | cps[nh] = cp; | 1195 | wait_hnd[nh] = cp->procinfo.hProcess; |
| 1150 | nh++; | 1196 | cps[nh] = cp; |
| 1151 | } | 1197 | nh++; |
| 1198 | } | ||
| 1199 | } | ||
| 1200 | if (nh == 0) | ||
| 1201 | { | ||
| 1202 | /* Nothing to wait on, so fail. */ | ||
| 1203 | errno = ECHILD; | ||
| 1204 | return -1; | ||
| 1205 | } | ||
| 1152 | } | 1206 | } |
| 1153 | 1207 | ||
| 1154 | if (nh == 0) | 1208 | if (dont_wait) |
| 1155 | { | 1209 | timeout_ms = 0; |
| 1156 | /* Nothing to wait on, so fail */ | 1210 | else |
| 1157 | errno = ECHILD; | 1211 | timeout_ms = 1000; /* check for quit about once a second. */ |
| 1158 | return -1; | ||
| 1159 | } | ||
| 1160 | 1212 | ||
| 1161 | do | 1213 | do |
| 1162 | { | 1214 | { |
| 1163 | /* Check for quit about once a second. */ | ||
| 1164 | QUIT; | 1215 | QUIT; |
| 1165 | active = WaitForMultipleObjects (nh, wait_hnd, FALSE, 1000); | 1216 | active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms); |
| 1166 | } while (active == WAIT_TIMEOUT); | 1217 | } while (active == WAIT_TIMEOUT); |
| 1167 | 1218 | ||
| 1168 | if (active == WAIT_FAILED) | 1219 | if (active == WAIT_FAILED) |
| @@ -1192,8 +1243,10 @@ get_result: | |||
| 1192 | } | 1243 | } |
| 1193 | if (retval == STILL_ACTIVE) | 1244 | if (retval == STILL_ACTIVE) |
| 1194 | { | 1245 | { |
| 1195 | /* Should never happen */ | 1246 | /* Should never happen. */ |
| 1196 | DebPrint (("Wait.WaitForMultipleObjects returned an active process\n")); | 1247 | DebPrint (("Wait.WaitForMultipleObjects returned an active process\n")); |
| 1248 | if (pid > 0 && dont_wait) | ||
| 1249 | return 0; | ||
| 1197 | errno = EINVAL; | 1250 | errno = EINVAL; |
| 1198 | return -1; | 1251 | return -1; |
| 1199 | } | 1252 | } |
| @@ -1207,6 +1260,8 @@ get_result: | |||
| 1207 | else | 1260 | else |
| 1208 | retval <<= 8; | 1261 | retval <<= 8; |
| 1209 | 1262 | ||
| 1263 | if (pid > 0 && active != 0) | ||
| 1264 | emacs_abort (); | ||
| 1210 | cp = cps[active]; | 1265 | cp = cps[active]; |
| 1211 | pid = cp->pid; | 1266 | pid = cp->pid; |
| 1212 | #ifdef FULL_DEBUG | 1267 | #ifdef FULL_DEBUG |
| @@ -1995,9 +2050,7 @@ count_children: | |||
| 1995 | DebPrint (("select calling SIGCHLD handler for pid %d\n", | 2050 | DebPrint (("select calling SIGCHLD handler for pid %d\n", |
| 1996 | cp->pid)); | 2051 | cp->pid)); |
| 1997 | #endif | 2052 | #endif |
| 1998 | dead_child = cp; | ||
| 1999 | sig_handlers[SIGCHLD] (SIGCHLD); | 2053 | sig_handlers[SIGCHLD] (SIGCHLD); |
| 2000 | dead_child = NULL; | ||
| 2001 | } | 2054 | } |
| 2002 | } | 2055 | } |
| 2003 | else if (fdindex[active] == -1) | 2056 | else if (fdindex[active] == -1) |