-
Notifications
You must be signed in to change notification settings - Fork 257
[Refactoring] - use ConcurrentDictionary in font caches - #330
Conversation
# Conflicts: # src/Directory.Packages.props
This reverts commit e279819
ThomasHoevel
commented
Feb 4, 2026
To me this looks as "hello world" example of refactoring to
ConcurrentDictionary.
With PDFsharp, one lock synchronizes access to several dictionaries, so they all are either locked or in a valid state.
With your proposed changes, a new font might be registered in one dictionary by one thread which is then suspended - and another thread might register it in another dictionary.
With other words: We have doubts that your changes really work in multi-threaded tasks, e.g. a web server creating PDF files.
The locks can all be safely removed in a single-thread single-task environment. But they are needed for web server use.
Your benchmarks do not prove that your changes are safe.
StLange
commented
Mar 23, 2026
We replaced Dictionary with ConcurrentDictionary to improve thread safety. However, we can't do without the lock. When a new XFont is created the first time, many things happen. The FontResolver provides the font file, a FontSource is created, along with an OpenTypeFontFace, an OpenTypeFontDescriptor, and a few other objects, all of which are cached. This must be an atomic operation, and that's what the lock is for.
On the one hand, it's very difficult to prove the formal correctness of caches in a multi-threaded environment, and on the other hand, the implementation is hardly testable. Even in a high-traffic web application, errors are extremely unlikely because no new fonts are created after a few PDFs have been generated, and the caches are stable. And if an error does occur, it's practically impossible to trace. All of this can be avoided from the outset with the lock.
Thanks for your support.
Problem
Different font info caches use
Dictionaryas a data store. To ensure exclusive access to this cachesMonitoris used throughLocks.EnterFontFactory. To me this looks as "hello world" example of refactoring toConcurrentDictionary. The benefits of this data structure are obviously benefitting:Measurements
Inside Stepami#1 I measured benefit of my refactoring with
BenchmarkDotNet. Here are the results: