Skip to content

[WIP] Make sure TryGetGlyphTypeface does not fail for concurrent access#21269

Open
Gillibald wants to merge 2 commits intoAvaloniaUI:masterfrom
Gillibald:fixes/tryGetGlyphTypefaceRace
Open

[WIP] Make sure TryGetGlyphTypeface does not fail for concurrent access#21269
Gillibald wants to merge 2 commits intoAvaloniaUI:masterfrom
Gillibald:fixes/tryGetGlyphTypefaceRace

Conversation

@Gillibald
Copy link
Copy Markdown
Contributor

What does the pull request do?

What is the current behavior?

What is the updated/expected behavior with this PR?

How was the solution implemented (if it's not obvious)?

Checklist

Breaking changes

Obsoletions / Deprecations

Fixed issues

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This WIP PR aims to prevent FontManager.TryGetGlyphTypeface failures under concurrent access by making font collection retrieval/creation more concurrency-safe, and adds a unit test that exercises concurrent embedded-font lookups.

Changes:

  • Update FontManager.TryGetFontCollection to use _fontCollections.GetOrAdd(...) when retrieving/creating system and embedded font collections.
  • Add a new unit test that repeatedly forces embedded font collection recreation and performs concurrent TryGetGlyphTypeface calls.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Avalonia.Base/Media/FontManager.cs Switches font collection caching logic to ConcurrentDictionary.GetOrAdd for concurrent access.
tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs Adds a concurrency regression test for embedded font lookups.

Comment on lines +363 to +376
fontCollection = _fontCollections.GetOrAdd(source, static (key, platformImpl) =>
{
if (source == SystemFontsKey)
if (key == SystemFontsKey)
{
fontCollection = new SystemFontCollection(PlatformImpl);
}
else
{
if (source.IsAbsoluteResm() || source.IsAvares())
{
fontCollection = new EmbeddedFontCollection(source, source);
}
return new SystemFontCollection(platformImpl);
}

if (fontCollection != null)
if (key.IsAbsoluteResm() || key.IsAvares())
{
return _fontCollections.TryAdd(fontCollection.Key, fontCollection);
return new EmbeddedFontCollection(key, key);
}
}

return null!;
}, PlatformImpl);
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConcurrentDictionary<Uri, IFontCollection> does not allow null values. Returning null! from the GetOrAdd valueFactory will cause GetOrAdd to throw (e.g., when source is a fonts: key that hasn’t been registered, or any other unsupported scheme). Consider handling unsupported schemes by returning false without calling GetOrAdd (or by using TryGetValue for non-embedded collections), and only using GetOrAdd for sources you can always create a non-null collection for (system fonts / embedded fonts).

Copilot uses AI. Check for mistakes.
Comment on lines +101 to +104
var fontCollections = (ConcurrentDictionary<Uri, IFontCollection>)
typeof(FontManager)
.GetField("_fontCollections", BindingFlags.NonPublic | BindingFlags.Instance)!
.GetValue(fontManager)!;
Copy link

Copilot AI Apr 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test reaches into FontManager internals via reflection to clear the cache. Since FontManager already exposes RemoveFontCollection(Uri key), using that API would make the test less brittle (field name/type changes won’t break it) and also aligns with production usage.

Copilot uses AI. Check for mistakes.
Comment thread tests/Avalonia.Base.UnitTests/Media/FontManagerTests.cs Outdated
@MrJul MrJul added bug area-textprocessing backport-candidate-12.0.x Consider this PR for backporting to 12.0 branch labels Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-textprocessing backport-candidate-12.0.x Consider this PR for backporting to 12.0 branch bug

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants