Merge waitpid() from g_spawn_sync into gmain()

This is preparatory work for a future commit which will add a
"catchall" waitpid API.  If we don't synchronize here with the worker
thread, race conditions are possible.

This also ensures we have an error message if someone adds a child
watch for a nonexistent pid, etc.  Previously, we'd simply keep
calling waitpid() getting ECHILD, and ignoring it until the source was
removed. Now, we g_warning() and fire the source.

Thirdly, this ensures that the waitpid() call in gmain handles EINTR,
like the g_spawn_sync() one did.

https://bugzilla.gnome.org/show_bug.cgi?id=687061
This commit is contained in:
Colin Walters
2012-10-29 15:44:16 -04:00
parent 0bdf7fecaf
commit ce0022933c
2 changed files with 56 additions and 40 deletions

View File

@@ -4421,12 +4421,24 @@ dispatch_unix_signals (void)
if (!source->child_exited)
{
if (waitpid (source->pid, &source->child_status, WNOHANG) > 0)
pid_t pid;
do
{
source->child_exited = TRUE;
wake_source ((GSource *) source);
pid = waitpid (source->pid, &source->child_status, WNOHANG);
if (pid > 0)
{
source->child_exited = TRUE;
wake_source ((GSource *) source);
}
else if (pid == -1 && errno == ECHILD)
{
g_warning ("GChildWatchSource: Exit status of a child process was requested but ECHILD was received by waitpid(). Most likely the process is ignoring SIGCHLD, or some other thread is invoking waitpid() with a nonpositive first argument; either behavior can break applications that use g_child_watch_add()/g_spawn_sync() either directly or indirectly.");
source->child_exited = TRUE;
source->child_status = 0;
wake_source ((GSource *) source);
}
}
while (pid == -1 && errno == EINTR);
}
}
}