-
Notifications
You must be signed in to change notification settings - Fork 1k
Fix thread safety in Application.ThreadContext.FromId #13489
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
Conversation
Co-authored-by: JeremyKuhne <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
The purpose of this PR is to ensure thread safety in Application.ThreadContext.FromId by adding proper locking when accessing the s_contextHash dictionary.
- The lock (s_lock) is added to wrap the dictionary access.
- The method now synchronously checks and creates a ThreadContext if necessary under the same locking mechanism.
src/System.Windows.Forms/System/Windows/Forms/Application.ThreadContext.cs
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Update the lock to not take it unless the item can not be found. Check the dictionary again after taking the lock.
Co-authored-by: JeremyKuhne <[email protected]>
I've updated the code to optimize the lock acquisition as requested. The lock is now only taken when the item is not found initially, and I added a second dictionary lookup inside the lock to handle cases where another thread might have added the context between our checks. This change is in commit f5de8f8. |
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #13489 +/- ##
====================================================
+ Coverage 76.59589% 97.40411% +20.80822%
====================================================
Files 3230 1176 -2054
Lines 639157 352557 -286600
Branches 47295 5356 -41939
====================================================
- Hits 489568 343405 -146163
+ Misses 146003 8398 -137605
+ Partials 3586 754 -2832
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, too!
@JeremyKuhne - this is fun! Let's collect note in thinks we notice, and then exchange experiences every 2 weeks or so? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not believe this PR fixes the bug or improves the thread safety at all. Dictionary<TKey, TValue>.TryGetValue is still called without a lock while another thread may be writing to the same dictionary. IIRC, such access can cause FindEntry on the reader thread to fall into an infinite loop.
@KalleOlaviNiemitalo we're looking the value up again after taking the lock which should handle the race you're talking about. |
No, the infinite loop would happen already in the first TryGetValue call before you take the lock. |
The risk here is not that multiple threads add the same key to the dictionary in parallel. That was already impossible because each thread only adds its own ID as the key, and no two running threads have the same ID. Rather, the risk is that the dictionary is temporarily in an inconsistent state while one thread is adding a key (and the dictionary is perhaps resizing itself), and that confuses the lookup of a different key on another thread. |
Issue
The
Application.ThreadContext.FromId
method was reading from thes_contextHash
dictionary without using a lock, which could cause errors if another thread writes to the dictionary at the same time.Before this PR:
s_contextHash
was being read without synchronization in theFromId
methods_lock
Fix
Added proper locking when accessing the
s_contextHash
dictionary in theFromId
method to ensure thread safety.This change ensures thread safety by using the same locking pattern that's already used in other parts of the codebase that write to the
s_contextHash
dictionary.Fixes #13246.
Warning
Firewall rules blocked me from connecting to one or more addresses
I tried to connect to the following addresses, but was blocked by firewall rules:
dc.services.visualstudio.com
/home/REDACTED/work/winforms/winforms/.dotnet/dotnet msbuild /m /nologo /clp:Summary /v:minimal /nr:true /warnaserror /p:TreatWarningsAsErrors=true /p:ContinuousIntegrationBuild=false /home/REDACTED/work/winforms/winforms/artifacts/toolset/restore.proj /t:__WriteToolsetLocation /clp:ErrorsOnly;NoSummary /p:__ToolsetLocationOutputFile=/home/REDACTED/work/winforms/winforms/artifacts/toolset/10.0.0-beta.25267.102.txt
(dns block)pkgs.dev.azure.com
/home/REDACTED/work/winforms/winforms/.dotnet/dotnet msbuild /m /nologo /clp:Summary /v:minimal /nr:true /warnaserror /p:TreatWarningsAsErrors=true /p:ContinuousIntegrationBuild=false /home/REDACTED/work/winforms/winforms/artifacts/toolset/restore.proj /t:__WriteToolsetLocation /clp:ErrorsOnly;NoSummary /p:__ToolsetLocationOutputFile=/home/REDACTED/work/winforms/winforms/artifacts/toolset/10.0.0-beta.25267.102.txt
(dns block)If you need me to access, download, or install something from one of these locations, you can either:
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.