aboutsummaryrefslogtreecommitdiffstats
path: root/src/w32proc.c
diff options
context:
space:
mode:
authorPaul Eggert2012-11-23 14:20:31 -0800
committerPaul Eggert2012-11-23 14:20:31 -0800
commit6d4e8f62e93b575a1da2cd2b4abeb9dce56e1e52 (patch)
tree2d3564ca26405db0e887bea037dcc175d85cc753 /src/w32proc.c
parent002c019c34eeb1cad4ce8f5ae721b1cdf22f0946 (diff)
downloademacs-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/w32proc.c')
-rw-r--r--src/w32proc.c127
1 files changed, 90 insertions, 37 deletions
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. */
784int child_proc_count = 0; 784int child_proc_count = 0;
785child_process child_procs[ MAX_CHILDREN ]; 785child_process child_procs[ MAX_CHILDREN ];
786child_process *dead_child = NULL;
787 786
788static DWORD WINAPI reader_thread (void *arg); 787static 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
1121int 1118pid_t
1122sys_wait (int *status) 1119waitpid (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)