GCancellable: add g_cancellable_create_source()

g_cancellable_create_source() returns a GSource that triggers when its
corresponding GCancellable is cancelled. This can be used with
g_source_add_child_source() to add cancellability to a source.

Port gasynchelper's FDSource to use this rather than doing its own
cancellable handling, and also fix up its callback argument order to
be more normal.

https://bugzilla.gnome.org/show_bug.cgi?id=634239
This commit is contained in:
Dan Winship 2010-11-06 15:49:55 -04:00
parent d15cdbefec
commit 6181c7de36
6 changed files with 146 additions and 37 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -32,6 +32,7 @@
#include <io.h>
#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 (&params, G_TYPE_CANCELLABLE);
g_value_set_object (&params, cancellable);
g_closure_invoke (closure, &result_value, 1, &params, NULL);
result = g_value_get_boolean (&result_value);
g_value_unset (&result_value);
g_value_unset (&params);
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;
}

View File

@ -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);

View File

@ -193,6 +193,7 @@ g_cancellable_reset
g_cancellable_cancel
g_cancellable_connect
g_cancellable_disconnect
g_cancellable_source_new
#endif
#endif

View File

@ -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__ */