[vte] lib: Ressurrect the reaper



commit b9b928a84559ea9eab5f415513d374f067b9847a
Author: Christian Persch <chpe gnome org>
Date:   Sun Jan 8 13:39:24 2017 +0100

    lib: Ressurrect the reaper
    
    Bring back the reaper as an internal-only thing. This makes
    for cleaner code when we need to reap a child process without
    a VteTerminalPrivate around.
    
    This reverts commit de112fd8039ab6244e2cf53dbacdfc4a49504c48.

 src/Makefile.am    |   14 +++
 src/reaper.cc      |  227 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/reaper.hh      |   30 +++++++
 src/vte.cc         |   60 ++++++++------
 src/vtegtk.cc      |   12 ++-
 src/vteinternal.hh |    3 +-
 6 files changed, 318 insertions(+), 28 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index 8d72475..da6c5d7 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -57,6 +57,8 @@ libvte_@VTE_API_MAJOR_VERSION@_@VTE_API_MINOR_VERSION@_la_SOURCES = \
        matcher.cc \
        matcher.h \
        pty.cc \
+       reaper.cc \
+       reaper.hh \
        ring.cc \
        ring.h \
        table.cc \
@@ -332,6 +334,7 @@ EXTRA_DIST += $(noinst_SCRIPTS)
 
 check_PROGRAMS = \
        dumpkeys \
+       reaper \
        reflect-text-view \
        reflect-vte mev \
        table \
@@ -347,6 +350,7 @@ dist_check_SCRIPTS = \
        $(NULL)
 
 TESTS = \
+       reaper \
        table \
        test-vtetypes \
        vteconv \
@@ -360,6 +364,16 @@ TESTS_ENVIRONMENT = \
        VTE_API_VERSION="$(VTE_API_VERSION)" \
        $(NULL)
 
+reaper_CPPFLAGS = -DMAIN -I$(srcdir) -I $(builddir) $(AM_CPPFLAGS)
+reaper_CXXFLAGS = $(VTE_CFLAGS) $(AM_CXXFLAGS)
+reaper_SOURCES = \
+       debug.cc \
+       debug.h \
+       reaper.cc \
+       reaper.hh \
+       $(NULL)
+reaper_LDADD = $(VTE_LIBS)
+
 reflect_text_view_CPPFLAGS = -DUSE_TEXT_VIEW -I$(srcdir) -I $(builddir) $(AM_CPPFLAGS)
 reflect_text_view_CFLAGS = $(VTE_CFLAGS) $(AM_CFLAGS)
 reflect_text_view_SOURCES = reflect.c
