[tracker-miners/wip/carlosg/shuffle-libtracker-miner: 31/116] libtracker-miner: Clean up TrackerMonitor



commit 69efd418c0de3620665e056d1f36a580a8467e85
Author: Carlos Garnacho <carlosg gnome org>
Date:   Fri Oct 20 01:04:58 2017 +0200

    libtracker-miner: Clean up TrackerMonitor
    
    We used to implement our own caching and timeout mechanism on top
    of GIO's, and our own "blacklisting" that would merge or transform
    events depending on the previouly cached content.
    
    This adds quite some extra latency in some cases on top of
    GFileMonitor's rate (up to 2s), and even in some cases do produce
    mistaken results (CREATE(a)+MOVE(a->b) != CREATE(b) if you are rewriting
    a file, but how can TrackerMonitor know).
    
    The code has been simplified in various fronts:
    - (Almost) no event caching. Only CREATED/UPDATED events are possibly
      cached awaiting for the CHANGES_DONE_HINT that must follow them.
    - No event manipulation nor merging. GFileMonitor does a good job at
      being truthful, and the upper layers do know better how to coalesce
      events into a more reduced set of equivalent tasks, since there's
      further info like file state in the database.
    - The deprecated SEND_MOVED flag has been replaced by WATCH_MOVES. The
      MOVED_IN/MOVED_OUT/RENAMED events can be handled in a simpler way each
      than the deprecated MOVED event.
    
    Overall this makes TrackerMonitor slightly more verbose (but still
    consistent wrt sending a meaninful sequential set of changes), but more
    reactive overall, since we now solely rely on GFileMonitor rate limits.
    
    With this change, TrackerMinerFS is left as the only place that does
    coalescing of events.

 src/libtracker-miner/tracker-monitor.c | 768 +++++----------------------------
 1 file changed, 115 insertions(+), 653 deletions(-)
---
diff --git a/src/libtracker-miner/tracker-monitor.c b/src/libtracker-miner/tracker-monitor.c
index 61075a642..bc82c4f34 100644
--- a/src/libtracker-miner/tracker-monitor.c
+++ b/src/libtracker-miner/tracker-monitor.c
@@ -53,9 +53,7 @@ struct TrackerMonitorPrivate {
         */
        gboolean       use_changed_event;
 
-       GHashTable    *pre_update;
-       GHashTable    *pre_delete;
-       guint          event_pairs_timeout_id;
+       GHashTable    *cached_events;
 
        TrackerIndexingTree *tree;
 };
@@ -101,9 +99,11 @@ static GFileMonitor * directory_monitor_new        (TrackerMonitor *monitor,
 static void           directory_monitor_cancel     (GFileMonitor     *dir_monitor);
 
 
-static void           event_data_free              (gpointer        data);
-static void           emit_signal_for_event        (TrackerMonitor *monitor,
-                                                    EventData      *event_data);
+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);
 
@@ -214,23 +214,18 @@ tracker_monitor_init (TrackerMonitor *object)
                                       (GDestroyNotify) g_object_unref,
                                       (GDestroyNotify) directory_monitor_cancel);
 
-       priv->pre_update =
+       priv->cached_events =
                g_hash_table_new_full (g_file_hash,
                                       (GEqualFunc) g_file_equal,
-                                      (GDestroyNotify) g_object_unref,
-                                      event_data_free);
-       priv->pre_delete =
-               g_hash_table_new_full (g_file_hash,
-                                      (GEqualFunc) g_file_equal,
-                                      (GDestroyNotify) g_object_unref,
-                                      event_data_free);
+                                      g_object_unref,
+                                      NULL);
 
        /* For the first monitor we get the type and find out if we
         * are using inotify, FAM, polling, etc.
         */
        file = g_file_new_for_path (g_get_home_dir ());
        monitor = g_file_monitor_directory (file,
-                                           G_FILE_MONITOR_WATCH_MOUNTS,
+                                           G_FILE_MONITOR_WATCH_MOVES,
                                            NULL,
                                            &error);
 
@@ -321,12 +316,7 @@ tracker_monitor_finalize (GObject *object)
 
        priv = TRACKER_MONITOR_GET_PRIVATE (object);
 
-       if (priv->event_pairs_timeout_id) {
-               g_source_remove (priv->event_pairs_timeout_id);
-       }
-
-       g_hash_table_unref (priv->pre_update);
-       g_hash_table_unref (priv->pre_delete);
+       g_hash_table_unref (priv->cached_events);
        g_hash_table_unref (priv->monitors);
 
        G_OBJECT_CLASS (tracker_monitor_parent_class)->finalize (object);
