Skip to content

BAML readers in Application.LoadContent() are not protected with locks, resulting in showstopper exceptions #3411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ClosetBugSlayer opened this issue Aug 29, 2020 · 3 comments

Comments

@ClosetBugSlayer
Copy link

BAML readers in Application.LoadContent() are not being properly protected with locks. This prevents windows with complex content from opening simultaneously on multiple separate threads because they crash with unhandled exceptions. It is almost impossibly difficult to lock this functionality at the application level. This bug is perfectly easy to demonstrate. This applies to any and all versions of WPF.

@ClosetBugSlayer ClosetBugSlayer changed the title BAML readers in Application.LoadContent() are not being properly protected with locks BAML readers in Application.LoadContent() are not protected with locks, resulting in showstopper exceptions Aug 29, 2020
@ryalanms
Copy link
Member

Could you provide a minimal repro application? Thanks.

@ClosetBugSlayer
Copy link
Author

ClosetBugSlayer commented Sep 18, 2020

Could you provide a minimal repro application? Thanks.

Too busy to create a repro yet but stay tuned.

This decade-old post on Stack Overflow illustrates another aspect of the problem. Again, the workaround offered is almost impossible because Application.LoadComponent() is wrapped inside autogenerated InitializeComponent() calls. This strategy is also prone to mystery deadlocks when deep objects in the XAML graph access dependency properties on objects belonging to other threads.

https://stackoverflow.com/questions/2463822/threading-errors-with-application-loadcomponent-key-already-exists


You are not doing something wrong. MSDN is wrong. Application.LoadComponent is not actually thread safe. This is a bug in WPF, in my opinion.

The problem is that whenever Application.LoadComponent loads a "Part" from a "Package" it:

  1. Checks its internal cache for the package to see if the part is already loaded & returns it if found
  2. Loads the part from the file
  3. Adds it to the internal cache
  4. Returns it

You have two threads calling Application.LoadComponent to load the same part at the same time. The MSDN documentation says this is ok, but what is happening is:

  1. Thread Use nameof(SRID.PropertyName) syntax instead of depending on GenerateResxSource.GenerateResourcesCodeAsConstants #1 checks the cache and starts loading from the file
  2. Thread initial commit of system.xaml #2 checks the cache and starts loading from the file
  3. Thread Use nameof(SRID.PropertyName) syntax instead of depending on GenerateResxSource.GenerateResourcesCodeAsConstants #1 finishes loading from the file and adds to the cache
  4. Thread initial commit of system.xaml #2 finishes loading from the file and tries to add to the cache, resulting in a duplicate key exception

The workaround for the bug is to wrap all calls to Application.LoadComponent inside a lock().

Your lock object can be created thusly in your App.cs or elsewhere (your choice):

public static object MyLoadComponentLock = new Object();

Then your LoadComponent call looks like this:

 lock(App.MyLoadComponentLock)
   genericDictionary = (ResourceDictionary)Application.LoadComponent(...

@ryalanms
Copy link
Member

Fixed by @SamBent.

@ghost ghost locked as resolved and limited conversation to collaborators Apr 11, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants