[gtk+] trashmonitor: change trash monitoring process



commit 65687ba3921f0d5db8b17a85a526a906b614fa0f
Author: Razvan Chitu <razvan ch95 gmail com>
Date:   Mon Mar 14 11:55:00 2016 +0200

    trashmonitor: change trash monitoring process
    
    The trash is monitored for state changes - going from empty to non-empty and the
    other way round. Monitoring is done by handling change signals from a regular
    file monitor. On each signal, an enumeration of the trash contents is started in
    order to see if it is empty or not. This causes issues when many files are
    trashed, because the gvfs trash backend is flooded with enumeration requests,
    resulting in CPU usage spikes. In order to fix this, the "item-count" attribute
    of the trash should be queried instead.
    
    Replace asynchronous enumeration with asynchronous information query and update
    the trash state based on the "item-count" attribute. Emit state change signal
    only when the state actually changes.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=763600

 gtk/gtktrashmonitor.c |   78 +++++++++++++++++-------------------------------
 1 files changed, 28 insertions(+), 50 deletions(-)
---
diff --git a/gtk/gtktrashmonitor.c b/gtk/gtktrashmonitor.c
index 6ea4563..73d0590 100644
--- a/gtk/gtktrashmonitor.c
+++ b/gtk/gtktrashmonitor.c
@@ -97,61 +97,43 @@ static void
 update_has_trash_and_notify (GtkTrashMonitor *monitor,
                             gboolean has_trash)
 {
+  if (monitor->has_trash == !!has_trash)
+    return;
+
   monitor->has_trash = !!has_trash;
 
   g_signal_emit (monitor, signals[TRASH_STATE_CHANGED], 0); 
 }
 
+/* Use G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT since we only want to know whether the
+ * trash is empty or not, not access its children. This is available for the
+ * trash backend since it uses a cache. In this way we prevent flooding the
+ * trash backend with enumeration requests when trashing > 1000 files
+ */
 static void
-trash_enumerate_next_files_cb (GObject *source,
-                              GAsyncResult *result,
-                              gpointer user_data)
+trash_query_info_cb (GObject *source,
+                     GAsyncResult *result,
+                     gpointer user_data)
 {
   GtkTrashMonitor *monitor = GTK_TRASH_MONITOR (user_data);
-  GFileEnumerator *enumerator;
-  GList *infos;
+  GFileInfo *info;
+  guint32 item_count;
+  gboolean has_trash = FALSE;
 
-  enumerator = G_FILE_ENUMERATOR (source);
+  info = g_file_query_info_finish (G_FILE (source), result, NULL);
 
-  infos = g_file_enumerator_next_files_finish (enumerator, result, NULL);
-  if (infos)
-    {
-      update_has_trash_and_notify (monitor, TRUE);
-      g_list_free_full (infos, g_object_unref);
-    }
-  else
+  if (info != NULL)
     {
-      update_has_trash_and_notify (monitor, FALSE);
+      item_count = g_file_info_get_attribute_uint32 (info,
+                                                     G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
+      has_trash = item_count > 0;
+
+      g_object_unref (info);
     }
 
-  g_object_unref (monitor); /* was reffed in recompute_trash_state() */
-}
+  update_has_trash_and_notify (monitor, has_trash);
 
-/* Callback used from g_file_enumerate_children_async() - this is what enumerates "trash:///" */
-static void
-trash_enumerate_children_cb (GObject *source,
-                            GAsyncResult *result,
-                            gpointer user_data)
-{
-  GtkTrashMonitor *monitor = GTK_TRASH_MONITOR (user_data);
-  GFileEnumerator *enumerator;
-
-  enumerator = g_file_enumerate_children_finish (G_FILE (source), result, NULL);
-  if (enumerator)
-    {
-      g_file_enumerator_next_files_async (enumerator,
-                                         1,
-                                         G_PRIORITY_DEFAULT,
-                                         NULL,
-                                         trash_enumerate_next_files_cb,
-                                         monitor);
-      g_object_unref (enumerator);
-    }
-  else
-    {
-      update_has_trash_and_notify (monitor, FALSE);
-      g_object_unref (monitor); /* was reffed in recompute_trash_state() */
-    }
+  g_object_unref (monitor); /* was reffed in recompute_trash_state() */
 }
 
 /* Asynchronously recomputes whether there is trash or not */
@@ -160,16 +142,12 @@ recompute_trash_state (GtkTrashMonitor *monitor)
 {
   GFile *file;
 
-  g_object_ref (monitor);
-
   file = g_file_new_for_uri ("trash:///");
-  g_file_enumerate_children_async (file,
-                                  G_FILE_ATTRIBUTE_STANDARD_TYPE,
-                                  G_FILE_QUERY_INFO_NONE,
-                                  G_PRIORITY_DEFAULT,
-                                  NULL,
-                                  trash_enumerate_children_cb,
-                                  monitor);
+  g_file_query_info_async (file,
+                           G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT,
+                           G_FILE_QUERY_INFO_NONE,
+                           G_PRIORITY_DEFAULT, NULL,
+                           trash_query_info_cb, g_object_ref (monitor));
 
   g_object_unref (file);
 }


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