@@ -441,52 +431,6 @@ check_is_directory (TrackerMonitor *monitor,
        return FALSE;
 }
 
-static EventData *
-event_data_new (GFile    *file,
-                GFile    *other_file,
-                gboolean  is_directory,
-                guint32   event_type)
-{
-       EventData *event;
-       GTimeVal now;
-
-       event = g_slice_new0 (EventData);
-       g_get_current_time (&now);
-
-       event->file = g_object_ref (file);
-       event->file_uri = g_file_get_uri (file);
-       if (other_file) {
-               event->other_file = g_object_ref (other_file);
-               event->other_file_uri = g_file_get_uri (other_file);
-       } else {
-               event->other_file = NULL;
-               event->other_file_uri = NULL;
-       }
-       event->is_directory = is_directory;
-       event->start_time = now;
-       event->event_type = event_type;
-       /* Always expirable when created */
-       event->expirable = TRUE;
-
-       return event;
-}
-
-static void
-event_data_free (gpointer data)
-{
-       EventData *event;
-
-       event = data;
-
-       g_object_unref (event->file);
-       g_free (event->file_uri);
-       if (event->other_file) {
-               g_object_unref (event->other_file);
-               g_free (event->other_file_uri);
-       }
-       g_slice_free (EventData, data);
-}
-
 gboolean
 tracker_monitor_move (TrackerMonitor *monitor,
                       GFile          *old_file,
@@ -601,8 +545,11 @@ monitor_event_to_string (GFileMonitorEvent event_type)
        case G_FILE_MONITOR_EVENT_MOVED:
                return "G_FILE_MONITOR_EVENT_MOVED";
        case G_FILE_MONITOR_EVENT_RENAMED:
+               return "G_FILE_MONITOR_EVENT_RENAMED";
        case G_FILE_MONITOR_EVENT_MOVED_IN:
+               return "G_FILE_MONITOR_EVENT_MOVED_IN";
        case G_FILE_MONITOR_EVENT_MOVED_OUT:
+               return "G_FILE_MONITOR_EVENT_MOVED_OUT";
                break;
        }
 
@@ -610,548 +557,78 @@ monitor_event_to_string (GFileMonitorEvent event_type)
 }
 
 static void
-emit_signal_for_event (TrackerMonitor *monitor,
-                       EventData      *event_data)
+emit_signal_for_event (TrackerMonitor    *monitor,
+                       GFileMonitorEvent  type,
+                       gboolean           is_directory,
+                       GFile             *file,
+                       GFile             *other_file)
 {
-       switch (event_data->event_type) {
+       /* Note that in any case we should be moving the monitors
+        * here to the new place, as the new place may be ignored.
+        * We should leave this to the upper layers. But one thing
+        * we must do is actually CANCEL all these monitors. */
+       if (is_directory &&
+           (type == G_FILE_MONITOR_EVENT_MOVED ||
+            type == G_FILE_MONITOR_EVENT_DELETED)) {
+               monitor_cancel_recursively (monitor, file);
+       }
+
+       switch (type) {
        case G_FILE_MONITOR_EVENT_CREATED:
-               g_debug ("Emitting ITEM_CREATED for (%s) '%s'",
-                        event_data->is_directory ? "DIRECTORY" : "FILE",
-                        event_data->file_uri);
                g_signal_emit (monitor,
                               signals[ITEM_CREATED], 0,
-                              event_data->file,
-                              event_data->is_directory);
-               /* Note that if the created item is a Directory, we must NOT
-                * add automatically a new monitor. */
+                              file, is_directory);
                break;
-
        case G_FILE_MONITOR_EVENT_CHANGED:
-       case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-               g_debug ("Emitting ITEM_UPDATED for (%s) '%s'",
-                        event_data->is_directory ? "DIRECTORY" : "FILE",
-                        event_data->file_uri);
                g_signal_emit (monitor,
                               signals[ITEM_UPDATED], 0,
-                              event_data->file,
-                              event_data->is_directory);
+                              file, is_directory);
                break;
-
        case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
-               g_debug ("Emitting ITEM_ATTRIBUTE_UPDATED for (%s) '%s'",
-                        event_data->is_directory ? "DIRECTORY" : "FILE",
-                        event_data->file_uri);
                g_signal_emit (monitor,
                               signals[ITEM_ATTRIBUTE_UPDATED], 0,
-                              event_data->file,
-                              event_data->is_directory);
+                              file, is_directory);
                break;
