Skip to content

Commit c241cc9

Browse files
authored
Avoid rare deadlocks when using TypeDescriptor (#103835)
1 parent 9317db0 commit c241cc9

File tree

2 files changed

+28
-25
lines changed

2 files changed

+28
-25
lines changed

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

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ private static Type[] InitializeSkipInterfaceAttributeList()
8282

8383
internal static Guid ExtenderProviderKey { get; } = Guid.NewGuid();
8484

85-
private static readonly object s_internalSyncObject = new object();
8685
/// <summary>
8786
/// Creates a new ReflectTypeDescriptionProvider. The type is the
8887
/// type we will obtain type information for.
@@ -224,7 +223,7 @@ internal static void AddEditorTable(Type editorBaseType, Hashtable table)
224223

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

227-
lock (s_internalSyncObject)
226+
lock (TypeDescriptor.s_commonSyncObject)
228227
{
229228
Hashtable editorTables = EditorTables;
230229
if (!editorTables.ContainsKey(editorBaseType))
@@ -436,7 +435,7 @@ internal TypeConverter GetConverterFromRegisteredType(Type type, object? instanc
436435
//
437436
if (table == null)
438437
{
439-
lock (s_internalSyncObject)
438+
lock (TypeDescriptor.s_commonSyncObject)
440439
{
441440
table = editorTables[editorBaseType];
442441
if (table == null)
@@ -863,7 +862,7 @@ internal Type[] GetPopulatedTypes(Module module)
863862
{
864863
List<Type> typeList = new List<Type>();
865864

866-
lock (s_internalSyncObject)
865+
lock (TypeDescriptor.s_commonSyncObject)
867866
{
868867
Dictionary<Type, ReflectedTypeData>? typeData = _typeData;
869868
if (typeData != null)
@@ -936,7 +935,7 @@ public override Type GetReflectionType(
936935
return td;
937936
}
938937

939-
lock (s_internalSyncObject)
938+
lock (TypeDescriptor.s_commonSyncObject)
940939
{
941940
if (_typeData != null && _typeData.TryGetValue(type, out td))
942941
{
@@ -999,7 +998,7 @@ private ReflectedTypeData GetTypeDataFromRegisteredType(Type type)
999998
return;
1000999
}
10011000

1002-
lock (s_internalSyncObject)
1001+
lock (TypeDescriptor.s_commonSyncObject)
10031002
{
10041003
if (_typeData != null && _typeData.ContainsKey(componentType))
10051004
{
@@ -1024,7 +1023,7 @@ private ReflectedTypeData GetOrRegisterType(Type type)
10241023
return td;
10251024
}
10261025

1027-
lock (s_internalSyncObject)
1026+
lock (TypeDescriptor.s_commonSyncObject)
10281027
{
10291028
if (_typeData != null && _typeData.TryGetValue(type, out td))
10301029
{
@@ -1122,7 +1121,7 @@ internal static Attribute[] ReflectGetAttributes(Type type)
11221121
return attrs;
11231122
}
11241123

1125-
lock (s_internalSyncObject)
1124+
lock (TypeDescriptor.s_commonSyncObject)
11261125
{
11271126
attrs = (Attribute[]?)attributeCache[type];
11281127
if (attrs == null)
@@ -1150,7 +1149,7 @@ internal static Attribute[] ReflectGetAttributes(MemberInfo member)
11501149
return attrs;
11511150
}
11521151

1153-
lock (s_internalSyncObject)
1152+
lock (TypeDescriptor.s_commonSyncObject)
11541153
{
11551154
attrs = (Attribute[]?)attributeCache[member];
11561155
if (attrs == null)
@@ -1178,7 +1177,7 @@ private static EventDescriptor[] ReflectGetEvents(Type type)
11781177
return events;
11791178
}
11801179

1181-
lock (s_internalSyncObject)
1180+
lock (TypeDescriptor.s_commonSyncObject)
11821181
{
11831182
events = (EventDescriptor[]?)eventCache[type];
11841183
if (events == null)
@@ -1275,7 +1274,7 @@ private static PropertyDescriptor[] ReflectGetExtendedProperties(IExtenderProvid
12751274
ReflectPropertyDescriptor[]? extendedProperties = (ReflectPropertyDescriptor[]?)extendedPropertyCache[providerType];
12761275
if (extendedProperties == null)
12771276
{
1278-
lock (s_internalSyncObject)
1277+
lock (TypeDescriptor.s_commonSyncObject)
12791278
{
12801279
extendedProperties = (ReflectPropertyDescriptor[]?)extendedPropertyCache[providerType];
12811280

@@ -1363,7 +1362,7 @@ private static PropertyDescriptor[] ReflectGetPropertiesImpl(Type type)
13631362
return properties;
13641363
}
13651364

1366-
lock (s_internalSyncObject)
1365+
lock (TypeDescriptor.s_commonSyncObject)
13671366
{
13681367
properties = (PropertyDescriptor[]?)propertyCache[type];
13691368

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

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,13 @@ public sealed class TypeDescriptor
4848
// class load anyway.
4949
private static readonly WeakHashtable s_providerTable = new WeakHashtable();
5050

51+
// This lock object protects access to several thread-unsafe areas below, and is a single lock object to prevent deadlocks.
52+
// - During s_providerTypeTable access.
53+
// - To act as a mutex for CheckDefaultProvider() when it needs to create the default provider, which may re-enter the above case.
54+
// - For cache access in the ReflectTypeDescriptionProvider class which may re-enter the above case.
55+
// - For logic added by consumers, such as custom provider, constructor and property logic, which may re-enter the above cases in unexpected ways.
56+
internal static readonly object s_commonSyncObject = new object();
57+
5158
// A direct mapping from type to provider.
5259
private static readonly Dictionary<Type, TypeDescriptionNode> s_providerTypeTable = new Dictionary<Type, TypeDescriptionNode>();
5360

@@ -240,7 +247,7 @@ public static void AddProvider(TypeDescriptionProvider provider, Type type)
240247
ArgumentNullException.ThrowIfNull(provider);
241248
ArgumentNullException.ThrowIfNull(type);
242249

243-
lock (s_providerTable)
250+
lock (s_commonSyncObject)
244251
{
245252
// Get the root node, hook it up, and stuff it back into
246253
// the provider cache.
@@ -270,7 +277,7 @@ public static void AddProvider(TypeDescriptionProvider provider, object instance
270277

271278
// Get the root node, hook it up, and stuff it back into
272279
// the provider cache.
273-
lock (s_providerTable)
280+
lock (s_commonSyncObject)
274281
{
275282
refreshNeeded = s_providerTable.ContainsKey(instance);
276283
TypeDescriptionNode node = NodeFor(instance, true);
@@ -331,18 +338,15 @@ private static void CheckDefaultProvider(Type type)
331338
return;
332339
}
333340

334-
// Lock on s_providerTable even though s_providerTable is not modified here.
335-
// Using a single lock prevents deadlocks since other methods that call into or are called
336-
// by this method also lock on s_providerTable and the ordering of the locks may be different.
337-
lock (s_providerTable)
341+
lock (s_commonSyncObject)
338342
{
339343
AddDefaultProvider(type);
340344
}
341345
}
342346

343347
/// <summary>
344348
/// Add the default provider, if it exists.
345-
/// For threading, this is always called under a 'lock (s_providerTable)'.
349+
/// For threading, this is always called under a 'lock (s_commonSyncObject)'.
346350
/// </summary>
347351
private static void AddDefaultProvider(Type type)
348352
{
@@ -1666,7 +1670,7 @@ private static TypeDescriptionNode NodeFor(Type type, bool createDelegator)
16661670

16671671
if (searchType == typeof(object) || baseType == null)
16681672
{
1669-
lock (s_providerTable)
1673+
lock (s_commonSyncObject)
16701674
{
16711675
node = (TypeDescriptionNode?)s_providerTable[searchType];
16721676

@@ -1682,7 +1686,7 @@ private static TypeDescriptionNode NodeFor(Type type, bool createDelegator)
16821686
else if (createDelegator)
16831687
{
16841688
node = new TypeDescriptionNode(new DelegatingTypeDescriptionProvider(baseType));
1685-
lock (s_providerTable)
1689+
lock (s_commonSyncObject)
16861690
{
16871691
s_providerTypeTable.TryAdd(searchType, node);
16881692
}
@@ -1793,7 +1797,7 @@ private static TypeDescriptionNode NodeFor(object instance, bool createDelegator
17931797
/// </summary>
17941798
private static void NodeRemove(object key, TypeDescriptionProvider provider)
17951799
{
1796-
lock (s_providerTable)
1800+
lock (s_commonSyncObject)
17971801
{
17981802
TypeDescriptionNode? head = (TypeDescriptionNode?)s_providerTable[key];
17991803
TypeDescriptionNode? target = head;
@@ -2314,7 +2318,7 @@ private static void Refresh(object component, bool refreshReflectionProvider)
23142318
{
23152319
Type type = component.GetType();
23162320

2317-
lock (s_providerTable)
2321+
lock (s_commonSyncObject)
23182322
{
23192323
// ReflectTypeDescritionProvider is only bound to object, but we
23202324
// need go to through the entire table to try to find custom
@@ -2398,7 +2402,7 @@ public static void Refresh(Type type)
23982402

23992403
bool found = false;
24002404

2401-
lock (s_providerTable)
2405+
lock (s_commonSyncObject)
24022406
{
24032407
// ReflectTypeDescritionProvider is only bound to object, but we
24042408
// need go to through the entire table to try to find custom
@@ -2463,7 +2467,7 @@ public static void Refresh(Module module)
24632467
// each of these levels.
24642468
Hashtable? refreshedTypes = null;
24652469

2466-
lock (s_providerTable)
2470+
lock (s_commonSyncObject)
24672471
{
24682472
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
24692473
IDictionaryEnumerator e = s_providerTable.GetEnumerator();

0 commit comments

Comments
 (0)