Commit 0bd50b39 authored by Allison Karlitskaya's avatar Allison Karlitskaya

Bug 623400 - acquire context before dispatching

For GSettings.

Use the functionality introduced in the last commit to simplify our
notify dispatching and increase the safety of doing so (by ensuring that
the context is acquired in the current thread for the duration of the
dispatch).

This closes bugs #623400 and #629849.
parent 92974b80
......@@ -70,21 +70,7 @@ g_delayed_settings_backend_notify_unapplied (GDelayedSettingsBackend *delayed)
g_static_mutex_unlock (&delayed->priv->lock);
if (target != NULL)
{
if (g_settings_backend_get_active_context () != target_context)
{
GSource *source;
source = g_idle_source_new ();
g_source_set_priority (source, G_PRIORITY_DEFAULT);
g_source_set_callback (source, invoke_notify_unapplied,
target, g_object_unref);
g_source_attach (source, target_context);
g_source_unref (source);
}
else
invoke_notify_unapplied (target);
}
g_main_context_invoke (target_context, invoke_notify_unapplied, target);
}
......
......@@ -119,26 +119,6 @@ is_path (const gchar *path)
return TRUE;
}
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;
}
struct _GSettingsBackendWatch
{
GObject *target;
......@@ -316,20 +296,7 @@ g_settings_backend_dispatch_signal (GSettingsBackend *backend,
GBoxedFreeFunc data1_free,
gpointer data2)
{
GMainContext *context, *here_and_now;
GSettingsBackendWatch *watch;
/* 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.
*/
GSettingsBackendWatch *suffix, *watch, *next;
if (data1_copy == NULL)
data1_copy = pointer_id;
......@@ -337,19 +304,34 @@ g_settings_backend_dispatch_signal (GSettingsBackend *backend,
if (data1_free == NULL)
data1_free = pointer_ignore;
context = g_settings_backend_get_active_context ();
here_and_now = g_main_context_new ();
/* traverse the (immutable while holding lock) list */
/* We're in a little bit of a tricky situation here. We need to hold
* a lock while traversing the list, but we don't want to hold the
* lock while calling back into user code.
*
* Since we're not holding the lock while we call user code, we can't
* render the list immutable. We can, however, store a pointer to a
* given suffix of the list and render that suffix immutable.
*
* Adds will never modify the suffix since adds always come in the
* form of prepends. We can also prevent removes from modifying the
* suffix since removes only happen in response to the last reference
* count dropping -- so just add a reference to everything in the
* suffix.
*/
g_static_mutex_lock (&backend->priv->lock);
for (watch = backend->priv->watches; watch; watch = watch->next)
suffix = backend->priv->watches;
for (watch = suffix; watch; watch = watch->next)
g_object_ref (watch->target);
g_static_mutex_unlock (&backend->priv->lock);
/* The suffix is now immutable, so this is safe. */
for (watch = suffix; watch; watch = next)
{
GSettingsBackendClosure *closure;
GSource *source;
closure = g_slice_new (GSettingsBackendClosure);
closure->backend = g_object_ref (backend);
closure->target = g_object_ref (watch->target);
closure->target = watch->target; /* we took our ref above */
closure->function = G_STRUCT_MEMBER (void *, watch->vtable,
function_offset);
closure->name = g_strdup (name);
......@@ -357,23 +339,18 @@ g_settings_backend_dispatch_signal (GSettingsBackend *backend,
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);
/* we do this here because 'watch' may not live to the end of this
* iteration of the loop (since we may unref the target below).
*/
next = watch->next;
if (watch->context && watch->context != context)
g_source_attach (source, watch->context);
if (watch->context)
g_main_context_invoke (watch->context,
g_settings_backend_invoke_closure,
closure);
else
g_source_attach (source, here_and_now);
g_source_unref (source);
g_settings_backend_invoke_closure (closure);
}
g_static_mutex_unlock (&backend->priv->lock);
while (g_main_context_iteration (here_and_now, FALSE));
g_main_context_unref (here_and_now);
}
/**
......
......@@ -92,8 +92,6 @@ G_GNUC_INTERNAL
GPermission * g_settings_backend_get_permission (GSettingsBackend *backend,
const gchar *path);
G_GNUC_INTERNAL
GMainContext * g_settings_backend_get_active_context (void);
G_GNUC_INTERNAL
GSettingsBackend * g_settings_backend_get_default (void);
G_GNUC_INTERNAL
void g_settings_backend_sync_default (void);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment