GFileMonitor: thread-safety fix for non-default-main-context monitors

When queuing events to another thread, we need a mutex around
priv->pending_file_changes and priv->pending_file_change_source.

https://bugzilla.gnome.org/show_bug.cgi?id=682950
This commit is contained in:
Dan Winship 2012-10-05 09:32:24 -04:00
parent 05461e5709
commit 7e6fa556ec

View File

@ -81,6 +81,7 @@ struct _GFileMonitorPrivate {
/* Rate limiting change events */ /* Rate limiting change events */
GHashTable *rate_limiter; GHashTable *rate_limiter;
GMutex mutex;
GSource *pending_file_change_source; GSource *pending_file_change_source;
GSList *pending_file_changes; /* FileChange */ GSList *pending_file_changes; /* FileChange */
@ -176,6 +177,7 @@ g_file_monitor_finalize (GObject *object)
g_hash_table_destroy (monitor->priv->rate_limiter); g_hash_table_destroy (monitor->priv->rate_limiter);
g_main_context_unref (monitor->priv->context); g_main_context_unref (monitor->priv->context);
g_mutex_clear (&monitor->priv->mutex);
G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object); G_OBJECT_CLASS (g_file_monitor_parent_class)->finalize (object);
} }
@ -272,6 +274,7 @@ g_file_monitor_init (GFileMonitor *monitor)
monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal, monitor->priv->rate_limiter = g_hash_table_new_full (g_file_hash, (GEqualFunc)g_file_equal,
NULL, (GDestroyNotify) rate_limiter_free); NULL, (GDestroyNotify) rate_limiter_free);
monitor->priv->context = g_main_context_ref_thread_default (); monitor->priv->context = g_main_context_ref_thread_default ();
g_mutex_init (&monitor->priv->mutex);
} }
/** /**
@ -374,7 +377,8 @@ emit_cb (gpointer data)
{ {
GFileMonitor *monitor = G_FILE_MONITOR (data); GFileMonitor *monitor = G_FILE_MONITOR (data);
GSList *pending, *iter; GSList *pending, *iter;
g_mutex_lock (&monitor->priv->mutex);
pending = g_slist_reverse (monitor->priv->pending_file_changes); pending = g_slist_reverse (monitor->priv->pending_file_changes);
monitor->priv->pending_file_changes = NULL; monitor->priv->pending_file_changes = NULL;
if (monitor->priv->pending_file_change_source) if (monitor->priv->pending_file_change_source)
@ -382,11 +386,13 @@ emit_cb (gpointer data)
g_source_unref (monitor->priv->pending_file_change_source); g_source_unref (monitor->priv->pending_file_change_source);
monitor->priv->pending_file_change_source = NULL; monitor->priv->pending_file_change_source = NULL;
} }
g_mutex_unlock (&monitor->priv->mutex);
g_object_ref (monitor); g_object_ref (monitor);
for (iter = pending; iter; iter = iter->next) for (iter = pending; iter; iter = iter->next)
{ {
FileChange *change = iter->data; FileChange *change = iter->data;
g_signal_emit (monitor, signals[CHANGED], 0, g_signal_emit (monitor, signals[CHANGED], 0,
change->child, change->other_file, change->event_type); change->child, change->other_file, change->event_type);
file_change_free (change); file_change_free (change);
@ -418,6 +424,7 @@ emit_in_idle (GFileMonitor *monitor,
change->other_file = NULL; change->other_file = NULL;
change->event_type = event_type; change->event_type = event_type;
g_mutex_lock (&monitor->priv->mutex);
if (!priv->pending_file_change_source) if (!priv->pending_file_change_source)
{ {
source = g_idle_source_new (); source = g_idle_source_new ();
@ -432,6 +439,7 @@ emit_in_idle (GFileMonitor *monitor,
} }
/* We reverse this in the processor */ /* We reverse this in the processor */
priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change); priv->pending_file_changes = g_slist_prepend (priv->pending_file_changes, change);
g_mutex_unlock (&monitor->priv->mutex);
} }
static guint32 static guint32