summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHans Petter Jansson <hpj@cl.no>2016-01-27 12:55:01 -0600
committerBehdad Esfahbod <behdad@behdad.org>2016-08-02 14:21:45 -0700
commit1057487ce8560ae0377bb509fc46eaf18df7aee0 (patch)
tree048f9bf5363527c42fb4741d97f658f7c728bba9
parent3f8241f48488d3da4afce6646e043ee70cf1cfd2 (diff)
scaled-font: Fix deadlock when recursing in _cairo_scaled_font_reset_cache()
The destruction of a scaled font could indirectly trigger the destruction of a second scaled font, causing the global cache to be locked twice in the same thread. This is solved by unlinking the font's glyph pages while holding the global lock, then releasing the lock before destruction takes place. Fixes: https://bugs.freedesktop.org/show_bug.cgi?id=93891
-rw-r--r--src/cairo-scaled-font.c24
1 files changed, 18 insertions, 6 deletions
diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index b2557d42b..38f3efac2 100644
--- a/src/cairo-scaled-font.c
+++ b/src/cairo-scaled-font.c
@@ -818,23 +818,35 @@ _cairo_scaled_font_thaw_cache (cairo_scaled_font_t *scaled_font)
void
_cairo_scaled_font_reset_cache (cairo_scaled_font_t *scaled_font)
{
+ cairo_scaled_glyph_page_t *page;
+
CAIRO_MUTEX_LOCK (scaled_font->mutex);
assert (! scaled_font->cache_frozen);
assert (! scaled_font->global_cache_frozen);
CAIRO_MUTEX_LOCK (_cairo_scaled_glyph_page_cache_mutex);
- while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
- cairo_scaled_glyph_page_t *page =
- cairo_list_first_entry (&scaled_font->glyph_pages,
- cairo_scaled_glyph_page_t,
- link);
+ cairo_list_foreach_entry (page,
+ cairo_scaled_glyph_page_t,
+ &scaled_font->glyph_pages,
+ link) {
cairo_scaled_glyph_page_cache.size -= page->cache_entry.size;
_cairo_hash_table_remove (cairo_scaled_glyph_page_cache.hash_table,
(cairo_hash_entry_t *) &page->cache_entry);
+ }
+ CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+
+ /* Destroy scaled_font's pages while holding its lock only, and not the
+ * global page cache lock. The destructor can cause us to recurse and
+ * end up back here for a different scaled_font. */
+
+ while (! cairo_list_is_empty (&scaled_font->glyph_pages)) {
+ page = cairo_list_first_entry (&scaled_font->glyph_pages,
+ cairo_scaled_glyph_page_t,
+ link);
_cairo_scaled_glyph_page_destroy (scaled_font, page);
}
- CAIRO_MUTEX_UNLOCK (_cairo_scaled_glyph_page_cache_mutex);
+
CAIRO_MUTEX_UNLOCK (scaled_font->mutex);
}