From: Matthias Clasen Date: Sat, 14 Feb 2004 00:23:36 +0000 (+0000) Subject: Wrap waitpid() as a GSource. This is a partial implementation of the "Unix X-Git-Url: http://git.openbox.org/?a=commitdiff_plain;h=540d02ba8b6ad0b1aa0c32f20f8c4f53cb305b7d;p=dana%2Fcg-glib.git Wrap waitpid() as a GSource. This is a partial implementation of the "Unix Sat Feb 14 01:21:34 2004 Matthias Clasen * glib/gmain.h: * glib/gmain.c (g_child_watch_source_new): * glib/gmain.c (g_child_watch_add): * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a GSource. This is a partial implementation of the "Unix signal source". (#50296, Jonathan R. Blandford) * configure.in: Add the necessary configury to typedef GPid appropriately. * tests/Makefile.am: * tests/child-test.c: Test child_watch sources. --- diff --git a/ChangeLog b/ChangeLog index 7e779d59..cc1641a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Sat Feb 14 01:21:34 2004 Matthias Clasen + + * glib/gmain.h: + * glib/gmain.c (g_child_watch_source_new): + * glib/gmain.c (g_child_watch_add): + * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a + GSource. This is a partial implementation of the "Unix signal + source". (#50296, Jonathan R. Blandford) + + * configure.in: Add the necessary configury to typedef GPid + appropriately. + + * tests/Makefile.am: + * tests/child-test.c: Test child_watch sources. + Sat Feb 7 15:02:01 2004 Manish Singh * tests/type-test.c: Fix broken test for gsize formats. diff --git a/ChangeLog.pre-2-10 b/ChangeLog.pre-2-10 index 7e779d59..cc1641a9 100644 --- a/ChangeLog.pre-2-10 +++ b/ChangeLog.pre-2-10 @@ -1,3 +1,18 @@ +Sat Feb 14 01:21:34 2004 Matthias Clasen + + * glib/gmain.h: + * glib/gmain.c (g_child_watch_source_new): + * glib/gmain.c (g_child_watch_add): + * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a + GSource. This is a partial implementation of the "Unix signal + source". (#50296, Jonathan R. Blandford) + + * configure.in: Add the necessary configury to typedef GPid + appropriately. + + * tests/Makefile.am: + * tests/child-test.c: Test child_watch sources. + Sat Feb 7 15:02:01 2004 Manish Singh * tests/type-test.c: Fix broken test for gsize formats. diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 7e779d59..cc1641a9 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,18 @@ +Sat Feb 14 01:21:34 2004 Matthias Clasen + + * glib/gmain.h: + * glib/gmain.c (g_child_watch_source_new): + * glib/gmain.c (g_child_watch_add): + * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a + GSource. This is a partial implementation of the "Unix signal + source". (#50296, Jonathan R. Blandford) + + * configure.in: Add the necessary configury to typedef GPid + appropriately. + + * tests/Makefile.am: + * tests/child-test.c: Test child_watch sources. + Sat Feb 7 15:02:01 2004 Manish Singh * tests/type-test.c: Fix broken test for gsize formats. diff --git a/ChangeLog.pre-2-4 b/ChangeLog.pre-2-4 index 7e779d59..cc1641a9 100644 --- a/ChangeLog.pre-2-4 +++ b/ChangeLog.pre-2-4 @@ -1,3 +1,18 @@ +Sat Feb 14 01:21:34 2004 Matthias Clasen + + * glib/gmain.h: + * glib/gmain.c (g_child_watch_source_new): + * glib/gmain.c (g_child_watch_add): + * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a + GSource. This is a partial implementation of the "Unix signal + source". (#50296, Jonathan R. Blandford) + + * configure.in: Add the necessary configury to typedef GPid + appropriately. + + * tests/Makefile.am: + * tests/child-test.c: Test child_watch sources. + Sat Feb 7 15:02:01 2004 Manish Singh * tests/type-test.c: Fix broken test for gsize formats. diff --git a/ChangeLog.pre-2-6 b/ChangeLog.pre-2-6 index 7e779d59..cc1641a9 100644 --- a/ChangeLog.pre-2-6 +++ b/ChangeLog.pre-2-6 @@ -1,3 +1,18 @@ +Sat Feb 14 01:21:34 2004 Matthias Clasen + + * glib/gmain.h: + * glib/gmain.c (g_child_watch_source_new): + * glib/gmain.c (g_child_watch_add): + * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a + GSource. This is a partial implementation of the "Unix signal + source". (#50296, Jonathan R. Blandford) + + * configure.in: Add the necessary configury to typedef GPid + appropriately. + + * tests/Makefile.am: + * tests/child-test.c: Test child_watch sources. + Sat Feb 7 15:02:01 2004 Manish Singh * tests/type-test.c: Fix broken test for gsize formats. diff --git a/ChangeLog.pre-2-8 b/ChangeLog.pre-2-8 index 7e779d59..cc1641a9 100644 --- a/ChangeLog.pre-2-8 +++ b/ChangeLog.pre-2-8 @@ -1,3 +1,18 @@ +Sat Feb 14 01:21:34 2004 Matthias Clasen + + * glib/gmain.h: + * glib/gmain.c (g_child_watch_source_new): + * glib/gmain.c (g_child_watch_add): + * glib/gmain.c (g_child_watch_add_full): Wrap waitpid() as a + GSource. This is a partial implementation of the "Unix signal + source". (#50296, Jonathan R. Blandford) + + * configure.in: Add the necessary configury to typedef GPid + appropriately. + + * tests/Makefile.am: + * tests/child-test.c: Test child_watch sources. + Sat Feb 7 15:02:01 2004 Manish Singh * tests/type-test.c: Fix broken test for gsize formats. diff --git a/configure.in b/configure.in index c6eb3706..d6bcbaf4 100644 --- a/configure.in +++ b/configure.in @@ -131,6 +131,7 @@ case "$host" in GOBJECT_DEF=gobject.def GTHREAD_DEF=gthread.def TESTGMODULE_EXP=testgmodule.exp + glib_pid_type=HANDLE ;; *) glib_native_win32=no @@ -143,6 +144,7 @@ case "$host" in GOBJECT_DEF= GTHREAD_DEF= TESTGMODULE_EXP= + glib_pid_type=int ;; esac AC_MSG_RESULT([$glib_native_win32]) @@ -2265,6 +2267,8 @@ _______EOF #define G_MODULE_SUFFIX "$g_module_suffix" +typedef $g_pid_type GPid; + G_END_DECLS #endif /* GLIBCONFIG_H */ @@ -2536,6 +2540,7 @@ g_mutex_contents="$glib_cv_byte_contents_gmutex" g_module_suffix="$glib_gmodule_suffix" +g_pid_type="$glib_pid_type" case $host in *-*-beos*) glib_os="#define G_OS_BEOS" diff --git a/docs/reference/ChangeLog b/docs/reference/ChangeLog index 5c267429..9ef02c2e 100644 --- a/docs/reference/ChangeLog +++ b/docs/reference/ChangeLog @@ -1,3 +1,9 @@ +Sat Feb 14 01:25:23 2004 Matthias Clasen + + * glib/glib-sections.txt: Add GPid, GChildWatchFunc, + g_child_watch_source_new, g_child_watch_add, + g_child_watch_add_full. + Fri Feb 13 23:16:25 2004 Matthias Clasen * glib/tmpl/macros_misc.sgml: Fix a typo. diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 4231b1a6..3fb0e5b2 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -426,6 +426,12 @@ g_idle_add g_idle_add_full g_idle_remove_by_data + +GPid +GChildWatchFunc +g_child_watch_source_new +g_child_watch_add +g_child_watch_add_full GPollFD @@ -468,6 +474,7 @@ GLIB_SYSDEF_POLLPRI G_WIN32_MSG_HANDLE g_idle_funcs g_timeout_funcs +g_child_watch_funcs
diff --git a/glib/gmain.c b/glib/gmain.c index 603eec07..18e503b8 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -62,9 +62,14 @@ #include #endif /* G_OS_BEOS */ +#ifdef G_OS_UNIX +#include +#include +#endif /* Types */ typedef struct _GTimeoutSource GTimeoutSource; +typedef struct _GChildWatchSource GChildWatchSource; typedef struct _GPollRec GPollRec; typedef struct _GSourceCallback GSourceCallback; @@ -157,6 +162,15 @@ struct _GTimeoutSource guint interval; }; +struct _GChildWatchSource +{ + GSource source; + GPid pid; + gint child_status; + gint count; + gboolean child_exited; +}; + struct _GPollRec { gint priority; @@ -175,8 +189,6 @@ struct _GPollRec #endif #define SOURCE_DESTROYED(source) (((source)->flags & G_HOOK_FLAG_ACTIVE) == 0) -#define SOURCE_BLOCKED(source) (((source)->flags & G_HOOK_FLAG_IN_CALL) != 0 && \ - ((source)->flags & G_SOURCE_CAN_RECURSE) == 0) #define SOURCE_UNREF(source, context) \ G_STMT_START { \ @@ -213,6 +225,12 @@ static gboolean g_timeout_check (GSource *source); static gboolean g_timeout_dispatch (GSource *source, GSourceFunc callback, gpointer user_data); +static gboolean g_child_watch_prepare (GSource *source, + gint *timeout); +static gboolean g_child_watch_check (GSource *source); +static gboolean g_child_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data); static gboolean g_idle_prepare (GSource *source, gint *timeout); static gboolean g_idle_check (GSource *source); @@ -224,6 +242,18 @@ G_LOCK_DEFINE_STATIC (main_loop); static GMainContext *default_main_context; static GSList *main_contexts_without_pipe = NULL; +/* Child status monitoring code */ +enum { + CHILD_WATCH_UNINITIALIZED, + CHILD_WATCH_INITIALIZED_SINGLE, + CHILD_WATCH_INITIALIZED_THREADED +}; +static gint child_watch_init_state = CHILD_WATCH_UNINITIALIZED; +static gint child_watch_count = 0; +static gint child_watch_wake_up_pipe[2] = {0, 0}; +G_LOCK_DEFINE_STATIC (main_context_list); +static GSList *main_context_list = NULL; + #if defined(G_PLATFORM_WIN32) && defined(__GNUC__) __declspec(dllexport) #endif @@ -235,6 +265,14 @@ GSourceFuncs g_timeout_funcs = NULL }; +GSourceFuncs g_child_watch_funcs = +{ + g_child_watch_prepare, + g_child_watch_check, + g_child_watch_dispatch, + NULL +}; + #if defined(G_PLATFORM_WIN32) && defined(__GNUC__) __declspec(dllexport) #endif @@ -586,6 +624,10 @@ g_main_context_unref_and_unlock (GMainContext *context) return; } + G_LOCK (main_context_list); + main_context_list = g_slist_remove (main_context_list, context); + G_UNLOCK (main_context_list); + source = context->source_list; while (source) { @@ -649,6 +691,8 @@ g_main_context_unref (GMainContext *context) static void g_main_context_init_pipe (GMainContext *context) { + if (context->wake_up_pipe[0] != -1) + return; # ifndef G_OS_WIN32 if (pipe (context->wake_up_pipe) < 0) g_error ("Cannot create pipe main loop wake-up: %s\n", @@ -702,7 +746,10 @@ g_main_context_new () context->owner = NULL; context->waiters = NULL; #endif - + + context->wake_up_pipe[0] = -1; + context->wake_up_pipe[1] = -1; + context->ref_count = 1; context->next_id = 1; @@ -730,6 +777,10 @@ g_main_context_new () context); #endif + G_LOCK (main_context_list); + main_context_list = g_slist_append (main_context_list, context); + G_UNLOCK (main_context_list); + return context; } @@ -920,17 +971,14 @@ g_source_destroy_internal (GSource *source, old_cb_funcs->unref (old_cb_data); LOCK_CONTEXT (context); } - - if (!SOURCE_BLOCKED (source)) + + tmp_list = source->poll_fds; + while (tmp_list) { - tmp_list = source->poll_fds; - while (tmp_list) - { - g_main_context_remove_poll_unlocked (context, tmp_list->data); - tmp_list = tmp_list->next; - } + g_main_context_remove_poll_unlocked (context, tmp_list->data); + tmp_list = tmp_list->next; } - + g_source_unref_internal (source, context, TRUE); } @@ -1036,8 +1084,7 @@ g_source_add_poll (GSource *source, if (context) { - if (!SOURCE_BLOCKED (source)) - g_main_context_add_poll_unlocked (context, source->priority, fd); + g_main_context_add_poll_unlocked (context, source->priority, fd); UNLOCK_CONTEXT (context); } } @@ -1069,8 +1116,7 @@ g_source_remove_poll (GSource *source, if (context) { - if (!SOURCE_BLOCKED (source)) - g_main_context_remove_poll_unlocked (context, fd); + g_main_context_remove_poll_unlocked (context, fd); UNLOCK_CONTEXT (context); } } @@ -1224,22 +1270,16 @@ g_source_set_priority (GSource *source, if (context) { - /* Remove the source from the context's source and then - * add it back so it is sorted in the correct plcae - */ - g_source_list_remove (source, source->context); - g_source_list_add (source, source->context); - - if (!SOURCE_BLOCKED (source)) + source->next = NULL; + source->prev = NULL; + + tmp_list = source->poll_fds; + while (tmp_list) { - tmp_list = source->poll_fds; - while (tmp_list) - { - g_main_context_remove_poll_unlocked (context, tmp_list->data); - g_main_context_add_poll_unlocked (context, priority, tmp_list->data); - - tmp_list = tmp_list->next; - } + g_main_context_remove_poll_unlocked (context, tmp_list->data); + g_main_context_add_poll_unlocked (context, priority, tmp_list->data); + + tmp_list = tmp_list->next; } UNLOCK_CONTEXT (source->context); @@ -1543,11 +1583,11 @@ g_main_context_find_source_by_user_data (GMainContext *context, * g_source_remove: * @tag: the id of the source to remove. * - * Removes the source with the given id from the default main - * context. The id of a #GSource is given by g_source_get_id(), - * or will be returned by the functions g_source_attach(), - * g_idle_add(), g_idle_add_full(), g_timeout_add(), - * g_timeout_add_full(), g_io_add_watch, and g_io_add_watch_full(). + * Removes the source with the given id from the default main context. The id of + * a #GSource is given by g_source_get_id(), or will be returned by the + * functions g_source_attach(), g_idle_add(), g_idle_add_full(), + * g_timeout_add(), g_timeout_add_full(), g_child_watch_add(), + * g_child_watch_add_full(), g_io_add_watch(), and g_io_add_watch_full(). * * See also g_source_destroy(). * @@ -1666,43 +1706,6 @@ g_get_current_time (GTimeVal *result) /* Running the main loop */ -/* Temporarily remove all this source's file descriptors from the - * poll(), so that if data comes available for one of the file descriptors - * we don't continually spin in the poll() - */ -/* HOLDS: source->context's lock */ -void -block_source (GSource *source) -{ - GSList *tmp_list; - - g_return_if_fail (!SOURCE_BLOCKED (source)); - - tmp_list = source->poll_fds; - while (tmp_list) - { - g_main_context_remove_poll_unlocked (source->context, tmp_list->data); - tmp_list = tmp_list->next; - } -} - -/* HOLDS: source->context's lock */ -void -unblock_source (GSource *source) -{ - GSList *tmp_list; - - g_return_if_fail (!SOURCE_BLOCKED (source)); /* Source already unblocked */ - g_return_if_fail (!SOURCE_DESTROYED (source)); - - tmp_list = source->poll_fds; - while (tmp_list) - { - g_main_context_add_poll_unlocked (source->context, source->priority, tmp_list->data); - tmp_list = tmp_list->next; - } -} - /* HOLDS: context's lock */ static void g_main_dispatch (GMainContext *context) @@ -1738,9 +1741,6 @@ g_main_dispatch (GMainContext *context) if (cb_funcs) cb_funcs->ref (cb_data); - if ((source->flags & G_SOURCE_CAN_RECURSE) == 0) - block_source (source); - was_in_call = source->flags & G_HOOK_FLAG_IN_CALL; source->flags |= G_HOOK_FLAG_IN_CALL; @@ -1760,10 +1760,6 @@ g_main_dispatch (GMainContext *context) if (!was_in_call) source->flags &= ~G_HOOK_FLAG_IN_CALL; - if ((source->flags & G_SOURCE_CAN_RECURSE) == 0 && - !SOURCE_DESTROYED (source)) - unblock_source (source); - /* Note: this depends on the fact that we can't switch * sources from one main context to another */ @@ -2052,7 +2048,7 @@ g_main_context_prepare (GMainContext *context, SOURCE_UNREF (source, context); break; } - if (SOURCE_BLOCKED (source)) + if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE)) goto next; if (!(source->flags & G_SOURCE_READY)) @@ -2203,8 +2199,8 @@ g_main_context_check (GMainContext *context, if (!context->poll_waiting) { #ifndef G_OS_WIN32 - gchar c; - read (context->wake_up_pipe[0], &c, 1); + gchar a; + read (context->wake_up_pipe[0], &a, 1); #endif } else @@ -2240,7 +2236,7 @@ g_main_context_check (GMainContext *context, SOURCE_UNREF (source, context); break; } - if (SOURCE_BLOCKED (source)) + if ((source->flags & G_HOOK_FLAG_IN_CALL) && !(source->flags & G_SOURCE_CAN_RECURSE)) goto next; if (!(source->flags & G_SOURCE_READY)) @@ -3241,6 +3237,274 @@ g_timeout_add (guint32 interval, interval, function, data, NULL); } +/* Child watch functions */ + +static void +check_for_child_exited (GSource *source) +{ + GChildWatchSource *child_watch_source; + gint count; + + /* protect against another SIGCHLD in the middle of this call */ + count = child_watch_count; + + child_watch_source = (GChildWatchSource *) source; + + if (child_watch_source->count < count) + { + gint child_status; + + if (waitpid (child_watch_source->pid, &child_status, WNOHANG) > 0) + { + child_watch_source->child_status = child_status; + child_watch_source->child_exited = TRUE; + } + child_watch_source->count = count; + } +} + +static gboolean +g_child_watch_prepare (GSource *source, + gint *timeout) +{ + GChildWatchSource *child_watch_source; + *timeout = -1; + + child_watch_source = (GChildWatchSource *) source; + + check_for_child_exited (source); + + return child_watch_source->child_exited; +} + + +static gboolean +g_child_watch_check (GSource *source) +{ + GChildWatchSource *child_watch_source; + + child_watch_source = (GChildWatchSource *) source; + + return (child_watch_source->count < child_watch_count); +} + +static gboolean +g_child_watch_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GChildWatchSource *child_watch_source; + GChildWatchFunc child_watch_callback = (GChildWatchFunc) callback; + + child_watch_source = (GChildWatchSource *) source; + + if (!callback) + { + g_warning ("Child watch source dispatched without callback\n" + "You must call g_source_set_callback()."); + return FALSE; + } + + (child_watch_callback) (child_watch_source->pid, child_watch_source->child_status, user_data); + + /* We never keep a child watch source around as the child is gone */ + return FALSE; +} + +static void +g_child_watch_signal_handler (int signum) +{ + child_watch_count ++; + + if (child_watch_init_state == CHILD_WATCH_INITIALIZED_THREADED) + { + write (child_watch_wake_up_pipe[1], "B", 1); + } + else + { + /* We count on the signal interrupting the poll in the same thread. + */ + } +} + +static void +g_child_watch_source_init_single (void) +{ + g_assert (! g_thread_supported()); + g_assert (child_watch_init_state == CHILD_WATCH_UNINITIALIZED); + + child_watch_init_state = CHILD_WATCH_INITIALIZED_SINGLE; + + signal (SIGCHLD, g_child_watch_signal_handler); +} + +static gpointer +child_watch_helper_thread (gpointer data) +{ + GPollFD fds; + GPollFunc poll_func; + +#ifdef HAVE_POLL + poll_func = (GPollFunc)poll; +#else + poll_func = g_poll; +#endif + + fds.fd = child_watch_wake_up_pipe[0]; + fds.events = G_IO_IN; + + while (1) + { + gchar b[20]; + GSList *list; + + read (child_watch_wake_up_pipe[0], b, 20); + + /* We were woken up. Wake up all other contexts in all other threads */ + G_UNLOCK (main_context_list); + for (list = main_context_list; list; list = list->next) + { + GMainContext *context; + + context = list->data; + g_main_context_wakeup (context); + } + G_LOCK (main_context_list); + } + return NULL; +} + +static void +g_child_watch_source_init_multi_threaded (void) +{ + GError *error = NULL; + + g_assert (g_thread_supported()); + + if (pipe (child_watch_wake_up_pipe) < 0) + g_error ("Cannot create wake up pipe: %s\n", g_strerror (errno)); + fcntl (child_watch_wake_up_pipe[1], F_SETFL, O_NONBLOCK | fcntl (child_watch_wake_up_pipe[1], F_GETFL)); + + /* We create a helper thread that polls on the wakeup pipe indefinitely */ + /* FIXME: Think this through for races */ + if (g_thread_create (child_watch_helper_thread, NULL, FALSE, &error) == NULL) + g_error ("Cannot create a thread to monitor child exit status: %s\n", error->message); + child_watch_init_state = CHILD_WATCH_INITIALIZED_THREADED; + signal (SIGCHLD, g_child_watch_signal_handler); +} + +static void +g_child_watch_source_init_promote_single_to_threaded (void) +{ + g_child_watch_source_init_multi_threaded (); +} + +static void +g_child_watch_source_init (void) +{ + if (g_thread_supported()) + { + if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED) + g_child_watch_source_init_multi_threaded (); + else if (child_watch_init_state == CHILD_WATCH_INITIALIZED_SINGLE) + g_child_watch_source_init_promote_single_to_threaded (); + } + else + { + if (child_watch_init_state == CHILD_WATCH_UNINITIALIZED) + g_child_watch_source_init_single (); + } +} + +/** + * g_child_watch_source_new: + * @pid: process id of a child process to watch + * + * Creates a new child_watch source. + * + * The source will not initially be associated with any #GMainContext + * and must be added to one with g_source_attach() before it will be + * executed. + * + * Return value: the newly-created child watch source + * + * Since: 2.4 + **/ +GSource * +g_child_watch_source_new (GPid pid) +{ + GSource *source = g_source_new (&g_child_watch_funcs, sizeof (GChildWatchSource)); + GChildWatchSource *child_watch_source = (GChildWatchSource *)source; + + g_child_watch_source_init (); + + child_watch_source->pid = pid; + + return source; +} + +/** + * g_child_watch_add_full: + * @priority: the priority of the idle source. Typically this will be in the + * range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE. + * @pid: process id of a child process to watch + * @function: function to call + * @data: data to pass to @function + * @notify: function to call when the idle is removed, or %NULL + * + * Sets a function to be called when the child indicated by @pid exits, at a + * default priority, #G_PRIORITY_DEFAULT. + * + * Return value: the id of event source. + * + * Since: 2.4 + **/ +guint +g_child_watch_add_full (gint priority, + GPid pid, + GChildWatchFunc function, + gpointer data, + GDestroyNotify notify) +{ + GSource *source; + guint id; + + g_return_val_if_fail (function != NULL, 0); + + source = g_child_watch_source_new (pid); + + if (priority != G_PRIORITY_DEFAULT) + g_source_set_priority (source, priority); + + g_source_set_callback (source, (GSourceFunc) function, data, notify); + id = g_source_attach (source, NULL); + g_source_unref (source); + + return id; +} + +/** + * g_child_watch_add: + * @pid: process id of a child process to watch + * @function: function to call + * @data: data to pass to @function + * + * Sets a function to be called when the child indicated by @pid exits, at a + * default priority, #G_PRIORITY_DEFAULT. + * + * Return value: the id of event source. + * + * Since: 2.4 + **/ +guint +g_child_watch_add (GPid pid, + GChildWatchFunc function, + gpointer data) +{ + return g_child_watch_add_full (G_PRIORITY_DEFAULT, pid, function, data, NULL); +} + + /* Idle functions */ static gboolean diff --git a/glib/gmain.h b/glib/gmain.h index 5a224bcd..fb7ac3e4 100644 --- a/glib/gmain.h +++ b/glib/gmain.h @@ -32,7 +32,9 @@ typedef struct _GSourceCallbackFuncs GSourceCallbackFuncs; typedef struct _GSourceFuncs GSourceFuncs; typedef gboolean (*GSourceFunc) (gpointer data); - +typedef void (*GChildWatchFunc) (GPid pid, + gint status, + gpointer data); struct _GSource { /*< private >*/ @@ -243,8 +245,9 @@ void g_source_get_current_time (GSource *source, /* Specific source types */ -GSource *g_idle_source_new (void); -GSource *g_timeout_source_new (guint interval); +GSource *g_idle_source_new (void); +GSource *g_child_watch_source_new (GPid pid); +GSource *g_timeout_source_new (guint interval); /* Miscellaneous functions */ @@ -278,25 +281,34 @@ gboolean g_source_remove_by_user_data (gpointer user_data); gboolean g_source_remove_by_funcs_user_data (GSourceFuncs *funcs, gpointer user_data); -/* Idles and timeouts */ -guint g_timeout_add_full (gint priority, - guint interval, - GSourceFunc function, - gpointer data, - GDestroyNotify notify); -guint g_timeout_add (guint interval, - GSourceFunc function, - gpointer data); -guint g_idle_add (GSourceFunc function, - gpointer data); -guint g_idle_add_full (gint priority, - GSourceFunc function, - gpointer data, - GDestroyNotify notify); -gboolean g_idle_remove_by_data (gpointer data); +/* Idles, child watchers and timeouts */ +guint g_timeout_add_full (gint priority, + guint interval, + GSourceFunc function, + gpointer data, + GDestroyNotify notify); +guint g_timeout_add (guint interval, + GSourceFunc function, + gpointer data); +guint g_child_watch_add_full (gint priority, + GPid pid, + GChildWatchFunc function, + gpointer data, + GDestroyNotify notify); +guint g_child_watch_add (GPid pid, + GChildWatchFunc function, + gpointer data); +guint g_idle_add (GSourceFunc function, + gpointer data); +guint g_idle_add_full (gint priority, + GSourceFunc function, + gpointer data, + GDestroyNotify notify); +gboolean g_idle_remove_by_data (gpointer data); /* Hook for GClosure / GSource integration. Don't touch */ GLIB_VAR GSourceFuncs g_timeout_funcs; +GLIB_VAR GSourceFuncs g_child_watch_funcs; GLIB_VAR GSourceFuncs g_idle_funcs; G_END_DECLS diff --git a/tests/Makefile.am b/tests/Makefile.am index 2357d90f..69c4764d 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -66,6 +66,7 @@ endif test_programs = \ array-test \ $(CXX_TEST) \ + child-test \ completion-test \ date-test \ dirname-test \ @@ -115,6 +116,7 @@ thread_ldadd = $(libgthread) $(G_THREAD_LIBS) $(progs_ldadd) module_ldadd = $(libgmodule) $(G_MODULE_LIBS) $(progs_ldadd) array_test_LDADD = $(progs_ldadd) +child_test_LDADD = $(thread_ldadd) completion_test_LDADD = $(progs_ldadd) date_test_LDADD = $(progs_ldadd) dirname_test_LDADD = $(progs_ldadd) diff --git a/tests/child-test.c b/tests/child-test.c new file mode 100644 index 00000000..d910b8c4 --- /dev/null +++ b/tests/child-test.c @@ -0,0 +1,101 @@ +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * 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 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GLib Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GLib Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GLib at ftp://ftp.gtk.org/pub/gtk/. + */ + +#include "config.h" + +#include +#include +#include + +#include + +GMainLoop *main_loop; +gint alive; + +gint +get_a_child (gint ttl) +{ + GPid pid; + + pid = fork (); + if (pid < 0) + exit (1); + + if (pid > 0) + return pid; + + sleep (ttl); + _exit (0); +} + +gboolean +child_watch_callback (GPid pid, gint status, gpointer data) +{ + g_print ("child %d exited, status %d\n", pid, status); + + if (--alive == 0) + g_main_loop_quit (main_loop); + + return TRUE; +} + +static gpointer +test_thread (gpointer data) +{ + GMainLoop *new_main_loop; + GSource *source; + GPid pid; + gint ttl = GPOINTER_TO_INT (data); + + new_main_loop = g_main_loop_new (NULL, FALSE); + + pid = get_a_child (ttl); + source = g_child_watch_source_new (pid); + g_source_set_callback (source, (GSourceFunc) child_watch_callback, NULL, NULL); + g_source_attach (source, g_main_loop_get_context (new_main_loop)); + g_source_unref (source); + + g_print ("whee! created pid: %d\n", pid); + g_main_loop_run (new_main_loop); + +} + +int +main (int argc, char *argv[]) +{ + g_thread_init (NULL); + main_loop = g_main_loop_new (NULL, FALSE); + + system ("/bin/true"); + + alive = 2; + g_thread_create (test_thread, GINT_TO_POINTER (10), FALSE, NULL); + g_thread_create (test_thread, GINT_TO_POINTER (20), FALSE, NULL); + + g_main_loop_run (main_loop); + + return 0; +}