[tracker-miners/wip/carlosg/monitor-thread: 5/5] libtracker-miner: Dispatch monitor events on a private thread




commit 51cf1bf833cc1b8549c6956245d70ace9362dd67
Author: Carlos Garnacho <carlosg gnome org>
Date:   Sun Mar 7 18:59:30 2021 +0100

    libtracker-miner: Dispatch monitor events on a private thread
    
    Each GFileMonitor creates a GSource on the thread default main
    context (the main one in our case), this ends up slowing everything
    down by the sheer amount of GSources to iterate each time on the
    GMainContext, whenever one of those needs dispatching.
    
    As GFileMonitor is friendly to the thread-default main context,
    take all file monitoring to a private thread, and make all those
    GFileMonitors add their GSources to a separate main context.
    
    Processing of monitor events will still trigger iterations over
    large lists of sources, but the main context will not be blocked
    by it, thus operations like extracting information from files and
    directories will be faster as a result.
    
    Numbers! This brings down first time indexing of 5826 files, 10377
    folders from ~40s to 18-20s, and second time indexing from ~30s to
    8-9s. Still can't beat enable-monitors=FALSE (15s and 2-3s
    respectively), but it is much closer.
    
    Despite monitoring being performed in another thread, we still
    need to make the monitor manipulation functions "synchronous" so
    that we are sure in our own code that monitor events will be
    received after e.g. tracker_monitor_add(). This is necessary for
    tests to stay non-racy.

 src/libtracker-miner/tracker-monitor.c        | 490 ++++++++++++++++++--------
 tests/libtracker-miner/tracker-monitor-test.c |   1 +
 2 files changed, 337 insertions(+), 154 deletions(-)
