Skip to content

Commit 9db17fd

Browse files
authored
Handle ThreadContext cache more cleanly (#13530)
Switch to a concurrent backing collection. Note that we only ever create them if you ask for them on the current thread.
1 parent 7ec2fbb commit 9db17fd

File tree

1 file changed

+14
-38
lines changed

1 file changed

+14
-38
lines changed

src/System.Windows.Forms/System/Windows/Forms/Application.ThreadContext.cs

Lines changed: 14 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Collections.Concurrent;
45
using System.ComponentModel;
56
using System.Runtime.ExceptionServices;
67
using Microsoft.Office;
@@ -22,9 +23,7 @@ internal abstract unsafe partial class ThreadContext : MarshalByRefObject, IHand
2223
private bool _inThreadException;
2324
private bool _filterSnapshotValid;
2425

25-
private static readonly Dictionary<uint, ThreadContext> s_contextHash = [];
26-
27-
private static readonly Lock s_lock = new();
26+
private static readonly ConcurrentDictionary<uint, ThreadContext> s_contextHash = [];
2827
private readonly Lock _marshallingControlLock = new();
2928

3029
private static int s_totalMessageLoopCount;
@@ -87,11 +86,7 @@ protected ThreadContext()
8786
_id = PInvokeCore.GetCurrentThreadId();
8887
_messageLoopCount = 0;
8988
t_currentThreadContext = this;
90-
91-
lock (s_lock)
92-
{
93-
s_contextHash[_id] = this;
94-
}
89+
s_contextHash[_id] = this;
9590
}
9691

9792
public ApplicationContext? ApplicationContext { get; private set; }
@@ -316,10 +311,7 @@ private void DisposeInternal(bool disposing)
316311
}
317312
finally
318313
{
319-
lock (s_lock)
320-
{
321-
s_contextHash.Remove(_id);
322-
}
314+
s_contextHash.Remove(_id, out _);
323315

324316
if (t_currentThreadContext == this)
325317
{
@@ -439,27 +431,17 @@ protected virtual void EndModalMessageLoop() { }
439431
/// <summary>
440432
/// Exits the program by disposing of all thread contexts and message loops.
441433
/// </summary>
442-
internal static void ExitApplication() => ExitCommon(disposing: true);
443-
444-
private static void ExitCommon(bool disposing)
434+
internal static void ExitApplication()
445435
{
446-
lock (s_lock)
436+
foreach ((_, var threadContext) in s_contextHash)
447437
{
448-
if (s_contextHash is not null)
438+
if (threadContext.ApplicationContext is ApplicationContext applicationContext)
449439
{
450-
ThreadContext[] contexts = new ThreadContext[s_contextHash.Values.Count];
451-
s_contextHash.Values.CopyTo(contexts, 0);
452-
for (int i = 0; i < contexts.Length; ++i)
453-
{
454-
if (contexts[i].ApplicationContext is ApplicationContext context)
455-
{
456-
context.ExitThread();
457-
}
458-
else
459-
{
460-
contexts[i].Dispose(disposing);
461-
}
462-
}
440+
applicationContext.ExitThread();
441+
}
442+
else
443+
{
444+
threadContext.Dispose(disposing: true);
463445
}
464446
}
465447
}
@@ -517,14 +499,8 @@ private static ThreadContext Create()
517499

518500
if (id == PInvokeCore.GetCurrentThreadId())
519501
{
520-
lock (s_lock)
521-
{
522-
// Check again inside the lock as another thread may have added it
523-
if (!s_contextHash.TryGetValue(id, out context))
524-
{
525-
context = Create();
526-
}
527-
}
502+
context = Create();
503+
Debug.Assert(context._id == id);
528504
}
529505

530506
return context;

0 commit comments

Comments
 (0)