diff options
author | Kalev Lember <klember@redhat.com> | 2018-12-14 11:40:46 +0100 |
---|---|---|
committer | Kalev Lember <klember@redhat.com> | 2018-12-14 13:44:33 +0100 |
commit | e5f73b24c4950ec8e51f6970ad658d604baf6d24 (patch) | |
tree | bbb17dd8ec3fb5b28f43a0e1ab28d3d3f75523c5 | |
parent | c2ea2b38da6fd4f2dbfb1a26980edd3e3faf5876 (diff) | |
download | appstream-glib-e5f73b24c4950ec8e51f6970ad658d604baf6d24.tar.gz |
store: Add thread safe dup() functions for multithreaded clients
gnome-software spawns a new worker thread for each of its plugin
operations and occasionally this leads to issues where one thread is
reading from AsStore, and another one is changing it. There's also file
monitor callbacks in AsStore that run in a yet another thread and can
update internal data structures as well.
This leads to a situation where functions returning pointers to internal
data structures can't guarantee the data structure lifetime because
another thread might be changing it in the background.
To fix this, this commit adds new as_store_dup_apps() and
as_store_dup_apps_by_id_merge() functions that return a deep copy of the
internal structure, and updates existing "transfer container"
as_store_get_apps_by_id() to return a deep copy, instead just returning
a reffed container.
-rw-r--r-- | libappstream-glib/as-self-test.c | 4 | ||||
-rw-r--r-- | libappstream-glib/as-store.c | 65 | ||||
-rw-r--r-- | libappstream-glib/as-store.h | 3 |
3 files changed, 69 insertions, 3 deletions
diff --git a/libappstream-glib/as-self-test.c b/libappstream-glib/as-self-test.c index d90af48..7fd7d75 100644 --- a/libappstream-glib/as-self-test.c +++ b/libappstream-glib/as-self-test.c @@ -3477,12 +3477,12 @@ as_test_store_flatpak_func (void) AsApp *app; AsFormat *format; GError *error = NULL; - GPtrArray *apps; gboolean ret; g_autofree gchar *filename = NULL; g_autofree gchar *filename_root = NULL; g_autoptr(AsStore) store = NULL; g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) apps = NULL; /* make throws us under a bus, yet again */ g_setenv ("AS_SELF_TEST_PREFIX_DELIM", "_", TRUE); @@ -3503,7 +3503,7 @@ as_test_store_flatpak_func (void) /* test extraction of symlink data */ g_assert_cmpstr (as_store_get_origin (store), ==, "flatpak"); g_assert_cmpint (as_store_get_size (store), ==, 1); - apps = as_store_get_apps (store); + apps = as_store_dup_apps (store); g_assert_cmpint (apps->len, ==, 1); app = g_ptr_array_index (apps, 0); g_assert_cmpstr (as_app_get_id (app), ==, "flatpak:test.desktop"); diff --git a/libappstream-glib/as-store.c b/libappstream-glib/as-store.c index d2075eb..59a7f1c 100644 --- a/libappstream-glib/as-store.c +++ b/libappstream-glib/as-store.c @@ -1,6 +1,7 @@ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- * * Copyright (C) 2013-2016 Richard Hughes <richard@hughsie.com> + * Copyright (C) 2015-2018 Kalev Lember <klember@redhat.com> * * Licensed under the GNU Lesser General Public License Version 2.1 * @@ -264,6 +265,22 @@ as_store_changed_uninhibit_cb (void *v) #define _cleanup_uninhibit_ __attribute__ ((cleanup(as_store_changed_uninhibit_cb))) +static GPtrArray * +_dup_app_array (GPtrArray *array) +{ + GPtrArray *array_dup; + + g_return_val_if_fail (array != NULL, NULL); + + array_dup = g_ptr_array_new_full (array->len, (GDestroyNotify) g_object_unref); + for (guint i = 0; i < array->len; i++) { + AsApp *app = g_ptr_array_index (array, i); + g_ptr_array_add (array_dup, g_object_ref (app)); + } + + return array_dup; +} + /** * as_store_add_filter: * @store: a #AsStore instance. @@ -345,6 +362,27 @@ as_store_get_apps (AsStore *store) } /** + * as_store_dup_apps: + * @store: a #AsStore instance. + * + * Gets an array of all the valid applications in the store. + * + * Returns: (element-type AsApp) (transfer container): an array + * + * Since: 0.7.15 + **/ +GPtrArray * +as_store_dup_apps (AsStore *store) +{ + + AsStorePrivate *priv = GET_PRIVATE (store); + + g_return_val_if_fail (AS_IS_STORE (store), NULL); + + return _dup_app_array (priv->array); +} + +/** * as_store_remove_all: * @store: a #AsStore instance. * @@ -471,7 +509,7 @@ as_store_get_apps_by_id (AsStore *store, const gchar *id) g_return_val_if_fail (AS_IS_STORE (store), NULL); apps = g_hash_table_lookup (priv->hash_id, id); if (apps != NULL) - return g_ptr_array_ref (apps); + return _dup_app_array (apps); return g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); } @@ -495,6 +533,31 @@ as_store_get_apps_by_id_merge (AsStore *store, const gchar *id) } /** + * as_store_dup_apps_by_id_merge: + * @store: a #AsStore instance. + * @id: the application full ID. + * + * Gets an array of all the merge applications that match a specific ID. + * + * Returns: (element-type AsApp) (transfer container): an array + * + * Since: 0.7.15 + **/ +GPtrArray * +as_store_dup_apps_by_id_merge (AsStore *store, const gchar *id) +{ + AsStorePrivate *priv = GET_PRIVATE (store); + GPtrArray *apps; + + g_return_val_if_fail (AS_IS_STORE (store), NULL); + + apps = g_hash_table_lookup (priv->hash_merge_id, id); + if (apps != NULL) + return _dup_app_array (apps); + return g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); +} + +/** * as_store_add_metadata_index: * @store: a #AsStore instance. * @key: the metadata key. diff --git a/libappstream-glib/as-store.h b/libappstream-glib/as-store.h index 3d8c749..b15aca4 100644 --- a/libappstream-glib/as-store.h +++ b/libappstream-glib/as-store.h @@ -209,10 +209,13 @@ void as_store_set_search_match (AsStore *store, guint16 as_store_get_search_match (AsStore *store); void as_store_remove_all (AsStore *store); GPtrArray *as_store_get_apps (AsStore *store); +GPtrArray *as_store_dup_apps (AsStore *store); GPtrArray *as_store_get_apps_by_id (AsStore *store, const gchar *id); GPtrArray *as_store_get_apps_by_id_merge (AsStore *store, const gchar *id); +GPtrArray *as_store_dup_apps_by_id_merge (AsStore *store, + const gchar *id); GPtrArray *as_store_get_apps_by_metadata (AsStore *store, const gchar *key, const gchar *value); |