18fbde
From b593bf334a7aada890d8ec4ce358c9f431b605ff Mon Sep 17 00:00:00 2001
18fbde
From: Carlos Garnacho <carlosg@gnome.org>
18fbde
Date: Mon, 3 Dec 2018 13:09:47 +0100
18fbde
Subject: [PATCH 1/4] shell-global: Make saving of persistent state
18fbde
 asynchronous
18fbde
18fbde
This is an expensive operation that is best avoided in the main loop. Given
18fbde
the call doesn't care much about returning error or status, it can just
18fbde
be made async within.
18fbde
18fbde
Every operation on a given file will be destructive wrt previous
18fbde
operations on the same file, so we just cancel any pending operation on
18fbde
it before batching the current one.
18fbde
18fbde
Closes: https://gitlab.gnome.org/GNOME/gnome-shell/issues/815
18fbde
---
18fbde
 src/shell-global.c | 85 ++++++++++++++++++++++++++++++++++++++++------
18fbde
 1 file changed, 75 insertions(+), 10 deletions(-)
18fbde
18fbde
diff --git a/src/shell-global.c b/src/shell-global.c
18fbde
index 961fd3a70..8f5418677 100644
18fbde
--- a/src/shell-global.c
18fbde
+++ b/src/shell-global.c
18fbde
@@ -88,6 +88,8 @@ struct _ShellGlobal {
18fbde
   /* For sound notifications */
18fbde
   ca_context *sound_context;
18fbde
 
18fbde
+  GHashTable *save_ops;
18fbde
+
18fbde
   gboolean has_modal;
18fbde
   gboolean frame_timestamps;
18fbde
   gboolean frame_finish_timestamp;
18fbde
@@ -331,6 +333,10 @@ shell_global_init (ShellGlobal *global)
18fbde
                                      NULL);
18fbde
 
18fbde
   g_strfreev (search_path);
18fbde
+
18fbde
+  global->save_ops = g_hash_table_new_full (g_file_hash,
18fbde
+                                            (GEqualFunc) g_file_equal,
18fbde
+                                            g_object_unref, g_object_unref);
18fbde
 }
18fbde
 
18fbde
 static void
18fbde
@@ -350,6 +356,8 @@ shell_global_finalize (GObject *object)
18fbde
   g_free (global->imagedir);
18fbde
   g_free (global->userdatadir);
18fbde
 
18fbde
+  g_hash_table_unref (global->save_ops);
18fbde
+
18fbde
   G_OBJECT_CLASS(shell_global_parent_class)->finalize (object);
18fbde
 }
18fbde
 
18fbde
@@ -1775,20 +1783,77 @@ shell_global_get_session_mode (ShellGlobal *global)
18fbde
 }
18fbde
 
18fbde
 static void
