aboutsummaryrefslogtreecommitdiffstats
path: root/src/process.c
diff options
context:
space:
mode:
authorPaul Eggert2012-11-03 11:32:41 -0700
committerPaul Eggert2012-11-03 11:32:41 -0700
commit0b3d4a4756646cc7e908375a457e36e15712ad00 (patch)
tree3aa128c2c41316fc912d10015b428f055ee76bc3 /src/process.c
parent0d879dca4648aa15bd3c9ae4ff8b6c39e0c07326 (diff)
downloademacs-0b3d4a4756646cc7e908375a457e36e15712ad00.tar.gz
emacs-0b3d4a4756646cc7e908375a457e36e15712ad00.zip
Fix a race condition that causes Emacs to mess up glib.
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. * 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. * process.h (Lisp_Process): New boolean member 'alive'. Fixes: debbugs:8855
Diffstat (limited to 'src/process.c')
-rw-r--r--src/process.c195
1 files changed, 100 insertions, 95 deletions
diff --git a/src/process.c b/src/process.c
index 29c880da3b0..3c0e53f7784 100644
--- a/src/process.c
+++ b/src/process.c
@@ -130,6 +130,10 @@ 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/* This is for DOS_NT ports. FIXME: Remove this old portability cruft
134 by having DOS_NT ports implement waitpid instead of wait. Nowadays
135 POSIXish hosts all define waitpid, WNOHANG, and WUNTRACED, as these
136 have been standard since POSIX.1-1988. */
133#ifndef WNOHANG 137#ifndef WNOHANG
134# undef waitpid 138# undef waitpid
135# define waitpid(pid, status, options) wait (status) 139# define waitpid(pid, status, options) wait (status)
@@ -795,9 +799,8 @@ get_process (register Lisp_Object name)
795#ifdef SIGCHLD 799#ifdef SIGCHLD
796/* Fdelete_process promises to immediately forget about the process, but in 800/* Fdelete_process promises to immediately forget about the process, but in
797 reality, Emacs needs to remember those processes until they have been 801 reality, Emacs needs to remember those processes until they have been
798 treated by the SIGCHLD handler; otherwise this handler would consider the 802 treated by the SIGCHLD handler and waitpid has been invoked on them;
799 process as being synchronous and say that the synchronous process is 803 otherwise they might fill up the kernel's process table. */
800 dead. */
801static Lisp_Object deleted_pid_list; 804static Lisp_Object deleted_pid_list;
802#endif 805#endif
803 806
@@ -1704,16 +1707,7 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
1704 if (inchannel > max_process_desc) 1707 if (inchannel > max_process_desc)
1705 max_process_desc = inchannel; 1708 max_process_desc = inchannel;
1706 1709
1707 /* Until we store the proper pid, enable the SIGCHLD handler 1710 /* 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); 1711 setup_process_coding_systems (process);
1718 1712
1719 encoded_current_dir = ENCODE_FILE (current_dir); 1713 encoded_current_dir = ENCODE_FILE (current_dir);
@@ -1880,6 +1874,8 @@ create_process (Lisp_Object process, char **new_argv, Lisp_Object current_dir)
1880#endif 1874#endif
1881 1875
1882 XPROCESS (process)->pid = pid; 1876 XPROCESS (process)->pid = pid;
1877 if (0 <= pid)
1878 XPROCESS (process)->alive = 1;
1883 1879
1884 /* Stop blocking signals in the parent. */ 1880 /* Stop blocking signals in the parent. */
1885#ifdef SIGCHLD 1881#ifdef SIGCHLD
@@ -6271,9 +6267,35 @@ process has been transmitted to the serial port. */)
6271 return process; 6267 return process;
6272} 6268}
6273 6269
6274/* On receipt of a signal that a child status has changed, loop asking 6270/* If the status of the process DESIRED has changed, return true and
6275 about children with changed statuses until the system says there 6271 set *STATUS to its exit status; otherwise, return false.
6276 are no more. 6272 If HAVE is nonnegative, assume that HAVE = waitpid (HAVE, STATUS, ...)
6273 has already been invoked, and do not invoke waitpid again. */
6274
6275static bool
6276process_status_retrieved (pid_t desired, pid_t have, int *status)
6277{
6278 if (have < 0)
6279 {
6280 /* Invoke waitpid only with a known process ID; do not invoke
6281 waitpid with a nonpositive argument. Otherwise, Emacs might
6282 reap an unwanted process by mistake. For example, invoking
6283 waitpid (-1, ...) can mess up glib by reaping glib's subprocesses,
6284 so that another thread running glib won't find them. */
6285 do
6286 have = waitpid (desired, status, WNOHANG | WUNTRACED);
6287 while (have < 0 && errno == EINTR);
6288 }
6289
6290 return have == desired;
6291}
6292
6293/* If PID is nonnegative, the child process PID with wait status W has
6294 changed its status; record this and return true.
6295
6296 If PID is negative, ignore W, and look for known child processes
6297 of Emacs whose status have changed. For each one found, record its new
6298 status.
6277 6299
6278 All we do is change the status; we do not run sentinels or print 6300 All we do is change the status; we do not run sentinels or print
6279 notifications. That is saved for the next time keyboard input is 6301 notifications. That is saved for the next time keyboard input is
@@ -6296,13 +6318,23 @@ process has been transmitted to the serial port. */)
6296 ** Malloc WARNING: This should never call malloc either directly or 6318 ** Malloc WARNING: This should never call malloc either directly or
6297 indirectly; if it does, that is a bug */ 6319 indirectly; if it does, that is a bug */
6298 6320
6299/* Record the changed status of the child process PID with wait status W. */
6300void 6321void
6301record_child_status_change (pid_t pid, int w) 6322record_child_status_change (pid_t pid, int w)
6302{ 6323{
6303#ifdef SIGCHLD 6324#ifdef SIGCHLD
6304 Lisp_Object proc; 6325
6305 struct Lisp_Process *p; 6326# ifdef WNOHANG
6327 /* On POSIXish hosts, record at most one child only if we already
6328 know one child that has exited. */
6329 bool record_at_most_one_child = 0 <= pid;
6330# else
6331 /* On DOS_NT (the only porting target that lacks WNOHANG),
6332 record the status of at most one child process, since the SIGCHLD
6333 handler must return right away. If any more processes want to
6334 signal us, we will get another signal. */
6335 bool record_at_most_one_child = 1;
6336# endif
6337
6306 Lisp_Object tail; 6338 Lisp_Object tail;
6307 6339
6308 /* Find the process that signaled us, and record its status. */ 6340 /* Find the process that signaled us, and record its status. */
@@ -6310,68 +6342,69 @@ record_child_status_change (pid_t pid, int w)
6310 /* The process can have been deleted by Fdelete_process. */ 6342 /* The process can have been deleted by Fdelete_process. */
6311 for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail)) 6343 for (tail = deleted_pid_list; CONSP (tail); tail = XCDR (tail))
6312 { 6344 {
6345 bool all_pids_are_fixnums
6346 = (MOST_NEGATIVE_FIXNUM <= TYPE_MINIMUM (pid_t)
6347 && TYPE_MAXIMUM (pid_t) <= MOST_POSITIVE_FIXNUM);
6313 Lisp_Object xpid = XCAR (tail); 6348 Lisp_Object xpid = XCAR (tail);
6314 if ((INTEGERP (xpid) && pid == XINT (xpid)) 6349 if (all_pids_are_fixnums ? INTEGERP (xpid) : NUMBERP (xpid))
6315 || (FLOATP (xpid) && pid == XFLOAT_DATA (xpid)))
6316 { 6350 {
6317 XSETCAR (tail, Qnil); 6351 pid_t deleted_pid;
6318 return; 6352 if (INTEGERP (xpid))
6353 deleted_pid = XINT (xpid);
6354 else
6355 deleted_pid = XFLOAT_DATA (xpid);
6356 if (process_status_retrieved (deleted_pid, pid, &w))
6357 {
6358 XSETCAR (tail, Qnil);
6359 if (record_at_most_one_child)
6360 return;
6361 }
6319 } 6362 }
6320 } 6363 }
6321 6364
6322 /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */ 6365 /* Otherwise, if it is asynchronous, it is in Vprocess_alist. */
6323 p = 0;
6324 for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail)) 6366 for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
6325 { 6367 {
6326 proc = XCDR (XCAR (tail)); 6368 Lisp_Object proc = XCDR (XCAR (tail));
6327 p = XPROCESS (proc); 6369 struct Lisp_Process *p = XPROCESS (proc);
6328 if (EQ (p->type, Qreal) && p->pid == pid) 6370 if (p->alive && process_status_retrieved (p->pid, pid, &w))
6329 break; 6371 {
6330 p = 0; 6372 /* Change the status of the process that was found. */
6331 } 6373 p->tick = ++process_tick;
6332 6374 p->raw_status = w;
6333 /* Look for an asynchronous process whose pid hasn't been filled 6375 p->raw_status_new = 1;
6334 in yet. */
6335 if (! p)
6336 for (tail = Vprocess_alist; CONSP (tail); tail = XCDR (tail))
6337 {
6338 proc = XCDR (XCAR (tail));
6339 p = XPROCESS (proc);
6340 if (p->pid == -1)
6341 break;
6342 p = 0;
6343 }
6344 6376
6345 /* Change the status of the process that was found. */ 6377 /* If process has terminated, stop waiting for its output. */
6346 if (p) 6378 if (WIFSIGNALED (w) || WIFEXITED (w))
6347 { 6379 {
6348 int clear_desc_flag = 0; 6380 int clear_desc_flag = 0;
6381 p->alive = 0;
6382 if (p->infd >= 0)
6383 clear_desc_flag = 1;
6349 6384
6350 p->tick = ++process_tick; 6385 /* clear_desc_flag avoids a compiler bug in Microsoft C. */
6351 p->raw_status = w; 6386 if (clear_desc_flag)
6352 p->raw_status_new = 1; 6387 {
6388 FD_CLR (p->infd, &input_wait_mask);
6389 FD_CLR (p->infd, &non_keyboard_wait_mask);
6390 }
6391 }
6353 6392
6354 /* If process has terminated, stop waiting for its output. */ 6393 /* Tell wait_reading_process_output that it needs to wake up and
6355 if ((WIFSIGNALED (w) || WIFEXITED (w)) 6394 look around. */
6356 && p->infd >= 0) 6395 if (input_available_clear_time)
6357 clear_desc_flag = 1; 6396 *input_available_clear_time = make_emacs_time (0, 0);
6358 6397
6359 /* We use clear_desc_flag to avoid a compiler bug in Microsoft C. */ 6398 if (record_at_most_one_child)
6360 if (clear_desc_flag) 6399 return;
6361 {
6362 FD_CLR (p->infd, &input_wait_mask);
6363 FD_CLR (p->infd, &non_keyboard_wait_mask);
6364 } 6400 }
6365
6366 /* Tell wait_reading_process_output that it needs to wake up and
6367 look around. */
6368 if (input_available_clear_time)
6369 *input_available_clear_time = make_emacs_time (0, 0);
6370 } 6401 }
6371 /* There was no asynchronous process found for that pid: we have 6402
6372 a synchronous process. */ 6403 if (0 <= pid)
6373 else
6374 { 6404 {
6405 /* The caller successfully waited for a pid but no asynchronous
6406 process was found for it, so this is a synchronous process. */
6407
6375 synch_process_alive = 0; 6408 synch_process_alive = 0;
6376 6409
6377 /* Report the status of the synchronous process. */ 6410 /* Report the status of the synchronous process. */
@@ -6390,38 +6423,10 @@ record_child_status_change (pid_t pid, int w)
6390 6423
6391#ifdef SIGCHLD 6424#ifdef SIGCHLD
6392 6425
6393/* On some systems, the SIGCHLD handler must return right away. If
6394 any more processes want to signal us, we will get another signal.
6395 Otherwise, loop around to use up all the processes that have
6396 something to tell us. */
6397#if (defined WINDOWSNT \
6398 || (defined USG && !defined GNU_LINUX \
6399 && !(defined HPUX && defined WNOHANG)))
6400enum { CAN_HANDLE_MULTIPLE_CHILDREN = 0 };
6401#else
6402enum { CAN_HANDLE_MULTIPLE_CHILDREN = 1 };
6403#endif
6404
6405static void 6426static void
6406handle_child_signal (int sig) 6427handle_child_signal (int sig)
6407{ 6428{
6408 do 6429 record_child_status_change (-1, 0);
6409 {
6410 pid_t pid;
6411 int status;
6412
6413 do
6414 pid = waitpid (-1, &status, WNOHANG | WUNTRACED);
6415 while (pid < 0 && errno == EINTR);
6416
6417 /* PID == 0 means no processes found, PID == -1 means a real failure.
6418 Either way, we have done all our job. */
6419 if (pid <= 0)
6420 break;
6421
6422 record_child_status_change (pid, status);
6423 }
6424 while (CAN_HANDLE_MULTIPLE_CHILDREN);
6425} 6430}
6426 6431
6427static void 6432static void