From: Matthias Clasen Date: Fri, 2 Jun 2006 02:36:30 +0000 (+0000) Subject: Add two new functions, g_source_set_funcs and g_source_is_destroyed, that X-Git-Url: http://git.openbox.org/?a=commitdiff_plain;h=bef319723baaeb219d94b37727de7e499cb71fdf;p=dana%2Fcg-glib.git Add two new functions, g_source_set_funcs and g_source_is_destroyed, that 2006-06-01 Matthias Clasen * glib/glib.symbols: * glib/gmain.h: * glib/gmain.c: Add two new functions, g_source_set_funcs and g_source_is_destroyed, that will be necessary to solve thread-safety issues with idles in GTK+. (#321886, Chris Wilson) --- diff --git a/ChangeLog b/ChangeLog index 95a25090..1580606f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2006-06-01 Matthias Clasen + + * glib/glib.symbols: + * glib/gmain.h: + * glib/gmain.c: Add two new functions, + g_source_set_funcs and g_source_is_destroyed, + that will be necessary to solve thread-safety + issues with idles in GTK+. (#321886, Chris Wilson) + 2006-06-01 Matthias Clasen * glib/giochannel.c (g_io_channel_write_chars): Avoid diff --git a/ChangeLog.pre-2-12 b/ChangeLog.pre-2-12 index 95a25090..1580606f 100644 --- a/ChangeLog.pre-2-12 +++ b/ChangeLog.pre-2-12 @@ -1,3 +1,12 @@ +2006-06-01 Matthias Clasen + + * glib/glib.symbols: + * glib/gmain.h: + * glib/gmain.c: Add two new functions, + g_source_set_funcs and g_source_is_destroyed, + that will be necessary to solve thread-safety + issues with idles in GTK+. (#321886, Chris Wilson) + 2006-06-01 Matthias Clasen * glib/giochannel.c (g_io_channel_write_chars): Avoid diff --git a/glib/glib.symbols b/glib/glib.symbols index 4c7b7c1b..0058353a 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -583,6 +583,7 @@ g_main_context_unref g_main_context_wait g_main_context_wakeup g_main_depth +g_main_current_source g_main_loop_get_context g_main_loop_is_running g_main_loop_new @@ -607,6 +608,8 @@ g_source_remove_poll g_source_set_callback g_source_set_callback_indirect g_source_set_can_recurse +g_source_set_funcs +g_source_is_destroyed g_source_set_priority g_source_unref g_idle_add diff --git a/glib/gmain.c b/glib/gmain.c index 8a0cbb97..de9cc222 100644 --- a/glib/gmain.c +++ b/glib/gmain.c @@ -102,6 +102,14 @@ struct _GMainWaiter }; #endif +typedef struct _GMainDispatch GMainDispatch; + +struct _GMainDispatch +{ + gint depth; + GSList *source; +}; + struct _GMainContext { #ifdef G_THREADS_ENABLED @@ -1227,6 +1235,29 @@ g_source_set_callback (GSource *source, g_source_set_callback_indirect (source, new_callback, &g_source_callback_funcs); } + +/** + * g_source_set_funcs: + * @source: a #GSource + * @funcs: the new #GSourceFuncs + * + * Sets the source functions (can be used to override + * default implementations) of an unattached source. + * + * Since: 2.12 + */ +void +g_source_set_funcs (GSource *source, + GSourceFuncs *funcs) +{ + g_return_if_fail (source != NULL); + g_return_if_fail (source->context == NULL); + g_return_if_fail (source->ref_count > 0); + g_return_if_fail (funcs != NULL); + + source->source_funcs = funcs; +} + /** * g_source_set_priority: * @source: a #GSource @@ -1690,19 +1721,18 @@ g_get_current_time (GTimeVal *result) /* Running the main loop */ -static gint * -get_depth_pointer (void) +static GMainDispatch * +get_dispatch (void) { static GStaticPrivate depth_private = G_STATIC_PRIVATE_INIT; - gint *depth_pointer = g_static_private_get (&depth_private); - if (!depth_pointer) + GMainDispatch *dispatch = g_static_private_get (&depth_private); + if (!dispatch) { - depth_pointer = g_new (gint, 1); - *depth_pointer = 0; - g_static_private_set (&depth_private, depth_pointer, g_free); + dispatch = g_slice_new0 (GMainDispatch); + g_static_private_set (&depth_private, dispatch, NULL); } - return depth_pointer; + return dispatch; } /** @@ -1824,10 +1854,96 @@ get_depth_pointer (void) int g_main_depth (void) { - gint *depth = get_depth_pointer (); - return *depth; + GMainDispatch *dispatch = get_dispatch (); + return dispatch->depth; +} + +/** + * g_main_current_source: + * + * Return value: The currently firing source for this thread or NULL. + */ +GSource * +g_main_current_source (void) +{ + GMainDispatch *dispatch = get_dispatch (); + return dispatch->source ? dispatch->source->data : NULL; } +/** + * g_source_is_destroyed: + * @source: a #GSource + * + * Returns whether @source has been destroyed. + * + * This is important when you operate upon your objects + * from within idle handlers, but may have freed the object + * before the dispatch of your idle handler. + * + * + * static gboolean + * idle_callback (gpointer data) + * { + * SomeWidget *self = data; + * + * GDK_THREADS_ENTER (); + * /* do stuff with self */ + * self->idle_id = 0; + * GDK_THREADS_LEAVE (); + * + * return FALSE; + * } + * + * static void + * some_widget_do_stuff_later (SomeWidget *self) + * { + * self->idle_id = g_idle_add (idle_callback, self); + * } + * + * static void + * some_widget_finalize (GObject *object) + * { + * SomeWidget *self = SOME_WIDGET (object); + * + * if (self->idle_id) + * g_source_remove (self->idle_id); + * + * G_OBJECT_CLASS (parent_class)->finalize (object); + * } + * + * + * This will fail in a multi-threaded application if the + * widget is destroyed before the idle handler fires due + * to the use after free in the callback. A solution, to + * this particular problem, is to check to if the source + * has already been destroy within the callback. + * + * + * static gboolean + * idle_callback (gpointer data) + * { + * SomeWidget *self = data; + * + * GDK_THREADS_ENTER (); + * if (!g_source_is_destroyed (g_main_get_current_source ())) + * { + * /* do stuff with self */ + * } + * GDK_THREADS_LEAVE (); + * + * return FALSE; + * } + * + * + * Return value: %TRUE if the source has been destroyed + */ +gboolean +g_source_is_destroyed (GSource *source) +{ + return SOURCE_DESTROYED (source); +} + + /* 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() @@ -1869,7 +1985,7 @@ unblock_source (GSource *source) static void g_main_dispatch (GMainContext *context) { - gint *depth = get_depth_pointer (); + GMainDispatch *current = get_dispatch (); guint i; for (i = 0; i < context->pending_dispatches->len; i++) @@ -1912,11 +2028,13 @@ g_main_dispatch (GMainContext *context) UNLOCK_CONTEXT (context); - (*depth)++; + current->depth++; + current->source = g_slist_prepend (current->source, source); need_destroy = ! dispatch (source, callback, user_data); - (*depth)--; + current->source = g_slist_remove (current->source, source); + current->depth--; if (cb_funcs) cb_funcs->unref (cb_data); diff --git a/glib/gmain.h b/glib/gmain.h index b064e64b..035b6152 100644 --- a/glib/gmain.h +++ b/glib/gmain.h @@ -177,19 +177,21 @@ gint g_main_context_check (GMainContext *context, gint n_fds); void g_main_context_dispatch (GMainContext *context); -void g_main_context_set_poll_func (GMainContext *context, - GPollFunc func); +void g_main_context_set_poll_func (GMainContext *context, + GPollFunc func); GPollFunc g_main_context_get_poll_func (GMainContext *context); /* Low level functions for use by source implementations */ -void g_main_context_add_poll (GMainContext *context, - GPollFD *fd, - gint priority); -void g_main_context_remove_poll (GMainContext *context, - GPollFD *fd); +void g_main_context_add_poll (GMainContext *context, + GPollFD *fd, + gint priority); +void g_main_context_remove_poll (GMainContext *context, + GPollFD *fd); + +gint g_main_depth (void); +GSource *g_main_current_source (void); -int g_main_depth (void); /* GMainLoop: */ @@ -223,11 +225,14 @@ guint g_source_get_id (GSource *source); GMainContext *g_source_get_context (GSource *source); -void g_source_set_callback (GSource *source, - GSourceFunc func, - gpointer data, - GDestroyNotify notify); +void g_source_set_callback (GSource *source, + GSourceFunc func, + gpointer data, + GDestroyNotify notify); +void g_source_set_funcs (GSource *source, + GSourceFuncs *funcs); +gboolean g_source_is_destroyed (GSource *source); /* Used to implement g_source_connect_closure and internally*/ void g_source_set_callback_indirect (GSource *source,