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