[glib: 1/2] GWin32AppInfo: Use a thread for async appinfo tree rebuilds




commit 427d4fad246526d919edf470bd2e916990522b41
Author: Руслан Ижбулатов <lrn1986 gmail com>
Date:   Sat Oct 3 22:44:19 2020 +0000

    GWin32AppInfo: Use a thread for async appinfo tree rebuilds

 gio/giomodule.c     |   4 ++
 gio/giowin32-priv.h |   2 +
 gio/gwin32appinfo.c | 145 +++++++++++++++++++++++++++++++++++++++++-----------
 3 files changed, 121 insertions(+), 30 deletions(-)
---
diff --git a/gio/giomodule.c b/gio/giomodule.c
index dc4d6d3b3..e27f1ab76 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -49,6 +49,7 @@
 #include "gmemorymonitordbus.h"
 #ifdef G_OS_WIN32
 #include "gregistrysettingsbackend.h"
+#include "giowin32-priv.h"
 #endif
 #include <glib/gstdio.h>
 
@@ -1068,7 +1069,10 @@ DllMain (HINSTANCE hinstDLL,
         LPVOID    lpvReserved)
 {
   if (fdwReason == DLL_PROCESS_ATTACH)
+    {
       gio_dll = hinstDLL;
+      gio_win32_appinfo_init (FALSE);
+    }
 
   return TRUE;
 }
diff --git a/gio/giowin32-priv.h b/gio/giowin32-priv.h
index 8843845d5..6f20a9545 100644
--- a/gio/giowin32-priv.h
+++ b/gio/giowin32-priv.h
@@ -35,6 +35,8 @@ GOutputStream *
 g_win32_output_stream_new_from_fd (gint      fd,
                                   gboolean  close_fd);
 
+void
+gio_win32_appinfo_init (gboolean do_wait);
 G_END_DECLS
 
 #endif /* __G_IO_MODULE_PRIV_H__ */
diff --git a/gio/gwin32appinfo.c b/gio/gwin32appinfo.c
index 612373a6a..8871acc9d 100644
--- a/gio/gwin32appinfo.c
+++ b/gio/gwin32appinfo.c
@@ -36,6 +36,7 @@
 #include <windows.h>
 
 #include <glib/gstdioprivate.h>
+#include "giowin32-priv.h"
 #include "glib-private.h"
 
 /* We need to watch 8 places:
@@ -519,7 +520,26 @@ g_win32_appinfo_application_init (GWin32AppInfoApplication *self)
   self->verbs = g_ptr_array_new_with_free_func (g_object_unref);
 }
 
-G_LOCK_DEFINE_STATIC (gio_win32_appinfo);
+/* The AppInfo threadpool that does asynchronous AppInfo tree rebuilds */
+static GThreadPool *gio_win32_appinfo_threadpool;
+
+/* This mutex is held by a thread that reads or writes the AppInfo tree.
+ * (tree object references can be obtained and later read without
+ *  holding this mutex, since objects are practically immutable).
+ */
+static GMutex gio_win32_appinfo_mutex;
+
+/* Any thread wanting to access AppInfo can wait on this condition */
+static GCond gio_win32_appinfo_cond;
+
+/* Increased to indicate that AppInfo tree does needs to be rebuilt.
+ * AppInfo thread checks this to see if it needs to
+ * do a tree re-build. If the value changes during a rebuild,
+ * another rebuild is triggered after that.
+ * Other threads check this to see if they need
+ * to wait for a tree re-build to finish.
+ */
+static gint gio_win32_appinfo_update_counter = 0;
 
 /* Map of owned ".ext" (with '.', UTF-8, folded)
  * to GWin32AppInfoFileExtension ptr
@@ -3046,6 +3066,19 @@ update_registry_data (void)
   return;
 }
 
+/* This function is called when any of our registry watchers detect
+ * changes in the registry.
+ */
+static void
+keys_updated (GWin32RegistryKey  *key,
+              gpointer            user_data)
+{
+  /* Indicate the tree as not up-to-date, push a new job for the AppInfo thread */
+  g_atomic_int_inc (&gio_win32_appinfo_update_counter);
+  /* We don't use the data pointer, but it must be non-NULL */
+  g_thread_pool_push (gio_win32_appinfo_threadpool, (gpointer) keys_updated, NULL);
+}
+
 static void
 watch_keys (void)
 {
@@ -3055,7 +3088,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3065,7 +3098,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3075,7 +3108,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3085,7 +3118,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3095,7 +3128,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3105,7 +3138,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3115,7 +3148,7 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 
@@ -3125,14 +3158,45 @@ watch_keys (void)
                                 G_WIN32_REGISTRY_WATCH_NAME |
                                 G_WIN32_REGISTRY_WATCH_ATTRIBUTES |
                                 G_WIN32_REGISTRY_WATCH_VALUES,
-                                NULL,
+                                keys_updated,
                                 NULL,
                                 NULL);
 }
 
