changeset 14480:1de5d45426e2

[gaim-migrate @ 17199] This should fix the bug where GStreamer would sometimes fail to initialize correctly. See the insanely long comment I added to the code if you want an explanation. I'm open to suggestions for a better way to fix this. committer: Tailor Script <tailor@pidgin.im>
author Mark Doliner <mark@kingant.net>
date Sat, 09 Sep 2006 10:39:06 +0000
parents aca9a7b62a23
children 452007468387
files gtk/gtkmain.c
diffstat 1 files changed, 47 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/gtk/gtkmain.c	Sat Sep 09 07:06:44 2006 +0000
+++ b/gtk/gtkmain.c	Sat Sep 09 10:39:06 2006 +0000
@@ -89,6 +89,8 @@
 #endif
 
 #ifdef HAVE_SIGNAL_H
+static guint clean_pid_timeout = 0;
+
 /*
  * Lists of signals we wish to catch and those we wish to ignore.
  * Each list terminated with -1
@@ -143,12 +145,44 @@
 }
 
 #ifdef HAVE_SIGNAL_H
-static void
-clean_pid(void)
+static void sighandler(int sig);
+
+/**
+ * Reap all our dead children.  Sometimes Gaim forks off a separate
+ * process to do some stuff.  When that process exits we are
+ * informed about it so that we can call waitpid() and let it
+ * stop being a zombie.
+ *
+ * We used to do this immediately when our signal handler was
+ * called, but because of GStreamer we now wait one second before
+ * reaping anything.  Why?  For some reason GStreamer fork()s
+ * during their initialization process.  I don't understand why...
+ * but they do it, and there's nothing we can do about it.
+ *
+ * Anyway, so then GStreamer waits for its child to die and then
+ * it continues with the initialization process.  This means that
+ * we have a race condition where GStreamer is waitpid()ing for its
+ * child to die and we're catching the SIGCHLD signal.  If GStreamer
+ * is awarded the zombied process then everything is ok.  But if Gaim
+ * reaps the zombie process then the GStreamer initialization sequence
+ * fails.
+ *
+ * So the ugly solution is to wait a second to give GStreamer time to
+ * reap that bad boy.
+ *
+ * GStreamer 0.10.10 and newer have a gst_register_fork_set_enabled()
+ * function that can be called by applications to disable forking
+ * during initialization.  But it's not in 0.10.0, so we shouldn't
+ * use it.
+ */
+static gboolean
+clean_pid(gpointer data)
 {
 	int status;
 	pid_t pid;
 
+	clean_pid_timeout = 0;
+
 	do {
 		pid = waitpid(-1, &status, WNOHANG);
 	} while (pid != 0 && pid != (pid_t)-1);
@@ -158,6 +192,12 @@
 		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 		perror(errmsg);
 	}
+
+	/* Restore signal catching */
+	signal(SIGCHLD, sighandler);
+
+	/* This timer should not be called again by glib */
+	return FALSE;
 }
 
 char *segfault_message;
@@ -175,8 +215,9 @@
 		abort();
 		break;
 	case SIGCHLD:
-		clean_pid();
-		signal(SIGCHLD, sighandler);    /* restore signal catching on this one! */
+		if (clean_pid_timeout > 0)
+			gaim_timeout_remove(clean_pid_timeout);
+		clean_pid_timeout = gaim_timeout_add(1000, clean_pid, NULL);
 		break;
 	default:
 		gaim_debug_warning("sighandler", "Caught signal %d\n", sig);
@@ -761,6 +802,8 @@
 	gtk_main();
 
 #ifdef HAVE_SIGNAL_H
+	if (clean_pid_timeout > 0)
+		gaim_timeout_remove(clean_pid_timeout);
 	g_free(segfault_message);
 #endif