@@ -48,6 +48,13 @@ public sealed class TypeDescriptor
48
48
// class load anyway.
49
49
private static readonly WeakHashtable s_providerTable = new WeakHashtable ( ) ;
50
50
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
+
51
58
// A direct mapping from type to provider.
52
59
private static readonly Dictionary < Type , TypeDescriptionNode > s_providerTypeTable = new Dictionary < Type , TypeDescriptionNode > ( ) ;
53
60
@@ -240,7 +247,7 @@ public static void AddProvider(TypeDescriptionProvider provider, Type type)
240
247
ArgumentNullException . ThrowIfNull ( provider ) ;
241
248
ArgumentNullException . ThrowIfNull ( type ) ;
242
249
243
- lock ( s_providerTable )
250
+ lock ( s_commonSyncObject )
244
251
{
245
252
// Get the root node, hook it up, and stuff it back into
246
253
// the provider cache.
@@ -270,7 +277,7 @@ public static void AddProvider(TypeDescriptionProvider provider, object instance
270
277
271
278
// Get the root node, hook it up, and stuff it back into
272
279
// the provider cache.
273
- lock ( s_providerTable )
280
+ lock ( s_commonSyncObject )
274
281
{
275
282
refreshNeeded = s_providerTable . ContainsKey ( instance ) ;
276
283
TypeDescriptionNode node = NodeFor ( instance , true ) ;
@@ -331,18 +338,15 @@ private static void CheckDefaultProvider(Type type)
331
338
return ;
332
339
}
333
340
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 )
338
342
{
339
343
AddDefaultProvider ( type ) ;
340
344
}
341
345
}
342
346
343
347
/// <summary>
344
348
/// 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 )'.
346
350
/// </summary>
347
351
private static void AddDefaultProvider ( Type type )
348
352
{
@@ -1666,7 +1670,7 @@ private static TypeDescriptionNode NodeFor(Type type, bool createDelegator)
1666
1670
1667
1671
if ( searchType == typeof ( object ) || baseType == null )
1668
1672
{
1669
- lock ( s_providerTable )
1673
+ lock ( s_commonSyncObject )
1670
1674
{
1671
1675
node = ( TypeDescriptionNode ? ) s_providerTable [ searchType ] ;
1672
1676
@@ -1682,7 +1686,7 @@ private static TypeDescriptionNode NodeFor(Type type, bool createDelegator)
1682
1686
else if ( createDelegator )
1683
1687
{
1684
1688
node = new TypeDescriptionNode ( new DelegatingTypeDescriptionProvider ( baseType ) ) ;
1685
- lock ( s_providerTable )
1689
+ lock ( s_commonSyncObject )
1686
1690
{
1687
1691
s_providerTypeTable . TryAdd ( searchType , node ) ;
1688
1692
}
@@ -1793,7 +1797,7 @@ private static TypeDescriptionNode NodeFor(object instance, bool createDelegator
1793
1797
/// </summary>
1794
1798
private static void NodeRemove ( object key , TypeDescriptionProvider provider )
1795
1799
{
1796
- lock ( s_providerTable )
1800
+ lock ( s_commonSyncObject )
1797
1801
{
1798
1802
TypeDescriptionNode ? head = ( TypeDescriptionNode ? ) s_providerTable [ key ] ;
1799
1803
TypeDescriptionNode ? target = head ;
@@ -2314,7 +2318,7 @@ private static void Refresh(object component, bool refreshReflectionProvider)
2314
2318
{
2315
2319
Type type = component . GetType ( ) ;
2316
2320
2317
- lock ( s_providerTable )
2321
+ lock ( s_commonSyncObject )
2318
2322
{
2319
2323
// ReflectTypeDescritionProvider is only bound to object, but we
2320
2324
// need go to through the entire table to try to find custom
@@ -2398,7 +2402,7 @@ public static void Refresh(Type type)
2398
2402
2399
2403
bool found = false ;
2400
2404
2401
- lock ( s_providerTable )
2405
+ lock ( s_commonSyncObject )
2402
2406
{
2403
2407
// ReflectTypeDescritionProvider is only bound to object, but we
2404
2408
// need go to through the entire table to try to find custom
@@ -2463,7 +2467,7 @@ public static void Refresh(Module module)
2463
2467
// each of these levels.
2464
2468
Hashtable ? refreshedTypes = null ;
2465
2469
2466
- lock ( s_providerTable )
2470
+ lock ( s_commonSyncObject )
2467
2471
{
2468
2472
// Manual use of IDictionaryEnumerator instead of foreach to avoid DictionaryEntry box allocations.
2469
2473
IDictionaryEnumerator e = s_providerTable . GetEnumerator ( ) ;
0 commit comments