---
diff --git a/src/libtracker-miner/tracker-monitor.c b/src/libtracker-miner/tracker-monitor.c
index 8d5953d35..aca697cb7 100644
--- a/src/libtracker-miner/tracker-monitor.c
+++ b/src/libtracker-miner/tracker-monitor.c
@@ -37,7 +37,7 @@
 typedef struct TrackerMonitorPrivate  TrackerMonitorPrivate;
 
 struct TrackerMonitorPrivate {
-       GHashTable    *monitors;
+       GHashTable    *monitored_dirs;
 
        gboolean       enabled;
 
@@ -50,9 +50,19 @@ struct TrackerMonitorPrivate {
         */
        gboolean       use_changed_event;
 
-       GHashTable    *cached_events;
-
        TrackerIndexingTree *tree;
+
+       struct {
+               GMainContext *owner_context;
+               GMainContext *monitor_context;
+               GMainLoop *monitor_thread_loop;
+               GThread *monitor_thread;
+               GHashTable *cached_events;
+               GHashTable *monitors;
+               GMutex mutex;
+               GCond cond;
+               gint n_requests;
+       } thread;
 };
 
 typedef struct {
@@ -68,11 +78,23 @@ typedef struct {
 typedef struct {
        TrackerMonitor *monitor;
        GFile *file;
+       GFile *other_file;
+       GSource *source;
        gboolean is_directory;
        GFileMonitorEvent event_type;
-       guint source_id;
 } MonitorEvent;
 
+typedef enum {
+       MONITOR_REQUEST_ADD,
+       MONITOR_REQUEST_REMOVE,
+} MonitorRequestType;
+
+typedef struct {
+       TrackerMonitor *monitor;
+       MonitorRequestType type;
+       GList *files;
+} MonitorRequest;
+
 enum {
        ITEM_CREATED,
        ITEM_UPDATED,
@@ -103,11 +125,6 @@ static GFileMonitor * directory_monitor_new        (TrackerMonitor *monitor,
 static void           directory_monitor_cancel     (GFileMonitor     *dir_monitor);
 
 
-static void           emit_signal_for_event        (TrackerMonitor    *monitor,
-                                                    GFileMonitorEvent  type,
-                                                    gboolean           is_directory,
-                                                    GFile             *file,
-                                                    GFile             *other_file);
 static gboolean       monitor_cancel_recursively   (TrackerMonitor *monitor,
                                                     GFile          *file);
 
@@ -120,6 +137,21 @@ G_DEFINE_TYPE_WITH_CODE (TrackerMonitor, tracker_monitor, G_TYPE_OBJECT,
                                                 tracker_monitor_initable_iface_init)
                          G_ADD_PRIVATE (TrackerMonitor))
 
+static gpointer
+monitor_thread_func (gpointer user_data)
+{
+       TrackerMonitor *monitor = user_data;
+       TrackerMonitorPrivate *priv;
+
+       priv = tracker_monitor_get_instance_private (monitor);
+       g_main_context_push_thread_default (priv->thread.monitor_context);
+       g_main_loop_run (priv->thread.monitor_thread_loop);
+       g_main_context_pop_thread_default (priv->thread.monitor_context);
+       g_main_loop_unref (priv->thread.monitor_thread_loop);
+
+       return NULL;
+}
+
 static gboolean
 tracker_monitor_initable_init (GInitable     *initable,
                                GCancellable  *cancellable,
@@ -213,6 +245,20 @@ tracker_monitor_initable_init (GInitable     *initable,
        g_object_unref (monitor);
        g_object_unref (file);
 
+       priv->thread.owner_context = g_main_context_ref_thread_default ();
+       priv->thread.monitor_context = g_main_context_new ();
+       priv->thread.monitor_thread_loop = g_main_loop_new (priv->thread.monitor_context, FALSE);
+
+       priv->thread.monitor_thread =
+               g_thread_try_new ("Monitor thread",
+                                 monitor_thread_func,
+                                 initable,
+                                 &inner_error);
+       if (inner_error) {
+               g_propagate_error (error, inner_error);
+               return FALSE;
+       }
+
        return TRUE;
 }
 
@@ -300,13 +346,32 @@ tracker_monitor_class_init (TrackerMonitorClass *klass)
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT | 
G_PARAM_STATIC_STRINGS));
 }
 
+static MonitorEvent *
+monitor_event_new (TrackerMonitor    *monitor,
+                   GFile             *file,
+                   GFile             *other_file,
+                   GFileMonitorEvent  event_type,
+                   gboolean           is_directory)
+{
+       MonitorEvent *event;
+
+       event = g_new0 (MonitorEvent, 1);
+       event->monitor = g_object_ref (monitor);
+       event->file = g_object_ref (file);
+       g_set_object (&event->other_file, other_file);
+       event->event_type = event_type;
+       event->is_directory = is_directory;
+
+       return event;
+}
+
 static void
 monitor_event_free (MonitorEvent *event)
 {
-       if (event->source_id != 0)
-               g_source_remove (event->source_id);
-
+       g_object_unref (event->monitor);
        g_object_unref (event->file);
+       g_clear_object (&event->other_file);
+       g_clear_pointer (&event->source, g_source_destroy);
        g_free (event);
 }
 
@@ -321,17 +386,37 @@ tracker_monitor_init (TrackerMonitor *object)
        priv->enabled = TRUE;
 
        /* Create monitors table for this module */
-       priv->monitors =
+       priv->monitored_dirs =
                g_hash_table_new_full (g_file_hash,
                                       (GEqualFunc) g_file_equal,
                                       (GDestroyNotify) g_object_unref,
-                                      (GDestroyNotify) directory_monitor_cancel);
+                                      NULL);
 
-       priv->cached_events =
+       priv->thread.cached_events =
                g_hash_table_new_full (g_file_hash,
                                       (GEqualFunc) g_file_equal,
                                       g_object_unref,
                                       (GDestroyNotify) monitor_event_free);
+
+       priv->thread.monitors =
+               g_hash_table_new_full (g_file_hash,
+                                      (GEqualFunc) g_file_equal,
+                                      (GDestroyNotify) g_object_unref,
+                                      (GDestroyNotify) directory_monitor_cancel);
+
+       g_mutex_init (&priv->thread.mutex);
+       g_cond_init (&priv->thread.cond);
+}
+
+static gboolean
+quit_thread (TrackerMonitor *monitor)
+{
+       TrackerMonitorPrivate *priv;
+
+       priv = tracker_monitor_get_instance_private (monitor);
+       g_main_loop_quit (priv->thread.monitor_thread_loop);
+
+       return G_SOURCE_REMOVE;
 }
 
 static void
@@ -341,8 +426,22 @@ tracker_monitor_finalize (GObject *object)
 
        priv = tracker_monitor_get_instance_private (TRACKER_MONITOR (object));
 
