[glib/tls: 6/10] GCancellable: add g_cancellable_create_source()



commit 3bd7305d4bcee58d7188d4a80037670d185c9b84
Author: Dan Winship <danw gnome org>
Date:   Sat Nov 6 15:49:55 2010 -0400

    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.

 docs/reference/gio/gio-sections.txt |    2 +
 gio/gasynchelper.c                  |   47 +++-----------
 gio/gcancellable.c                  |  116 +++++++++++++++++++++++++++++++++++
 gio/gcancellable.h                  |    2 +
 gio/gio.symbols                     |    1 +
 gio/giotypes.h                      |   15 +++++
 6 files changed, 146 insertions(+), 37 deletions(-)
---
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 1daec67..46544fe 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 57590a1..fb3d266 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 3a99532..65e15bc 100644
--- a/gio/gcancellable.c
+++ b/gio/gcancellable.c
@@ -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;
+}
diff --git a/gio/gcancellable.h b/gio/gcancellable.h
index dc73ccd..d8fa633 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 90ea290..360a9ad 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -191,6 +191,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 ffd38ef..829d285 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__ */



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