Skip to content

Commit e2e089c

Browse files
authored
[release/8.0] ComponentModel threading fixes (#107353)
1 parent 8072b23 commit e2e089c

File tree

5 files changed

+107
-87
lines changed

5 files changed

+107
-87
lines changed

src/libraries/System.ComponentModel.TypeConverter/src/System.ComponentModel.TypeConverter.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@
253253
SetTargetFramework="TargetFramework=netstandard2.0"
254254
OutputItemType="Analyzer" />
255255
<Reference Include="System.Collections" />
256+
<Reference Include="System.Collections.Concurrent" />
256257
<Reference Include="System.Collections.NonGeneric" />
257258
<Reference Include="System.Collections.Specialized" />
258259
<Reference Include="System.ComponentModel" />

src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/PropertyDescriptor.cs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections;
5+
using System.Collections.Concurrent;
56
using System.Collections.Generic;
67
using System.Diagnostics.CodeAnalysis;
78
using System.Reflection;
9+
using System.Threading;
810

911
namespace System.ComponentModel
1012
{
@@ -16,10 +18,11 @@ public abstract class PropertyDescriptor : MemberDescriptor
1618
internal const string PropertyDescriptorPropertyTypeMessage = "PropertyDescriptor's PropertyType cannot be statically discovered.";
1719

1820
private TypeConverter? _converter;
19-
private Dictionary<object, EventHandler?>? _valueChangedHandlers;
21+
private ConcurrentDictionary<object, EventHandler?>? _valueChangedHandlers;
2022
private object?[]? _editors;
2123
private Type[]? _editorTypes;
2224
private int _editorCount;
25+
private object? _syncObject;
2326

2427
/// <summary>
2528
/// Initializes a new instance of the <see cref='System.ComponentModel.PropertyDescriptor'/> class with the specified name and
@@ -49,6 +52,8 @@ protected PropertyDescriptor(MemberDescriptor descr, Attribute[]? attrs) : base(
4952
{
5053
}
5154

55+
private object SyncObject => LazyInitializer.EnsureInitialized(ref _syncObject);
56+
5257
/// <summary>
5358
/// When overridden in a derived class, gets the type of the
5459
/// component this property is bound to.
@@ -124,10 +129,11 @@ public virtual void AddValueChanged(object component, EventHandler handler)
124129
ArgumentNullException.ThrowIfNull(component);
125130
ArgumentNullException.ThrowIfNull(handler);
126131

127-
_valueChangedHandlers ??= new Dictionary<object, EventHandler?>();
128-
129-
EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null);
130-
_valueChangedHandlers[component] = (EventHandler?)Delegate.Combine(h, handler);
132+
lock (SyncObject)
133+
{
134+
_valueChangedHandlers ??= new ConcurrentDictionary<object, EventHandler?>(concurrencyLevel: 1, capacity: 0);
135+
_valueChangedHandlers.AddOrUpdate(component, handler, (k, v) => (EventHandler?)Delegate.Combine(v, handler));
136+
}
131137
}
132138

133139
/// <summary>
@@ -392,15 +398,18 @@ public virtual void RemoveValueChanged(object component, EventHandler handler)
392398

393399
if (_valueChangedHandlers != null)
394400
{
395-
EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null);
396-
h = (EventHandler?)Delegate.Remove(h, handler);
397-
if (h != null)
398-
{
399-
_valueChangedHandlers[component] = h;
400-
}
401-
else
401+
lock (SyncObject)
402402
{
403-
_valueChangedHandlers.Remove(component);
403+
EventHandler? h = _valueChangedHandlers.GetValueOrDefault(component, defaultValue: null);
404+
h = (EventHandler?)Delegate.Remove(h, handler);
405+
if (h != null)
406+
{
407+
_valueChangedHandlers[component] = h;
408+
}
409+
else
410+
{
411+
_valueChangedHandlers.Remove(component, out EventHandler? _);
412+
}
404413
}
405414
}
406415
}