-
        case G_FILE_MONITOR_EVENT_DELETED:
-               g_debug ("Emitting ITEM_DELETED for (%s) '%s'",
-                        event_data->is_directory ? "DIRECTORY" : "FILE",
-                        event_data->file_uri);
                g_signal_emit (monitor,
                               signals[ITEM_DELETED], 0,
-                              event_data->file,
-                              event_data->is_directory);
+                              file, is_directory);
                break;
-
        case G_FILE_MONITOR_EVENT_MOVED:
-               /* Note that in any case we should be moving the monitors
-                * here to the new place, as the new place may be ignored.
-                * We should leave this to the upper layers. But one thing
-                * we must do is actually CANCEL all these monitors. */
-               if (event_data->is_directory) {
-                       monitor_cancel_recursively (monitor,
-                                                   event_data->file);
-               }
-
-               /* Try to avoid firing up events for ignored files */
-               if (monitor->priv->tree &&
-                   !tracker_indexing_tree_file_is_indexable (monitor->priv->tree,
-                                                             event_data->file,
-                                                             (event_data->is_directory ?
-                                                              G_FILE_TYPE_DIRECTORY :
-                                                              G_FILE_TYPE_REGULAR))) {
-                       g_debug ("Emitting ITEM_UPDATED for %s (%s) from "
-                                "a move event, source is not indexable",
-                                event_data->other_file_uri,
-                                event_data->is_directory ? "DIRECTORY" : "FILE");
-                       g_signal_emit (monitor,
-                                      signals[ITEM_UPDATED], 0,
-                                      event_data->other_file,
-                                      event_data->is_directory);
-               } else if (monitor->priv->tree &&
-                          !tracker_indexing_tree_file_is_indexable (monitor->priv->tree,
-                                                                    event_data->other_file,
-                                                                    (event_data->is_directory ?
-                                                                     G_FILE_TYPE_DIRECTORY :
-                                                                     G_FILE_TYPE_REGULAR))) {
-                       g_debug ("Emitting ITEM_DELETED for %s (%s) from "
-                                "a move event, destination is not indexable",
-                                event_data->file_uri,
-                                event_data->is_directory ? "DIRECTORY" : "FILE");
-                       g_signal_emit (monitor,
-                                      signals[ITEM_DELETED], 0,
-                                      event_data->file,
-                                      event_data->is_directory);
-               } else  {
-                       g_debug ("Emitting ITEM_MOVED for (%s) '%s'->'%s'",
-                                event_data->is_directory ? "DIRECTORY" : "FILE",
-                                event_data->file_uri,
-                                event_data->other_file_uri);
-                       g_signal_emit (monitor,
-                                      signals[ITEM_MOVED], 0,
-                                      event_data->file,
-                                      event_data->other_file,
-                                      event_data->is_directory,
-                                      TRUE);
-               }
-
+               g_signal_emit (monitor,
+                              signals[ITEM_MOVED], 0,
+                              file, other_file, is_directory, TRUE);
                break;
-
-       case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
-       case G_FILE_MONITOR_EVENT_UNMOUNTED:
-               /* Do nothing */
+       default:
+               g_warning ("Trying to emit monitor signal with unhandled event %d",
+                          type);
                break;
        }
 }
 
 static void
