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
This commit is contained in:
Hans Petter Jansson 2016-01-27 12:55:01 -06:00 committed by Bryce Harrington
parent 53f3077b06
commit 4f0abfb8c7

View file

@ -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);
}