diff --git a/src/reaper.cc b/src/reaper.cc
new file mode 100644
index 0000000..e630a05
--- /dev/null
+++ b/src/reaper.cc
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "config.h"
+
+#include "debug.h"
+#include "reaper.hh"
+
+struct _VteReaper {
+        GObject parent_instance;
+};
+
+struct _VteReaperClass {
+        GObjectClass parent_class;
+};
+typedef struct _VteReaperClass VteReaperClass;
+
+#define VTE_TYPE_REAPER            (vte_reaper_get_type())
+#define VTE_REAPER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), VTE_TYPE_REAPER, VteReaper))
+#define VTE_REAPER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  VTE_TYPE_REAPER, VteReaperClass))
+#define VTE_IS_REAPER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), VTE_TYPE_REAPER))
+#define VTE_IS_REAPER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  VTE_TYPE_REAPER))
+#define VTE_REAPER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  VTE_TYPE_REAPER, VteReaperClass))
+
+static GType vte_reaper_get_type(void);
+
+G_DEFINE_TYPE(VteReaper, vte_reaper, G_TYPE_OBJECT)
+
+static VteReaper *singleton_reaper = nullptr;
+
+static void
+vte_reaper_child_watch_cb(GPid pid,
+                          int status,
+                          gpointer data)
+{
+        _vte_debug_print(VTE_DEBUG_SIGNALS,
+                         "Reaper emitting child-exited signal.\n");
+        g_signal_emit_by_name(data, "child-exited", pid, status);
+        g_spawn_close_pid (pid);
+}
+
+/*
+ * vte_reaper_add_child:
+ * @pid: the ID of a child process which will be monitored
+ *
+ * Ensures that child-exited signals will be emitted when @pid exits.
+ */
+void
+vte_reaper_add_child(GPid pid)
+{
+        g_child_watch_add_full(G_PRIORITY_LOW,
+                               pid,
+                               vte_reaper_child_watch_cb,
+                               vte_reaper_ref(),
+                               (GDestroyNotify)g_object_unref);
+}
+
+static void
+vte_reaper_init(VteReaper *reaper)
+{
+}
+
+static GObject*
+vte_reaper_constructor (GType                  type,
+                        guint                  n_construct_properties,
+                        GObjectConstructParam *construct_properties)
+{
+  if (singleton_reaper) {
+          return (GObject*)g_object_ref (singleton_reaper);
+  } else {
+          GObject *obj;
+          obj = G_OBJECT_CLASS (vte_reaper_parent_class)->constructor (type, n_construct_properties, 
construct_properties);
+          singleton_reaper = VTE_REAPER (obj);
+          return obj;
+  }
+}
+
+
+static void
+vte_reaper_finalize(GObject *reaper)
+{
+        G_OBJECT_CLASS(vte_reaper_parent_class)->finalize(reaper);
+        singleton_reaper = nullptr;
+}
+
+static void
+vte_reaper_class_init(VteReaperClass *klass)
+{
+        GObjectClass *gobject_class;
+
+        /*
+         * VteReaper::child-exited:
+         * @vtereaper: the object which received the signal
+         * @arg1: the process ID of the exited child
+         * @arg2: the status of the exited child, as returned by waitpid()
+         *
+         * Emitted when the #VteReaper object detects that a child of the
+         * current process has exited.
+         */
+        g_signal_new(g_intern_static_string("child-exited"),
+                     G_OBJECT_CLASS_TYPE(klass),
+                     G_SIGNAL_RUN_LAST,
+                     0,
+                     nullptr,
+                     nullptr,
+                     g_cclosure_marshal_generic,
+                     G_TYPE_NONE,
+                     2,
+                     G_TYPE_INT, G_TYPE_INT);
+        
+        gobject_class = G_OBJECT_CLASS(klass);
+        gobject_class->constructor = vte_reaper_constructor;
+        gobject_class->finalize = vte_reaper_finalize;
+}
+
+/*
+ * vte_reaper_ref:
+ *
+ * Finds the address of the global #VteReaper object, creating the object if
+ * necessary.
+ *
+ * Returns: a reference to the global #VteReaper object, which
+ *  must be unreffed when done with it
+ */
+VteReaper *
+vte_reaper_ref(void)
+{
+        return (VteReaper*)g_object_new(VTE_TYPE_REAPER, nullptr);
+}
+
+#ifdef MAIN
+
+#include <unistd.h>
+
+GMainContext *context;
+GMainLoop *loop;
+pid_t child;
+
+static void
+child_exited(GObject *object, int pid, int status, gpointer data)
+{
+        g_print("[parent] Child with pid %d exited with code %d, "
+                "was waiting for %d.\n", pid, status, GPOINTER_TO_INT(data));
+        if (child == pid) {
+                g_print("[parent] Quitting.\n");
+                g_main_loop_quit(loop);
+        }
+}
+
+int
+main(int argc, char **argv)
+{
+        VteReaper *reaper;
+        pid_t p, q;
+
+        _vte_debug_init();
+
+        g_type_init();
+        context = g_main_context_default();
+        loop = g_main_loop_new(context, FALSE);
+        reaper = vte_reaper_ref();
+
+        g_print("[parent] Forking1.\n");
+        p = fork();
+        switch (p) {
+                case -1:
+                        g_print("[parent] Fork1 failed.\n");
+                        g_assert_not_reached();
+                        break;
+                case 0:
+                        g_print("[child1]  Going to sleep.\n");
+                        sleep(10);
+                        g_print("[child1]  Quitting.\n");
+                        _exit(30);
+                        break;
+                default:
+                        g_print("[parent] Starting to wait for %d.\n", p);
+                        vte_reaper_add_child(p);
+                        child = p;
+                        g_signal_connect(reaper,
+                                         "child-exited",
+                                         G_CALLBACK(child_exited),
+                                         GINT_TO_POINTER(child));
+                        break;
+        }
+
+        g_print("[parent] Forking2.\n");
+        q = fork();
+        switch (q) {
+                case -1:
+                        g_print("[parent] Fork2 failed.\n");
+                        g_assert_not_reached();
+                        break;
+                case 0:
+                        g_print("[child2]  Going to sleep.\n");
+                        sleep(5);
+                        g_print("[child2]  Quitting.\n");
+                        _exit(5);
+                        break;
+                default:
+                        g_print("[parent] Not waiting for %d.\n", q);
+                        break;
+        }
+
+
+        g_main_loop_run(loop);
+
+        g_object_unref(reaper);
+
+        return 0;
+}
+#endif
diff --git a/src/reaper.hh b/src/reaper.hh
new file mode 100644
index 0000000..3426d7c
--- /dev/null
+++ b/src/reaper.hh
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2002 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#pragma once
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib-object.h>
+
+typedef struct _VteReaper VteReaper;
+
+VteReaper *vte_reaper_ref(void);
+
+void vte_reaper_add_child(GPid pid);
diff --git a/src/vte.cc b/src/vte.cc
index 70733a6..e241907 100644
--- a/src/vte.cc
+++ b/src/vte.cc
@@ -40,6 +40,7 @@
 #include "debug.h"
 #include "vteconv.h"
 #include "vtedraw.hh"
+#include "reaper.hh"
 #include "ring.h"
 #include "caps.h"
 
@@ -3104,15 +3105,12 @@ not_inserted:
 }
 
 static void