-event_pairs_process_in_ht (TrackerMonitor *monitor,
-                           GHashTable     *ht,
-                           GTimeVal       *now)
-{
-       GHashTableIter iter;
-       gpointer key, value;
-       GList *expired_events = NULL;
-       GList *l;
-
-       /* Start iterating the HT of events, and see if any of them was expired.
-        * If so, STEAL the item from the HT, add it in an auxiliary list, and
-        * once the whole HT has been iterated, emit the signals for the stolen
-        * items. If the signal is emitted WHILE iterating the HT, we may end up
-        * with some upper layer action modifying the HT we are iterating, and
-        * that is not good. */
-       g_hash_table_iter_init (&iter, ht);
-       while (g_hash_table_iter_next (&iter, &key, &value)) {
-               EventData *event_data = value;
-               glong seconds;
-
-               /* If event is not yet expirable, keep it */
-               if (!event_data->expirable)
-                       continue;
-
-               /* If event is expirable, but didn't expire yet, keep it */
-               seconds = now->tv_sec - event_data->start_time.tv_sec;
-               if (seconds < 2)
-                       continue;
-
-               g_debug ("Event '%s' for URI '%s' has timed out (%ld seconds have elapsed)",
-                        monitor_event_to_string (event_data->event_type),
-                        event_data->file_uri,
-                        seconds);
-               /* STEAL the item from the HT, so that disposal methods
-                * for key and value are not called. */
-               g_hash_table_iter_steal (&iter);
-               /* Unref the key, as no longer needed */
-               g_object_unref (key);
-               /* Add the expired event to our temp list */
-               expired_events = g_list_append (expired_events, event_data);
-       }
-
-       for (l = expired_events; l; l = g_list_next (l)) {
-               /* Emit signal for the expired event */
-               emit_signal_for_event (monitor, l->data);
-               /* And dispose the event data */
-               event_data_free (l->data);
-       }
-       g_list_free (expired_events);
-}
-
-static gboolean
-event_pairs_timeout_cb (gpointer user_data)
-{
-       TrackerMonitor *monitor;
-       GTimeVal now;
-
-       monitor = user_data;
-       g_get_current_time (&now);
-
-       /* Process PRE-UPDATE hash table */
-       event_pairs_process_in_ht (monitor, monitor->priv->pre_update, &now);
-
-       /* Process PRE-DELETE hash table */
-       event_pairs_process_in_ht (monitor, monitor->priv->pre_delete, &now);
-
-       if (g_hash_table_size (monitor->priv->pre_update) > 0 ||
-           g_hash_table_size (monitor->priv->pre_delete) > 0) {
-               return TRUE;
-       }
-
-       g_debug ("No more events to pair");
-       monitor->priv->event_pairs_timeout_id = 0;
-       return FALSE;
-}
-
-static void
-monitor_event_file_created (TrackerMonitor *monitor,
-                            GFile          *file)
-{
-       EventData *new_event;
-
-       /*  - When a G_FILE_MONITOR_EVENT_CREATED(A) is received,
-        *    -- Add it to the cache, replacing any previous element
-        *       (shouldn't be any)
-        */
-       new_event = event_data_new (file,
-                                   NULL,
-                                   FALSE,
-                                   G_FILE_MONITOR_EVENT_CREATED);
-
-       /* There will be a CHANGES_DONE_HINT emitted after the CREATED
-        * event, so we set it as non-expirable. It will be set as
-        * expirable when the CHANGES_DONE_HINT is received.
-        */
-       new_event->expirable = FALSE;
-
-       g_hash_table_replace (monitor->priv->pre_update,
-                             g_object_ref (file),
-                             new_event);
-}
-
-static void
-monitor_event_file_changed (TrackerMonitor *monitor,
-                            GFile          *file)
-{
-       EventData *previous_update_event_data;
-
-       /* Get previous event data, if any */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, file);
-
-       /* If use_changed_event, treat as an ATTRIBUTE_CHANGED. Otherwise,
-        * assume there will be a CHANGES_DONE_HINT afterwards... */
-       if (!monitor->priv->use_changed_event) {
-               /* Process the CHANGED event knowing that there will be a CHANGES_DONE_HINT */
-               if (previous_update_event_data) {
-                       if (previous_update_event_data->event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) 
{
-                               /* If there is a previous ATTRIBUTE_CHANGED still not notified,
-                                * remove it, as we know there will be a CHANGES_DONE_HINT afterwards
-                                */
-                               g_hash_table_remove (monitor->priv->pre_update, file);
-                       } else if (previous_update_event_data->event_type == G_FILE_MONITOR_EVENT_CREATED) {
-                               /* If we got a CHANGED event before the CREATED was expired,
-                                * set the CREATED as not expirable, as we expect a CHANGES_DONE_HINT
-                                * afterwards. */
-                               previous_update_event_data->expirable = FALSE;
-                       }
-               }
-               return;
-       }
-
-       /* For FAM-based monitors, there won't be a CHANGES_DONE_HINT, so use the CHANGED
-        * events. */
-
-       if (!previous_update_event_data) {
-               /* If no previous one, insert it */
-               g_hash_table_insert (monitor->priv->pre_update,
-                                    g_object_ref (file),
-                                    event_data_new (file,
-                                                    NULL,
-                                                    FALSE,
-                                                    G_FILE_MONITOR_EVENT_CHANGED));
-               return;
-       }
-
-       if (previous_update_event_data->event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) {
-               /* Replace the previous ATTRIBUTE_CHANGED event with a CHANGED one. */
-               g_hash_table_replace (monitor->priv->pre_update,
-                                     g_object_ref (file),
-                                     event_data_new (file,
-                                                     NULL,
-                                                     FALSE,
-                                                     G_FILE_MONITOR_EVENT_CHANGED));
-       } else {
-               /* Update the start_time of the previous one */
-               g_get_current_time (&(previous_update_event_data->start_time));
-       }
-}
-
-static void
-monitor_event_file_attribute_changed (TrackerMonitor *monitor,
-                                      GFile          *file)
-{
-       EventData *previous_update_event_data;
-
-       /* Get previous event data, if any */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, file);
-       if (!previous_update_event_data) {
-               /* If no previous one, insert it */
-               g_hash_table_insert (monitor->priv->pre_update,
-                                    g_object_ref (file),
-                                    event_data_new (file,
-                                                    NULL,
-                                                    FALSE,
-                                                    G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED));
-               return;
-       }
-
-       if (previous_update_event_data->event_type == G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED) {
-               /* Update the start_time of the previous one, if it is an ATTRIBUTE_CHANGED
-                * event. */
-               g_get_current_time (&(previous_update_event_data->start_time));
-
-               /* No need to update event time in CREATED, as these events
-                * only expire when there is a CHANGES_DONE_HINT.
-                */
-       }
-}
-
-static void
-monitor_event_file_changes_done (TrackerMonitor *monitor,
-                                 GFile          *file)
-{
-       EventData *previous_update_event_data;
-
-       /* Get previous event data, if any */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, file);
-       if (!previous_update_event_data) {
-               /* Insert new update item in cache */
-               g_hash_table_insert (monitor->priv->pre_update,
-                                    g_object_ref (file),
-                                    event_data_new (file,
-                                                    NULL,
-                                                    FALSE,
-                                                    G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT));
-               return;
-       }
-
-       /* Refresh event timer, and make sure the event is now set as expirable */
-       g_get_current_time (&(previous_update_event_data->start_time));
-       previous_update_event_data->expirable = TRUE;
-}
-
-static void
-monitor_event_file_deleted (TrackerMonitor *monitor,
-                            GFile          *file)
-{
-       EventData *new_event;
-       EventData *previous_update_event_data;
-
-       /* Get previous event data, if any */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, file);
-
-       /* Remove any previous pending event (if blacklisting enabled, there may be some) */
-       if (previous_update_event_data) {
-               GFileMonitorEvent previous_update_event_type;
-
-               previous_update_event_type = previous_update_event_data->event_type;
-               g_hash_table_remove (monitor->priv->pre_update, file);
-
-               if (previous_update_event_type == G_FILE_MONITOR_EVENT_CREATED) {
-                       /* Oh, oh, oh, we got a previous CREATED event waiting in the event
-                        * cache... so we cancel it with the DELETED and don't notify anything */
-                       return;
-               }
-               /* else, keep on notifying the event */
-       }
-
-       new_event = event_data_new (file,
-                                   NULL,
-                                   FALSE,
-                                   G_FILE_MONITOR_EVENT_DELETED);
-       emit_signal_for_event (monitor, new_event);
-       event_data_free (new_event);
-}
-
-static void
-monitor_event_file_moved (TrackerMonitor *monitor,
-                          GFile          *src_file,
-                          GFile          *dst_file)
-{
-       EventData *new_event;
-       EventData *previous_update_event_data;
-
-       /* Get previous event data, if any */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, src_file);
-
-       /* Some event-merging that can also be enabled if doing blacklisting, as we are
-        * queueing the CHANGES_DONE_HINT in the cache :
-        *   (a) CREATED(A)      + MOVED(A->B)  = CREATED (B)
-        *   (b) UPDATED(A)      + MOVED(A->B)  = MOVED(A->B) + UPDATED(B)
-        *   (c) ATTR_UPDATED(A) + MOVED(A->B)  = MOVED(A->B) + UPDATED(B)
-        * Notes:
-        *  - In case (a), the CREATED event is queued in the cache.
-        *  - In case (a), note that B may already exist before, so instead of a CREATED
-        *    we should be issuing an UPDATED instead... we don't do it as at the end we
-        *    don't really mind, and we save a call to g_file_query_exists().
-        *  - In case (b), the MOVED event is emitted right away, while the UPDATED
-        *    one is queued in the cache.
-        *  - In case (c), we issue an UPDATED instead of ATTR_UPDATED, because there may
-        *    already be another UPDATED or CREATED event for B, so we make sure in this
-        *    way that not only attributes get checked at the end.
-        * */
-       if (previous_update_event_data) {
-               if (previous_update_event_data->event_type == G_FILE_MONITOR_EVENT_CREATED) {
-                       /* (a) CREATED(A) + MOVED(A->B)  = UPDATED (B)
-                        *
-                        * Oh, oh, oh, we got a previous created event
-                        * waiting in the event cache... so we remove it, and we
-                        * convert the MOVE event into a UPDATED(other_file),
-                        *
-                        * It is UPDATED instead of CREATED because the destination
-                        * file could probably exist, and mistakenly reporting
-                        * a CREATED event there can trick the monitor event
-                        * compression (for example, if we get a DELETED right after,
-                        * both CREATED/DELETED events turn into NOOP, so a no
-                        * longer existing file didn't trigger a DELETED.
-                        *
-                        * Instead, it is safer to just issue UPDATED, this event
-                        * wouldn't get compressed, and CREATED==UPDATED to the
-                        * miners' eyes.
-                        */
-                       g_hash_table_remove (monitor->priv->pre_update, src_file);
-                       g_hash_table_replace (monitor->priv->pre_update,
-                                             g_object_ref (dst_file),
-                                             event_data_new (dst_file,
-                                                             NULL,
-                                                             FALSE,
-                                                             G_FILE_MONITOR_EVENT_CHANGED));
-
-                       /* Do not notify the moved event now */
-                       return;
-               }
-
-               /*   (b) UPDATED(A)      + MOVED(A->B)  = MOVED(A->B) + UPDATED(B)
-                *   (c) ATTR_UPDATED(A) + MOVED(A->B)  = MOVED(A->B) + UPDATED(B)
-                *
-                * We setup here the UPDATED(B) event, added to the cache */
-               g_hash_table_replace (monitor->priv->pre_update,
-                                     g_object_ref (dst_file),
-                                     event_data_new (dst_file,
-                                                     NULL,
-                                                     FALSE,
-                                                     G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT));
-               /* Remove previous event */
-               g_hash_table_remove (monitor->priv->pre_update, src_file);
-
-               /* And keep on notifying the MOVED event */
-       }
-
-       new_event = event_data_new (src_file,
-                                   dst_file,
-                                   FALSE,
-                                   G_FILE_MONITOR_EVENT_MOVED);
-       emit_signal_for_event (monitor, new_event);
-       event_data_free (new_event);
-}
-
-static void
-monitor_event_directory_created_or_changed (TrackerMonitor    *monitor,
-                                            GFile             *dir,
-                                            GFileMonitorEvent  event_type)
-{
-       EventData *previous_delete_event_data;
-
-       /* If any previous event on this item, notify it
-        *  before creating it */
-       previous_delete_event_data = g_hash_table_lookup (monitor->priv->pre_delete, dir);
-       if (previous_delete_event_data) {
-               emit_signal_for_event (monitor, previous_delete_event_data);
-               g_hash_table_remove (monitor->priv->pre_delete, dir);
-       }
-
-       if (!g_hash_table_lookup (monitor->priv->pre_update, dir)) {
-               g_hash_table_insert (monitor->priv->pre_update,
-                                    g_object_ref (dir),
-                                    event_data_new (dir,
-                                                    NULL,
-                                                    TRUE,
-                                                    event_type));
-       }
-}
-
-static void
-monitor_event_directory_deleted (TrackerMonitor *monitor,
-                                 GFile          *dir)
+flush_cached_event (TrackerMonitor *monitor,
+                    GFile          *file,
+                    gboolean        is_directory)
 {
-       EventData *previous_update_event_data;
-       EventData *previous_delete_event_data;
-
-       /* If any previous update event on this item, notify it */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, dir);
-       if (previous_update_event_data) {
-               emit_signal_for_event (monitor, previous_update_event_data);
-               g_hash_table_remove (monitor->priv->pre_update, dir);
-       }
+       GFileMonitorEvent prev_event_type;
 
