[glib] GSettings: support emitting signals in threads
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] GSettings: support emitting signals in threads
- Date: Mon, 17 May 2010 11:17:21 +0000 (UTC)
commit 984258c662d3f571fcd0ea415923aec7a3746826
Author: Ryan Lortie <desrt desrt ca>
Date: Sun May 16 13:02:23 2010 +0200
GSettings: support emitting signals in threads
The thread-default context that was in effect at the time that the
GSettings was created will be used for emitting signals on that
GSettings.
gio/gdelayedsettingsbackend.c | 2 +-
gio/gsettings.c | 18 +++++
gio/gsettingsbackend.c | 139 +++++++++++++++++++++++++++++++++++++++-
gio/gsettingsbackendinternal.h | 2 +
4 files changed, 159 insertions(+), 2 deletions(-)
---
diff --git a/gio/gdelayedsettingsbackend.c b/gio/gdelayedsettingsbackend.c
index c09be7f..ed18058 100644
--- a/gio/gdelayedsettingsbackend.c
+++ b/gio/gdelayedsettingsbackend.c
@@ -367,7 +367,7 @@ g_delayed_settings_backend_new (GSettingsBackend *backend,
delayed->priv->backend = g_object_ref (backend);
delayed->priv->owner = owner;
- g_settings_backend_watch (delayed->priv->backend,
+ g_settings_backend_watch (delayed->priv->backend, NULL,
delayed_backend_changed,
delayed_backend_path_changed,
delayed_backend_keys_changed,
diff --git a/gio/gsettings.c b/gio/gsettings.c
index 5b894e6..aa465ea 100644
--- a/gio/gsettings.c
+++ b/gio/gsettings.c
@@ -146,6 +146,9 @@
struct _GSettingsPrivate
{
+ /* where the signals go... */
+ GMainContext *main_context;
+
GSettingsBackend *backend;
GSettingsSchema *schema;
gchar *schema_name;
@@ -363,6 +366,7 @@ g_settings_constructed (GObject *object)
settings->priv->backend = g_settings_backend_get_with_context (settings->priv->context);
g_settings_backend_watch (settings->priv->backend,
+ settings->priv->main_context,
settings_backend_changed,
settings_backend_path_changed,
settings_backend_keys_changed,
@@ -379,6 +383,13 @@ g_settings_init (GSettings *settings)
settings->priv = G_TYPE_INSTANCE_GET_PRIVATE (settings,
G_TYPE_SETTINGS,
GSettingsPrivate);
+
+ settings->priv->main_context = g_main_context_get_thread_default ();
+
+ if (settings->priv->main_context == NULL)
+ settings->priv->main_context = g_main_context_default ();
+
+ g_main_context_ref (settings->priv->main_context);
}
/**
@@ -406,6 +417,7 @@ g_settings_delay (GSettings *settings)
settings->priv->backend = G_SETTINGS_BACKEND (settings->priv->delayed);
g_settings_backend_watch (settings->priv->backend,
+ settings->priv->main_context,
settings_backend_changed,
settings_backend_path_changed,
settings_backend_keys_changed,
@@ -537,6 +549,7 @@ g_settings_finalize (GObject *object)
g_settings_backend_unwatch (settings->priv->backend, settings);
g_settings_backend_unsubscribe (settings->priv->backend,
settings->priv->path);
+ g_main_context_unref (settings->priv->main_context);
g_object_unref (settings->priv->backend);
g_object_unref (settings->priv->schema);
g_free (settings->priv->schema_name);
@@ -1016,6 +1029,11 @@ g_settings_get_child (GSettings *settings,
*
* Creates a new #GSettings object with a given schema.
*
+ * Signals on the newly created #GSettings object will be dispatched
+ * via the thread-default #GMainContext in effect at the time of the
+ * call to g_settings_new(). The new #GSettings will hold a reference
+ * on the context. See g_main_context_push_thread_default().
+ *
* Since: 2.26
*/
GSettings *
diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c
index b5699b4..189b640 100644
--- a/gio/gsettingsbackend.c
+++ b/gio/gsettingsbackend.c
@@ -88,6 +88,7 @@ enum
struct _GSettingsBackendWatch
{
+ GMainContext *context;
GSettingsBackendChangedFunc changed;
GSettingsBackendPathChangedFunc path_changed;
GSettingsBackendKeysChangedFunc keys_changed;
@@ -98,8 +99,22 @@ struct _GSettingsBackendWatch
GSettingsBackendWatch *next;
};
+/*< private >
+ * g_settings_backend_watch:
+ * @backend: a #GSettingsBackend
+ * @context: a #GMainContext, or %NULL
+ * ...: callbacks...
+ * @user_data: the user_data for the callbacks
+ *
+ * Registers a new watch on a #GSettingsBackend.
+ *
+ * note: %NULL @context does not mean "default main context" but rather,
+ * "it is okay to dispatch in any context". If the default main context
+ * is specifically desired then it must be given.
+ **/
void
g_settings_backend_watch (GSettingsBackend *backend,
+ GMainContext *context,
GSettingsBackendChangedFunc changed,
GSettingsBackendPathChangedFunc path_changed,
GSettingsBackendKeysChangedFunc keys_changed,
@@ -110,6 +125,11 @@ g_settings_backend_watch (GSettingsBackend *backend,
GSettingsBackendWatch *watch;
watch = g_slice_new (GSettingsBackendWatch);
+ /* don't need to ref the context here because the watch is
+ * only valid for the lifecycle of the attaching GSettings
+ * and it is already holding the context.
+ */
+ watch->context = context;
watch->changed = changed;
watch->path_changed = path_changed;
watch->keys_changed = keys_changed;
@@ -179,6 +199,115 @@ is_path (const gchar *path)
return TRUE;
}
+static GMainContext *
+g_settings_backend_get_active_context (void)
+{
+ GMainContext *context;
+ GSource *source;
+
+ if ((source = g_main_current_source ()))
+ context = g_source_get_context (source);
+
+ else
+ {
+ context = g_main_context_get_thread_default ();
+
+ if (context == NULL)
+ context = g_main_context_default ();
+ }
+
+ return context;
+}
+
+typedef struct
+{
+ void (*function) (GSettingsBackend *backend,
+ const gchar *name,
+ gpointer data1,
+ gpointer data2,
+ gpointer data3);
+ GSettingsBackend *backend;
+ gchar *name;
+
+ gpointer data1;
+ gpointer data2;
+ gpointer data3;
+
+ GDestroyNotify destroy_data1;
+} GSettingsBackendClosure;
+
+static gboolean
+g_settings_backend_invoke_closure (gpointer user_data)
+{
+ GSettingsBackendClosure *closure = user_data;
+
+ closure->function (closure->backend, closure->name,
+ closure->data1, closure->data2, closure->data3);
+
+ g_object_unref (closure->backend);
+
+ if (closure->destroy_data1)
+ closure->destroy_data1 (closure->data1);
+
+ g_free (closure->name);
+
+ /* make a donation to the magazine in this thread... */
+ g_slice_free (GSettingsBackendClosure, closure);
+
+ return FALSE;
+}
+
+static void
+g_settings_backend_dispatch_signal (GSettingsBackend *backend,
+ GMainContext *context,
+ gpointer function,
+ const gchar *name,
+ gpointer data1,
+ GDestroyNotify destroy_data1,
+ gpointer data2,
+ gpointer data3)
+{
+ GSettingsBackendClosure *closure;
+ GSource *source;
+
+ /* XXX we have a rather obnoxious race condition here.
+ *
+ * In the meantime of this call being dispatched to the other thread,
+ * the GSettings instance that is pointed to by the user_data may have
+ * been freed.
+ *
+ * There are two ways of handling this:
+ *
+ * 1) Ensure that the watch is still valid by the time it arrives in
+ * the destination thread (potentially expensive)
+ *
+ * 2) Do proper refcounting (requires changing the interface).
+ */
+ closure = g_slice_new (GSettingsBackendClosure);
+ closure->backend = g_object_ref (backend);
+ closure->function = function;
+
+ /* we could make more effort to share this between all of the
+ * dispatches but it seems that it might be an overall loss in terms
+ * of performance/memory use and is definitely not worth the effort.
+ */
+ closure->name = g_strdup (name);
+
+ /* ditto. */
+ closure->data1 = data1;
+ closure->destroy_data1 = destroy_data1;
+
+ closure->data2 = data2;
+ closure->data3 = data3;
+
+ source = g_idle_source_new ();
+ g_source_set_callback (source,
+ g_settings_backend_invoke_closure,
+ closure, NULL);
+ g_source_attach (source, context);
+ g_source_unref (source);
+}
+
/**
* g_settings_backend_changed:
* @backend: a #GSettingsBackend implementation
@@ -216,12 +345,20 @@ g_settings_backend_changed (GSettingsBackend *backend,
gpointer origin_tag)
{
GSettingsBackendWatch *watch;
+ GMainContext *context;
g_return_if_fail (G_IS_SETTINGS_BACKEND (backend));
g_return_if_fail (is_key (key));
+ context = g_settings_backend_get_active_context ();
+
for (watch = backend->priv->watches; watch; watch = watch->next)
- watch->changed (backend, key, origin_tag, watch->user_data);
+ if (watch->context == context || watch->context == NULL)
+ watch->changed (backend, key, origin_tag, watch->user_data);
+ else
+ g_settings_backend_dispatch_signal (backend, watch->context,
+ watch->changed, key, origin_tag,
+ NULL, watch->user_data, NULL);
}
/**
diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h
index b36eaa3..62867fb 100644
--- a/gio/gsettingsbackendinternal.h
+++ b/gio/gsettingsbackendinternal.h
@@ -48,6 +48,7 @@ typedef void (*GSettingsBackendPathWritableChangedFunc) (GSettin
G_GNUC_INTERNAL
void g_settings_backend_watch (GSettingsBackend *backend,
+ GMainContext *context,
GSettingsBackendChangedFunc changed,
GSettingsBackendPathChangedFunc path_changed,
GSettingsBackendKeysChangedFunc keys_changed,
@@ -97,4 +98,5 @@ void g_settings_backend_unsubscribe (GSettin
G_GNUC_INTERNAL
void g_settings_backend_subscribe (GSettingsBackend *backend,
const char *name);
+
#endif /* __G_SETTINGS_BACKEND_INTERNAL_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]