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().
This commit is contained in:
Ell 2018-11-30 03:28:18 -05:00
parent a779dd3849
commit 49fd2847ac
2 changed files with 142 additions and 34 deletions

View File

@ -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,7 +491,8 @@ 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;
@ -456,6 +501,57 @@ gimp_async_add_callback (GimpAsync *async,
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);
}
/* removes all callbacks previously registered through
* 'gimp_async_add_callback()', matching 'callback' and 'data', which hasn't
* been called yet.
@ -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);

View File

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