-       /* Check if there is a previous delete event */
-       previous_delete_event_data = g_hash_table_lookup (monitor->priv->pre_delete, dir);
-       if (previous_delete_event_data &&
-           previous_delete_event_data->event_type == G_FILE_MONITOR_EVENT_MOVED) {
-               g_debug ("Processing MOVE(A->B) + DELETE(A) as MOVE(A->B) for directory '%s->%s'",
-                        previous_delete_event_data->file_uri,
-                        previous_delete_event_data->other_file_uri);
-
-               emit_signal_for_event (monitor, previous_delete_event_data);
-               g_hash_table_remove (monitor->priv->pre_delete, dir);
-               return;
+       if (g_hash_table_lookup_extended (monitor->priv->cached_events,
+                                         file, NULL, (gpointer*) &prev_event_type)) {
+               g_hash_table_remove (monitor->priv->cached_events, file);
+               emit_signal_for_event (monitor, prev_event_type,
+                                      is_directory, file, NULL);
        }
-
-       /* If no previous, add to HT */
-       g_hash_table_replace (monitor->priv->pre_delete,
-                             g_object_ref (dir),
-                             event_data_new (dir,
-                                             NULL,
-                                             TRUE,
-                                             G_FILE_MONITOR_EVENT_DELETED));
 }
 
 static void