-       g_hash_table_unref (priv->cached_events);
-       g_hash_table_unref (priv->monitors);
+       if (priv->thread.monitor_thread_loop) {
+               g_main_context_invoke_full (priv->thread.monitor_context,
+                                           G_PRIORITY_HIGH,
+                                           (GSourceFunc) quit_thread,
+                                           object, NULL);
+       }
+
+       if (priv->thread.monitor_thread)
+               g_thread_join (priv->thread.monitor_thread);
+
+       g_clear_pointer (&priv->thread.monitor_context, g_main_context_unref);
+       g_clear_pointer (&priv->thread.owner_context, g_main_context_unref);
+       g_clear_pointer (&priv->thread.cached_events, g_hash_table_unref);
+       g_clear_pointer (&priv->thread.monitors, g_hash_table_unref);
+
+       g_hash_table_unref (priv->monitored_dirs);
 
        G_OBJECT_CLASS (tracker_monitor_parent_class)->finalize (object);
 }
@@ -453,13 +552,85 @@ check_is_directory (TrackerMonitor *monitor,
                 * hashtable to know whether it was a directory
                 * we knew about
                 */
-               if (g_hash_table_lookup (priv->monitors, file) != NULL)
+               if (g_hash_table_lookup (priv->thread.monitors, file) != NULL)
                        return TRUE;
        }
 
        return FALSE;
 }
 
