On Wed, 2024-11-06 at 15:11 +0200, Eli Zaretskii wrote: > > > Thanks.  Can you try calling hb_font_destroy in > ftcrhbfont_end_hb_font > and setting ftcrfont_info->hb_font to NULL right after that?  If that > solves the problem, we could at least install this for now, until we > have a better solution (if one exists). Yes, that appears to work. And I don't think there's an obvious better solution. See attached patch. And I think I understand the root cause now: What the cairo backend persists is a cairo_font_face_t, from which it will obtain an FT_Face on demand using cairo_ft_scaled_font_lock_face (and cairo_ft_scaled_font_unlock_face), e.g., to pass it to hb_ft_font_create_referenced to create a hb_font_t. The lock/unlock interface implies that we should not keep a reference to the FT_Face in hb_font_t after cairo_ft_scaled_font_unlock_face, and so we should not persist the hb_font_t. Here's an unconfirmed theory what actually happens under the hood:  Cairo internally caches an unscaled FT_Face and scales it on demand to a specific size. That means that cairo_ft_scaled_font_lock_face will scale the one FT_Face to which we still hold possibly multiple references (one for each size) inside the hb_font_t object. Since we call cairo_ft_scaled_font_lock_face every time before accessing the hb_font_t, we happen to see the scaled version with the correct size every time. So far we were lucky. But Cairo's cache is limited to (currently) 10 elements [1]. As a result, if many fonts are used, Cairo will evict a random cached FT_Face, and later create a new one if requested via cairo_ft_scaled_font_lock_face. In that case, we hold a reference to the outdated FT_Face, which won't be scaled any longer by cairo_ft_scaled_font_lock_face, and thus we pass a face with the wrong size to harfbuzz. The caching logic also explains why removing the unlock call makes the problem disappear: Cairo won't evict the FT_Face of locked cairo_scaled_font_t objects, even if this means going over 10. This suggests that removing the cairo_ft_scaled_font_unlock_face call is another fix, probably slightly more efficient, but also somewhat frivolous. The docs explicitly say this: "Since the FT_Face object can be shared between multiple cairo_scaled_font_t objects, you must not lock any other font objects until you unlock this one." Though, if you look at the implementation, they release the mutex explicitly, so there can't be any deadlocks in the end. (Not suggesting we should rely on this...) [1] See MAX_OPEN_FACES in src/cairo-ft-font.c in the Cairo source code.