-child_watch_cb(GPid pid,
-               int status,
-               VteTerminalPrivate *that)
+reaper_child_exited_cb(VteReaper *reaper,
+                       int ipid,
+                       int status,
+                       VteTerminalPrivate *that)
 {
-       if (that == NULL) {
-               /* The child outlived us. Do nothing, we're happy that Glib
-                * read its exit data and hence it's no longer there as zombie. */
-               return;
-       }
+        GPid pid = GPid(ipid);
 
         auto terminal = that->m_terminal;
         /* keep the VteTerminalPrivate in a death grip */
@@ -3146,7 +3144,15 @@ VteTerminalPrivate::child_watch_done(GPid pid,
 #endif
         }
 
-        m_child_watch_source = 0;
+        /* Disconnect from the reaper */
+        if (m_reaper) {
+                g_signal_handlers_disconnect_by_func(m_reaper,
+                                                     (gpointer)reaper_child_exited_cb,
+                                                     this);
+                g_object_unref(m_reaper);
+                m_reaper = nullptr;
+        }
+
         m_pty_pid = -1;
 
         /* Close out the PTY. */
@@ -3304,14 +3310,22 @@ VteTerminalPrivate::watch_child (GPid child_pid)
         m_pty_pid = child_pid;
 
         /* Catch a child-exited signal from the child pid. */
-        if (m_child_watch_source != 0) {
-                g_source_remove (m_child_watch_source);
+        auto reaper = vte_reaper_ref();
+        vte_reaper_add_child(child_pid);
+        if (reaper != m_reaper) {
+                if (m_reaper) {
+                        g_signal_handlers_disconnect_by_func(m_reaper,
+                                                             (gpointer)reaper_child_exited_cb,
+                                                             this);
+                        g_object_unref(m_reaper);
+                }
+                m_reaper = reaper; /* adopts */
+                g_signal_connect(m_reaper, "child-exited",
+                                 G_CALLBACK(reaper_child_exited_cb),
+                                 this);
+        } else {
+                g_object_unref(reaper);
         }
-        m_child_watch_source =
-                g_child_watch_add_full(G_PRIORITY_HIGH,
-                                       child_pid,
-                                       (GChildWatchFunc)child_watch_cb,
-                                       this, nullptr);
 
         /* FIXMEchpe: call set_size() here? */
 
@@ -8239,14 +8253,12 @@ VteTerminalPrivate::~VteTerminalPrivate()
                m_outgoing_conv = VTE_INVALID_CONV;
        }
 
-       /* Start listening for child-exited signals and ignore them, so that no zombie child is left behind. 
*/
-        if (m_child_watch_source != 0) {
-                g_source_remove (m_child_watch_source);
-                m_child_watch_source = 0;
-                g_child_watch_add_full(G_PRIORITY_HIGH,
-                                       m_pty_pid,
-                                       (GChildWatchFunc)child_watch_cb,
-                                       NULL, NULL);
+        /* Stop listening for child-exited signals. */
+        if (m_reaper) {
+                g_signal_handlers_disconnect_by_func(m_reaper,
+                                                     (gpointer)reaper_child_exited_cb,
+                                                     this);
+                g_object_unref(m_reaper);
         }
 
        /* Stop processing input. */
diff --git a/src/vtegtk.cc b/src/vtegtk.cc
index 94f07ac..f763b46 100644
--- a/src/vtegtk.cc
+++ b/src/vtegtk.cc
@@ -49,6 +49,7 @@
 
 #include "debug.h"
 #include "marshal.h"
+#include "reaper.hh"
 #include "vtedefines.hh"
 #include "vteinternal.hh"
 #include "vteaccess.h"
@@ -2326,18 +2327,20 @@ spawn_async_cb (GObject *source,
         VteTerminal* terminal = (VteTerminal*)g_weak_ref_get(&data->wref);
 
         /* Automatically watch the child */
-        if (terminal) {
+        if (terminal != nullptr) {
                 if (pid != -1)
                         vte_terminal_watch_child(terminal, pid);
                 else
                         vte_terminal_set_pty(terminal, nullptr);
+        } else {
+                if (pid != -1) {
+                        vte_reaper_add_child(pid);
+                }
         }
 
         if (data->callback)
                 data->callback(terminal, pid, error, data->user_data);
 
-        g_clear_error(&error);
-
         if (terminal == nullptr) {
                 /* If the terminal was destroyed, we need to abort the child process, if any */
                 if (pid != -1) {
@@ -2352,6 +2355,9 @@ spawn_async_cb (GObject *source,
                 }
         }
 
+        if (error)
+                g_error_free(error);
+
         spawn_async_callback_data_free(data);
 }
 
diff --git a/src/vteinternal.hh b/src/vteinternal.hh
index 947eed4..ff5b93c 100644
--- a/src/vteinternal.hh
+++ b/src/vteinternal.hh
@@ -22,6 +22,7 @@
 
 #include "vtedefines.hh"
 #include "vtetypes.hh"
+#include "reaper.hh"
 #include "ring.h"
 #include "vteconv.h"
 #include "buffer.h"
@@ -286,7 +287,7 @@ public:
         guint m_pty_output_source;
         gboolean m_pty_input_active;
         GPid m_pty_pid;                        /* pid of child process */
-        guint m_child_watch_source;
+        VteReaper *m_reaper;
 
        /* Input data queues. */
         const char *m_encoding;            /* the pty's encoding */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]