diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index cd515bf52..25532adcc 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -1107,6 +1107,8 @@ g_cancellable_set_error_if_cancelled g_cancellable_get_fd g_cancellable_make_pollfd g_cancellable_release_fd +g_cancellable_source_new +GCancellableSourceFunc g_cancellable_get_current g_cancellable_pop_current g_cancellable_push_current diff --git a/gio/gasynchelper.c b/gio/gasynchelper.c index 57590a1ec..fb3d266e3 100644 --- a/gio/gasynchelper.c +++ b/gio/gasynchelper.c @@ -43,18 +43,14 @@ typedef struct { GSource source; GPollFD pollfd; - GCancellable *cancellable; - gulong cancelled_tag; } FDSource; static gboolean fd_source_prepare (GSource *source, gint *timeout) { - FDSource *fd_source = (FDSource *)source; *timeout = -1; - - return g_cancellable_is_cancelled (fd_source->cancellable); + return FALSE; } static gboolean @@ -62,9 +58,7 @@ fd_source_check (GSource *source) { FDSource *fd_source = (FDSource *)source; - return - g_cancellable_is_cancelled (fd_source->cancellable) || - fd_source->pollfd.revents != 0; + return fd_source->pollfd.revents != 0; } static gboolean @@ -84,18 +78,6 @@ fd_source_dispatch (GSource *source, static void fd_source_finalize (GSource *source) { - FDSource *fd_source = (FDSource *)source; - - /* we don't use g_cancellable_disconnect() here, since we are holding - * the main context lock here, and the ::disconnect signal handler - * will try to grab that, and deadlock looms. - */ - if (fd_source->cancelled_tag) - g_signal_handler_disconnect (fd_source->cancellable, - fd_source->cancelled_tag); - - if (fd_source->cancellable) - g_object_unref (fd_source->cancellable); } static gboolean @@ -160,15 +142,6 @@ static GSourceFuncs fd_source_funcs = { (GSourceDummyMarshal)fd_source_closure_marshal, }; -/* Might be called on another thread */ -static void -fd_source_cancelled_cb (GCancellable *cancellable, - gpointer data) -{ - /* Wake up the mainloop in case we're waiting on async calls with FDSource */ - g_main_context_wakeup (NULL); -} - GSource * _g_fd_source_new (int fd, gushort events, @@ -180,18 +153,18 @@ _g_fd_source_new (int fd, source = g_source_new (&fd_source_funcs, sizeof (FDSource)); fd_source = (FDSource *)source; - if (cancellable) - fd_source->cancellable = g_object_ref (cancellable); - fd_source->pollfd.fd = fd; fd_source->pollfd.events = events; g_source_add_poll (source, &fd_source->pollfd); if (cancellable) - fd_source->cancelled_tag = - g_cancellable_connect (cancellable, - (GCallback)fd_source_cancelled_cb, - NULL, NULL); - + { + GSource *cancellable_source = g_cancellable_source_new (cancellable); + + g_source_set_dummy_callback (cancellable_source); + g_source_add_child_source (source, cancellable_source); + g_source_unref (cancellable_source); + } + return source; } diff --git a/gio/gcancellable.c b/gio/gcancellable.c index 3a9953299..65e15bc12 100644 --- a/gio/gcancellable.c +++ b/gio/gcancellable.c @@ -32,6 +32,7 @@ #include #endif #include "gcancellable.h" +#include "gio-marshal.h" #include "glibintl.h" @@ -770,3 +771,118 @@ g_cancellable_disconnect (GCancellable *cancellable, g_signal_handler_disconnect (cancellable, handler_id); G_UNLOCK (cancellable); } + +typedef struct { + GSource source; + + GCancellable *cancellable; + GPollFD pollfd; +} GCancellableSource; + +static gboolean +cancellable_source_prepare (GSource *source, + gint *timeout) +{ + GCancellableSource *cancellable_source = (GCancellableSource *)source; + + *timeout = -1; + return g_cancellable_is_cancelled (cancellable_source->cancellable); +} + +static gboolean +cancellable_source_check (GSource *source) +{ + GCancellableSource *cancellable_source = (GCancellableSource *)source; + + return g_cancellable_is_cancelled (cancellable_source->cancellable); +} + +static gboolean +cancellable_source_dispatch (GSource *source, + GSourceFunc callback, + gpointer user_data) +{ + GCancellableSourceFunc func = (GCancellableSourceFunc)callback; + GCancellableSource *cancellable_source = (GCancellableSource *)source; + + return (*func) (cancellable_source->cancellable, user_data); +} + +static void +cancellable_source_finalize (GSource *source) +{ + GCancellableSource *cancellable_source = (GCancellableSource *)source; + + if (cancellable_source->cancellable) + g_object_unref (cancellable_source->cancellable); +} + +static gboolean +cancellable_source_closure_callback (GCancellable *cancellable, + gpointer data) +{ + GClosure *closure = data; + + GValue params = { 0, }; + GValue result_value = { 0, }; + gboolean result; + + g_value_init (&result_value, G_TYPE_BOOLEAN); + + g_value_init (¶ms, G_TYPE_CANCELLABLE); + g_value_set_object (¶ms, cancellable); + + g_closure_invoke (closure, &result_value, 1, ¶ms, NULL); + + result = g_value_get_boolean (&result_value); + g_value_unset (&result_value); + g_value_unset (¶ms); + + return result; +} + +static GSourceFuncs cancellable_source_funcs = +{ + cancellable_source_prepare, + cancellable_source_check, + cancellable_source_dispatch, + cancellable_source_finalize, + (GSourceFunc)cancellable_source_closure_callback, + (GSourceDummyMarshal)_gio_marshal_BOOLEAN__VOID, +}; + +/** + * g_cancellable_source_new: + * @cancellable: a #GCancellable, or %NULL + * + * Creates a source that triggers if @cancellable is cancelled and + * calls its callback of type #GCancellableSourceFunc. This is + * primarily useful for attaching to another (non-cancellable) source + * with g_source_add_child_source() to add cancellability to it. + * + * For convenience, you can call this with a %NULL #GCancellable, + * in which case the source will never trigger. + * + * Return value: the new #GSource. + * + * Since: 2.28 + */ +GSource * +g_cancellable_source_new (GCancellable *cancellable) +{ + GSource *source; + GCancellableSource *cancellable_source; + + source = g_source_new (&cancellable_source_funcs, sizeof (GCancellableSource)); + g_source_set_name (source, "GCancellable"); + cancellable_source = (GCancellableSource *)source; + + if (g_cancellable_make_pollfd (cancellable, + &cancellable_source->pollfd)) + { + cancellable_source->cancellable = g_object_ref (cancellable); + g_source_add_poll (source, &cancellable_source->pollfd); + } + + return source; +} diff --git a/gio/gcancellable.h b/gio/gcancellable.h index dc73ccd32..d8fa63368 100644 --- a/gio/gcancellable.h +++ b/gio/gcancellable.h @@ -83,6 +83,8 @@ gboolean g_cancellable_make_pollfd (GCancellable *cancellable, GPollFD *pollfd); void g_cancellable_release_fd (GCancellable *cancellable); +GSource * g_cancellable_source_new (GCancellable *cancellable); + GCancellable *g_cancellable_get_current (void); void g_cancellable_push_current (GCancellable *cancellable); void g_cancellable_pop_current (GCancellable *cancellable); diff --git a/gio/gio.symbols b/gio/gio.symbols index 6e2f2322c..b417ebae2 100644 --- a/gio/gio.symbols +++ b/gio/gio.symbols @@ -193,6 +193,7 @@ g_cancellable_reset g_cancellable_cancel g_cancellable_connect g_cancellable_disconnect +g_cancellable_source_new #endif #endif diff --git a/gio/giotypes.h b/gio/giotypes.h index ffd38ef26..829d28534 100644 --- a/gio/giotypes.h +++ b/gio/giotypes.h @@ -376,6 +376,21 @@ typedef struct _GDBusPropertyInfo GDBusPropertyInfo; typedef struct _GDBusInterfaceInfo GDBusInterfaceInfo; typedef struct _GDBusNodeInfo GDBusNodeInfo; +/** + * GCancellableSourceFunc: + * @cancellable: the #GCancellable + * @user_data: data passed in by the user. + * + * This is the function type of the callback used for the #GSource + * returned by g_cancellable_source_new(). + * + * Returns: it should return %FALSE if the source should be removed. + * + * Since: 2.28 + */ +typedef gboolean (*GCancellableSourceFunc) (GCancellable *cancellable, + gpointer user_data); + G_END_DECLS #endif /* __GIO_TYPES_H__ */