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.
This commit is contained in:
Ryan Lortie 2010-10-03 17:30:10 -04:00
parent 92974b80fc
commit 0bd50b39eb
3 changed files with 33 additions and 72 deletions

View File

@ -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);
}

View File

@ -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);
}
/**

View File

@ -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);