+/* Executed in monitor thread */
+static gboolean
+monitor_request_execute (MonitorRequest *request)
+{
+       TrackerMonitorPrivate *priv;
+
+       priv = tracker_monitor_get_instance_private (request->monitor);
+
+       g_mutex_lock (&priv->thread.mutex);
+
+       while (request->files) {
+               GFile *file = request->files->data;
+
+               if (request->type == MONITOR_REQUEST_ADD) {
+                       GFileMonitor *monitor;
+
+                       monitor = directory_monitor_new (request->monitor,
+                                                        file);
+                       if (monitor) {
+                               g_hash_table_insert (priv->thread.monitors,
+                                                    g_object_ref (file),
+                                                    monitor);
+                       }
+               } else if (request->type == MONITOR_REQUEST_REMOVE) {
+                       g_hash_table_remove (priv->thread.monitors,
+                                            file);
+               } else {
+                       g_assert_not_reached ();
+               }
+
+               request->files = g_list_remove (request->files, file);
+       }
+
+       if (g_atomic_int_dec_and_test (&priv->thread.n_requests))
+               g_cond_signal (&priv->thread.cond);
+
+       g_mutex_unlock (&priv->thread.mutex);
+
+       return G_SOURCE_REMOVE;
+}
+
+/* Executed in main thread */
+static void
+monitor_request_queue (TrackerMonitor *monitor,
+                       MonitorRequest *request)
+{
+       TrackerMonitorPrivate *priv;
+
+       priv = tracker_monitor_get_instance_private (request->monitor);
+
+       g_atomic_int_inc (&priv->thread.n_requests);
+       g_main_context_invoke_full (priv->thread.monitor_context,
+                                   G_PRIORITY_DEFAULT,
+                                   (GSourceFunc) monitor_request_execute,
+                                   request, g_free);
+}
+
+static void
+block_for_requests (TrackerMonitor *monitor)
+{
+       TrackerMonitorPrivate *priv;
+
+       priv = tracker_monitor_get_instance_private (monitor);
+
+       g_mutex_lock (&priv->thread.mutex);
+
+       while (g_atomic_int_get (&priv->thread.n_requests) != 0)
+               g_cond_wait (&priv->thread.cond, &priv->thread.mutex);
+
+       g_mutex_unlock (&priv->thread.mutex);
+}
+
 gboolean
 tracker_monitor_move (TrackerMonitor *monitor,
                       GFile          *old_file,
@@ -467,9 +638,9 @@ tracker_monitor_move (TrackerMonitor *monitor,
 {
        TrackerMonitorPrivate *priv;
        GHashTableIter iter;
-       GHashTable *new_monitors;
+       MonitorRequest *request;
        gchar *old_prefix;
-       gpointer iter_file, iter_file_monitor;
+       gpointer iter_file;
        guint items_moved = 0;
 
        priv = tracker_monitor_get_instance_private (monitor);
@@ -484,15 +655,15 @@ tracker_monitor_move (TrackerMonitor *monitor,
         * asynchronously on IN_IGNORE, so the opposite sequence
         * may possibly remove valid, just added, monitors.
         */
-       new_monitors = g_hash_table_new_full (g_file_hash,
-                                             (GEqualFunc) g_file_equal,
-                                             (GDestroyNotify) g_object_unref,
-                                             NULL);
+       request = g_new0 (MonitorRequest, 1);
+       request->monitor = monitor;
+       request->type = MONITOR_REQUEST_ADD;
+
        old_prefix = g_file_get_path (old_file);
 
        /* Find out which subdirectories should have a file monitor added */
-       g_hash_table_iter_init (&iter, priv->monitors);
-       while (g_hash_table_iter_next (&iter, &iter_file, &iter_file_monitor)) {
+       g_hash_table_iter_init (&iter, priv->monitored_dirs);
+       while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
                GFile *f;
                gchar *old_path, *new_path;
                gchar *new_prefix;
@@ -527,12 +698,9 @@ tracker_monitor_move (TrackerMonitor *monitor,
                f = g_file_new_for_path (new_path);
                g_free (new_path);
 
-               if (!g_hash_table_lookup (new_monitors, f)) {
-                       g_hash_table_insert (new_monitors, f, GINT_TO_POINTER (1));
-               } else {
-                       g_object_unref (f);
-               }
+               request->files = g_list_prepend (request->files, g_object_ref (f));
 
+               g_object_unref (f);
                g_free (old_path);
                items_moved++;
        }
@@ -540,19 +708,16 @@ tracker_monitor_move (TrackerMonitor *monitor,
        /* Add a new monitor for the top level directory */
        tracker_monitor_add (monitor, new_file);
 
-       /* Add a new monitor for all subdirectories */
-       g_hash_table_iter_init (&iter, new_monitors);
-       while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
-               tracker_monitor_add (monitor, iter_file);
-               g_hash_table_iter_remove (&iter);
-       }
+       /* Add new monitors for all subdirectories */
+       monitor_request_queue (monitor, request);
 
        /* Remove the monitor for the old top level directory hierarchy */
        tracker_monitor_remove_recursively (monitor, old_file);
 
-       g_hash_table_unref (new_monitors);
        g_free (old_prefix);
 
+       block_for_requests (monitor);
+
        return items_moved > 0;
 }
 
@@ -588,14 +753,16 @@ monitor_event_to_string (GFileMonitorEvent event_type)
        return "unknown";
 }
 
-static void
-emit_signal_for_event (TrackerMonitor    *monitor,
-                       GFileMonitorEvent  type,
-                       gboolean           is_directory,
-                       GFile             *file,
-                       GFile             *other_file)
+/* Executed in main thread */
+static gboolean
+emit_signal_for_event (MonitorEvent *event)
 {
-       switch (type) {
+       TrackerMonitor *monitor = event->monitor;
+       gboolean is_directory = event->is_directory;
+       GFile *file = event->file;
+       GFile *other_file = event->other_file;
+
+       switch (event->event_type) {
        case G_FILE_MONITOR_EVENT_CREATED:
                g_signal_emit (monitor,
                               signals[ITEM_CREATED], 0,
@@ -623,46 +790,56 @@ emit_signal_for_event (TrackerMonitor    *monitor,
                break;
        default:
                g_warning ("Trying to emit monitor signal with unhandled event %d",
-                          type);
+                          event->event_type);
                break;
        }
+
+       return G_SOURCE_REMOVE;
 }
 
+/* Executed in monitor thread */
 static void
-flush_event_now (TrackerMonitor *monitor,
-                 GFile          *file)
+queue_signal_for_event (TrackerMonitor    *monitor,
+                        GFileMonitorEvent  type,
+                        gboolean           is_directory,
+                        GFile             *file,
+                        GFile             *other_file)
 {
        TrackerMonitorPrivate *priv;
        MonitorEvent *event;
 
        priv = tracker_monitor_get_instance_private (monitor);
 
-       event = g_hash_table_lookup (priv->cached_events, file);
+       event = monitor_event_new (monitor, file, other_file,
+                                  type, is_directory);
 
-       if (event) {
-               emit_signal_for_event (monitor, event->event_type,
-                                      event->is_directory, event->file, NULL);
-               g_hash_table_remove (priv->cached_events, file);
-       }
+       g_main_context_invoke_full (priv->thread.owner_context,
+                                   G_PRIORITY_HIGH,
+                                   (GSourceFunc) emit_signal_for_event,
+                                   event,
+                                   (GDestroyNotify) monitor_event_free);
 }
 
-static MonitorEvent *
-monitor_event_new (TrackerMonitor    *monitor,
-                   GFile             *file,
-                   GFileMonitorEvent  event_type,
-                   gboolean           is_directory)
+/* Executed in monitor thread */
+static void
+flush_cached_event (TrackerMonitor *monitor,
+                    GFile          *file)
 {
+       TrackerMonitorPrivate *priv;
        MonitorEvent *event;
 
-       event = g_new0 (MonitorEvent, 1);
-       event->monitor = monitor;
-       event->file = g_object_ref (file);
-       event->event_type = event_type;
-       event->is_directory = is_directory;
+       priv = tracker_monitor_get_instance_private (monitor);
 
-       return event;
+       event = g_hash_table_lookup (priv->thread.cached_events, file);
+
+       if (event) {
+               queue_signal_for_event (monitor, event->event_type,
+                                       event->is_directory, event->file, NULL);
+               g_hash_table_remove (priv->thread.cached_events, file);
+       }
 }
 
+/* Executed in monitor thread */
 static void
 cache_event (TrackerMonitor    *monitor,
              GFile             *file,
@@ -673,11 +850,12 @@ cache_event (TrackerMonitor    *monitor,
        MonitorEvent *event;
 
        priv = tracker_monitor_get_instance_private (monitor);
-       event = g_hash_table_lookup (priv->cached_events, file);
+       event = g_hash_table_lookup (priv->thread.cached_events, file);
 
        if (!event) {
-               event = monitor_event_new (monitor, file, event_type, is_directory);
-               g_hash_table_insert (priv->cached_events,
+               event = monitor_event_new (monitor, file, NULL,
+                                          event_type, is_directory);
+               g_hash_table_insert (priv->thread.cached_events,
                                     g_object_ref (file),
                                     event);
        }
@@ -686,31 +864,34 @@ cache_event (TrackerMonitor    *monitor,
 static gboolean
 flush_event_idle_cb (gpointer user_data)
 {
-       MonitorEvent *event = user_data;
-       TrackerMonitorPrivate *priv = tracker_monitor_get_instance_private (event->monitor);
+       MonitorEvent *event = user_data;
+       TrackerMonitorPrivate *priv = tracker_monitor_get_instance_private (event->monitor);
 
-       event->source_id = 0;
-       emit_signal_for_event (event->monitor, event->event_type,
-                              event->is_directory, event->file, NULL);
-       g_hash_table_remove (priv->cached_events, event->file);
+       queue_signal_for_event (event->monitor, event->event_type,
+                               event->is_directory, event->file, NULL);
+       g_hash_table_remove (priv->thread.cached_events, event->file);
 
-       return G_SOURCE_REMOVE;
+       return G_SOURCE_REMOVE;
 }
 
 static void
 flush_event_later (TrackerMonitor *monitor,
                    GFile          *file)
 {
-       TrackerMonitorPrivate *priv = tracker_monitor_get_instance_private (monitor);
-       MonitorEvent *event;
+       TrackerMonitorPrivate *priv = tracker_monitor_get_instance_private (monitor);
+       MonitorEvent *event;
 
-       event = g_hash_table_lookup (priv->cached_events, file);
-       if (!event)
-               return;
+       event = g_hash_table_lookup (priv->thread.cached_events, file);
+       if (!event)
+               return;
 
-       event->source_id = g_idle_add (flush_event_idle_cb, event);
+       event->source = g_idle_source_new ();
+       g_source_set_callback (event->source, flush_event_idle_cb, event, NULL);
+       g_source_attach (event->source,
+                        priv->thread.monitor_context);
 }
 
+/* Executed in monitor thread */
 static void
 monitor_event_cb (GFileMonitor      *file_monitor,
                   GFile             *file,
@@ -727,7 +908,7 @@ monitor_event_cb (GFileMonitor      *file_monitor,
 
        monitor = user_data;
        priv = tracker_monitor_get_instance_private (monitor);
-       prev_event = g_hash_table_lookup (priv->cached_events, file);
+       prev_event = g_hash_table_lookup (priv->thread.cached_events, file);
 
        if (G_UNLIKELY (!priv->enabled)) {
                TRACKER_NOTE (MONITORS, g_message ("Silently dropping monitor event, monitor disabled for 
now"));
@@ -774,7 +955,7 @@ monitor_event_cb (GFileMonitor      *file_monitor,
                         * this event before this one, we should ensure it's cleared
                         * out.
                         */
-                       g_hash_table_remove (priv->cached_events, file);
+                       g_hash_table_remove (priv->thread.cached_events, file);
                }
        }
 
@@ -795,30 +976,30 @@ monitor_event_cb (GFileMonitor      *file_monitor,
                if (!priv->use_changed_event) {
                        cache_event (monitor, file, event_type, is_directory);
                } else {
-                       emit_signal_for_event (monitor, event_type,
-                                              is_directory, file, NULL);
+                       queue_signal_for_event (monitor, event_type,
+                                               is_directory, file, NULL);
                }
                break;
        case G_FILE_MONITOR_EVENT_DELETED:
                if (prev_event &&
                    prev_event->event_type == G_FILE_MONITOR_EVENT_CREATED) {
                        /* Consume both the cached CREATED event and this one */
-                       g_hash_table_remove (priv->cached_events, file);
+                       g_hash_table_remove (priv->thread.cached_events, file);
                        break;
                }
 
                /* In any case, cached events are stale */
-               g_hash_table_remove (priv->cached_events, file);
+               g_hash_table_remove (priv->thread.cached_events, file);
 
                cache_event (monitor, file, event_type, is_directory);
                flush_event_later (monitor, file);
                break;
        case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
-               emit_signal_for_event (monitor, event_type,
-                                      is_directory, file, NULL);
+               queue_signal_for_event (monitor, event_type,
+                                       is_directory, file, NULL);
                break;
        case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-               flush_event_now (monitor, file);
+               flush_cached_event (monitor, file);
                break;
        case G_FILE_MONITOR_EVENT_MOVED_IN:
                if (other_file) {
@@ -826,31 +1007,31 @@ monitor_event_cb (GFileMonitor      *file_monitor,
                         * ::item-moved when source/dest are known. We choose
                         * to emit it here, and ignore the MOVE_OUT.
                         */
-                       emit_signal_for_event (monitor,
-                                              G_FILE_MONITOR_EVENT_MOVED,
-                                              is_directory,
-                                              other_file, file);
+                       queue_signal_for_event (monitor,
+                                               G_FILE_MONITOR_EVENT_MOVED,
+                                               is_directory,
+                                               other_file, file);
                } else {
                        /* No known origin, treat as a new file */
-                       emit_signal_for_event (monitor,
-                                              G_FILE_MONITOR_EVENT_CREATED,
-                                              is_directory,
-                                              file, NULL);
+                       queue_signal_for_event (monitor,
+                                               G_FILE_MONITOR_EVENT_CREATED,
+                                               is_directory,
+                                               file, NULL);
                }
                break;
        case G_FILE_MONITOR_EVENT_MOVED_OUT:
                if (!other_file) {
                        /* No known destination. Treat as remove */
-                       emit_signal_for_event (monitor,
-                                              G_FILE_MONITOR_EVENT_DELETED,
-                                              is_directory,
-                                              file, NULL);
+                       queue_signal_for_event (monitor,
+                                               G_FILE_MONITOR_EVENT_DELETED,
+                                               is_directory,
+                                               file, NULL);
                }
                break;
        case G_FILE_MONITOR_EVENT_RENAMED:
-               emit_signal_for_event (monitor,
-                                      G_FILE_MONITOR_EVENT_MOVED,
-                                      is_directory, file, other_file);
+               queue_signal_for_event (monitor,
+                                       G_FILE_MONITOR_EVENT_MOVED,
+                                       is_directory, file, other_file);
                break;
        case G_FILE_MONITOR_EVENT_MOVED:
                g_warn_if_reached ();
@@ -928,7 +1109,7 @@ tracker_monitor_set_enabled (TrackerMonitor *monitor,
                              gboolean        enabled)
 {
        TrackerMonitorPrivate *priv;
-       GList *keys, *k;
+       MonitorRequest *request;
 
        g_return_if_fail (TRACKER_IS_MONITOR (monitor));
 
@@ -944,28 +1125,13 @@ tracker_monitor_set_enabled (TrackerMonitor *monitor,
        priv->enabled = enabled;
        g_object_notify (G_OBJECT (monitor), "enabled");
 
-       keys = g_hash_table_get_keys (priv->monitors);
-
-       /* Update state on all monitored dirs */
-       for (k = keys; k != NULL; k = k->next) {
-               GFile *file;
+       request = g_new0 (MonitorRequest, 1);
+       request->monitor = monitor;
+       request->files = g_hash_table_get_keys (priv->monitored_dirs);
+       request->type = enabled ? MONITOR_REQUEST_ADD : MONITOR_REQUEST_REMOVE;
 
-               file = k->data;
-
-               if (enabled) {
-                       GFileMonitor *dir_monitor;
-
-                       dir_monitor = directory_monitor_new (monitor, file);
-                       g_hash_table_replace (priv->monitors,
-                                             g_object_ref (file), dir_monitor);
-               } else {
-                       /* Remove monitor */
-                       g_hash_table_replace (priv->monitors,
-                                             g_object_ref (file), NULL);
-               }
-       }
-
-       g_list_free (keys);
+       monitor_request_queue (monitor, request);
+       block_for_requests (monitor);
 }
 
 gboolean
@@ -973,7 +1139,6 @@ tracker_monitor_add (TrackerMonitor *monitor,
                      GFile          *file)
 {
        TrackerMonitorPrivate *priv;
-       GFileMonitor *dir_monitor = NULL;
        gchar *uri;
 
        g_return_val_if_fail (TRACKER_IS_MONITOR (monitor), FALSE);
@@ -981,12 +1146,12 @@ tracker_monitor_add (TrackerMonitor *monitor,
 
        priv = tracker_monitor_get_instance_private (monitor);
 
-       if (g_hash_table_lookup (priv->monitors, file)) {
+       if (g_hash_table_lookup (priv->monitored_dirs, file)) {
                return TRUE;
        }
 
        /* Cap the number of monitors */
-       if (g_hash_table_size (priv->monitors) >= priv->monitor_limit) {
+       if (g_hash_table_size (priv->monitored_dirs) >= priv->monitor_limit) {
                priv->monitors_ignored++;
 
                if (!priv->monitor_limit_warned) {
@@ -1007,27 +1172,22 @@ tracker_monitor_add (TrackerMonitor *monitor,
                 *
                 * Also, we assume ALL paths passed are directories.
                 */
-               dir_monitor = directory_monitor_new (monitor, file);
+               MonitorRequest *request;
 
-               if (!dir_monitor) {
-                       g_warning ("Could not add monitor for path:'%s'",
-                                  uri);
-                       g_free (uri);
-                       return FALSE;
-               }
+               request = g_new0 (MonitorRequest, 1);
+               request->monitor = monitor;
+               request->files = g_list_prepend (NULL, g_object_ref (file));
+               request->type = MONITOR_REQUEST_ADD;
+
+               monitor_request_queue (monitor, request);
+               block_for_requests (monitor);
        }
 
-       /* NOTE: it is ok to add a NULL file_monitor, when our
-        * enabled/disabled state changes, we iterate all keys and
-        * add or remove monitors.
-        */
-       g_hash_table_insert (priv->monitors,
-                            g_object_ref (file),
-                            dir_monitor);
+       g_hash_table_add (priv->monitored_dirs, g_object_ref (file));
 
        TRACKER_NOTE (MONITORS, g_message ("Added monitor for path:'%s', total monitors:%d",
                                           uri,
-                                          g_hash_table_size (priv->monitors)));
+                                          g_hash_table_size (priv->monitored_dirs)));
 
        g_free (uri);
 
@@ -1045,15 +1205,24 @@ tracker_monitor_remove (TrackerMonitor *monitor,
        g_return_val_if_fail (G_IS_FILE (file), FALSE);
 
        priv = tracker_monitor_get_instance_private (monitor);
-       removed = g_hash_table_remove (priv->monitors, file);
+       removed = g_hash_table_remove (priv->monitored_dirs, file);
 
        if (removed) {
+               MonitorRequest *request;
                gchar *uri;
 
+               request = g_new0 (MonitorRequest, 1);
+               request->monitor = monitor;
+               request->files = g_list_prepend (NULL, g_object_ref (file));
+               request->type = MONITOR_REQUEST_REMOVE;
+
+               monitor_request_queue (monitor, request);
+               block_for_requests (monitor);
+
                uri = g_file_get_uri (file);
                TRACKER_NOTE (MONITORS, g_message ("Removed monitor for path:'%s', total monitors:%d",
                                                   uri,
-                                                  g_hash_table_size (priv->monitors)));
+                                                  g_hash_table_size (priv->monitored_dirs)));
 
                g_free (uri);
        }
@@ -1080,7 +1249,8 @@ remove_recursively (TrackerMonitor *monitor,
 {
        TrackerMonitorPrivate *priv;
        GHashTableIter iter;
-       gpointer iter_file, iter_file_monitor;
+       MonitorRequest *request;
+       gpointer iter_file;
        guint items_removed = 0;
        gchar *uri;
 
@@ -1089,13 +1259,18 @@ remove_recursively (TrackerMonitor *monitor,
 
        priv = tracker_monitor_get_instance_private (monitor);
 
-       g_hash_table_iter_init (&iter, priv->monitors);
-       while (g_hash_table_iter_next (&iter, &iter_file, &iter_file_monitor)) {
+       request = g_new0 (MonitorRequest, 1);
+       request->monitor = monitor;
+       request->type = MONITOR_REQUEST_REMOVE;
+
+       g_hash_table_iter_init (&iter, priv->monitored_dirs);
+       while (g_hash_table_iter_next (&iter, &iter_file, NULL)) {
                if (!file_has_maybe_strict_prefix (iter_file, file,
                                                   !remove_top_level)) {
                        continue;
                }
 
+               request->files = g_list_prepend (request->files, g_object_ref (file));
                g_hash_table_iter_remove (&iter);
                items_removed++;
        }
@@ -1105,9 +1280,12 @@ remove_recursively (TrackerMonitor *monitor,
                      g_message ("Removed all monitors %srecursively for path:'%s', )"
                                 "total monitors:%d",
                                 !remove_top_level ? "(except top level) " : "",
-                                uri, g_hash_table_size (priv->monitors)));
+                                uri, g_hash_table_size (priv->monitored_dirs)));
        g_free (uri);
 
+       monitor_request_queue (monitor, request);
+       block_for_requests (monitor);
+
        if (items_removed > 0) {
                /* We reset this because now it is possible we have limit - 1 */
                priv->monitor_limit_warned = FALSE;
@@ -1131,6 +1309,7 @@ tracker_monitor_remove_children_recursively (TrackerMonitor *monitor,
        return remove_recursively (monitor, file, FALSE);
 }
 
+/* Runs in the monitor thread */
 static gboolean
 monitor_cancel_recursively (TrackerMonitor *monitor,
                             GFile          *file)
@@ -1142,7 +1321,7 @@ monitor_cancel_recursively (TrackerMonitor *monitor,
 
        priv = tracker_monitor_get_instance_private (monitor);
 
-       g_hash_table_iter_init (&iter, priv->monitors);
+       g_hash_table_iter_init (&iter, priv->thread.monitors);
        while (g_hash_table_iter_next (&iter, &iter_file, &iter_file_monitor)) {
                gchar *uri;
 
@@ -1173,7 +1352,10 @@ tracker_monitor_is_watched (TrackerMonitor *monitor,
 
        priv = tracker_monitor_get_instance_private (monitor);
 
-       return g_hash_table_lookup (priv->monitors, file) != NULL;
+       if (!priv->enabled)
+               return FALSE;
+
+       return g_hash_table_contains (priv->monitored_dirs, file);
 }
 
 guint
@@ -1185,7 +1367,7 @@ tracker_monitor_get_count (TrackerMonitor *monitor)
 
        priv = tracker_monitor_get_instance_private (monitor);
 
-       return g_hash_table_size (priv->monitors);
+       return g_hash_table_size (priv->monitored_dirs);
 }
 
 guint
diff --git a/tests/libtracker-miner/tracker-monitor-test.c b/tests/libtracker-miner/tracker-monitor-test.c
index cafe2f339..606550565 100644
--- a/tests/libtracker-miner/tracker-monitor-test.c
+++ b/tests/libtracker-miner/tracker-monitor-test.c
@@ -458,6 +458,7 @@ test_monitor_common_teardown (TrackerMonitorTestFixture *fixture,
 
        /* Destroy monitor */
        g_assert_true (fixture->monitor != NULL);
+       g_signal_handlers_disconnect_by_data (fixture->monitor, fixture);
        g_object_unref (fixture->monitor);
 
        /* Remove the hash_tables of events */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]