[gtk/wip/otte/listmodel: 18/22] testtreelistmodel: Make the directory loading async



commit 8e1f3c7d8c81df157bbe9dee314150a5f7b9b6bf
Author: Benjamin Otte <otte redhat com>
Date:   Wed Sep 12 05:04:17 2018 +0200

    testtreelistmodel: Make the directory loading async
    
    This is way more complicated than it should be, because it requires
    manually limiting the number of open file enumerators.
    
    On the other hand, it exhaustively tests the items-changed emission of
    all involved listmodels because those signals come in pretty much
    randomly.
    
    It's also 50% slower than the sync version, with the caeat that the sync
    version only shows the UI after it's done loading, while this version
    shows it right away.

 tests/testtreelistmodel.c | 119 ++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 100 insertions(+), 19 deletions(-)
---
diff --git a/tests/testtreelistmodel.c b/tests/testtreelistmodel.c
index 6b3fa7a9c9..42ec20a3b2 100644
--- a/tests/testtreelistmodel.c
+++ b/tests/testtreelistmodel.c
@@ -2,42 +2,123 @@
 
 #define ROWS 30
 
-static GListModel *
-create_list_model_for_directory (gpointer file,
-                                 gpointer unused)
+GSList *pending;
+guint active = 0;
+
+static void
+got_files (GObject      *enumerate,
+           GAsyncResult *res,
+           gpointer      store);
+
+static gboolean
+start_enumerate (GListStore *store)
 {
   GFileEnumerator *enumerate;
-  GListStore *store;
-  GFile *child;
-  GFileInfo *info;
-
-  if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) != G_FILE_TYPE_DIRECTORY)
-    return NULL;
+  GFile *file = g_object_get_data (G_OBJECT (store), "file");
+  GError *error = NULL;
 
   enumerate = g_file_enumerate_children (file,
                                          G_FILE_ATTRIBUTE_STANDARD_TYPE
                                          "," G_FILE_ATTRIBUTE_STANDARD_NAME,
                                          0,
                                          NULL,
-                                         NULL);
+                                         &error);
+
   if (enumerate == NULL)
-    return NULL;
+    {
+      if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_OPEN_FILES))
+        {
+          g_clear_error (&error);
+          pending = g_slist_prepend (pending, g_object_ref (store));
+          return TRUE;
+        }
+
+      g_clear_error (&error);
+      g_object_unref (store);
+      return FALSE;
+    }
 
-  store = g_list_store_new (G_TYPE_FILE);
+  if (active > 20)
+    {
+      g_object_unref (enumerate);
+      pending = g_slist_prepend (pending, g_object_ref (store));
+      return TRUE;
+    }
 
-  while (g_file_enumerator_iterate (enumerate, &info, NULL, NULL, NULL))
+  active++;
+  g_file_enumerator_next_files_async (enumerate,
+                                      g_file_is_native (file) ? 5000 : 100,
+                                      G_PRIORITY_DEFAULT_IDLE,
+                                      NULL,
+                                      got_files,
+                                      g_object_ref (store));
+
+  g_object_unref (enumerate);
+  return TRUE;
+}
+
+static void
+got_files (GObject      *enumerate,
+           GAsyncResult *res,
+           gpointer      store)
+{
+  GList *l, *files;
+  GFile *file = g_object_get_data (store, "file");
+  GPtrArray *array;
+
+  files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (enumerate), res, NULL);
+  if (files == NULL)
+    {
+      g_object_unref (store);
+      if (pending)
+        {
+          GListStore *store = pending->data;
+          pending = g_slist_remove (pending, store);
+          start_enumerate (store);
+        }
+      active--;
+      return;
+    }
+
+  array = g_ptr_array_new ();
+  g_ptr_array_new_with_free_func (g_object_unref);
+  for (l = files; l; l = l->next)
     {
-      if (info == NULL)
-        break;
+      GFileInfo *info = l->data;
+      GFile *child;
 
       child = g_file_get_child (file, g_file_info_get_name (info));
-      g_list_store_append (store, child);
-      g_object_unref (child);
+      g_ptr_array_add (array, child);
     }
+  g_list_free_full (files, g_object_unref);
 
-  g_object_unref (enumerate);
+  g_list_store_splice (store, g_list_model_get_n_items (store), 0, array->pdata, array->len);
+  g_ptr_array_unref (array);
 
-  return G_LIST_MODEL (store);
+  g_file_enumerator_next_files_async (G_FILE_ENUMERATOR (enumerate),
+                                      g_file_is_native (file) ? 5000 : 100,
+                                      G_PRIORITY_DEFAULT_IDLE,
+                                      NULL,
+                                      got_files,
+                                      store);
+}
+
+static GListModel *
+create_list_model_for_directory (gpointer file,
+                                 gpointer unused)
+{
+  GListStore *store;
+
+  if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) != G_FILE_TYPE_DIRECTORY)
+    return NULL;
+
+  store = g_list_store_new (G_TYPE_FILE);
+  g_object_set_data_full (G_OBJECT (store), "file", g_object_ref (file), g_object_unref);
+
+  if (start_enumerate (store))
+    return G_LIST_MODEL (store);
+  else
+    return NULL;
 }
 
 static GtkWidget *


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