From 34680b75275e331b9efa122c7d37ae8e6222c48d Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Thu, 8 Jan 2015 21:49:34 -0500 Subject: [PATCH] GContextSpecificGroup: support for complex signals Add support for non-VOID__VOID signals to GContextSpecificGroup. We keep the VOID__VOID case as a special optimised case by usual the usual bit-stealing tricks. --- gio/gappinfo.c | 2 +- gio/gcontextspecificgroup.c | 122 +++++++++++++++++++++++++++++++++--- gio/gcontextspecificgroup.h | 4 +- gio/gunixmounts.c | 8 +-- 4 files changed, 120 insertions(+), 16 deletions(-) diff --git a/gio/gappinfo.c b/gio/gappinfo.c index fe2fa3d14..4d9724376 100644 --- a/gio/gappinfo.c +++ b/gio/gappinfo.c @@ -1131,5 +1131,5 @@ g_app_info_monitor_get (void) void g_app_info_monitor_fire (void) { - g_context_specific_group_emit (&g_app_info_monitor_group, g_app_info_monitor_changed_signal); + g_context_specific_group_emit (&g_app_info_monitor_group, g_app_info_monitor_changed_signal, 0); } diff --git a/gio/gcontextspecificgroup.c b/gio/gcontextspecificgroup.c index ff44578b9..a96d049b4 100644 --- a/gio/gcontextspecificgroup.c +++ b/gio/gcontextspecificgroup.c @@ -22,8 +22,97 @@ #include "gcontextspecificgroup.h" #include +#include #include "glib-private.h" +#include + +typedef struct +{ + gint ref_count; + guint signal_id; + GQuark detail; + guint n_params; + GValue params[1]; +} GContextSpecificEmission; + +static GContextSpecificEmission * +g_context_specific_emission_newv (guint signal_id, + GQuark detail, + va_list ap) +{ + GContextSpecificEmission *emission; + GSignalQuery query; + guint i; + + g_signal_query (signal_id, &query); + + if (query.n_params == 0 && detail == 0) + return GUINT_TO_POINTER (signal_id * 2 + 1); + + emission = g_malloc (G_STRUCT_OFFSET (GContextSpecificEmission, params[query.n_params])); + emission->ref_count = 1; + emission->signal_id = signal_id; + emission->detail = detail; + emission->n_params = query.n_params; + + for (i = 0; i < query.n_params; i++) + { + gchar *error; + + G_VALUE_COLLECT_INIT (&emission->params[i], query.param_types[i], ap, 0, &error); + if (error) + g_error ("g_contect_specific_emission_newv: %s", error); + } + + return emission; +} + +static GContextSpecificEmission * +g_context_specific_emission_ref (GContextSpecificEmission *emission) +{ + if (~GPOINTER_TO_UINT (emission) & 1) + g_atomic_int_inc (&emission->ref_count); + + return emission; +} + +static void +g_context_specific_emission_unref (GContextSpecificEmission *emission) +{ + if (~GPOINTER_TO_UINT (emission) & 1) + { + if (g_atomic_int_dec_and_test (&emission->ref_count)) + { + guint i; + + for (i = 0; i < emission->n_params; i++) + g_value_reset (&emission->params[i]); + + g_free (emission); + } + } +} + +static void +g_context_specific_emission_emit_on_instance (GContextSpecificEmission *emission, + gpointer instance) +{ + if (~GPOINTER_TO_UINT (emission) & 1) + { + GValue *instance_and_params; + + instance_and_params = g_newa (GValue, 1 + emission->n_params); + instance_and_params[0].g_type = G_TYPE_FROM_INSTANCE (instance); + instance_and_params[0].data[0].v_pointer = instance; + memcpy (instance_and_params + 1, emission->params, sizeof (GValue) * emission->n_params); + + g_signal_emitv (instance_and_params, emission->signal_id, emission->detail, NULL); + } + else + g_signal_emit (instance, GPOINTER_TO_UINT (emission) / 2, 0); +} + typedef struct { GSource source; @@ -39,19 +128,20 @@ g_context_specific_source_dispatch (GSource *source, gpointer user_data) { GContextSpecificSource *css = (GContextSpecificSource *) source; - guint signal_id; + GContextSpecificEmission *emission; g_mutex_lock (&css->lock); g_assert (!g_queue_is_empty (&css->pending)); - signal_id = GPOINTER_TO_UINT (g_queue_pop_head (&css->pending)); + emission = g_queue_pop_head (&css->pending); if (g_queue_is_empty (&css->pending)) g_source_set_ready_time (source, -1); g_mutex_unlock (&css->lock); - g_signal_emit (css->instance, signal_id, 0); + g_context_specific_emission_emit_on_instance (emission, css->instance); + g_context_specific_emission_unref (emission); return TRUE; } @@ -61,8 +151,11 @@ g_context_specific_source_finalize (GSource *source) { GContextSpecificSource *css = (GContextSpecificSource *) source; - g_mutex_clear (&css->lock); + while (!g_queue_is_empty (&css->pending)) + g_context_specific_emission_unref (g_queue_pop_head (&css->pending)); + g_queue_clear (&css->pending); + g_mutex_clear (&css->lock); } static GContextSpecificSource * @@ -182,17 +275,23 @@ g_context_specific_group_remove (GContextSpecificGroup *group, void g_context_specific_group_emit (GContextSpecificGroup *group, - guint signal_id) + guint signal_id, + GQuark detail, + ...) { + GContextSpecificEmission *emission; + va_list ap; + + va_start (ap, detail); + emission = g_context_specific_emission_newv (signal_id, detail, ap); + va_end (ap); + g_mutex_lock (&group->lock); if (group->table) { GHashTableIter iter; gpointer value; - gpointer ptr; - - ptr = GUINT_TO_POINTER (signal_id); g_hash_table_iter_init (&iter, group->table); while (g_hash_table_iter_next (&iter, NULL, &value)) @@ -201,8 +300,9 @@ g_context_specific_group_emit (GContextSpecificGroup *group, g_mutex_lock (&css->lock); - g_queue_remove (&css->pending, ptr); - g_queue_push_tail (&css->pending, ptr); + /* this will find repeat instances of void handlers */ + if (!g_queue_find (&css->pending, emission)) + g_queue_push_tail (&css->pending, g_context_specific_emission_ref (emission)); g_source_set_ready_time ((GSource *) css, 0); @@ -211,4 +311,6 @@ g_context_specific_group_emit (GContextSpecificGroup *group, } g_mutex_unlock (&group->lock); + + g_context_specific_emission_unref (emission); } diff --git a/gio/gcontextspecificgroup.h b/gio/gcontextspecificgroup.h index bd2ea5dbf..5f538d482 100644 --- a/gio/gcontextspecificgroup.h +++ b/gio/gcontextspecificgroup.h @@ -42,6 +42,8 @@ g_context_specific_group_remove (GContextSpecificGroup *group, void g_context_specific_group_emit (GContextSpecificGroup *group, - guint signal_id); + guint signal_id, + GQuark detail, + ...); #endif /* __G_CONTEXT_SPECIFIC_GROUP_H__ */ diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c index 518bc4496..bef3f50e0 100644 --- a/gio/gunixmounts.c +++ b/gio/gunixmounts.c @@ -1303,7 +1303,7 @@ fstab_file_changed (GFileMonitor *monitor, event_type != G_FILE_MONITOR_EVENT_DELETED) return; - g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]); + g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED], 0); } static void @@ -1318,7 +1318,7 @@ mtab_file_changed (GFileMonitor *monitor, event_type != G_FILE_MONITOR_EVENT_DELETED) return; - g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]); + g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED], 0); } static gboolean @@ -1327,7 +1327,7 @@ proc_mounts_changed (GIOChannel *channel, gpointer user_data) { if (cond & G_IO_ERR) - g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED]); + g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTS_CHANGED], 0); return TRUE; } @@ -1360,7 +1360,7 @@ mount_change_poller (gpointer user_data) if (has_changed) { mount_poller_time = (guint64) g_get_monotonic_time (); - g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED]); + g_context_specific_group_emit (&mount_monitor_group, signals[MOUNTPOINTS_CHANGED], 0); } return TRUE;