From: Daniel Drake <dsd@laptop.org>
http://bugzilla.gnome.org/show_bug.cgi?id=481569

Fix the PySignal_SetWakeupFd() implementation:
 - Prepare file descriptors and call PySignal_SetWakeupFd() just once, outside
   of the mainloop iterations
 - Fix source_prepare semantics
 - Fix source_check semantics, and actually read from the pipe
 - Clean up during pyg_main_loop_dealloc()

Index: pygobject-2.15.4/glib/pygmainloop.c
===================================================================
--- pygobject-2.15.4.orig/glib/pygmainloop.c
+++ pygobject-2.15.4/glib/pygmainloop.c
@@ -119,16 +119,15 @@ static gboolean
 pyg_signal_watch_prepare(GSource *source,
 			 int     *timeout)
 {
-#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
-    PySignalWatchSource *real_source = (PySignalWatchSource *)source;
-#endif
-    
     /* Python only invokes signal handlers from the main thread,
      * so if a thread other than the main thread receives the signal
      * from the kernel, PyErr_CheckSignals() from that thread will
      * do nothing.
      */
 
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+    return FALSE;
+#else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
     /* On Windows g_poll() won't be interrupted by a signal
      * (AFAIK), so we need the timeout there too, even if there's
      * only one thread.
@@ -136,37 +135,16 @@ pyg_signal_watch_prepare(GSource *source
 #ifndef PLATFORM_WIN32
     if (!pyglib_threads_enabled())
 	return FALSE;
-#endif
-
-#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
-    if (real_source->fds[0] != 0)
-	return FALSE;
+#endif /* PLATFORM_WIN32 */
 
-    /* Unfortunately we need to create a new pipe here instead of
-     * reusing the pipe inside the GMainContext.
-     * Ideally an api should be added to GMainContext which allows us
-     * to reuse that pipe which would suit us perfectly fine.     
-     */
-    if (pipe(real_source->fds) < 0)
-	g_error("Cannot create main loop pipe: %s\n",
-		g_strerror(errno));
-
-    real_source->fd.fd = real_source->fds[0];
-    real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
-    g_source_add_poll(source, &real_source->fd);
-
-    PySignal_SetWakeupFd(real_source->fds[1]);
-
-#else /* !HAVE_PYSIGNAL_SETWAKEUPFD */
     /* If we're using 2.5 or an earlier version of python we
      * will default to a timeout every second, be aware,
      * this will cause unnecessary wakeups, see
      * http://bugzilla.gnome.org/show_bug.cgi?id=481569
      */
     *timeout = 1000;
-#endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
-
     return FALSE;
+#endif /* HAVE_PYSIGNAL_SETWAKEUPFD */
 }
 
 static gboolean
@@ -175,6 +153,14 @@ pyg_signal_watch_check(GSource *source)
     PyGILState_STATE state;
     GMainLoop *main_loop;
 
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+    PySignalWatchSource *real_source = (PySignalWatchSource *)source;
+    GPollFD *poll_fd = &real_source->fd;
+    unsigned char dummy;
+    if (poll_fd->revents)
+	read(poll_fd->fd, &dummy, 1);
+#endif
+
     state = pyglib_gil_state_ensure();
 
     main_loop = pyg_get_current_main_loop();
@@ -224,7 +210,30 @@ static GSourceFuncs pyg_signal_watch_fun
 static GSource *
 pyg_signal_watch_new(void)
 {
-    return g_source_new(&pyg_signal_watch_funcs, sizeof(PySignalWatchSource));
+    GSource *source = g_source_new(&pyg_signal_watch_funcs,
+	sizeof(PySignalWatchSource));
+
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+    PySignalWatchSource *real_source = (PySignalWatchSource *)source;
+
+    /* Unfortunately we need to create a new pipe here instead of
+     * reusing the pipe inside the GMainContext.
+     * Ideally an api should be added to GMainContext which allows us
+     * to reuse that pipe which would suit us perfectly fine.     
+     * XXX More efficient than a pipe, we could use an eventfd on Linux
+     * kernels that support it.
+     */
+    if (pipe(real_source->fds) < 0)
+	g_error("Cannot create main loop pipe: %s\n",
+		g_strerror(errno));
+
+    real_source->fd.fd = real_source->fds[0];
+    real_source->fd.events = G_IO_IN | G_IO_HUP | G_IO_ERR;
+    g_source_add_poll(source, &real_source->fd);
+
+    PySignal_SetWakeupFd(real_source->fds[1]);
+#endif
+    return source;
 }
 
 PYGLIB_DEFINE_TYPE("glib.MainLoop", PyGMainLoop_Type, PyGMainLoop)
@@ -267,7 +276,26 @@ static void
 pyg_main_loop_dealloc(PyGMainLoop *self)
 {
     if (self->signal_source != NULL) {
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+	PySignalWatchSource *real_source =
+	    (PySignalWatchSource *)self->signal_source;
+
+	/* increase reference count so that we can access our fds after
+	 * g_source_destroy */
+	g_source_ref(self->signal_source);
+#endif
+
 	g_source_destroy(self->signal_source);
+
+#ifdef HAVE_PYSIGNAL_SETWAKEUPFD
+	PySignal_SetWakeupFd(-1);
+	close(real_source->fds[0]);
+	close(real_source->fds[1]);
+
+	/* now we can free it */
+	g_source_unref(self->signal_source);
+#endif
+
 	self->signal_source = NULL;
     }
 