-
+/* This is the main function of the AppInfo thread */
 static void
-g_win32_appinfo_init (void)
+gio_win32_appinfo_thread_func (gpointer data,
+                               gpointer user_data)
+{
+  gint saved_counter;
+  g_mutex_lock (&gio_win32_appinfo_mutex);
+  saved_counter = g_atomic_int_get (&gio_win32_appinfo_update_counter);
+
+  if (saved_counter > 0)
+    update_registry_data ();
+  /* If the counter didn't change while we were working, then set it to zero.
+   * Otherwise we need to rebuild the tree again, so keep it greater than zero.
+   * Numeric value doesn't matter - even if we're asked to rebuild N times,
+   * we just need to rebuild once, and as long as there were no new rebuild
+   * requests while we were working, we're done.
+   */
+  if (g_atomic_int_compare_and_exchange  (&gio_win32_appinfo_update_counter,
+                                          saved_counter,
+                                          0))
+    g_cond_broadcast (&gio_win32_appinfo_cond);
+
+  g_mutex_unlock (&gio_win32_appinfo_mutex);
+}
+
+/* Initializes Windows AppInfo. Creates the registry watchers,
+ * the AppInfo thread, and initiates an update of the AppInfo tree.
+ * Called with do_wait = `FALSE` at startup to prevent it from
+ * blocking until the tree is updated. All subsequent calls
+ * from everywhere else are made with do_wait = `TRUE`, blocking
+ * until the tree is re-built (if needed).
+ */
+void
+gio_win32_appinfo_init (gboolean do_wait)
 {
   static gsize initialized;
 
@@ -3165,11 +3229,31 @@ g_win32_appinfo_init (void)
 
       watch_keys ();
 
-      update_registry_data ();
+      /* We don't really require an exclusive pool, but the implementation
+       * details might cause the g_thread_pool_push() call below to block
+       * if the pool is not exclusive (specifically - for POSIX threads backend
+       * lacking thread scheduler settings).
+       */
+      gio_win32_appinfo_threadpool = g_thread_pool_new (gio_win32_appinfo_thread_func,
+                                                        NULL,
+                                                        1,
+                                                        TRUE,
+                                                        NULL);
+      g_mutex_init (&gio_win32_appinfo_mutex);
+      g_cond_init (&gio_win32_appinfo_cond);
+      g_atomic_int_set (&gio_win32_appinfo_update_counter, 1);
+      /* Trigger initial tree build. Fake data pointer. */
+      g_thread_pool_push (gio_win32_appinfo_threadpool, (gpointer) keys_updated, NULL);
 
       g_once_init_leave (&initialized, TRUE);
     }
 
+  if (!do_wait)
+    return;
+
+  /* If any of the keys had a change, then we've already initiated
+   * a tree re-build in keys_updated(). Just wait for it to finish.
+   */
   if ((url_associations_key       && g_win32_registry_key_has_changed (url_associations_key))       ||
       (file_exts_key              && g_win32_registry_key_has_changed (file_exts_key))              ||
       (user_clients_key           && g_win32_registry_key_has_changed (user_clients_key))           ||
@@ -3179,10 +3263,11 @@ g_win32_appinfo_init (void)
       (system_registered_apps_key && g_win32_registry_key_has_changed (system_registered_apps_key)) ||
       (classes_root_key           && g_win32_registry_key_has_changed (classes_root_key)))
     {
-      G_LOCK (gio_win32_appinfo);
-      update_registry_data ();
+      g_mutex_lock (&gio_win32_appinfo_mutex);
+      while (g_atomic_int_get (&gio_win32_appinfo_update_counter) > 0)
+        g_cond_wait (&gio_win32_appinfo_cond, &gio_win32_appinfo_mutex);
       watch_keys ();
-      G_UNLOCK (gio_win32_appinfo);
+      g_mutex_unlock (&gio_win32_appinfo_mutex);
     }
 }
 