-monitor_event_directory_moved (TrackerMonitor *monitor,
-                               GFile          *src_dir,
-                               GFile          *dst_dir)
+cache_event (TrackerMonitor    *monitor,
+             GFile             *file,
+             GFileMonitorEvent  event_type)
 {
-       EventData *previous_update_event_data;
-       EventData *previous_delete_event_data;
-
-       /* If any previous update event on this item, notify it */
-       previous_update_event_data = g_hash_table_lookup (monitor->priv->pre_update, src_dir);
-       if (previous_update_event_data) {
-               emit_signal_for_event (monitor, previous_update_event_data);
-               g_hash_table_remove (monitor->priv->pre_update, src_dir);
-       }
-
-       /* Check if there is a previous delete event */
-       previous_delete_event_data = g_hash_table_lookup (monitor->priv->pre_delete, src_dir);
-       if (previous_delete_event_data &&
-           previous_delete_event_data->event_type == G_FILE_MONITOR_EVENT_DELETED) {
-               EventData *new_event;
-
-               new_event = event_data_new (src_dir,
-                                           dst_dir,
-                                           TRUE,
-                                           G_FILE_MONITOR_EVENT_MOVED);
-               g_debug ("Processing DELETE(A) + MOVE(A->B) as MOVE(A->B) for directory '%s->%s'",
-                        new_event->file_uri,
-                        new_event->other_file_uri);
-
-               emit_signal_for_event (monitor, new_event);
-               event_data_free (new_event);
-
-               /* And remove the previous DELETE */
-               g_hash_table_remove (monitor->priv->pre_delete, src_dir);
-               return;
-       }
-
-       /* If no previous, add to HT */
-       g_hash_table_replace (monitor->priv->pre_delete,
-                             g_object_ref (src_dir),
-                             event_data_new (src_dir,
-                                             dst_dir,
-                                             TRUE,
-                                             G_FILE_MONITOR_EVENT_MOVED));
+       g_hash_table_insert (monitor->priv->cached_events,
+                            g_object_ref (file),
+                            GUINT_TO_POINTER (event_type));
 }
 
 static void
