From 427d4fad246526d919edf470bd2e916990522b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A0=D1=83=D1=81=D0=BB=D0=B0=D0=BD=20=D0=98=D0=B6=D0=B1?= =?UTF-8?q?=D1=83=D0=BB=D0=B0=D1=82=D0=BE=D0=B2?= Date: Sat, 3 Oct 2020 22:44:19 +0000 Subject: [PATCH] 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 @@ -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 #include +#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;