@@ -3248,8 +3333,8 @@ g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
 
   new_info->app = g_object_ref (app);
 
-  g_win32_appinfo_init ();
-  G_LOCK (gio_win32_appinfo);
+  gio_win32_appinfo_init (TRUE);
+  g_mutex_lock (&gio_win32_appinfo_mutex);
 
   i = 0;
   g_hash_table_iter_init (&iter, new_info->app->supported_exts);
@@ -3274,7 +3359,7 @@ g_win32_app_info_new_from_app (GWin32AppInfoApplication *app,
       i += 1;
     }
 
-  G_UNLOCK (gio_win32_appinfo);
+  g_mutex_unlock (&gio_win32_appinfo_mutex);
 
   new_info->supported_types[i] = NULL;
 
@@ -4216,13 +4301,13 @@ g_app_info_get_default_for_uri_scheme (const char *uri_scheme)
       return NULL;
     }
 
-  g_win32_appinfo_init ();
-  G_LOCK (gio_win32_appinfo);
+  gio_win32_appinfo_init (TRUE);
+  g_mutex_lock (&gio_win32_appinfo_mutex);
 
   g_set_object (&scheme, g_hash_table_lookup (urls, scheme_down));
   g_free (scheme_down);
 
-  G_UNLOCK (gio_win32_appinfo);
+  g_mutex_unlock (&gio_win32_appinfo_mutex);
 
   result = NULL;
 
@@ -4252,14 +4337,14 @@ g_app_info_get_default_for_type (const char *content_type,
   if (!ext_down)
     return NULL;
 
-  g_win32_appinfo_init ();
-  G_LOCK (gio_win32_appinfo);
+  gio_win32_appinfo_init (TRUE);
+  g_mutex_lock (&gio_win32_appinfo_mutex);
 
   /* Assuming that "content_type" is a file extension, not a MIME type */
   g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
   g_free (ext_down);
 
-  G_UNLOCK (gio_win32_appinfo);
+  g_mutex_unlock (&gio_win32_appinfo_mutex);
 
   if (ext == NULL)
     return NULL;
@@ -4309,15 +4394,15 @@ g_app_info_get_all (void)
   GList *apps;
   GList *apps_i;
 
-  g_win32_appinfo_init ();
-  G_LOCK (gio_win32_appinfo);
+  gio_win32_appinfo_init (TRUE);
+  g_mutex_lock (&gio_win32_appinfo_mutex);
 
   apps = NULL;
   g_hash_table_iter_init (&iter, apps_by_id);
   while (g_hash_table_iter_next (&iter, NULL, &value))
     apps = g_list_prepend (apps, g_object_ref (G_OBJECT (value)));
 
-  G_UNLOCK (gio_win32_appinfo);
+  g_mutex_unlock (&gio_win32_appinfo_mutex);
 
   infos = NULL;
   for (apps_i = apps; apps_i; apps_i = apps_i->next)
@@ -4345,14 +4430,14 @@ g_app_info_get_all_for_type (const char *content_type)
   if (!ext_down)
     return NULL;
 
-  g_win32_appinfo_init ();
-  G_LOCK (gio_win32_appinfo);
+  gio_win32_appinfo_init (TRUE);
+  g_mutex_lock (&gio_win32_appinfo_mutex);
 
   /* Assuming that "content_type" is a file extension, not a MIME type */
   g_set_object (&ext, g_hash_table_lookup (extensions, ext_down));
   g_free (ext_down);
 
-  G_UNLOCK (gio_win32_appinfo);
+  g_mutex_unlock (&gio_win32_appinfo_mutex);
 
   if (ext == NULL)
     return NULL;


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