From 61219e264083184eada5c5ef2795b7c531470704 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Sun, 16 May 2010 14:17:34 -0400 Subject: [PATCH] GSettingsBackend: make signal dispatch threadsafe This commit fixes up a few race conditions in the GSettingsBackend, mostly with respect to change notifications occuring at the same time as the last reference count on a GSettings is dropped. With GDBus feeding us our incoming signals in a separate thread, this is something that could easily happen. --- gio/gdelayedsettingsbackend.c | 40 ++-- gio/gsettings.c | 43 ++-- gio/gsettingsbackend.c | 410 ++++++++++++++++++++------------- gio/gsettingsbackendinternal.h | 26 +-- 4 files changed, 301 insertions(+), 218 deletions(-) diff --git a/gio/gdelayedsettingsbackend.c b/gio/gdelayedsettingsbackend.c index ed180586a..8eeaa6ac6 100644 --- a/gio/gdelayedsettingsbackend.c +++ b/gio/gdelayedsettingsbackend.c @@ -199,11 +199,11 @@ g_delayed_settings_backend_revert (GDelayedSettingsBackend *delayed) /* change notification */ static void delayed_backend_changed (GSettingsBackend *backend, + GObject *target, const gchar *key, - gpointer origin_tag, - gpointer user_data) + gpointer origin_tag) { - GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data); + GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); if (origin_tag != delayed->priv) g_settings_backend_changed (G_SETTINGS_BACKEND (delayed), @@ -212,12 +212,12 @@ delayed_backend_changed (GSettingsBackend *backend, static void delayed_backend_keys_changed (GSettingsBackend *backend, + GObject *target, const gchar *path, const gchar * const *items, - gpointer origin_tag, - gpointer user_data) + gpointer origin_tag) { - GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data); + GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); if (origin_tag != delayed->priv) g_settings_backend_keys_changed (G_SETTINGS_BACKEND (delayed), @@ -225,12 +225,12 @@ delayed_backend_keys_changed (GSettingsBackend *backend, } static void -delayed_backend_path_changed (GSettingsBackend *backend, - const gchar *path, - gpointer origin_tag, - gpointer user_data) +delayed_backend_path_changed (GSettingsBackend *backend, + GObject *target, + const gchar *path, + gpointer origin_tag) { - GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data); + GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); if (origin_tag != delayed->priv) g_settings_backend_path_changed (G_SETTINGS_BACKEND (delayed), @@ -239,10 +239,10 @@ delayed_backend_path_changed (GSettingsBackend *backend, static void delayed_backend_writable_changed (GSettingsBackend *backend, - const gchar *key, - gpointer user_data) + GObject *target, + const gchar *key) { - GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data); + GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); if (g_tree_lookup (delayed->priv->delayed, key) && !g_settings_backend_get_writable (delayed->priv->backend, key)) @@ -285,10 +285,10 @@ check_prefix (gpointer key, static void delayed_backend_path_writable_changed (GSettingsBackend *backend, - const gchar *path, - gpointer user_data) + GObject *target, + const gchar *path) { - GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (user_data); + GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (target); gsize n_keys; n_keys = g_tree_nnodes (delayed->priv->delayed); @@ -323,7 +323,6 @@ g_delayed_settings_backend_finalize (GObject *object) { GDelayedSettingsBackend *delayed = G_DELAYED_SETTINGS_BACKEND (object); - g_settings_backend_unwatch (delayed->priv->backend, delayed); g_object_unref (delayed->priv->backend); } @@ -367,13 +366,12 @@ 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, NULL, + g_settings_backend_watch (delayed->priv->backend, G_OBJECT (delayed), NULL, delayed_backend_changed, delayed_backend_path_changed, delayed_backend_keys_changed, delayed_backend_writable_changed, - delayed_backend_path_writable_changed, - delayed); + delayed_backend_path_writable_changed); return delayed; } diff --git a/gio/gsettings.c b/gio/gsettings.c index aa465ea7d..f53eac7a3 100644 --- a/gio/gsettings.c +++ b/gio/gsettings.c @@ -224,11 +224,11 @@ g_settings_real_writable_change_event (GSettings *settings, static void settings_backend_changed (GSettingsBackend *backend, + GObject *target, const gchar *key, - gpointer origin_tag, - gpointer user_data) + gpointer origin_tag) { - GSettings *settings = G_SETTINGS (user_data); + GSettings *settings = G_SETTINGS (target); gboolean ignore_this; gint i; @@ -249,11 +249,11 @@ settings_backend_changed (GSettingsBackend *backend, static void settings_backend_path_changed (GSettingsBackend *backend, + GObject *target, const gchar *path, - gpointer origin_tag, - gpointer user_data) + gpointer origin_tag) { - GSettings *settings = G_SETTINGS (user_data); + GSettings *settings = G_SETTINGS (target); gboolean ignore_this; g_assert (settings->priv->backend == backend); @@ -265,12 +265,12 @@ settings_backend_path_changed (GSettingsBackend *backend, static void settings_backend_keys_changed (GSettingsBackend *backend, + GObject *target, const gchar *path, const gchar * const *items, - gpointer origin_tag, - gpointer user_data) + gpointer origin_tag) { - GSettings *settings = G_SETTINGS (user_data); + GSettings *settings = G_SETTINGS (target); gboolean ignore_this; gint i; @@ -309,10 +309,10 @@ settings_backend_keys_changed (GSettingsBackend *backend, static void settings_backend_writable_changed (GSettingsBackend *backend, - const gchar *key, - gpointer user_data) + GObject *target, + const gchar *key) { - GSettings *settings = G_SETTINGS (user_data); + GSettings *settings = G_SETTINGS (target); gboolean ignore_this; gint i; @@ -328,10 +328,10 @@ settings_backend_writable_changed (GSettingsBackend *backend, static void settings_backend_path_writable_changed (GSettingsBackend *backend, - const gchar *path, - gpointer user_data) + GObject *target, + const gchar *path) { - GSettings *settings = G_SETTINGS (user_data); + GSettings *settings = G_SETTINGS (target); gboolean ignore_this; g_assert (settings->priv->backend == backend); @@ -365,14 +365,13 @@ g_settings_constructed (GObject *object) } settings->priv->backend = g_settings_backend_get_with_context (settings->priv->context); - g_settings_backend_watch (settings->priv->backend, + g_settings_backend_watch (settings->priv->backend, G_OBJECT (settings), settings->priv->main_context, settings_backend_changed, settings_backend_path_changed, settings_backend_keys_changed, settings_backend_writable_changed, - settings_backend_path_writable_changed, - settings); + settings_backend_path_writable_changed); g_settings_backend_subscribe (settings->priv->backend, settings->priv->path); } @@ -412,18 +411,17 @@ g_settings_delay (GSettings *settings) settings->priv->delayed = g_delayed_settings_backend_new (settings->priv->backend, settings); - g_settings_backend_unwatch (settings->priv->backend, settings); + g_settings_backend_unwatch (settings->priv->backend, G_OBJECT (settings)); g_object_unref (settings->priv->backend); settings->priv->backend = G_SETTINGS_BACKEND (settings->priv->delayed); - g_settings_backend_watch (settings->priv->backend, + g_settings_backend_watch (settings->priv->backend, G_OBJECT (settings), settings->priv->main_context, settings_backend_changed, settings_backend_path_changed, settings_backend_keys_changed, settings_backend_writable_changed, - settings_backend_path_writable_changed, - settings); + settings_backend_path_writable_changed); } /** @@ -546,7 +544,6 @@ g_settings_finalize (GObject *object) { GSettings *settings = G_SETTINGS (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); diff --git a/gio/gsettingsbackend.c b/gio/gsettingsbackend.c index 189b640ee..5679f5724 100644 --- a/gio/gsettingsbackend.c +++ b/gio/gsettingsbackend.c @@ -37,11 +37,13 @@ G_DEFINE_ABSTRACT_TYPE (GSettingsBackend, g_settings_backend, G_TYPE_OBJECT) -typedef struct _GSettingsBackendWatch GSettingsBackendWatch; +typedef struct _GSettingsBackendClosure GSettingsBackendClosure; +typedef struct _GSettingsBackendWatch GSettingsBackendWatch; struct _GSettingsBackendPrivate { GSettingsBackendWatch *watches; + GStaticMutex lock; gchar *context; }; @@ -86,81 +88,6 @@ enum * **/ -struct _GSettingsBackendWatch -{ - GMainContext *context; - GSettingsBackendChangedFunc changed; - GSettingsBackendPathChangedFunc path_changed; - GSettingsBackendKeysChangedFunc keys_changed; - GSettingsBackendWritableChangedFunc writable_changed; - GSettingsBackendPathWritableChangedFunc path_writable_changed; - gpointer user_data; - - 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, - GSettingsBackendWritableChangedFunc writable_changed, - GSettingsBackendPathWritableChangedFunc path_writable_changed, - gpointer user_data) -{ - 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; - watch->writable_changed = writable_changed; - watch->path_writable_changed = path_writable_changed; - watch->user_data = user_data; - - watch->next = backend->priv->watches; - backend->priv->watches = watch; -} - -void -g_settings_backend_unwatch (GSettingsBackend *backend, - gpointer user_data) -{ - GSettingsBackendWatch **ptr; - - for (ptr = &backend->priv->watches; *ptr; ptr = &(*ptr)->next) - if ((*ptr)->user_data == user_data) - { - GSettingsBackendWatch *tmp = *ptr; - - *ptr = tmp->next; - g_slice_free (GSettingsBackendWatch, tmp); - - return; - } - - g_assert_not_reached (); -} - static gboolean is_key (const gchar *key) { @@ -219,93 +146,254 @@ g_settings_backend_get_active_context (void) return context; } -typedef struct +struct _GSettingsBackendWatch +{ + GObject *target; + GMainContext *context; + GSettingsBackendChangedFunc changed; + GSettingsBackendPathChangedFunc path_changed; + GSettingsBackendKeysChangedFunc keys_changed; + GSettingsBackendWritableChangedFunc writable_changed; + GSettingsBackendPathWritableChangedFunc path_writable_changed; + + GSettingsBackendWatch *next; +}; + +struct _GSettingsBackendClosure { void (*function) (GSettingsBackend *backend, + GObject *target, const gchar *name, gpointer data1, - gpointer data2, - gpointer data3); + gpointer data2); + GSettingsBackend *backend; - gchar *name; + GObject *target; + gchar *name; + gpointer data1; + GBoxedFreeFunc data1_free; + gpointer data2; +}; - gpointer data1; - gpointer data2; - gpointer data3; +static void +g_settings_backend_watch_weak_notify (gpointer data, + GObject *where_object_was) +{ + GSettingsBackend *backend = data; + GSettingsBackendWatch **ptr; - GDestroyNotify destroy_data1; -} GSettingsBackendClosure; + /* search and remove */ + g_static_mutex_lock (&backend->priv->lock); + for (ptr = &backend->priv->watches; *ptr; ptr = &(*ptr)->next) + if ((*ptr)->target == where_object_was) + { + GSettingsBackendWatch *tmp = *ptr; + + *ptr = tmp->next; + g_slice_free (GSettingsBackendWatch, tmp); + + g_static_mutex_unlock (&backend->priv->lock); + return; + } + + /* we didn't find it. that shouldn't happen. */ + g_assert_not_reached (); +} + +/*< private > + * g_settings_backend_watch: + * @backend: a #GSettingsBackend + * @target: the GObject (typically GSettings instance) to call back to + * @context: a #GMainContext, or %NULL + * ...: 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. + * + * note also: if you want to get meaningful values for the @origin_tag + * that appears as an argument to some of the callbacks, you *must* have + * @context as %NULL. Otherwise, you are subject to cross-thread + * dispatching and whatever owned @origin_tag at the time that the event + * occured may no longer own it. This is a problem if you consider that + * you may now be the new owner of that address and mistakenly think + * that the event in question originated from yourself. + * + * tl;dr: If you give a non-%NULL @context then you must ignore the + * value of @origin_tag given to any callbacks. + **/ +void +g_settings_backend_watch (GSettingsBackend *backend, + GObject *target, + GMainContext *context, + GSettingsBackendChangedFunc changed, + GSettingsBackendPathChangedFunc path_changed, + GSettingsBackendKeysChangedFunc keys_changed, + GSettingsBackendWritableChangedFunc writable_changed, + GSettingsBackendPathWritableChangedFunc path_writable_changed) +{ + GSettingsBackendWatch *watch; + + /* For purposes of discussion, we assume that our target is a + * GSettings instance. + * + * Our strategy to defend against the final reference dropping on the + * GSettings object in a thread other than the one that is doing the + * dispatching is as follows: + * + * 1) hold a GObject reference on the GSettings during an outstanding + * dispatch. This ensures that the delivery is always possible. + * + * 2) hold a weak reference on the GSettings at other times. This + * allows us to receive early notification of pending destruction + * of the object. At this point, it is still safe to obtain a + * reference on the GObject to keep it alive, so #1 will work up + * to that point. After that point, we'll have been able to drop + * the watch from the list. + * + * Note, in particular, that it's not possible to simply have an + * "unwatch" function that gets called from the finalize function of + * the GSettings instance because, by that point it is no longer + * possible to keep the object alive using g_object_ref() and we would + * have no way of knowing this. + * + * Note also that we do not need to hold a reference on the main + * context here since the GSettings instance does that for us and we + * will receive the weak notify long before it is dropped. We don't + * even need to hold it during dispatches because our reference on the + * GSettings will prevent the finalize from running and dropping the + * ref on the context. + * + * All access to the list holds a mutex. We have some strategies to + * avoid some of the pain that would be associated with that. + */ + + watch = g_slice_new (GSettingsBackendWatch); + watch->context = context; + watch->target = target; + g_object_weak_ref (target, g_settings_backend_watch_weak_notify, backend); + + watch->changed = changed; + watch->path_changed = path_changed; + watch->keys_changed = keys_changed; + watch->writable_changed = writable_changed; + watch->path_writable_changed = path_writable_changed; + + /* linked list prepend */ + g_static_mutex_lock (&backend->priv->lock); + watch->next = backend->priv->watches; + backend->priv->watches = watch; + g_static_mutex_unlock (&backend->priv->lock); +} + +void +g_settings_backend_unwatch (GSettingsBackend *backend, + GObject *target) +{ + /* Our caller surely owns a reference on 'target', so the order of + * these two calls is unimportant. + */ + g_object_weak_unref (target, g_settings_backend_watch_weak_notify, backend); + g_settings_backend_watch_weak_notify (backend, target); +} 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); + closure->function (closure->backend, closure->target, closure->name, + closure->data1, closure->data2); + closure->data1_free (closure->data1); g_object_unref (closure->backend); - - if (closure->destroy_data1) - closure->destroy_data1 (closure->data1); - + g_object_unref (closure->target); g_free (closure->name); - /* make a donation to the magazine in this thread... */ g_slice_free (GSettingsBackendClosure, closure); return FALSE; } +static gpointer +pointer_id (gpointer a) +{ + return a; +} + +static void +pointer_ignore (gpointer a) +{ +} + static void g_settings_backend_dispatch_signal (GSettingsBackend *backend, - GMainContext *context, - gpointer function, + gsize function_offset, const gchar *name, gpointer data1, - GDestroyNotify destroy_data1, - gpointer data2, - gpointer data3) + GBoxedCopyFunc data1_copy, + GBoxedFreeFunc data1_free, + gpointer data2) { - GSettingsBackendClosure *closure; - GSource *source; + GMainContext *context, *here_and_now; + GSettingsBackendWatch *watch; - /* 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). + /* We need to hold the mutex here (to prevent a node from being + * deleted as we are traversing the list). Since we should not + * re-enter user code while holding this mutex, we create a + * one-time-use GMainContext and populate it with the events that we + * would have called directly. We dispatch these events after + * releasing the lock. Note that the GObject reference is acquired on + * the target while holding the mutex and the mutex needs to be held + * as part of the destruction of any GSettings instance (via the weak + * reference handling). This is the key to the safety of the whole + * setup. */ - 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); + if (data1_copy == NULL) + data1_copy = pointer_id; - /* ditto. */ - closure->data1 = data1; - closure->destroy_data1 = destroy_data1; + if (data1_free == NULL) + data1_free = pointer_ignore; + + context = g_settings_backend_get_active_context (); + here_and_now = g_main_context_new (); - closure->data2 = data2; - closure->data3 = data3; + /* traverse the (immutable while holding lock) list */ + g_static_mutex_lock (&backend->priv->lock); + for (watch = backend->priv->watches; watch; watch = watch->next) + { + GSettingsBackendClosure *closure; + GSource *source; - 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); + closure = g_slice_new (GSettingsBackendClosure); + closure->backend = g_object_ref (backend); + closure->target = g_object_ref (watch->target); + closure->function = G_STRUCT_MEMBER (void *, watch, function_offset); + closure->name = g_strdup (name); + closure->data1 = data1_copy (data1); + closure->data1_free = data1_free; + closure->data2 = data2; + + source = g_idle_source_new (); + g_source_set_priority (source, G_PRIORITY_DEFAULT); + g_source_set_callback (source, + g_settings_backend_invoke_closure, + closure, NULL); + + if (watch->context && watch->context != context) + g_source_attach (source, watch->context); + else + g_source_attach (source, here_and_now); + + g_source_unref (source); + } + g_static_mutex_unlock (&backend->priv->lock); + + while (g_main_context_iteration (here_and_now, FALSE)); + g_main_context_unref (here_and_now); } /** @@ -344,21 +432,13 @@ g_settings_backend_changed (GSettingsBackend *backend, const gchar *key, 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) - 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); + g_settings_backend_dispatch_signal (backend, + G_STRUCT_OFFSET (GSettingsBackendWatch, + changed), + key, origin_tag, NULL, NULL, NULL); } /** @@ -398,14 +478,19 @@ g_settings_backend_keys_changed (GSettingsBackend *backend, gchar const * const *items, gpointer origin_tag) { - GSettingsBackendWatch *watch; - g_return_if_fail (G_IS_SETTINGS_BACKEND (backend)); - g_return_if_fail (path[0] == '\0' || is_path (path)); + g_return_if_fail (is_path (path)); + + /* XXX: should do stricter checking (ie: inspect each item) */ g_return_if_fail (items != NULL); - for (watch = backend->priv->watches; watch; watch = watch->next) - watch->keys_changed (backend, path, items, origin_tag, watch->user_data); + g_settings_backend_dispatch_signal (backend, + G_STRUCT_OFFSET (GSettingsBackendWatch, + keys_changed), + path, (gpointer) items, + (GBoxedCopyFunc) g_strdupv, + (GBoxedFreeFunc) g_strfreev, + origin_tag); } /** @@ -443,13 +528,13 @@ g_settings_backend_path_changed (GSettingsBackend *backend, const gchar *path, gpointer origin_tag) { - GSettingsBackendWatch *watch; - g_return_if_fail (G_IS_SETTINGS_BACKEND (backend)); g_return_if_fail (is_path (path)); - for (watch = backend->priv->watches; watch; watch = watch->next) - watch->path_changed (backend, path, origin_tag, watch->user_data); + g_settings_backend_dispatch_signal (backend, + G_STRUCT_OFFSET (GSettingsBackendWatch, + path_changed), + path, origin_tag, NULL, NULL, NULL); } /** @@ -468,13 +553,13 @@ void g_settings_backend_writable_changed (GSettingsBackend *backend, const gchar *key) { - GSettingsBackendWatch *watch; - g_return_if_fail (G_IS_SETTINGS_BACKEND (backend)); g_return_if_fail (is_key (key)); - for (watch = backend->priv->watches; watch; watch = watch->next) - watch->writable_changed (backend, key, watch->user_data); + g_settings_backend_dispatch_signal (backend, + G_STRUCT_OFFSET (GSettingsBackendWatch, + writable_changed), + key, NULL, NULL, NULL, NULL); } /** @@ -494,13 +579,13 @@ void g_settings_backend_path_writable_changed (GSettingsBackend *backend, const gchar *path) { - GSettingsBackendWatch *watch; - g_return_if_fail (G_IS_SETTINGS_BACKEND (backend)); g_return_if_fail (is_path (path)); - for (watch = backend->priv->watches; watch; watch = watch->next) - watch->path_writable_changed (backend, path, watch->user_data); + g_settings_backend_dispatch_signal (backend, + G_STRUCT_OFFSET (GSettingsBackendWatch, + path_writable_changed), + path, NULL, NULL, NULL, NULL); } typedef struct @@ -636,7 +721,7 @@ g_settings_backend_changed_tree (GSettingsBackend *backend, g_settings_backend_flatten_tree (tree, &path, &keys, NULL); for (watch = backend->priv->watches; watch; watch = watch->next) - watch->keys_changed (backend, path, keys, origin_tag, watch->user_data); + watch->keys_changed (backend, watch->target, path, keys, origin_tag); g_free (path); g_free (keys); @@ -866,9 +951,11 @@ g_settings_backend_finalize (GObject *object) { GSettingsBackend *backend = G_SETTINGS_BACKEND (object); + g_static_mutex_unlock (&backend->priv->lock); g_free (backend->priv->context); - G_OBJECT_CLASS (g_settings_backend_parent_class)->finalize (object); + G_OBJECT_CLASS (g_settings_backend_parent_class) + ->finalize (object); } static void @@ -883,6 +970,7 @@ g_settings_backend_init (GSettingsBackend *backend) backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, G_TYPE_SETTINGS_BACKEND, GSettingsBackendPrivate); + g_static_mutex_init (&backend->priv->lock); } static void diff --git a/gio/gsettingsbackendinternal.h b/gio/gsettingsbackendinternal.h index 62867fb22..b6679830c 100644 --- a/gio/gsettingsbackendinternal.h +++ b/gio/gsettingsbackendinternal.h @@ -27,37 +27,37 @@ #include "gsettingsbackend.h" typedef void (*GSettingsBackendChangedFunc) (GSettingsBackend *backend, + GObject *target, const gchar *key, - gpointer origin_tag, - gpointer user_data); + gpointer origin_tag); typedef void (*GSettingsBackendPathChangedFunc) (GSettingsBackend *backend, + GObject *target, const gchar *path, - gpointer origin_tag, - gpointer user_data); + gpointer origin_tag); typedef void (*GSettingsBackendKeysChangedFunc) (GSettingsBackend *backend, + GObject *target, const gchar *prefix, const gchar * const *names, - gpointer origin_tag, - gpointer user_data); + gpointer origin_tag); typedef void (*GSettingsBackendWritableChangedFunc) (GSettingsBackend *backend, - const gchar *key, - gpointer user_data); + GObject *target, + const gchar *key); typedef void (*GSettingsBackendPathWritableChangedFunc) (GSettingsBackend *backend, - const gchar *path, - gpointer user_data); + GObject *target, + const gchar *path); G_GNUC_INTERNAL void g_settings_backend_watch (GSettingsBackend *backend, + GObject *target, GMainContext *context, GSettingsBackendChangedFunc changed, GSettingsBackendPathChangedFunc path_changed, GSettingsBackendKeysChangedFunc keys_changed, GSettingsBackendWritableChangedFunc writable_changed, - GSettingsBackendPathWritableChangedFunc path_writable_changed, - gpointer user_data); + GSettingsBackendPathWritableChangedFunc path_writable_changed); G_GNUC_INTERNAL void g_settings_backend_unwatch (GSettingsBackend *backend, - gpointer user_data); + GObject *target); G_GNUC_INTERNAL gboolean g_settings_backend_supports_context (const gchar *context);