src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs

Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections;
55
using System.Collections.Generic;
6+
using System.Collections.Concurrent;
67
using System.ComponentModel.Design;
78
using System.Diagnostics;
89
using System.Diagnostics.CodeAnalysis;
@@ -22,10 +23,8 @@ namespace System.ComponentModel
2223
/// </summary>
2324
internal sealed partial class ReflectTypeDescriptionProvider : TypeDescriptionProvider
2425
{
25-
// Hastable of Type -> ReflectedTypeData. ReflectedTypeData contains all
26-
// of the type information we have gathered for a given type.
27-
//
28-
private Hashtable? _typeData;
26+
// ReflectedTypeData contains all of the type information we have gathered for a given type.
27+
private readonly ConcurrentDictionary<Type, ReflectedTypeData> _typeData = new ConcurrentDictionary<Type, ReflectedTypeData>();
2928

3029
// This is the signature we look for when creating types that are generic, but
3130
// want to know what type they are dealing with. Enums are a good example of this;
@@ -91,8 +90,6 @@ private static Type[] InitializeSkipInterfaceAttributeList()
9190

9291
internal static Guid ExtenderProviderKey { get; } = Guid.NewGuid();
9392

94-
95-
private static readonly object s_internalSyncObject = new object();
9693
/// <summary>
9794
/// Creates a new ReflectTypeDescriptionProvider. The type is the
9895
/// type we will obtain type information for.
@@ -243,7 +240,7 @@ internal static void AddEditorTable(Type editorBaseType, Hashtable table)
243240

244241
Debug.Assert(table != null, "COMPAT: Editor table should not be null"); // don't throw; RTM didn't so we can't do it either.
245242

246-
lock (s_internalSyncObject)
243+
lock (TypeDescriptor.s_commonSyncObject)
247244
{
248245
Hashtable editorTables = EditorTables;
249246
if (!editorTables.ContainsKey(editorBaseType))
@@ -298,7 +295,6 @@ internal static void AddEditorTable(Type editorBaseType, Hashtable table)
298295
return obj ?? Activator.CreateInstance(objectType, args);
299296
}
300297

301-
302298
/// <summary>
303299
/// Helper method to create editors and type converters. This checks to see if the
304300
/// type implements a Type constructor, and if it does it invokes that ctor.
@@ -429,7 +425,7 @@ internal TypeConverter GetConverter([DynamicallyAccessedMembers(DynamicallyAcces
429425
//
430426
if (table == null)
431427
{
432-
lock (s_internalSyncObject)
428+
lock (TypeDescriptor.s_commonSyncObject)
433429
{
434430
table = editorTables[editorBaseType];
435431
if (table == null)
@@ -837,22 +833,11 @@ internal Type[] GetPopulatedTypes(Module module)
837833
{
838834
List<Type> typeList = new List<Type>();
839835

840-
lock (s_internalSyncObject)
836+
foreach (KeyValuePair<Type, ReflectedTypeData> kvp in _typeData)
841837
{
842-
Hashtable? typeData = _typeData;
843-
if (typeData != null)
838+
if (kvp.Key.Module == module && kvp.Value!.IsPopulated)
844839
{
845-
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
846-
IDictionaryEnumerator e = typeData.GetEnumerator();
847-
while (e.MoveNext())
848-
{
849-
DictionaryEntry de = e.Entry;
850-
Type type = (Type)de.Key;
851-
if (type.Module == module && ((ReflectedTypeData)de.Value!).IsPopulated)
852-
{
853-
typeList.Add(type);
854-
}
855-
}
840+
typeList.Add(kvp.Key);
856841
}
857842
}
858843

@@ -897,28 +882,23 @@ public override Type GetReflectionType(
897882
/// </summary>
898883
private ReflectedTypeData? GetTypeData([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool createIfNeeded)
899884
{
900-
ReflectedTypeData? td = null;
901-
902-
if (_typeData != null)
885+
if (_typeData.TryGetValue(type, out ReflectedTypeData? td))
903886
{
904-
td = (ReflectedTypeData?)_typeData[type];
905-
if (td != null)
906-
{
907-
return td;
908-
}
887+
Debug.Assert(td != null);
888+
return td;
909889
}
910890

911-
lock (s_internalSyncObject)
891+
lock (TypeDescriptor.s_commonSyncObject)
912892
{
913-
if (_typeData != null)
893+
if (_typeData.TryGetValue(type, out td))
914894
{
915-
td = (ReflectedTypeData?)_typeData[type];
895+
Debug.Assert(td != null);
896+
return td;
916897
}
917898

918-
if (td == null && createIfNeeded)
899+
if (createIfNeeded)
919900
{
920901
td = new ReflectedTypeData(type);
921-
_typeData ??= new Hashtable();
922902
_typeData[type] = td;
923903
}
924904
}
@@ -1006,7 +986,7 @@ internal static Attribute[] ReflectGetAttributes(Type type)
1006986
return attrs;
1007987
}
1008988

1009-
lock (s_internalSyncObject)
989+
lock (TypeDescriptor.s_commonSyncObject)
1010990
{
1011991
attrs = (Attribute[]?)attributeCache[type];
1012992
if (attrs == null)
@@ -1034,7 +1014,7 @@ internal static Attribute[] ReflectGetAttributes(MemberInfo member)
10341014
return attrs;
10351015
}
10361016

1037-
lock (s_internalSyncObject)
1017+
lock (TypeDescriptor.s_commonSyncObject)
10381018
{
10391019
attrs = (Attribute[]?)attributeCache[member];
10401020
if (attrs == null)
@@ -1063,7 +1043,7 @@ private static EventDescriptor[] ReflectGetEvents(
10631043
return events;
10641044
}
10651045

1066-
lock (s_internalSyncObject)
1046+
lock (TypeDescriptor.s_commonSyncObject)
10671047
{
10681048
events = (EventDescriptor[]?)eventCache[type];
10691049
if (events == null)
@@ -1160,7 +1140,7 @@ private static PropertyDescriptor[] ReflectGetExtendedProperties(IExtenderProvid
11601140
ReflectPropertyDescriptor[]? extendedProperties = (ReflectPropertyDescriptor[]?)extendedPropertyCache[providerType];
11611141
if (extendedProperties == null)
11621142
{
1163-
lock (s_internalSyncObject)
1143+
lock (TypeDescriptor.s_commonSyncObject)
11641144
{
11651145
extendedProperties = (ReflectPropertyDescriptor[]?)extendedPropertyCache[providerType];
11661146

@@ -1240,7 +1220,7 @@ private static PropertyDescriptor[] ReflectGetProperties(
12401220
return properties;
12411221
}
12421222

1243-
lock (s_internalSyncObject)
1223+
lock (TypeDescriptor.s_commonSyncObject)
12441224
{
12451225
properties = (PropertyDescriptor[]?)propertyCache[type];
12461226

0 commit comments

Comments
 (0)