18fbde
-save_variant (GFile      *dir,
18fbde
-              const char *property_name,
18fbde
-              GVariant   *variant)
18fbde
+delete_variant_cb (GObject      *object,
18fbde
+                   GAsyncResult *result,
18fbde
+                   gpointer      user_data)
18fbde
+{
18fbde
+  ShellGlobal *global = user_data;
18fbde
+  GError *error = NULL;
18fbde
+
18fbde
+  if (!g_file_delete_finish (G_FILE (object), result, &error))
18fbde
+    {
18fbde
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
18fbde
+        {
18fbde
+          g_warning ("Could not delete runtime/persistent state file: %s\n",
18fbde
+                     error->message);
18fbde
+        }
18fbde
+
18fbde
+      g_error_free (error);
18fbde
+    }
18fbde
+
18fbde
+  g_hash_table_remove (global->save_ops, object);
18fbde
+}
18fbde
+
18fbde
+static void
18fbde
+replace_variant_cb (GObject      *object,
18fbde
+                    GAsyncResult *result,
18fbde
+                    gpointer      user_data)
18fbde
+{
18fbde
+  ShellGlobal *global = user_data;
18fbde
+  GError *error = NULL;
18fbde
+
18fbde
+  if (!g_file_replace_contents_finish (G_FILE (object), result, NULL, &error))
18fbde
+    {
18fbde
+      if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
18fbde
+        {
18fbde
+          g_warning ("Could not replace runtime/persistent state file: %s\n",
18fbde
+                     error->message);
18fbde
+        }
18fbde
+
18fbde
+      g_error_free (error);
18fbde
+    }
18fbde
+
18fbde
+  g_hash_table_remove (global->save_ops, object);
18fbde
+}
18fbde
+
18fbde
+static void
18fbde
+save_variant (ShellGlobal *global,
18fbde
+              GFile       *dir,
18fbde
+              const char  *property_name,
18fbde
+              GVariant    *variant)
18fbde
 {
18fbde
   GFile *path = g_file_get_child (dir, property_name);
18fbde
+  GCancellable *cancellable;
18fbde
+
18fbde
+  cancellable = g_hash_table_lookup (global->save_ops, path);
18fbde
+  g_cancellable_cancel (cancellable);
18fbde
+
18fbde
+  cancellable = g_cancellable_new ();
18fbde
+  g_hash_table_insert (global->save_ops, g_object_ref (path), cancellable);
18fbde
 
18fbde
   if (variant == NULL || g_variant_get_data (variant) == NULL)
18fbde
-    (void) g_file_delete (path, NULL, NULL);
18fbde
+    {
18fbde
+      g_file_delete_async (path, G_PRIORITY_DEFAULT, cancellable,
18fbde
+                           delete_variant_cb, global);
18fbde
+    }
18fbde
   else
18fbde
     {
18fbde
-      gsize size = g_variant_get_size (variant);
18fbde
-      g_file_replace_contents (path, g_variant_get_data (variant), size,
18fbde
-                               NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION,
18fbde
-                               NULL, NULL, NULL);
18fbde
+      g_file_replace_contents_async (path,
18fbde
+                                     g_variant_get_data (variant),
18fbde
+                                     g_variant_get_size (variant),
18fbde
+                                     NULL, FALSE,
18fbde
+                                     G_FILE_CREATE_REPLACE_DESTINATION,
18fbde
+                                     cancellable, replace_variant_cb, global);
18fbde
     }
18fbde
 
18fbde
   g_object_unref (path);
18fbde
@@ -1842,7 +1907,7 @@ shell_global_set_runtime_state (ShellGlobal  *global,
18fbde
                                 const char   *property_name,
18fbde
                                 GVariant     *variant)
18fbde
 {
18fbde
-  save_variant (global->runtime_state_path, property_name, variant);
18fbde
+  save_variant (global, global->runtime_state_path, property_name, variant);
18fbde
 }
18fbde
 
18fbde
 /**
18fbde
@@ -1877,7 +1942,7 @@ shell_global_set_persistent_state (ShellGlobal *global,
18fbde
                                    const char  *property_name,
18fbde
                                    GVariant    *variant)
18fbde
 {
18fbde
-  save_variant (global->userdatadir_path, property_name, variant);
18fbde
+  save_variant (global, global->userdatadir_path, property_name, variant);
18fbde
 }
18fbde
 
18fbde
 /**
18fbde
-- 
18fbde
2.23.0
18fbde
18fbde
18fbde
From 6e898656365c18ad48349b7e3586e2d10b9ead9a Mon Sep 17 00:00:00 2001
18fbde
From: Carlos Garnacho <carlosg@gnome.org>
18fbde
Date: Mon, 3 Dec 2018 13:18:19 +0100
18fbde
Subject: [PATCH 2/4] appDisplay: Reduce g_app_info_get_all() calls
18fbde
18fbde
Whenever the AllView needs (re)populating, we used to do one general
18fbde
g_app_info_get_all() to get all GAppInfo, plus one per app folder in order
18fbde
to check the ones that fall within that category. This calls results in a
18fbde
fair amount of I/O blocking the main loop.
18fbde
18fbde
In order to ease this, keep the GAppInfo list around in AllView, and make
18fbde
the AppFolders use it when figuring out the contained apps. Since reloading
18fbde
the AllView results in AppFolders regenerated from scratch, the app info
18fbde
list is ensured to be up-to-date for any later change within the AppFolder
18fbde
(eg. through the GSettings key changing).
18fbde
18fbde
As the list was already filtered in the first place, we can also remove
18fbde
the try{}catch() in AppFolder in order to discard desktop files with
18fbde
invalid encoding.
18fbde
18fbde
Related: https://gitlab.gnome.org/GNOME/gnome-shell/issues/832
18fbde
---
18fbde
 js/ui/appDisplay.js | 18 +++++++++++-------
18fbde
 1 file changed, 11 insertions(+), 7 deletions(-)
18fbde
18fbde
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
18fbde
index 070057c69..115eb8f92 100644
18fbde
--- a/js/ui/appDisplay.js
18fbde
+++ b/js/ui/appDisplay.js
18fbde
@@ -498,15 +498,21 @@ var AllView = new Lang.Class({
18fbde
         });
18fbde
     },
18fbde
 
18fbde
+    getAppInfos() {
18fbde
+        return this._appInfoList;
18fbde
+    },
18fbde
+
18fbde
     _loadApps() {
18fbde
-        let apps = Gio.AppInfo.get_all().filter(appInfo => {
18fbde
+        this._appInfoList = Gio.AppInfo.get_all().filter(appInfo => {
18fbde
             try {
18fbde
                 let id = appInfo.get_id(); // catch invalid file encodings
18fbde
             } catch(e) {
18fbde
                 return false;
18fbde
             }
18fbde
             return appInfo.should_show();
18fbde
-        }).map(app => app.get_id());
18fbde
+        });
18fbde
+
18fbde
+        let apps = this._appInfoList.map(app => app.get_id());
18fbde
 
18fbde
         let appSys = Shell.AppSystem.get_default();
18fbde
 
18fbde
@@ -1329,15 +1335,13 @@ var FolderIcon = new Lang.Class({
18fbde
         folderApps.forEach(addAppId);
18fbde
 
18fbde
         let folderCategories = this._folder.get_strv('categories');
18fbde
-        Gio.AppInfo.get_all().forEach(appInfo => {
18fbde
+        let appInfos = this._parentView.getAppInfos();
18fbde
+        appInfos.forEach(appInfo => {
18fbde
             let appCategories = _getCategories(appInfo);
18fbde
             if (!_listsIntersect(folderCategories, appCategories))
18fbde
                 return;
18fbde
 
18fbde
-            try {
18fbde
-                addAppId(appInfo.get_id()); // catch invalid file encodings
18fbde
-            } catch(e) {
18fbde
-            }
18fbde
+            addAppId(appInfo.get_id());
18fbde
         });
18fbde
 
18fbde
         this.actor.visible = this.view.getAllItems().length > 0;
18fbde
-- 
18fbde
2.23.0
18fbde
18fbde
18fbde
From cf823de60bff23872d20dfbb05ee4c51e3ebe9a3 Mon Sep 17 00:00:00 2001
18fbde
From: Carlos Garnacho <carlosg@gnome.org>
18fbde
Date: Wed, 19 Dec 2018 13:13:42 +0100
18fbde
Subject: [PATCH 3/4] shell-app-system: Cache GAppInfos around
18fbde
18fbde
This was called here just to end up emitting ::installed-changed,
18fbde
which would trigger other g_app_info_get_all() calls. Cache it here
18fbde
so it may be reused later on.
18fbde
---
18fbde
 src/shell-app-system.c | 30 +++++++++++++++++++++++++-----
18fbde
 src/shell-app-system.h |  2 ++
18fbde
 2 files changed, 27 insertions(+), 5 deletions(-)
18fbde
18fbde
diff --git a/src/shell-app-system.c b/src/shell-app-system.c
18fbde
index 8edf757a9..58e0185d1 100644
18fbde
--- a/src/shell-app-system.c
18fbde
+++ b/src/shell-app-system.c
18fbde
@@ -50,6 +50,7 @@ struct _ShellAppSystemPrivate {
18fbde
   GHashTable *running_apps;
18fbde
   GHashTable *id_to_app;
18fbde
   GHashTable *startup_wm_class_to_id;
18fbde
+  GList *installed_apps;
18fbde
 };
18fbde
 
18fbde
 static void shell_app_system_finalize (GObject *object);
18fbde
@@ -82,12 +83,14 @@ static void
18fbde
 scan_startup_wm_class_to_id (ShellAppSystem *self)
18fbde
 {
18fbde
   ShellAppSystemPrivate *priv = self->priv;
18fbde
-  GList *apps, *l;
18fbde
+  GList *l;
18fbde
 
18fbde
   g_hash_table_remove_all (priv->startup_wm_class_to_id);
18fbde
 
18fbde
-  apps = g_app_info_get_all ();
18fbde
-  for (l = apps; l != NULL; l = l->next)
18fbde
+  g_list_free_full (priv->installed_apps, g_object_unref);
18fbde
+  priv->installed_apps = g_app_info_get_all ();
18fbde
+
18fbde
+  for (l = priv->installed_apps; l != NULL; l = l->next)
18fbde
     {
18fbde
       GAppInfo *info = l->data;
18fbde
       const char *startup_wm_class, *id, *old_id;
18fbde
@@ -105,8 +108,6 @@ scan_startup_wm_class_to_id (ShellAppSystem *self)
18fbde
         g_hash_table_insert (priv->startup_wm_class_to_id,
18fbde
                              g_strdup (startup_wm_class), g_strdup (id));
18fbde
     }
18fbde
-
18fbde
-  g_list_free_full (apps, g_object_unref);
18fbde
 }
18fbde
 
18fbde
 static gboolean
18fbde
@@ -198,6 +199,7 @@ shell_app_system_finalize (GObject *object)
18fbde
   g_hash_table_destroy (priv->running_apps);
18fbde
   g_hash_table_destroy (priv->id_to_app);
18fbde
   g_hash_table_destroy (priv->startup_wm_class_to_id);
18fbde
+  g_list_free_full (priv->installed_apps, g_object_unref);
18fbde
 
18fbde
   G_OBJECT_CLASS (shell_app_system_parent_class)->finalize (object);
18fbde
 }
18fbde
@@ -437,3 +439,21 @@ shell_app_system_search (const char *search_string)
18fbde
 
18fbde
   return results;
18fbde
 }
18fbde
+
18fbde
+/**
18fbde
+ * shell_app_system_get_installed:
18fbde
+ * @self: the #ShellAppSystem
18fbde
+ *
18fbde
+ * Returns all installed apps, as a list of #GAppInfo
18fbde
+ *
18fbde
+ * Returns: (transfer none) (element-type GAppInfo): a list of #GAppInfo
18fbde
+ *   describing all known applications. This memory is owned by the
18fbde
+ *   #ShellAppSystem and should not be freed.
18fbde
+ **/
18fbde
+GList *
18fbde
+shell_app_system_get_installed (ShellAppSystem *self)
18fbde
+{
18fbde
+  ShellAppSystemPrivate *priv = self->priv;
18fbde
+
18fbde
+  return priv->installed_apps;
18fbde
+}
18fbde
diff --git a/src/shell-app-system.h b/src/shell-app-system.h
18fbde
index 445671f5b..8719dbcf2 100644
18fbde
--- a/src/shell-app-system.h
18fbde
+++ b/src/shell-app-system.h
18fbde
@@ -27,4 +27,6 @@ ShellApp       *shell_app_system_lookup_desktop_wmclass       (ShellAppSystem *s
18fbde
 GSList         *shell_app_system_get_running               (ShellAppSystem  *self);
18fbde
 char         ***shell_app_system_search                    (const char *search_string);
18fbde
 
18fbde
+GList          *shell_app_system_get_installed             (ShellAppSystem  *self);
18fbde
+
18fbde
 #endif /* __SHELL_APP_SYSTEM_H__ */
18fbde
-- 
18fbde
2.23.0
18fbde
18fbde
18fbde
From e294793e3d5525897e1c293cd9fd77ccf8983ca7 Mon Sep 17 00:00:00 2001
18fbde
From: Carlos Garnacho <carlosg@gnome.org>
18fbde
Date: Wed, 19 Dec 2018 13:26:54 +0100
18fbde
Subject: [PATCH 4/4] appDisplay: Use GAppInfo list from ShellAppSystem
18fbde
18fbde
It is now cached there, so the number of g_app_info_get_all() calls
18fbde
is reduced to one per change.
18fbde
---
18fbde
 js/ui/appDisplay.js | 2 +-
18fbde
 1 file changed, 1 insertion(+), 1 deletion(-)
18fbde
18fbde
diff --git a/js/ui/appDisplay.js b/js/ui/appDisplay.js
18fbde
index 115eb8f92..233deeb92 100644
18fbde
--- a/js/ui/appDisplay.js
18fbde
+++ b/js/ui/appDisplay.js
18fbde
@@ -503,7 +503,7 @@ var AllView = new Lang.Class({
18fbde
     },
18fbde
 
18fbde
     _loadApps() {
18fbde
-        this._appInfoList = Gio.AppInfo.get_all().filter(appInfo => {
18fbde
+        this._appInfoList = Shell.AppSystem.get_default().get_installed().filter(appInfo => {
18fbde
             try {
18fbde
                 let id = appInfo.get_id(); // catch invalid file encodings
18fbde
             } catch(e) {
18fbde
-- 
18fbde
2.23.0
18fbde