@@ -1164,7 +641,7 @@ monitor_event_cb (GFileMonitor      *file_monitor,
        TrackerMonitor *monitor;
        gchar *file_uri;
        gchar *other_file_uri;
-       gboolean is_directory;
+       gboolean is_directory = FALSE;
 
        monitor = user_data;
 
@@ -1197,10 +674,12 @@ monitor_event_cb (GFileMonitor      *file_monitor,
                         is_directory ? "directory" : "file",
                         file_uri);
        } else {
-               /* If we have other_file, it means an item was moved from file to other_file;
-                * so, it makes sense to check if the other_file is directory instead of
-                * the origin file, as this one will not exist any more */
-               is_directory = check_is_directory (monitor, other_file);
+               if (event_type == G_FILE_MONITOR_EVENT_RENAMED ||
+                   event_type == G_FILE_MONITOR_EVENT_MOVED_OUT) {
+                       is_directory = check_is_directory (monitor, other_file);
+               } else if (event_type == G_FILE_MONITOR_EVENT_MOVED_IN) {
+                       is_directory = check_is_directory (monitor, file);
+               }
 
                /* Avoid doing anything of both
                 * file/other_file are non-indexable
@@ -1228,78 +707,61 @@ monitor_event_cb (GFileMonitor      *file_monitor,
                         other_file_uri);
        }
 
-       if (!is_directory) {
-               /* FILE Events */
-               switch (event_type) {
-               case G_FILE_MONITOR_EVENT_CREATED:
-                       monitor_event_file_created (monitor, file);
-                       break;
-               case G_FILE_MONITOR_EVENT_CHANGED:
-                       monitor_event_file_changed (monitor, file);
-                       break;
-               case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
-                       monitor_event_file_attribute_changed (monitor, file);
-                       break;
-               case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-                       monitor_event_file_changes_done (monitor, file);
-                       break;
-               case G_FILE_MONITOR_EVENT_DELETED:
-                       monitor_event_file_deleted (monitor, file);
-                       break;
-               case G_FILE_MONITOR_EVENT_MOVED:
-                       monitor_event_file_moved (monitor, file, other_file);
-                       break;
-               case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
-               case G_FILE_MONITOR_EVENT_UNMOUNTED:
-                       /* Do nothing */
-               case G_FILE_MONITOR_EVENT_RENAMED:
-               case G_FILE_MONITOR_EVENT_MOVED_IN:
-               case G_FILE_MONITOR_EVENT_MOVED_OUT:
-                       /* We don't use G_FILE_MONITOR_WATCH_MOVES */
-                       break;
-               }
-       } else {
-               /* DIRECTORY Events */
-
-               switch (event_type) {
-               case G_FILE_MONITOR_EVENT_CREATED:
-               case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
-               case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
-               case G_FILE_MONITOR_EVENT_CHANGED:
-                       monitor_event_directory_created_or_changed (monitor, file, event_type);
-                       break;
-               case G_FILE_MONITOR_EVENT_DELETED:
-                       monitor_event_directory_deleted (monitor, file);
-                       break;
-               case G_FILE_MONITOR_EVENT_MOVED:
-                       monitor_event_directory_moved (monitor, file, other_file);
-                       break;
-               case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
-               case G_FILE_MONITOR_EVENT_UNMOUNTED:
-                       /* Do nothing */
-               case G_FILE_MONITOR_EVENT_RENAMED:
-               case G_FILE_MONITOR_EVENT_MOVED_IN:
-               case G_FILE_MONITOR_EVENT_MOVED_OUT:
-                       /* We don't use G_FILE_MONITOR_WATCH_MOVES */
-                       break;
+       switch (event_type) {
+       case G_FILE_MONITOR_EVENT_CREATED:
+       case G_FILE_MONITOR_EVENT_CHANGED:
+               if (monitor->priv->use_changed_event) {
+                       cache_event (monitor, file, event_type);
+               } else {
+                       emit_signal_for_event (monitor, event_type,
+                                              is_directory, file, NULL);
                }
-       }
-
-       if (g_hash_table_size (monitor->priv->pre_update) > 0 ||
-           g_hash_table_size (monitor->priv->pre_delete) > 0) {
-               if (monitor->priv->event_pairs_timeout_id == 0) {
-                       g_debug ("Waiting for event pairs");
-                       monitor->priv->event_pairs_timeout_id =
-                               g_timeout_add_seconds (CACHE_LIFETIME_SECONDS,
-                                                      event_pairs_timeout_cb,
-                                                      monitor);
+               break;
+       case G_FILE_MONITOR_EVENT_DELETED:
+       case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+               emit_signal_for_event (monitor, event_type,
+                                      is_directory, file, NULL);
+               break;
+       case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+               flush_cached_event (monitor, file, is_directory);
+               break;
+       case G_FILE_MONITOR_EVENT_MOVED_IN:
+               if (other_file) {
+                       /* Both MOVED_IN and MOVE_OUT are fine points to emit
+                        * ::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);
+               } else {
+                       /* No known origin, treat as a new file */
+                       emit_signal_for_event (monitor,
+                                              G_FILE_MONITOR_EVENT_CREATED,
+                                              is_directory,
+                                              file, NULL);
                }
-       } else {
-               if (monitor->priv->event_pairs_timeout_id != 0) {
-                       g_source_remove (monitor->priv->event_pairs_timeout_id);
+               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);
                }
-
-               monitor->priv->event_pairs_timeout_id = 0;
+               break;
+       case G_FILE_MONITOR_EVENT_RENAMED:
+               emit_signal_for_event (monitor,
+                                      G_FILE_MONITOR_EVENT_MOVED,
+                                      is_directory, file, other_file);
+               break;
+       case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
+       case G_FILE_MONITOR_EVENT_UNMOUNTED:
+       case G_FILE_MONITOR_EVENT_MOVED:
+               g_warn_if_reached ();
+               break;
        }
 
        g_free (file_uri);
@@ -1314,7 +776,7 @@ directory_monitor_new (TrackerMonitor *monitor,
        GError *error = NULL;
 
        file_monitor = g_file_monitor_directory (file,
-                                                G_FILE_MONITOR_SEND_MOVED | G_FILE_MONITOR_WATCH_MOUNTS,
+                                                G_FILE_MONITOR_WATCH_MOVES,
                                                 NULL,
                                                 &error);
 


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