[gtk: 1/40] GtkIconTheme: Make the base GtkIconTheme threadsafe
- From: Alexander Larsson <alexl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gtk: 1/40] GtkIconTheme: Make the base GtkIconTheme threadsafe
- Date: Thu, 30 Jan 2020 17:31:13 +0000 (UTC)
commit aef67bd53aaab7faaca3b980ef19af8ee101dd8e
Author: Alexander Larsson <alexl redhat com>
Date: Fri Jan 24 16:22:26 2020 +0100
GtkIconTheme: Make the base GtkIconTheme threadsafe
This makes the core icon theme object threadsafe, however its
not yet very useful because the IconInfo objects are not threadsafe.
gtk/gtkicontheme.c | 499 ++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 416 insertions(+), 83 deletions(-)
---
diff --git a/gtk/gtkicontheme.c b/gtk/gtkicontheme.c
index e5abec19fc..63c247cdd3 100644
--- a/gtk/gtkicontheme.c
+++ b/gtk/gtkicontheme.c
@@ -104,6 +104,53 @@
* ]|
*/
+/* Threading:
+ *
+ * GtkIconTheme is partially threadsafe, construction and setup can
+ * only be done on the main thread (and this is not really fixable
+ * since it uses other things like GdkDisplay and GSettings and
+ * signals on those. However, once the icon theme is set up on the
+ * main thread we can pass it to a thread and do basic lookups on
+ * it. This will cause any parallel calls on the main thread (or any
+ * other thread) to block until its done, but most of the time
+ * lookups are fast. The only time its not fast is when we need
+ * to rescan the theme, but then it would be slow if we didn't block
+ * and did the rescan ourselves anyway.
+ *
+ * The threadsafe calls are marked in the docs.
+ *
+ * All private functions that take a GtkIconTheme (or one of its
+ * private data types (like IconThemeDir, UnthemedIcon, etc) arg are
+ * expected to be called with the icon theme lock held, unless the
+ * funcion has a _unlocked suffix. Any similar function that must be
+ * called on the main thread, will have a _mainthread suffix.
+ *
+ * So the rules for such functions are:
+ *
+ * * non-_unlocked function cannot call _unlocked functions.
+ * * _unlocked must lock before calling a non-_unlocked.
+ * * non-_mainthread cannot call _mainthread.
+ * * Public APIs must lock before calling a non-_unlocked private function
+ * * Public APIs that never call _mainthread and threadsafe.
+ *
+ * Additionally there is a global "info_cache" G_LOCK, which protects
+ * both the GtkIconTheme->info_cache and its reverse pointer
+ * GtkIconInfo->in_cache. This is sometimes taken with the
+ * theme lock held (from the theme side) and sometimes not (from the
+ * icon info side), but we never take another lock after taking it, so
+ * this is safe.
+ *
+ * Sometimes there are references to the icon theme that are weak that
+ * can call into the icon theme. For example, from the "theme-changed"
+ * signal. Since these don't own the theme they can run in parallel
+ * with some other thread wich is finalizing the theme. To avoid this
+ * all such references are done via the GtkIconThemeRef object which
+ * contains an NULL:able pointer to the theme and the main lock for
+ * that theme. Using this we can safely generate a ref for the theme
+ * if it still lives (or get NULL if it doesn't).
+ */
+
+
#define FALLBACK_ICON_THEME "hicolor"
typedef enum
@@ -137,6 +184,9 @@ typedef enum
typedef struct _GtkIconInfoClass GtkIconInfoClass;
typedef struct _GtkIconThemeClass GtkIconThemeClass;
+
+typedef struct _GtkIconThemeRef GtkIconThemeRef;
+
/**
* GtkIconTheme:
*
@@ -151,8 +201,9 @@ typedef struct _GtkIconThemeClass GtkIconThemeClass;
struct _GtkIconTheme
{
GObject parent_instance;
+ GtkIconThemeRef *ref;
- GHashTable *info_cache;
+ GHashTable *info_cache; /* Protected by info_cache lock */
GtkIconInfo *lru_cache[LRU_CACHE_SIZE];
int lru_cache_next;
@@ -176,6 +227,7 @@ struct _GtkIconTheme
/* GdkDisplay for the icon theme (may be NULL) */
GdkDisplay *display;
+ GtkSettings *display_settings;
/* time when we last stat:ed for theme changes */
glong last_stat_time;
@@ -203,6 +255,10 @@ struct _GtkIconInfoClass
GObjectClass parent_class;
};
+/* This lock protects both IconTheme.info_cache and the dependent IconInfo.in_cache.
+ * Its a global lock, so hold it only for short times. */
+G_LOCK_DEFINE_STATIC(info_cache);
+
/**
* GtkIconInfo:
*
@@ -216,7 +272,7 @@ struct _GtkIconInfo
/* Information about the source
*/
IconInfoKey key;
- GtkIconTheme *in_cache;
+ GtkIconTheme *in_cache; /* Protected by info_cache lock */
gchar *filename;
GLoadableIcon *loadable;
@@ -300,6 +356,7 @@ typedef struct
} IconThemeDirMtime;
static void gtk_icon_theme_finalize (GObject *object);
+static void gtk_icon_theme_dispose (GObject *object);
static void theme_dir_destroy (IconThemeDir *dir);
static void theme_destroy (IconTheme *theme);
static GtkIconInfo *theme_lookup_icon (IconTheme *theme,
@@ -326,9 +383,98 @@ static GtkIconInfo *icon_info_new (IconThemeDirType type,
gint dir_scale);
static IconSuffix suffix_from_name (const gchar *name);
static gboolean icon_info_ensure_scale_and_texture (GtkIconInfo* icon_info);
+static void unset_display (GtkIconTheme *self);
+static void update_current_theme__mainthread (GtkIconTheme *self);
static guint signal_changed = 0;
+/* This is like a weak ref with a lock, anyone doing
+ * operations on the theme must take the lock in this,
+ * but you can also take the lock even if the theme
+ * has been finalized (but theme will then be NULL).
+ *
+ * This is used to avoid race conditions where signals
+ * like theme-changed happen on the main thread while
+ * the last active owning ref of the icon theme is
+ * on some thread.
+ */
+struct _GtkIconThemeRef
+{
+ gatomicrefcount count;
+ GMutex lock;
+ GtkIconTheme *theme;
+};
+
+static GtkIconThemeRef *
+gtk_icon_theme_ref_new (GtkIconTheme *theme)
+{
+ GtkIconThemeRef *ref = g_new0 (GtkIconThemeRef, 1);
+
+ g_atomic_ref_count_init (&ref->count);
+ g_mutex_init (&ref->lock);
+ ref->theme = theme;
+
+ return ref;
+}
+
+static GtkIconThemeRef *
+gtk_icon_theme_ref_ref (GtkIconThemeRef *ref)
+{
+ g_atomic_ref_count_inc (&ref->count);
+ return ref;
+}
+
+static void
+gtk_icon_theme_ref_unref (GtkIconThemeRef *ref)
+{
+ if (g_atomic_ref_count_dec (&ref->count))
+ {
+ g_assert (ref->theme == NULL);
+ g_mutex_clear (&ref->lock);
+ g_free (ref);
+ }
+}
+
+/* Take the lock and if available ensure the theme lives until (at
+ * least) ref_release is called. */
+static GtkIconTheme *
+gtk_icon_theme_ref_aquire (GtkIconThemeRef *ref)
+{
+ g_mutex_lock (&ref->lock);
+ if (ref->theme)
+ g_object_ref (ref->theme);
+ return ref->theme;
+}
+
+static void
+gtk_icon_theme_ref_release (GtkIconThemeRef *ref)
+{
+ if (ref->theme)
+ g_object_unref (ref->theme);
+ g_mutex_unlock (&ref->lock);
+}
+
+static void
+gtk_icon_theme_ref_dispose (GtkIconThemeRef *ref)
+{
+ gtk_icon_theme_ref_aquire (ref);
+ ref->theme = NULL;
+ gtk_icon_theme_ref_release (ref);
+}
+
+static void
+gtk_icon_theme_lock (GtkIconTheme *self)
+{
+ g_mutex_lock (&self->ref->lock);
+}
+
+static void
+gtk_icon_theme_unlock (GtkIconTheme *self)
+{
+ g_mutex_unlock (&self->ref->lock);
+}
+
+
static guint
icon_info_key_hash (gconstpointer _key)
{
@@ -376,13 +522,8 @@ icon_info_key_equal (gconstpointer _a,
G_DEFINE_TYPE (GtkIconTheme, gtk_icon_theme, G_TYPE_OBJECT)
static void
-add_to_lru_cache (GtkIconInfo *info)
+add_to_lru_cache (GtkIconTheme *self, GtkIconInfo *info)
{
- GtkIconTheme *self = info->in_cache;
-
- if (!self)
- return;
-
if (info->texture &&
info->texture->width <= MAX_LRU_TEXTURE_SIZE &&
info->texture->height <= MAX_LRU_TEXTURE_SIZE)
@@ -480,6 +621,7 @@ gtk_icon_theme_class_init (GtkIconThemeClass *klass)
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = gtk_icon_theme_finalize;
+ gobject_class->dispose = gtk_icon_theme_dispose;
/**
* GtkIconTheme::changed:
@@ -504,28 +646,35 @@ gtk_icon_theme_class_init (GtkIconThemeClass *klass)
* for the display, drop the reference
*/
static void
-display_closed (GdkDisplay *display,
- gboolean is_error,
- GtkIconTheme *self)
+display_closed__mainthread_unlocked (GdkDisplay *display,
+ gboolean is_error,
+ GtkIconThemeRef *ref)
{
- gboolean was_display_singleton = self->is_display_singleton;
+ GtkIconTheme *self = gtk_icon_theme_ref_aquire (ref);
+ gboolean was_display_singleton;
- if (was_display_singleton)
+ if (self)
{
- g_object_set_data (G_OBJECT (display), I_("gtk-icon-theme"), NULL);
- self->is_display_singleton = FALSE;
- }
+ /* This is only set at construction and accessed here in the main thread, so no locking necessary */
+ was_display_singleton = self->is_display_singleton;
+ if (was_display_singleton)
+ {
+ g_object_set_data (G_OBJECT (display), I_("gtk-icon-theme"), NULL);
+ self->is_display_singleton = FALSE;
+ }
- gtk_icon_theme_set_display (self, NULL);
+ unset_display (self);
+ update_current_theme__mainthread (self);
- if (was_display_singleton)
- {
- g_object_unref (self);
+ if (was_display_singleton)
+ g_object_unref (self);
}
+
+ gtk_icon_theme_ref_release (ref);
}
static void
-update_current_theme (GtkIconTheme *self)
+update_current_theme__mainthread (GtkIconTheme *self)
{
#define theme_changed(_old, _new) \
((_old && !_new) || (!_old && _new) || \
@@ -560,30 +709,32 @@ update_current_theme (GtkIconTheme *self)
/* Callback when the icon theme GtkSetting changes
*/
static void
-theme_changed (GtkSettings *settings,
- GParamSpec *pspec,
- GtkIconTheme *self)
+theme_changed__mainthread_unlocked (GtkSettings *settings,
+ GParamSpec *pspec,
+ GtkIconThemeRef *ref)
{
- update_current_theme (self);
+ GtkIconTheme *self = gtk_icon_theme_ref_aquire (ref);
+
+ if (self)
+ update_current_theme__mainthread (self);
+
+ gtk_icon_theme_ref_release (ref);
}
static void
unset_display (GtkIconTheme *self)
{
- GtkSettings *settings;
-
if (self->display)
{
- settings = gtk_settings_get_for_display (self->display);
-
g_signal_handlers_disconnect_by_func (self->display,
- (gpointer) display_closed,
- self);
- g_signal_handlers_disconnect_by_func (settings,
- (gpointer) theme_changed,
- self);
+ (gpointer) display_closed__mainthread_unlocked,
+ self->ref);
+ g_signal_handlers_disconnect_by_func (self->display_settings,
+ (gpointer) theme_changed__mainthread_unlocked,
+ self->ref);
self->display = NULL;
+ self->display_settings = NULL;
}
}
@@ -600,26 +751,33 @@ void
gtk_icon_theme_set_display (GtkIconTheme *self,
GdkDisplay *display)
{
- GtkSettings *settings;
-
g_return_if_fail (GTK_ICON_THEME (self));
g_return_if_fail (display == NULL || GDK_IS_DISPLAY (display));
+ gtk_icon_theme_lock (self);
+
unset_display (self);
-
+
if (display)
{
- settings = gtk_settings_get_for_display (display);
-
self->display = display;
-
- g_signal_connect (display, "closed",
- G_CALLBACK (display_closed), self);
- g_signal_connect (settings, "notify::gtk-icon-theme-name",
- G_CALLBACK (theme_changed), self);
+ self->display_settings = gtk_settings_get_for_display (display);
+
+ g_signal_connect_data (display, "closed",
+ G_CALLBACK (display_closed__mainthread_unlocked),
+ gtk_icon_theme_ref_ref (self->ref),
+ (GClosureNotify)gtk_icon_theme_ref_unref,
+ 0);
+ g_signal_connect_data (self->display_settings, "notify::gtk-icon-theme-name",
+ G_CALLBACK (theme_changed__mainthread_unlocked),
+ gtk_icon_theme_ref_ref (self->ref),
+ (GClosureNotify)gtk_icon_theme_ref_unref,
+ 0);
}
- update_current_theme (self);
+ update_current_theme__mainthread (self);
+
+ gtk_icon_theme_unlock (self);
}
/* Checks whether a loader for SVG files has been registered
@@ -657,7 +815,7 @@ pixbuf_supports_svg (void)
return found_svg;
}
-/* The icon info was removed from the icon_info_hash hash table */
+/* The icon info was removed from the icon_info_hash hash table. */
static void
icon_info_uncached (GtkIconInfo *icon_info)
{
@@ -667,7 +825,8 @@ icon_info_uncached (GtkIconInfo *icon_info)
icon_info->key.size, icon_info->key.flags,
self,
icon_theme != NULL ? g_hash_table_size (self->info_cache) : 0));
-
+ /* This is a callback from the info_cache hashtable, so the info_cache lock is already held */
+ g_assert (icon_info->in_cache != NULL);
icon_info->in_cache = NULL;
}
@@ -677,6 +836,8 @@ gtk_icon_theme_init (GtkIconTheme *self)
const gchar * const *xdg_data_dirs;
int i, j;
+ self->ref = gtk_icon_theme_ref_new (self);
+
self->info_cache = g_hash_table_new_full (icon_info_key_hash, icon_info_key_equal, NULL,
(GDestroyNotify)icon_info_uncached);
@@ -719,18 +880,37 @@ free_dir_mtime (IconThemeDirMtime *dir_mtime)
}
static gboolean
-theme_changed_idle (gpointer user_data)
+theme_changed_idle__mainthread_unlocked (gpointer user_data)
{
+ GtkIconThemeRef *ref = (GtkIconThemeRef *)user_data;
GtkIconTheme *self;
+ GdkDisplay *display = NULL;
+
+ self = gtk_icon_theme_ref_aquire (ref);
+ if (self)
+ {
+ g_object_ref (self); /* Ensure theme lives during the changed signal emissions */
+
+ self->theme_changed_idle = 0;
- self = GTK_ICON_THEME (user_data);
+ if (self->display && self->is_display_singleton)
+ display = g_object_ref (self->display);
+ }
+ gtk_icon_theme_ref_release (ref);
- g_signal_emit (self, signal_changed, 0);
+ if (self)
+ {
+ /* Emit signals outside locks. */
+ g_signal_emit (self, signal_changed, 0);
- if (self->display && self->is_display_singleton)
- gtk_style_context_reset_widgets (self->display);
+ if (display)
+ {
+ gtk_style_context_reset_widgets (self->display);
+ g_object_unref (display);
+ }
- self->theme_changed_idle = 0;
+ g_object_unref (self);
+ }
return FALSE;
}
@@ -741,9 +921,9 @@ queue_theme_changed (GtkIconTheme *self)
if (!self->theme_changed_idle)
{
self->theme_changed_idle = g_idle_add_full (GTK_PRIORITY_RESIZE - 2,
- theme_changed_idle,
- self,
- NULL);
+ theme_changed_idle__mainthread_unlocked,
+ gtk_icon_theme_ref_ref (self->ref),
+ (GDestroyNotify)gtk_icon_theme_ref_unref);
g_source_set_name_by_id (self->theme_changed_idle, "[gtk] theme_changed_idle");
}
}
@@ -751,7 +931,9 @@ queue_theme_changed (GtkIconTheme *self)
static void
do_theme_change (GtkIconTheme *self)
{
+ G_LOCK (info_cache);
g_hash_table_remove_all (self->info_cache);
+ G_UNLOCK (info_cache);
clear_lru_cache (self);
if (!self->themes_valid)
@@ -780,13 +962,40 @@ blow_themes (GtkIconTheme *self)
self->themes_valid = FALSE;
}
+static void
+gtk_icon_theme_dispose (GObject *object)
+{
+ GtkIconTheme *self = GTK_ICON_THEME (object);
+
+ /* We make sure all outstanding GtkIconThemeRefs to us are NULLed
+ * out so that no other threads than the one running finalize will
+ * refer to the icon theme after this. This could happen if
+ * we finalize on a thread and on the main thread some display or
+ * setting signal is emitted.
+ *
+ * It is possible that before we aquire the lock this happens
+ * and the other thread refs the icon theme for some reason, but
+ * this is ok as it is allowed to resurrect objects in dispose
+ * (but not in finalize).
+ */
+ gtk_icon_theme_ref_dispose (self->ref);
+
+ G_OBJECT_CLASS (gtk_icon_theme_parent_class)->dispose (object);
+}
+
static void
gtk_icon_theme_finalize (GObject *object)
{
GtkIconTheme *self = GTK_ICON_THEME (object);
int i;
+ /* We don't actually need to take the lock here, because by now
+ there can be no other threads that own a ref to this object, but
+ technically this is considered "locked" */
+
+ G_LOCK(info_cache);
g_hash_table_destroy (self->info_cache);
+ G_UNLOCK(info_cache);
if (self->theme_changed_idle)
g_source_remove (self->theme_changed_idle);
@@ -804,6 +1013,8 @@ gtk_icon_theme_finalize (GObject *object)
blow_themes (self);
clear_lru_cache (self);
+ gtk_icon_theme_ref_unref (self->ref);
+
G_OBJECT_CLASS (gtk_icon_theme_parent_class)->finalize (object);
}
@@ -838,6 +1049,8 @@ gtk_icon_theme_set_search_path (GtkIconTheme *self,
g_return_if_fail (GTK_IS_ICON_THEME (self));
+ gtk_icon_theme_lock (self);
+
for (i = 0; i < self->search_path_len; i++)
g_free (self->search_path[i]);
@@ -850,6 +1063,8 @@ gtk_icon_theme_set_search_path (GtkIconTheme *self,
self->search_path[i] = g_strdup (path[i]);
do_theme_change (self);
+
+ gtk_icon_theme_unlock (self);
}
/**
@@ -871,9 +1086,11 @@ gtk_icon_theme_get_search_path (GtkIconTheme *self,
g_return_if_fail (GTK_IS_ICON_THEME (self));
+ gtk_icon_theme_lock (self);
+
if (n_elements)
*n_elements = self->search_path_len;
-
+
if (path)
{
*path = g_new (gchar *, self->search_path_len + 1);
@@ -881,6 +1098,8 @@ gtk_icon_theme_get_search_path (GtkIconTheme *self,
(*path)[i] = g_strdup (self->search_path[i]);
(*path)[i] = NULL;
}
+
+ gtk_icon_theme_unlock (self);
}
/**
@@ -898,12 +1117,16 @@ gtk_icon_theme_append_search_path (GtkIconTheme *self,
g_return_if_fail (GTK_IS_ICON_THEME (self));
g_return_if_fail (path != NULL);
+ gtk_icon_theme_lock (self);
+
self->search_path_len++;
self->search_path = g_renew (gchar *, self->search_path, self->search_path_len);
self->search_path[self->search_path_len-1] = g_strdup (path);
do_theme_change (self);
+
+ gtk_icon_theme_unlock (self);
}
/**
@@ -923,15 +1146,19 @@ gtk_icon_theme_prepend_search_path (GtkIconTheme *self,
g_return_if_fail (GTK_IS_ICON_THEME (self));
g_return_if_fail (path != NULL);
+ gtk_icon_theme_lock (self);
+
self->search_path_len++;
self->search_path = g_renew (gchar *, self->search_path, self->search_path_len);
for (i = self->search_path_len - 1; i > 0; i--)
self->search_path[i] = self->search_path[i - 1];
-
+
self->search_path[0] = g_strdup (path);
do_theme_change (self);
+
+ gtk_icon_theme_unlock (self);
}
/**
@@ -958,9 +1185,13 @@ gtk_icon_theme_add_resource_path (GtkIconTheme *self,
g_return_if_fail (GTK_IS_ICON_THEME (self));
g_return_if_fail (path != NULL);
+ gtk_icon_theme_lock (self);
+
self->resource_paths = g_list_append (self->resource_paths, g_strdup (path));
do_theme_change (self);
+
+ gtk_icon_theme_unlock (self);
}
/**
@@ -981,7 +1212,9 @@ gtk_icon_theme_set_custom_theme (GtkIconTheme *self,
g_return_if_fail (GTK_IS_ICON_THEME (self));
g_return_if_fail (!self->is_display_singleton);
-
+
+ gtk_icon_theme_lock (self);
+
if (theme_name != NULL)
{
self->custom_theme = TRUE;
@@ -998,9 +1231,11 @@ gtk_icon_theme_set_custom_theme (GtkIconTheme *self,
if (self->custom_theme)
{
self->custom_theme = FALSE;
- update_current_theme (self);
+ update_current_theme__mainthread (self);
}
}
+
+ gtk_icon_theme_unlock (self);
}
static const gchar builtin_hicolor_index[] =
@@ -1376,7 +1611,9 @@ ensure_valid_themes (GtkIconTheme *self)
if (ABS (tv.tv_sec - self->last_stat_time) > 5 &&
rescan_themes (self))
{
+ G_LOCK(info_cache);
g_hash_table_remove_all (self->info_cache);
+ G_UNLOCK(info_cache);
blow_themes (self);
clear_lru_cache (self);
}
@@ -1468,7 +1705,12 @@ real_choose_icon (GtkIconTheme *self,
key.scale = scale;
key.flags = flags;
+ G_LOCK(info_cache);
icon_info = g_hash_table_lookup (self->info_cache, &key);
+ if (icon_info != NULL)
+ icon_info = g_object_ref (icon_info);
+ G_UNLOCK(info_cache);
+
if (icon_info != NULL)
{
DEBUG_CACHE (("cache hit %p (%s %d 0x%x) (cache size %d)\n",
@@ -1477,10 +1719,8 @@ real_choose_icon (GtkIconTheme *self,
icon_info->key.size, icon_info->key.flags,
g_hash_table_size (self->info_cache)));
- icon_info = g_object_ref (icon_info);
-
/* Move item to front in LRU cache */
- add_to_lru_cache (icon_info);
+ add_to_lru_cache (self, icon_info);
return icon_info;
}
@@ -1621,13 +1861,15 @@ real_choose_icon (GtkIconTheme *self,
icon_info->key.size = size;
icon_info->key.scale = scale;
icon_info->key.flags = flags;
+ G_LOCK(info_cache);
icon_info->in_cache = self;
+ g_hash_table_insert (self->info_cache, &icon_info->key, icon_info);
+ G_UNLOCK(info_cache);
DEBUG_CACHE (("adding %p (%s %d 0x%x) to cache (cache size %d)\n",
icon_info,
g_strjoinv (",", icon_info->key.icon_names),
icon_info->key.size, icon_info->key.flags,
g_hash_table_size (self->info_cache)));
- g_hash_table_insert (self->info_cache, &icon_info->key, icon_info);
}
else
{
@@ -1798,7 +2040,10 @@ choose_icon (GtkIconTheme *self,
* like gdk_surface_get_scale_factor(). Instead, you should use
* gtk_icon_theme_lookup_icon_for_scale(), as the assets loaded
* for a given scaling factor may be different.
- *
+ *
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: (nullable) (transfer full): a #GtkIconInfo object
* containing information about the icon, or %NULL if the
* icon wasn’t found.
@@ -1835,6 +2080,9 @@ gtk_icon_theme_lookup_icon (GtkIconTheme *self,
* gtk_icon_info_load_icon(). (gtk_icon_theme_load_icon() combines
* these two steps if all you need is the pixbuf.)
*
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: (nullable) (transfer full): a #GtkIconInfo object
* containing information about the icon, or %NULL if the
* icon wasn’t found.
@@ -1857,6 +2105,8 @@ gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *self,
GTK_DISPLAY_NOTE (self->display, ICONTHEME,
g_message ("looking up icon %s for scale %d", icon_name, scale));
+ gtk_icon_theme_lock (self);
+
if (flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK)
{
gchar **names, **nonsymbolic_names;
@@ -1918,6 +2168,8 @@ gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *self,
info = choose_icon (self, names, size, scale, flags);
}
+ gtk_icon_theme_unlock (self);
+
return info;
}
@@ -1938,7 +2190,10 @@ gtk_icon_theme_lookup_icon_for_scale (GtkIconTheme *self,
* If @icon_names contains more than one name, this function
* tries them all in the given order before falling back to
* inherited icon themes.
- *
+ *
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: (nullable) (transfer full): a #GtkIconInfo object
* containing information about the icon, or %NULL if the icon wasn’t
* found.
@@ -1949,13 +2204,21 @@ gtk_icon_theme_choose_icon (GtkIconTheme *self,
gint size,
GtkIconLookupFlags flags)
{
+ GtkIconInfo *info;
+
g_return_val_if_fail (GTK_IS_ICON_THEME (self), NULL);
g_return_val_if_fail (icon_names != NULL, NULL);
g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
(flags & GTK_ICON_LOOKUP_FORCE_SVG) == 0, NULL);
g_warn_if_fail ((flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) == 0);
- return choose_icon (self, icon_names, size, 1, flags);
+ gtk_icon_theme_lock (self);
+
+ info = choose_icon (self, icon_names, size, 1, flags);
+
+ gtk_icon_theme_unlock (self);
+
+ return info;
}
/**
@@ -1976,7 +2239,10 @@ gtk_icon_theme_choose_icon (GtkIconTheme *self,
* If @icon_names contains more than one name, this function
* tries them all in the given order before falling back to
* inherited icon themes.
- *
+ *
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: (nullable) (transfer full): a #GtkIconInfo object
* containing information about the icon, or %NULL if the
* icon wasn’t found.
@@ -1988,6 +2254,8 @@ gtk_icon_theme_choose_icon_for_scale (GtkIconTheme *self,
gint scale,
GtkIconLookupFlags flags)
{
+ GtkIconInfo *info;
+
g_return_val_if_fail (GTK_IS_ICON_THEME (self), NULL);
g_return_val_if_fail (icon_names != NULL, NULL);
g_return_val_if_fail ((flags & GTK_ICON_LOOKUP_NO_SVG) == 0 ||
@@ -1995,7 +2263,13 @@ gtk_icon_theme_choose_icon_for_scale (GtkIconTheme *self,
g_return_val_if_fail (scale >= 1, NULL);
g_warn_if_fail ((flags & GTK_ICON_LOOKUP_GENERIC_FALLBACK) == 0);
- return choose_icon (self, icon_names, size, scale, flags);
+ gtk_icon_theme_lock (self);
+
+ info = choose_icon (self, icon_names, size, scale, flags);
+
+ gtk_icon_theme_unlock (self);
+
+ return info;
}
@@ -2119,10 +2393,13 @@ gtk_icon_theme_load_icon_for_scale (GtkIconTheme *self,
* gtk_icon_theme_has_icon:
* @self: a #GtkIconTheme
* @icon_name: the name of an icon
- *
+ *
* Checks whether an icon theme includes an icon
* for a particular name.
- *
+ *
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: %TRUE if @self includes an
* icon for @icon_name.
*/
@@ -2131,28 +2408,40 @@ gtk_icon_theme_has_icon (GtkIconTheme *self,
const gchar *icon_name)
{
GList *l;
+ gboolean res = FALSE;
g_return_val_if_fail (GTK_IS_ICON_THEME (self), FALSE);
g_return_val_if_fail (icon_name != NULL, FALSE);
+ gtk_icon_theme_lock (self);
+
ensure_valid_themes (self);
for (l = self->dir_mtimes; l; l = l->next)
{
IconThemeDirMtime *dir_mtime = l->data;
GtkIconCache *cache = dir_mtime->cache;
-
+
if (cache && gtk_icon_cache_has_icon (cache, icon_name))
- return TRUE;
+ {
+ res = TRUE;
+ goto out;
+ }
}
for (l = self->themes; l; l = l->next)
{
if (theme_has_icon (l->data, icon_name))
- return TRUE;
+ {
+ res = TRUE;
+ goto out;
+ }
}
- return FALSE;
+ out:
+ gtk_icon_theme_unlock (self);
+
+ return res;
}
static void
@@ -2176,7 +2465,10 @@ add_size (gpointer key,
* the icon is available without scaling. A size of -1 means
* that the icon is available in a scalable format. The array
* is zero-terminated.
- *
+ *
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: (array zero-terminated=1) (transfer full): A newly
* allocated array describing the sizes at which the icon is
* available. The array should be freed with g_free() when it is no
@@ -2189,10 +2481,12 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *self,
GList *l, *d;
GHashTable *sizes;
gint *result, *r;
- guint suffix;
+ guint suffix;
g_return_val_if_fail (GTK_IS_ICON_THEME (self), NULL);
-
+
+ gtk_icon_theme_lock (self);
+
ensure_valid_themes (self);
sizes = g_hash_table_new (g_direct_hash, g_direct_equal);
@@ -2222,7 +2516,9 @@ gtk_icon_theme_get_icon_sizes (GtkIconTheme *self,
g_hash_table_foreach (sizes, add_size, &r);
g_hash_table_destroy (sizes);
-
+
+ gtk_icon_theme_unlock (self);
+
return result;
}
@@ -2261,6 +2557,9 @@ add_key_to_list (gpointer key,
* The standard contexts are listed in the
* [Icon Naming Specification](http://www.freedesktop.org/wiki/Specifications/icon-naming-spec).
*
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: (element-type utf8) (transfer full): a #GList list
* holding the names of all the icons in the theme. You must
* first free each element in the list with g_free(), then
@@ -2273,7 +2572,9 @@ gtk_icon_theme_list_icons (GtkIconTheme *self,
GHashTable *icons;
GList *list, *l;
GQuark context_quark;
-
+
+ gtk_icon_theme_lock (self);
+
ensure_valid_themes (self);
if (context)
@@ -2281,7 +2582,7 @@ gtk_icon_theme_list_icons (GtkIconTheme *self,
context_quark = g_quark_try_string (context);
if (!context_quark)
- return NULL;
+ goto out;
}
else
context_quark = 0;
@@ -2307,7 +2608,11 @@ gtk_icon_theme_list_icons (GtkIconTheme *self,
&list);
g_hash_table_destroy (icons);
-
+
+ out:
+
+ gtk_icon_theme_unlock (self);
+
return list;
}
@@ -2353,6 +2658,9 @@ rescan_themes (GtkIconTheme *self)
* currently cached information is discarded and will be reloaded
* next time @self is accessed.
*
+ * This call is threadsafe, you can safely pass a GtkIconTheme
+ * to another thread and call this method on it.
+ *
* Returns: %TRUE if the icon theme has changed and needed
* to be reloaded.
*/
@@ -2363,10 +2671,14 @@ gtk_icon_theme_rescan_if_needed (GtkIconTheme *self)
g_return_val_if_fail (GTK_IS_ICON_THEME (self), FALSE);
+ gtk_icon_theme_lock (self);
+
retval = rescan_themes (self);
if (retval)
do_theme_change (self);
+ gtk_icon_theme_unlock (self);
+
return retval;
}
@@ -3047,8 +3359,10 @@ gtk_icon_info_finalize (GObject *object)
{
GtkIconInfo *icon_info = (GtkIconInfo *) object;
+ G_LOCK(info_cache);
if (icon_info->in_cache)
g_hash_table_remove (icon_info->in_cache->info_cache, &icon_info->key);
+ G_UNLOCK(info_cache);
g_strfreev (icon_info->key.icon_names);
@@ -3172,6 +3486,25 @@ icon_info_get_pixbuf_ready (GtkIconInfo *icon_info)
return FALSE;
}
+static void
+icon_info_add_to_lru_cache (GtkIconInfo *info)
+{
+ GtkIconTheme *theme = NULL;
+
+ G_LOCK(info_cache);
+ if (info->in_cache)
+ theme = g_object_ref (info->in_cache);
+ G_UNLOCK(info_cache);
+
+ if (theme)
+ {
+ gtk_icon_theme_lock (theme);
+ add_to_lru_cache (theme, info);
+ gtk_icon_theme_unlock (theme);
+ g_object_unref (theme);
+ }
+}
+
static GLoadableIcon *
icon_info_get_loadable (GtkIconInfo *icon_info)
{
@@ -3404,7 +3737,7 @@ icon_info_ensure_scale_and_texture (GtkIconInfo *icon_info)
}
g_assert (icon_info->texture != NULL);
- add_to_lru_cache (icon_info);
+ icon_info_add_to_lru_cache (icon_info);
return TRUE;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]