[cairo] scaled_font lifecycle issue

Fred Bca fredbca21 at gmail.com
Wed Apr 6 08:59:27 UTC 2016


With the current implementation of scaled fonts, I am encountering
lifecycle issues when using system fonts (HFONT on windows or
CGFontRef on Mac) that are already managed by third party components.

Typically, I would expect that after the following code, the system
font can be fully released (cairo not holding any reference to it
anymore):, but it is not the case

// win32 example but same problem with other backends
cairo_font_face_t* font_face=cairo_win32_font_face_create_for_hfont(my_hfont);
// I want to be able to destroy the my_hfont object here - if I do it,
subsequent calls to cairo using the same font face with same size will
fail because the hfont has been kept in cache.

The problem lies in the _cairo_scaled_font_map cache that holds
references to unused scaled fonts in the holdovers list, and in the
mru_scaled_font. So even if the font is not used anymore, it will
remain in this extra cache forever.

It is of course easy to fix this by completely removing the holdovers
and mru cache (it works very well with the attached patch) but given
the impact this is probably not a long term solution. I know that
there are callbacks to be notified when fonts are actually destroyed,
but it is not an option when font management is done by an external
component (and anyway in this case it only happens when calling the
cairo_debug_reset_static_data, so it will just leak in production

So would it be possible to add something to disable this cache of
unused items at runtime or buildtime? For example, a macro or a global
function that would be called on initialization would be nice.

Index: cairo-scaled-font.c
--- cairo-scaled-font.c (cairo-1.14.6)
+++ cairo-scaled-font.c (Working Copy)
@@ -351,12 +351,13 @@
 /* This defines the size of the holdover array ... that is, the number
  * of scaled fonts we keep around even when not otherwise referenced

 typedef struct _cairo_scaled_font_map {
     cairo_scaled_font_t *mru_scaled_font;
     cairo_hash_table_t *hash_table;
-    cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS];
+    cairo_scaled_font_t *holdovers[CAIRO_SCALED_FONT_MAX_HOLDOVERS_ARRAY_SIZE];
     int num_holdovers;
 } cairo_scaled_font_map_t;

@@ -1099,9 +1100,11 @@
      * held. */

     old = font_map->mru_scaled_font;
     font_map->mru_scaled_font = scaled_font;
     /* increment reference count for the mru cache */
     _cairo_reference_count_inc (&scaled_font->ref_count);
     /* and increment for the returned reference */
     _cairo_reference_count_inc (&scaled_font->ref_count);
     _cairo_scaled_font_map_unlock ();
@@ -1175,8 +1178,10 @@
     if (likely (status == CAIRO_STATUS_SUCCESS)) {
  old = font_map->mru_scaled_font;
  font_map->mru_scaled_font = scaled_font;
  _cairo_reference_count_inc (&scaled_font->ref_count);

     _cairo_scaled_font_map_unlock ();
@@ -1335,7 +1340,7 @@
      * the reference count). To make room for it, we do actually
      * destroy the least-recently-used holdover.
     if (font_map->num_holdovers == CAIRO_SCALED_FONT_MAX_HOLDOVERS) {
  lru = font_map->holdovers[0];
  assert (! CAIRO_REFERENCE_COUNT_HAS_REFERENCE (&lru->ref_count));
@@ -1351,6 +1356,11 @@

     font_map->holdovers[font_map->num_holdovers++] = scaled_font;
     scaled_font->holdover = TRUE;
+        lru = scaled_font;
+        _cairo_hash_table_remove (font_map->hash_table,
+  &lru->hash_entry);
  } else
     lru = scaled_font;

More information about the cairo mailing list