[cairo-commit] src/cairo-scaled-font.c

Behdad Esfahbod behdad at kemper.freedesktop.org
Tue Aug 2 21:21:56 UTC 2016


 src/cairo-scaled-font.c |   24 ++++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

New commits:
commit 1057487ce8560ae0377bb509fc46eaf18df7aee0
Author: Hans Petter Jansson <hpj at cl.no>
Date:   Wed Jan 27 12:55:01 2016 -0600

    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

diff --git a/src/cairo-scaled-font.c b/src/cairo-scaled-font.c
index b2557d4..38f3efa 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);
 }
 


More information about the cairo-commit mailing list