diff --git a/src/foundation/src/MigraDoc/src/MigraDoc.Rendering/Rendering/FontHandler.cs b/src/foundation/src/MigraDoc/src/MigraDoc.Rendering/Rendering/FontHandler.cs index 075e4e82..90dc3959 100644 --- a/src/foundation/src/MigraDoc/src/MigraDoc.Rendering/Rendering/FontHandler.cs +++ b/src/foundation/src/MigraDoc/src/MigraDoc.Rendering/Rendering/FontHandler.cs @@ -23,10 +23,13 @@ static class FontHandler /// internal static XFont FontToXFont(Font font) { - // Check if both WeakReferences are still valid and point to the font we need. - if (_lastXFont != null && _lastFont != null && - _lastFont.TryGetTarget(out var lastFont) && font == lastFont && - _lastXFont.TryGetTarget(out var lastXFont)) + // Read the whole cache entry once. The Font and the XFont are published together by a single + // reference assignment, so a concurrent update can never be observed half-applied and this can + // never return an XFont belonging to a different Font. See FontCacheEntry. + var entry = _lastEntry; + if (entry != null && + entry.FontRef.TryGetTarget(out var lastFont) && font == lastFont && + entry.XFontRef.TryGetTarget(out var lastXFont)) return lastXFont; XFontStyleEx style = GetXStyle(font); @@ -39,16 +42,31 @@ internal static XFont FontToXFont(Font font) #if DEBUG_ CreateFontCounter++; #endif - _lastFont = new(font); - _lastXFont = new(xFont); + _lastEntry = new FontCacheEntry(font, xFont); #if FORCE_MEMORYLEAK _lastFont2 = font; #endif return xFont; } - static WeakReference? _lastXFont; - static WeakReference? _lastFont; + /// + /// The single-entry font cache. Both WeakReferences are assigned in the constructor and the instance + /// is published by one atomic reference assignment, so a reader sees either the complete previous + /// entry or the complete new one — never the Font from one and the XFont from another. + /// + sealed class FontCacheEntry + { + internal FontCacheEntry(Font font, XFont xFont) + { + FontRef = new WeakReference(font); + XFontRef = new WeakReference(xFont); + } + + internal readonly WeakReference FontRef; + internal readonly WeakReference XFontRef; + } + + static volatile FontCacheEntry? _lastEntry; #if FORCE_MEMORYLEAK static Font? _lastFont2; #endif