summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKalev Lember <klember@redhat.com>2018-12-14 11:40:46 +0100
committerKalev Lember <klember@redhat.com>2018-12-14 13:44:33 +0100
commite5f73b24c4950ec8e51f6970ad658d604baf6d24 (patch)
treebbb17dd8ec3fb5b28f43a0e1ab28d3d3f75523c5
parentc2ea2b38da6fd4f2dbfb1a26980edd3e3faf5876 (diff)
downloadappstream-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.c4
-rw-r--r--libappstream-glib/as-store.c65
-rw-r--r--libappstream-glib/as-store.h3
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);