[gimp/gimp-2-10] app: add gimp_async_add_callback_for_object()



commit 34cb63a04844e88dc2c4b78b3e5cb2ef628d5032
Author: Ell <ell_se yahoo com>
Date:   Fri Nov 30 03:28:18 2018 -0500

    app: add gimp_async_add_callback_for_object()
    
    ... which is similar to gimp_async_add_callback(), taking an
    additional GObject argument.  The object is kept alive for the
    duration of the callback, and the callback is automatically removed
    when the object is destroyed (if it hasn't been already called).
    
    This is analogous to g_signal_connect_object(), compared to
    g_signal_connect().
    
    (cherry picked from commit 49fd2847ac6ed02963a3feed31f3e704ee217d55)

 app/core/gimpasync.c | 130 +++++++++++++++++++++++++++++++++++++++++++++------
 app/core/gimpasync.h |  46 +++++++++---------
 2 files changed, 142 insertions(+), 34 deletions(-)
---
diff --git a/app/core/gimpasync.c b/app/core/gimpasync.c
index 7a57f0f674..322d57d317 100644
--- a/app/core/gimpasync.c
+++ b/app/core/gimpasync.c
@@ -73,8 +73,10 @@ typedef struct _GimpAsyncCallbackInfo GimpAsyncCallbackInfo;
 
 struct _GimpAsyncCallbackInfo
 {
-  GimpAsyncCallback callback;
-  gpointer          data;
+  GimpAsync         *async;
+  GimpAsyncCallback  callback;
+  gpointer           data;
+  gpointer           gobject;
 };
 
 struct _GimpAsyncPrivate
@@ -102,23 +104,26 @@ struct _GimpAsyncPrivate
 
 /*  local function prototypes  */
 
-static void       gimp_async_waitable_iface_init   (GimpWaitableInterface *iface);
+static void       gimp_async_waitable_iface_init   (GimpWaitableInterface   *iface);
 
 static void       gimp_async_cancelable_iface_init (GimpCancelableInterface *iface);
 
-static void       gimp_async_finalize              (GObject               *object);
+static void       gimp_async_finalize              (GObject                 *object);
 
-static void       gimp_async_wait                  (GimpWaitable          *waitable);
-static gboolean   gimp_async_try_wait              (GimpWaitable          *waitable);
-static gboolean   gimp_async_wait_until            (GimpWaitable          *waitable,
-                                                    gint64                 end_time);
+static void       gimp_async_wait                  (GimpWaitable            *waitable);
+static gboolean   gimp_async_try_wait              (GimpWaitable            *waitable);
+static gboolean   gimp_async_wait_until            (GimpWaitable            *waitable,
+                                                    gint64                   end_time);
 
-static void       gimp_async_cancel                (GimpCancelable        *cancelable);
+static void       gimp_async_cancel                (GimpCancelable          *cancelable);
 
-static gboolean   gimp_async_idle                  (GimpAsync             *async);
+static gboolean   gimp_async_idle                  (GimpAsync               *async);
 
-static void       gimp_async_stop                  (GimpAsync             *async);
-static void       gimp_async_run_callbacks         (GimpAsync             *async);
+static void       gimp_async_callback_weak_notify  (GimpAsyncCallbackInfo   *callback_info,
+                                                    GObject                 *gobject);
+
+static void       gimp_async_stop                  (GimpAsync               *async);
+static void       gimp_async_run_callbacks         (GimpAsync               *async);
 
 
 G_DEFINE_TYPE_WITH_CODE (GimpAsync, gimp_async, G_TYPE_OBJECT,
@@ -330,6 +335,33 @@ gimp_async_idle (GimpAsync *async)
   return G_SOURCE_REMOVE;
 }
 
+static void
+gimp_async_callback_weak_notify (GimpAsyncCallbackInfo *callback_info,
+                                 GObject               *gobject)
+{
+  GimpAsync *async       = callback_info->async;
+  gboolean   unref_async = FALSE;
+
+  g_mutex_lock (&async->priv->mutex);
+
+  g_queue_remove (&async->priv->callbacks, callback_info);
+
+  g_slice_free (GimpAsyncCallbackInfo, callback_info);
+
+  if (g_queue_is_empty (&async->priv->callbacks) && async->priv->idle_id)
+    {
+      g_source_remove (async->priv->idle_id);
+      async->priv->idle_id = 0;
+
+      unref_async = TRUE;
+    }
+
+  g_mutex_unlock (&async->priv->mutex);
+
+  if (unref_async)
+    g_object_unref (async);
+}
+
 static void
 gimp_async_stop (GimpAsync *async)
 {
@@ -377,8 +409,20 @@ gimp_async_run_callbacks (GimpAsync *async)
 
   while ((callback_info = g_queue_pop_head (&async->priv->callbacks)))
     {
+      if (callback_info->gobject)
+        {
+          g_object_ref (callback_info->gobject);
+
+          g_object_weak_unref (callback_info->gobject,
+                               (GWeakNotify) gimp_async_callback_weak_notify,
+                               callback_info);
+        }
+
       callback_info->callback (async, callback_info->data);
 
+      if (callback_info->gobject)
+        g_object_unref (callback_info->gobject);
+
       g_slice_free (GimpAsyncCallbackInfo, callback_info);
     }
 
@@ -447,12 +491,64 @@ gimp_async_add_callback (GimpAsync         *async,
       return;
     }
 
-  callback_info           = g_slice_new (GimpAsyncCallbackInfo);
+  callback_info           = g_slice_new0 (GimpAsyncCallbackInfo);
+  callback_info->async    = async;
+  callback_info->callback = callback;
+  callback_info->data     = data;
+
+  g_queue_push_tail (&async->priv->callbacks, callback_info);
+
+  g_mutex_unlock (&async->priv->mutex);
+}
+
+/* same as 'gimp_async_add_callback()', however, takes an additional 'gobject'
+ * argument, which should be a GObject.
+ *
+ * 'gobject' is guaranteed to remain alive for the duration of the callback.
+ *
+ * When 'gobject' is destroyed, the callback is automatically removed.
+ */
+void
+gimp_async_add_callback_for_object (GimpAsync         *async,
+                                    GimpAsyncCallback  callback,
+                                    gpointer           data,
+                                    gpointer           gobject)
+{
+  GimpAsyncCallbackInfo *callback_info;
+
+  g_return_if_fail (GIMP_IS_ASYNC (async));
+  g_return_if_fail (callback != NULL);
+  g_return_if_fail (G_IS_OBJECT (gobject));
+
+  g_mutex_lock (&async->priv->mutex);
+
+  if (async->priv->stopped && g_queue_is_empty (&async->priv->callbacks))
+    {
+      async->priv->synced = TRUE;
+
+      g_mutex_unlock (&async->priv->mutex);
+
+      g_object_ref (gobject);
+
+      callback (async, data);
+
+      g_object_unref (gobject);
+
+      return;
+    }
+
+  callback_info           = g_slice_new0 (GimpAsyncCallbackInfo);
+  callback_info->async    = async;
   callback_info->callback = callback;
   callback_info->data     = data;
+  callback_info->gobject  = gobject;
 
   g_queue_push_tail (&async->priv->callbacks, callback_info);
 
+  g_object_weak_ref (gobject,
+                     (GWeakNotify) gimp_async_callback_weak_notify,
+                     callback_info);
+
   g_mutex_unlock (&async->priv->mutex);
 }
 
@@ -485,6 +581,14 @@ gimp_async_remove_callback (GimpAsync         *async,
       if (callback_info->callback == callback &&
           callback_info->data     == data)
         {
+          if (callback_info->gobject)
+            {
+              g_object_weak_unref (
+                callback_info->gobject,
+                (GWeakNotify) gimp_async_callback_weak_notify,
+                callback_info);
+            }
+
           g_queue_delete_link (&async->priv->callbacks, iter);
 
           g_slice_free (GimpAsyncCallbackInfo, callback_info);
diff --git a/app/core/gimpasync.h b/app/core/gimpasync.h
index 5a7510fb90..0c2a671c94 100644
--- a/app/core/gimpasync.h
+++ b/app/core/gimpasync.h
@@ -53,39 +53,43 @@ struct _GimpAsyncClass
 };
 
 
-GType       gimp_async_get_type        (void) G_GNUC_CONST;
+GType       gimp_async_get_type                (void) G_GNUC_CONST;
 
-GimpAsync * gimp_async_new             (void);
+GimpAsync * gimp_async_new                     (void);
 
-gboolean    gimp_async_is_synced       (GimpAsync         *async);
+gboolean    gimp_async_is_synced               (GimpAsync         *async);
 
-void        gimp_async_add_callback    (GimpAsync         *async,
-                                        GimpAsyncCallback  callback,
-                                        gpointer           data);
-void        gimp_async_remove_callback (GimpAsync         *async,
-                                        GimpAsyncCallback  callback,
-                                        gpointer           data);
+void        gimp_async_add_callback            (GimpAsync         *async,
+                                                GimpAsyncCallback  callback,
+                                                gpointer           data);
+void        gimp_async_add_callback_for_object (GimpAsync         *async,
+                                                GimpAsyncCallback  callback,
+                                                gpointer           data,
+                                                gpointer           gobject);
+void        gimp_async_remove_callback         (GimpAsync         *async,
+                                                GimpAsyncCallback  callback,
+                                                gpointer           data);
 
-gboolean    gimp_async_is_stopped      (GimpAsync         *async);
+gboolean    gimp_async_is_stopped              (GimpAsync         *async);
 
-void        gimp_async_finish          (GimpAsync         *async,
-                                        gpointer           result);
-void        gimp_async_finish_full     (GimpAsync         *async,
-                                        gpointer           result,
-                                        GDestroyNotify     result_destroy_func);
-gboolean    gimp_async_is_finished     (GimpAsync         *async);
-gpointer    gimp_async_get_result      (GimpAsync         *async);
+void        gimp_async_finish                  (GimpAsync         *async,
+                                                gpointer           result);
+void        gimp_async_finish_full             (GimpAsync         *async,
+                                                gpointer           result,
+                                                GDestroyNotify     result_destroy_func);
+gboolean    gimp_async_is_finished             (GimpAsync         *async);
+gpointer    gimp_async_get_result              (GimpAsync         *async);
 
-void        gimp_async_abort           (GimpAsync         *async);
+void        gimp_async_abort                   (GimpAsync         *async);
 
-gboolean    gimp_async_is_canceled     (GimpAsync         *async);
+gboolean    gimp_async_is_canceled             (GimpAsync         *async);
 
-void        gimp_async_cancel_and_wait (GimpAsync         *async);
+void        gimp_async_cancel_and_wait         (GimpAsync         *async);
 
 
 /*  stats  */
 
-gint        gimp_async_get_n_running   (void);
+gint        gimp_async_get_n_running           (void);
 
 
 #endif /* __GIMP_ASYNC_H__ */


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