From 56fd769602697fc4f6eba1c730420e3e7277345b Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 15:57:37 +0800 Subject: [PATCH 01/13] minimal entirety lock to fix bug --- .../System/ComponentModel/TypeDescriptor.cs | 79 ++++++++++--------- 1 file changed, 42 insertions(+), 37 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index 2efd53e6223ae7..ba0b88f1d3a9e6 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -1446,58 +1446,62 @@ public static Type GetReflectionType(object instance) private static TypeDescriptionNode NodeFor(Type type, bool createDelegator) { Debug.Assert(type != null, "Caller should validate"); - CheckDefaultProvider(type); - // First, check our provider type table to see if we have a matching - // provider for this type. The provider type table is a cache that - // matches types to providers. When a new provider is added or - // an existing one removed, the provider type table is torn - // down and automatically rebuilt on demand. - // - TypeDescriptionNode? node = null; - Type searchType = type; - - while (node == null) + lock (s_internalSyncObject) { - node = (TypeDescriptionNode?)s_providerTypeTable[searchType] ?? - (TypeDescriptionNode?)s_providerTable[searchType]; + CheckDefaultProvider(type); - if (node == null) + // First, check our provider type table to see if we have a matching + // provider for this type. The provider type table is a cache that + // matches types to providers. When a new provider is added or + // an existing one removed, the provider type table is torn + // down and automatically rebuilt on demand. + // + TypeDescriptionNode? node = null; + Type searchType = type; + + while (node == null) { - Type? baseType = GetNodeForBaseType(searchType); + node = (TypeDescriptionNode?)s_providerTypeTable[searchType] ?? + (TypeDescriptionNode?)s_providerTable[searchType]; - if (searchType == typeof(object) || baseType == null) + if (node == null) { - lock (s_providerTable) + Type? baseType = GetNodeForBaseType(searchType); + + if (searchType == typeof(object) || baseType == null) { - node = (TypeDescriptionNode?)s_providerTable[searchType]; + lock (s_providerTable) + { + node = (TypeDescriptionNode?)s_providerTable[searchType]; - if (node == null) + if (node == null) + { + // The reflect type description provider is a default provider that + // can provide type information for all objects. + node = new TypeDescriptionNode(new ReflectTypeDescriptionProvider()); + s_providerTable[searchType] = node; + } + } + } + else if (createDelegator) + { + node = new TypeDescriptionNode(new DelegatingTypeDescriptionProvider(baseType)); + lock (s_providerTable) { - // The reflect type description provider is a default provider that - // can provide type information for all objects. - node = new TypeDescriptionNode(new ReflectTypeDescriptionProvider()); - s_providerTable[searchType] = node; + s_providerTypeTable[searchType] = node; } } - } - else if (createDelegator) - { - node = new TypeDescriptionNode(new DelegatingTypeDescriptionProvider(baseType)); - lock (s_providerTable) + else { - s_providerTypeTable[searchType] = node; + // Continue our search + searchType = baseType; } } - else - { - // Continue our search - searchType = baseType; - } } - } - return node; + return node; + } } /// @@ -1560,7 +1564,8 @@ private static TypeDescriptionNode NodeFor(object instance, bool createDelegator } else { - node = NodeFor(type); + //lock (s_internalSyncObject) + node = NodeFor(type); } } From 47d9fe658c624599e159ad9a1ba0e27d919dd7c1 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 16:00:42 +0800 Subject: [PATCH 02/13] remove unused code --- .../src/System/ComponentModel/TypeDescriptor.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index ba0b88f1d3a9e6..1273dec3d1ab87 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -1447,7 +1447,7 @@ private static TypeDescriptionNode NodeFor(Type type, bool createDelegator) { Debug.Assert(type != null, "Caller should validate"); - lock (s_internalSyncObject) + lock (s_providerTable) { CheckDefaultProvider(type); @@ -1564,8 +1564,7 @@ private static TypeDescriptionNode NodeFor(object instance, bool createDelegator } else { - //lock (s_internalSyncObject) - node = NodeFor(type); + node = NodeFor(type); } } From 7b743c3799679b0e2d68a4593a62f16e0360ca1f Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 16:18:05 +0800 Subject: [PATCH 03/13] minimize lock range --- .../System/ComponentModel/TypeDescriptor.cs | 116 ++++++++---------- 1 file changed, 54 insertions(+), 62 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index 1273dec3d1ab87..96d26259c8151e 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -262,10 +262,7 @@ public static void AddProviderTransparent(TypeDescriptionProvider provider, obje /// private static void CheckDefaultProvider(Type type) { - if (s_defaultProviders.ContainsKey(type)) - { - return; - } + bool providerAdded = false; lock (s_internalSyncObject) { @@ -278,25 +275,24 @@ private static void CheckDefaultProvider(Type type) // and it starts messing around with type information, // this could infinitely recurse. s_defaultProviders[type] = null; - } - // Always use core reflection when checking for - // the default provider attribute. If there is a - // provider, we probably don't want to build up our - // own cache state against the type. There shouldn't be - // more than one of these, but walk anyway. Walk in - // reverse order so that the most derived takes precidence. - object[] attrs = type.GetCustomAttributes(typeof(TypeDescriptionProviderAttribute), false); - bool providerAdded = false; - for (int idx = attrs.Length - 1; idx >= 0; idx--) - { - TypeDescriptionProviderAttribute pa = (TypeDescriptionProviderAttribute)attrs[idx]; - Type? providerType = Type.GetType(pa.TypeName); - if (providerType != null && typeof(TypeDescriptionProvider).IsAssignableFrom(providerType)) - { - TypeDescriptionProvider prov = (TypeDescriptionProvider)Activator.CreateInstance(providerType)!; - AddProvider(prov, type); - providerAdded = true; + // Always use core reflection when checking for + // the default provider attribute. If there is a + // provider, we probably don't want to build up our + // own cache state against the type. There shouldn't be + // more than one of these, but walk anyway. Walk in + // reverse order so that the most derived takes precedence. + object[] attrs = type.GetCustomAttributes(typeof(TypeDescriptionProviderAttribute), false); + for (int idx = attrs.Length - 1; idx >= 0; idx--) + { + TypeDescriptionProviderAttribute pa = (TypeDescriptionProviderAttribute)attrs[idx]; + Type? providerType = Type.GetType(pa.TypeName); + if (providerType != null && typeof(TypeDescriptionProvider).IsAssignableFrom(providerType)) + { + TypeDescriptionProvider prov = (TypeDescriptionProvider)Activator.CreateInstance(providerType)!; + AddProvider(prov, type); + providerAdded = true; + } } } @@ -1446,62 +1442,58 @@ public static Type GetReflectionType(object instance) private static TypeDescriptionNode NodeFor(Type type, bool createDelegator) { Debug.Assert(type != null, "Caller should validate"); + CheckDefaultProvider(type); - lock (s_providerTable) - { - CheckDefaultProvider(type); + // First, check our provider type table to see if we have a matching + // provider for this type. The provider type table is a cache that + // matches types to providers. When a new provider is added or + // an existing one removed, the provider type table is torn + // down and automatically rebuilt on demand. + // + TypeDescriptionNode? node = null; + Type searchType = type; - // First, check our provider type table to see if we have a matching - // provider for this type. The provider type table is a cache that - // matches types to providers. When a new provider is added or - // an existing one removed, the provider type table is torn - // down and automatically rebuilt on demand. - // - TypeDescriptionNode? node = null; - Type searchType = type; + while (node == null) + { + node = (TypeDescriptionNode?)s_providerTypeTable[searchType] ?? + (TypeDescriptionNode?)s_providerTable[searchType]; - while (node == null) + if (node == null) { - node = (TypeDescriptionNode?)s_providerTypeTable[searchType] ?? - (TypeDescriptionNode?)s_providerTable[searchType]; + Type? baseType = GetNodeForBaseType(searchType); - if (node == null) + if (searchType == typeof(object) || baseType == null) { - Type? baseType = GetNodeForBaseType(searchType); - - if (searchType == typeof(object) || baseType == null) + lock (s_providerTable) { - lock (s_providerTable) - { - node = (TypeDescriptionNode?)s_providerTable[searchType]; + node = (TypeDescriptionNode?)s_providerTable[searchType]; - if (node == null) - { - // The reflect type description provider is a default provider that - // can provide type information for all objects. - node = new TypeDescriptionNode(new ReflectTypeDescriptionProvider()); - s_providerTable[searchType] = node; - } - } - } - else if (createDelegator) - { - node = new TypeDescriptionNode(new DelegatingTypeDescriptionProvider(baseType)); - lock (s_providerTable) + if (node == null) { - s_providerTypeTable[searchType] = node; + // The reflect type description provider is a default provider that + // can provide type information for all objects. + node = new TypeDescriptionNode(new ReflectTypeDescriptionProvider()); + s_providerTable[searchType] = node; } } - else + } + else if (createDelegator) + { + node = new TypeDescriptionNode(new DelegatingTypeDescriptionProvider(baseType)); + lock (s_providerTable) { - // Continue our search - searchType = baseType; + s_providerTypeTable[searchType] = node; } } + else + { + // Continue our search + searchType = baseType; + } } - - return node; } + + return node; } /// From ba1e17688e83e4f8d76364cf307985b2ee180b79 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 16:57:41 +0800 Subject: [PATCH 04/13] add test --- .../tests/ConcurrentTypeDescriptorTests.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs new file mode 100644 index 00000000000000..2d9dec0eb85675 --- /dev/null +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs @@ -0,0 +1,110 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Xunit; + +namespace System.ComponentModel.Tests +{ + [Collection(nameof(DisableParallelization))] // manipulates cache + public class ConcurrentTypeDescriptorTests + { + private long _error = 0; + private bool Error + { + get => Interlocked.Read(ref _error) == 1; + set => Interlocked.Exchange(ref _error, value ? 1 : 0); + } + private void ConcurrentTest(SomeType instance) + { + var properties = TypeDescriptor.GetProperties(instance); + Thread.Sleep(10); + if (properties.Count > 0) + { + Error = true; + } + } + + [Fact] + public void GetProperties_ReturnsExpected() + { + const int Timeout = 60000; + int concurrentCount = Environment.ProcessorCount * 2; + + using var finished = new CountdownEvent(concurrentCount); + + var instances = new SomeType[concurrentCount]; + for (int i = 0; i < concurrentCount; i++) + { + instances[i] = new SomeType(); + } + + for (int i = 0; i < concurrentCount; i++) + { + int i2 = i; + new Thread(() => + { + ConcurrentTest(instances[i2]); + finished.Signal(); + }).Start(); + } + + finished.Wait(Timeout); + + if (finished.CurrentCount != 0) + { + Assert.Fail("Timeout. Possible deadlock."); + } + else + { + Assert.False(Error, "Fallback type descriptor is used."); + } + } + internal class SomeTypeProvider : TypeDescriptionProvider + { + public static ThreadLocal Constructed = new ThreadLocal(); + public static ThreadLocal GetPropertiesCalled = new ThreadLocal(); + private class CTD : ICustomTypeDescriptor + { + public AttributeCollection GetAttributes() => AttributeCollection.Empty; + public string? GetClassName() => null; + public string? GetComponentName() => null; + public TypeConverter GetConverter() => new TypeConverter(); + public EventDescriptor? GetDefaultEvent() => null; + public PropertyDescriptor? GetDefaultProperty() => null; + public object? GetEditor(Type editorBaseType) => null; + public EventDescriptorCollection GetEvents() => EventDescriptorCollection.Empty; + public EventDescriptorCollection GetEvents(Attribute[]? attributes) => EventDescriptorCollection.Empty; + + public PropertyDescriptorCollection GetProperties() + { + GetPropertiesCalled.Value = true; + return PropertyDescriptorCollection.Empty; + } + + public PropertyDescriptorCollection GetProperties(Attribute[]? attributes) + { + throw new NotImplementedException(); + } + + public object? GetPropertyOwner(PropertyDescriptor? pd) => null; + } + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? instance) + { + Constructed.Value = true; + return new CTD(); + } + } + + [TypeDescriptionProvider(typeof(SomeTypeProvider))] + internal sealed class SomeType + { + public int SomeProperty { get; set; } + } + } +} From 49aef1a35a65e470b1c6d8216dd5b7b25c6c7879 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 17:00:08 +0800 Subject: [PATCH 05/13] fix code style --- .../tests/ConcurrentTypeDescriptorTests.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs index 2d9dec0eb85675..e75c0210c6e67b 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs @@ -20,6 +20,7 @@ private bool Error get => Interlocked.Read(ref _error) == 1; set => Interlocked.Exchange(ref _error, value ? 1 : 0); } + private void ConcurrentTest(SomeType instance) { var properties = TypeDescriptor.GetProperties(instance); @@ -65,7 +66,8 @@ public void GetProperties_ReturnsExpected() Assert.False(Error, "Fallback type descriptor is used."); } } - internal class SomeTypeProvider : TypeDescriptionProvider + + private class SomeTypeProvider : TypeDescriptionProvider { public static ThreadLocal Constructed = new ThreadLocal(); public static ThreadLocal GetPropertiesCalled = new ThreadLocal(); @@ -102,7 +104,7 @@ public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? } [TypeDescriptionProvider(typeof(SomeTypeProvider))] - internal sealed class SomeType + private sealed class SomeType { public int SomeProperty { get; set; } } From a8a259c626a401d8df2e681901ddada4f324e7b4 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 18:22:35 +0800 Subject: [PATCH 06/13] Make `TypeDescriptor` thread safe with custom providers Originally a race condition exists in `CheckDefaultProvider` and leads to wrong results when many methods are called simultaneously. The PR fixes that by extending the lock statement. Fix #92934 --- .../src/System/ComponentModel/TypeDescriptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index 96d26259c8151e..b3a6851487d486 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -1456,7 +1456,7 @@ private static TypeDescriptionNode NodeFor(Type type, bool createDelegator) while (node == null) { node = (TypeDescriptionNode?)s_providerTypeTable[searchType] ?? - (TypeDescriptionNode?)s_providerTable[searchType]; + (TypeDescriptionNode?)s_providerTable[searchType]; if (node == null) { From e4c4296078ea36931550b0029c0f70f16be18ef6 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Sat, 23 Sep 2023 18:47:52 +0800 Subject: [PATCH 07/13] Originally a race condition exists in `CheckDefaultProvider` and leads to wrong results when many methods are called simultaneously. The PR fixes that by extending the lock statement. Fix #92394 --- .../tests/ConcurrentTypeDescriptorTests.cs | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs index e75c0210c6e67b..2d125e7d261d1f 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs @@ -21,7 +21,7 @@ private bool Error set => Interlocked.Exchange(ref _error, value ? 1 : 0); } - private void ConcurrentTest(SomeType instance) + private void ConcurrentTest(TypeWithProperty instance) { var properties = TypeDescriptor.GetProperties(instance); Thread.Sleep(10); @@ -39,10 +39,10 @@ public void GetProperties_ReturnsExpected() using var finished = new CountdownEvent(concurrentCount); - var instances = new SomeType[concurrentCount]; + var instances = new TypeWithProperty[concurrentCount]; for (int i = 0; i < concurrentCount; i++) { - instances[i] = new SomeType(); + instances[i] = new TypeWithProperty(); } for (int i = 0; i < concurrentCount; i++) @@ -63,50 +63,48 @@ public void GetProperties_ReturnsExpected() } else { - Assert.False(Error, "Fallback type descriptor is used."); + Assert.False(Error, "Fallback type descriptor is used. Possible race condition."); } } - private class SomeTypeProvider : TypeDescriptionProvider + public sealed class EmptyPropertiesTypeProvider : TypeDescriptionProvider { - public static ThreadLocal Constructed = new ThreadLocal(); - public static ThreadLocal GetPropertiesCalled = new ThreadLocal(); - private class CTD : ICustomTypeDescriptor + private sealed class EmptyPropertyListDescriptor : ICustomTypeDescriptor { public AttributeCollection GetAttributes() => AttributeCollection.Empty; + public string? GetClassName() => null; + public string? GetComponentName() => null; - public TypeConverter GetConverter() => new TypeConverter(); + + public TypeConverter? GetConverter() => new TypeConverter(); + public EventDescriptor? GetDefaultEvent() => null; + public PropertyDescriptor? GetDefaultProperty() => null; + public object? GetEditor(Type editorBaseType) => null; + public EventDescriptorCollection GetEvents() => EventDescriptorCollection.Empty; - public EventDescriptorCollection GetEvents(Attribute[]? attributes) => EventDescriptorCollection.Empty; - public PropertyDescriptorCollection GetProperties() - { - GetPropertiesCalled.Value = true; - return PropertyDescriptorCollection.Empty; - } + public EventDescriptorCollection GetEvents(Attribute[]? attributes) => GetEvents(); - public PropertyDescriptorCollection GetProperties(Attribute[]? attributes) - { - throw new NotImplementedException(); - } + public PropertyDescriptorCollection GetProperties() => PropertyDescriptorCollection.Empty; + + public PropertyDescriptorCollection GetProperties(Attribute[]? attributes) => GetProperties(); public object? GetPropertyOwner(PropertyDescriptor? pd) => null; } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? instance) { - Constructed.Value = true; - return new CTD(); + return new EmptyPropertyListDescriptor(); } } - [TypeDescriptionProvider(typeof(SomeTypeProvider))] - private sealed class SomeType + [TypeDescriptionProvider(typeof(EmptyPropertiesTypeProvider))] + public sealed class TypeWithProperty { - public int SomeProperty { get; set; } + public int OneProperty { get; set; } } } } From 8a7df35fe22f9efe07c03ec9d410407171cf7d95 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Wed, 15 Nov 2023 22:47:14 +0800 Subject: [PATCH 08/13] Added another HashTable to reduce locks. Moved tests to the main testing file Adopted tests from #85156 Co-authored-by: Maximys --- .../System/ComponentModel/TypeDescriptor.cs | 8 + .../tests/ConcurrentTypeDescriptorTests.cs | 110 ---------- .../tests/TypeDescriptorTests.cs | 193 ++++++++++++++++++ 3 files changed, 201 insertions(+), 110 deletions(-) delete mode 100644 src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index b3a6851487d486..f2490dcb60c9b2 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -29,6 +29,7 @@ public sealed class TypeDescriptor private static readonly WeakHashtable s_providerTable = new WeakHashtable(); // mapping of type or object hash to a provider list private static readonly Hashtable s_providerTypeTable = new Hashtable(); // A direct mapping from type to provider. private static readonly Hashtable s_defaultProviders = new Hashtable(); // A table of type -> default provider to track DefaultTypeDescriptionProviderAttributes. + private static readonly Hashtable s_defaultProvidersCreated = new Hashtable(); // A table of type -> default provider to track DefaultTypeDescriptionProviderAttributes. private static WeakHashtable? s_associationTable; private static int s_metadataVersion; // a version stamp for our metadata. Used by property descriptors to know when to rebuild attributes. @@ -264,6 +265,11 @@ private static void CheckDefaultProvider(Type type) { bool providerAdded = false; + if (s_defaultProvidersCreated.ContainsKey(type)) + { + return; + } + lock (s_internalSyncObject) { if (s_defaultProviders.ContainsKey(type)) @@ -294,6 +300,8 @@ private static void CheckDefaultProvider(Type type) providerAdded = true; } } + + s_defaultProvidersCreated[type] = null; } // If we did not add a provider, check the base class. diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs deleted file mode 100644 index 2d125e7d261d1f..00000000000000 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs +++ /dev/null @@ -1,110 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace System.ComponentModel.Tests -{ - [Collection(nameof(DisableParallelization))] // manipulates cache - public class ConcurrentTypeDescriptorTests - { - private long _error = 0; - private bool Error - { - get => Interlocked.Read(ref _error) == 1; - set => Interlocked.Exchange(ref _error, value ? 1 : 0); - } - - private void ConcurrentTest(TypeWithProperty instance) - { - var properties = TypeDescriptor.GetProperties(instance); - Thread.Sleep(10); - if (properties.Count > 0) - { - Error = true; - } - } - - [Fact] - public void GetProperties_ReturnsExpected() - { - const int Timeout = 60000; - int concurrentCount = Environment.ProcessorCount * 2; - - using var finished = new CountdownEvent(concurrentCount); - - var instances = new TypeWithProperty[concurrentCount]; - for (int i = 0; i < concurrentCount; i++) - { - instances[i] = new TypeWithProperty(); - } - - for (int i = 0; i < concurrentCount; i++) - { - int i2 = i; - new Thread(() => - { - ConcurrentTest(instances[i2]); - finished.Signal(); - }).Start(); - } - - finished.Wait(Timeout); - - if (finished.CurrentCount != 0) - { - Assert.Fail("Timeout. Possible deadlock."); - } - else - { - Assert.False(Error, "Fallback type descriptor is used. Possible race condition."); - } - } - - public sealed class EmptyPropertiesTypeProvider : TypeDescriptionProvider - { - private sealed class EmptyPropertyListDescriptor : ICustomTypeDescriptor - { - public AttributeCollection GetAttributes() => AttributeCollection.Empty; - - public string? GetClassName() => null; - - public string? GetComponentName() => null; - - public TypeConverter? GetConverter() => new TypeConverter(); - - public EventDescriptor? GetDefaultEvent() => null; - - public PropertyDescriptor? GetDefaultProperty() => null; - - public object? GetEditor(Type editorBaseType) => null; - - public EventDescriptorCollection GetEvents() => EventDescriptorCollection.Empty; - - public EventDescriptorCollection GetEvents(Attribute[]? attributes) => GetEvents(); - - public PropertyDescriptorCollection GetProperties() => PropertyDescriptorCollection.Empty; - - public PropertyDescriptorCollection GetProperties(Attribute[]? attributes) => GetProperties(); - - public object? GetPropertyOwner(PropertyDescriptor? pd) => null; - } - public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? instance) - { - return new EmptyPropertyListDescriptor(); - } - } - - [TypeDescriptionProvider(typeof(EmptyPropertiesTypeProvider))] - public sealed class TypeWithProperty - { - public int OneProperty { get; set; } - } - } -} diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index 299d73cfe31502..1e7232278a729a 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.ComponentModel.Design; using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; using Moq; using Xunit; @@ -1231,5 +1234,195 @@ protected DerivedCultureInfo() : base("hello") class TwiceDerivedCultureInfo : DerivedCultureInfo { } + + private long _concurrentError = 0; + private bool ConcurrentError + { + get => Interlocked.Read(ref _concurrentError) == 1; + set => Interlocked.Exchange(ref _concurrentError, value ? 1 : 0); + } + + private void ConcurrentTest(TypeWithProperty instance) + { + var properties = TypeDescriptor.GetProperties(instance); + Thread.Sleep(10); + if (properties.Count > 0) + { + ConcurrentError = true; + } + } + + [Fact] + public void GetProperties_ReturnsExpected() + { + const int Timeout = 60000; + int concurrentCount = Environment.ProcessorCount * 2; + + using var finished = new CountdownEvent(concurrentCount); + + var instances = new TypeWithProperty[concurrentCount]; + for (int i = 0; i < concurrentCount; i++) + { + instances[i] = new TypeWithProperty(); + } + + for (int i = 0; i < concurrentCount; i++) + { + int i2 = i; + new Thread(() => + { + ConcurrentTest(instances[i2]); + finished.Signal(); + }).Start(); + } + + finished.Wait(Timeout); + + if (finished.CurrentCount != 0) + { + Assert.Fail("Timeout. Possible deadlock."); + } + else + { + Assert.False(ConcurrentError, "Fallback type descriptor is used. Possible race condition."); + } + } + + public sealed class EmptyPropertiesTypeProvider : TypeDescriptionProvider + { + private sealed class EmptyPropertyListDescriptor : ICustomTypeDescriptor + { + public AttributeCollection GetAttributes() => AttributeCollection.Empty; + + public string? GetClassName() => null; + + public string? GetComponentName() => null; + + public TypeConverter? GetConverter() => new TypeConverter(); + + public EventDescriptor? GetDefaultEvent() => null; + + public PropertyDescriptor? GetDefaultProperty() => null; + + public object? GetEditor(Type editorBaseType) => null; + + public EventDescriptorCollection GetEvents() => EventDescriptorCollection.Empty; + + public EventDescriptorCollection GetEvents(Attribute[]? attributes) => GetEvents(); + + public PropertyDescriptorCollection GetProperties() => PropertyDescriptorCollection.Empty; + + public PropertyDescriptorCollection GetProperties(Attribute[]? attributes) => GetProperties(); + + public object? GetPropertyOwner(PropertyDescriptor? pd) => null; + } + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object? instance) + { + return new EmptyPropertyListDescriptor(); + } + } + + [TypeDescriptionProvider(typeof(EmptyPropertiesTypeProvider))] + public sealed class TypeWithProperty + { + public int OneProperty { get; set; } + } + + public static IEnumerable GetConverter_ByMultithread_ReturnsExpected_TestData() + { + yield return new object[] { typeof(MyClass), typeof(MyTypeConverter) }; + yield return new object[] { typeof(MyInheritedClassWithCustomTypeDescriptionProvider), typeof(MyInheritedClassWithCustomTypeDescriptionProviderConverter) }; + yield return new object[] { typeof(MyInheritedClassWithInheritedTypeDescriptionProvider), typeof(MyTypeConverter) }; + } + + [Theory] + [MemberData(nameof(GetConverter_ByMultithread_ReturnsExpected_TestData))] + public async void GetConverter_ByMultithread_ReturnsExpected(Type typeForGetConverter, Type expectedConverterType) + { + TypeConverter[] actualConverters = await Task.WhenAll( + Enumerable.Range(0, 100).Select(_ => + Task.Run(() => TypeDescriptor.GetConverter(typeForGetConverter)))); + Assert.All(actualConverters, + currentConverter => Assert.IsType(expectedConverterType, currentConverter)); + } + + public static IEnumerable GetConverterWithAddProvider_ByMultithread_Success_TestData() + { + foreach (object[] currentTestCase in GetConverter_ByMultithread_ReturnsExpected_TestData()) + { + yield return currentTestCase; + } + } + + [Theory] + [MemberData(nameof(GetConverterWithAddProvider_ByMultithread_Success_TestData))] + public async void GetConverterWithAddProvider_ByMultithread_Success(Type typeForGetConverter, Type expectedConverterType) + { + TypeConverter[] actualConverters = await Task.WhenAll( + Enumerable.Range(0, 200).Select(_ => + Task.Run(() => + { + var mockProvider = new Mock(MockBehavior.Strict); + var someInstance = new object(); + TypeDescriptor.AddProvider(mockProvider.Object, someInstance); + return TypeDescriptor.GetConverter(typeForGetConverter); + }))); + Assert.All(actualConverters, + currentConverter => Assert.IsType(expectedConverterType, currentConverter)); + } + + [TypeDescriptionProvider(typeof(MyClassTypeDescriptionProvider))] + public class MyClass + { + } + + [TypeDescriptionProvider(typeof(MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptionProvider))] + public class MyInheritedClassWithCustomTypeDescriptionProvider : MyClass + { + } + + public class MyInheritedClassWithInheritedTypeDescriptionProvider : MyClass + { + } + + public class MyClassTypeDescriptionProvider : TypeDescriptionProvider + { + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + return new MyClassTypeDescriptor(); + } + } + + public class MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptionProvider : TypeDescriptionProvider + { + public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) + { + return new MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptor(); + } + } + + public class MyClassTypeDescriptor : CustomTypeDescriptor + { + public override TypeConverter GetConverter() + { + return new MyTypeConverter(); + } + } + + public class MyInheritedClassWithCustomTypeDescriptionProviderTypeDescriptor : CustomTypeDescriptor + { + public override TypeConverter GetConverter() + { + return new MyInheritedClassWithCustomTypeDescriptionProviderConverter(); + } + } + + public class MyTypeConverter : TypeConverter + { + } + + public class MyInheritedClassWithCustomTypeDescriptionProviderConverter : TypeConverter + { + } } } From 0e3fc99d2658da82021adca86df1f9626efa71ac Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Wed, 15 Nov 2023 23:20:10 +0800 Subject: [PATCH 09/13] better test names --- .../tests/TypeDescriptorTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index 1e7232278a729a..78000975fc7a25 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -1253,7 +1253,7 @@ private void ConcurrentTest(TypeWithProperty instance) } [Fact] - public void GetProperties_ReturnsExpected() + public void ConcurrentGetProperties_ReturnsExpected() { const int Timeout = 60000; int concurrentCount = Environment.ProcessorCount * 2; From b067518f127d02022fc56e103c2e67d8228222d0 Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Wed, 15 Nov 2023 23:22:37 +0800 Subject: [PATCH 10/13] improve comments --- .../src/System/ComponentModel/TypeDescriptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index f2490dcb60c9b2..26e95993675d48 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -29,7 +29,7 @@ public sealed class TypeDescriptor private static readonly WeakHashtable s_providerTable = new WeakHashtable(); // mapping of type or object hash to a provider list private static readonly Hashtable s_providerTypeTable = new Hashtable(); // A direct mapping from type to provider. private static readonly Hashtable s_defaultProviders = new Hashtable(); // A table of type -> default provider to track DefaultTypeDescriptionProviderAttributes. - private static readonly Hashtable s_defaultProvidersCreated = new Hashtable(); // A table of type -> default provider to track DefaultTypeDescriptionProviderAttributes. + private static readonly Hashtable s_defaultProvidersCreated = new Hashtable(); // A table similar to s_defaultProviders but only set after providers are added, in order to reduce locks. private static WeakHashtable? s_associationTable; private static int s_metadataVersion; // a version stamp for our metadata. Used by property descriptors to know when to rebuild attributes. From c55d87691d3a9fc667659773012e4da361677c7f Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Wed, 15 Nov 2023 23:23:25 +0800 Subject: [PATCH 11/13] renamed variable --- .../System.ComponentModel.TypeConverter.sln | 26 ++++++++++++------- .../System/ComponentModel/TypeDescriptor.cs | 6 ++--- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln b/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln index e033bb82d481ef..b4e7c36c84ed41 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln +++ b/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln @@ -1,4 +1,8 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.8.34112.27 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{0CF88416-BA94-4C5C-8947-D21F4897C70B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections.NonGeneric", "..\System.Collections.NonGeneric\ref\System.Collections.NonGeneric.csproj", "{07C9AD7A-505A-4B20-81BF-A90ED3D161D3}" @@ -47,11 +51,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CD3CE573-098 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{D06EAE06-636A-4A7A-B22F-5D106F7CCC75}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{1DF76A2A-9700-4D63-8F6F-2DFCA289E70E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{1DF76A2A-9700-4D63-8F6F-2DFCA289E70E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{82259970-A9FA-4264-A47F-7B697B317AE6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{82259970-A9FA-4264-A47F-7B697B317AE6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{0507C945-5BC2-4234-AF07-D766E2CCCDEC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{0507C945-5BC2-4234-AF07-D766E2CCCDEC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{4690D584-4BB0-4C37-9887-6ED204389F4D}" EndProject @@ -147,30 +151,34 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {0CF88416-BA94-4C5C-8947-D21F4897C70B} = {E80037BD-B4E3-43B7-A7C8-F6C3C6DA4EF8} - {6A370CD8-E44B-46C9-B29A-87F847483336} = {E80037BD-B4E3-43B7-A7C8-F6C3C6DA4EF8} {07C9AD7A-505A-4B20-81BF-A90ED3D161D3} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {63D9B86C-EAA1-4FB9-8FA9-DFFCD0C267EE} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {AD743D00-58FF-4C2A-A214-28BA1BEBC4DA} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {0B2F32D4-9E58-4680-AB59-35D86E4162CE} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} + {CE7DA286-D119-4087-A953-D542727AFA02} = {CD3CE573-0987-4590-8D30-EE1F1296D931} + {6A370CD8-E44B-46C9-B29A-87F847483336} = {E80037BD-B4E3-43B7-A7C8-F6C3C6DA4EF8} {70CD261C-9771-49DF-B445-896702524E06} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {FFD73901-CE23-4002-8F31-6210280BE5C6} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {4EFC8E5E-5DAE-4B77-BB92-91DD1C86C597} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} - {AA260434-0F27-4878-8883-1B9690421BD2} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} - {CE7DA286-D119-4087-A953-D542727AFA02} = {CD3CE573-0987-4590-8D30-EE1F1296D931} {CD069E8D-AA7A-4958-B872-932CBA5EABFE} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} {113EA49F-2525-47A0-AD4D-EA510476F50B} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} {970B0849-990C-4368-B317-063BFD82CAD5} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} + {AA260434-0F27-4878-8883-1B9690421BD2} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {B681D74E-6DAD-4C73-9EEF-CB1C17063E32} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} {2B00B94A-91A7-445F-A1B5-E24FA25C10E0} = {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} {FB3C7146-8622-418A-8D8B-AE819738E0B3} = {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} - {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} = {4690D584-4BB0-4C37-9887-6ED204389F4D} {945372C6-9242-441A-BFCD-4404F74FF95C} = {82259970-A9FA-4264-A47F-7B697B317AE6} {EDA39F11-6F85-4E4D-93D3-E178AC13F3FE} = {82259970-A9FA-4264-A47F-7B697B317AE6} - {82259970-A9FA-4264-A47F-7B697B317AE6} = {4690D584-4BB0-4C37-9887-6ED204389F4D} {C4C8D5AF-7402-4C82-BB99-B8EB18A52E8A} = {0507C945-5BC2-4234-AF07-D766E2CCCDEC} + {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} = {4690D584-4BB0-4C37-9887-6ED204389F4D} + {82259970-A9FA-4264-A47F-7B697B317AE6} = {4690D584-4BB0-4C37-9887-6ED204389F4D} {0507C945-5BC2-4234-AF07-D766E2CCCDEC} = {4690D584-4BB0-4C37-9887-6ED204389F4D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1D4639D9-A4BD-410A-9491-E5F8D82DE8BF} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{eda39f11-6f85-4e4d-93d3-e178ac13f3fe}*SharedItemsImports = 5 + ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{fb3c7146-8622-418a-8d8b-ae819738e0b3}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs index 26e95993675d48..69da04776057ed 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs @@ -29,7 +29,7 @@ public sealed class TypeDescriptor private static readonly WeakHashtable s_providerTable = new WeakHashtable(); // mapping of type or object hash to a provider list private static readonly Hashtable s_providerTypeTable = new Hashtable(); // A direct mapping from type to provider. private static readonly Hashtable s_defaultProviders = new Hashtable(); // A table of type -> default provider to track DefaultTypeDescriptionProviderAttributes. - private static readonly Hashtable s_defaultProvidersCreated = new Hashtable(); // A table similar to s_defaultProviders but only set after providers are added, in order to reduce locks. + private static readonly Hashtable s_defaultProvidersChecked = new Hashtable(); // A table similar to s_defaultProviders but only set after providers are checked, in order to reduce locks. private static WeakHashtable? s_associationTable; private static int s_metadataVersion; // a version stamp for our metadata. Used by property descriptors to know when to rebuild attributes. @@ -265,7 +265,7 @@ private static void CheckDefaultProvider(Type type) { bool providerAdded = false; - if (s_defaultProvidersCreated.ContainsKey(type)) + if (s_defaultProvidersChecked.ContainsKey(type)) { return; } @@ -301,7 +301,7 @@ private static void CheckDefaultProvider(Type type) } } - s_defaultProvidersCreated[type] = null; + s_defaultProvidersChecked[type] = null; } // If we did not add a provider, check the base class. From 8707cd9cd4aa216ee01139ba86b2f7e812ae4c7f Mon Sep 17 00:00:00 2001 From: Gan Keyu Date: Wed, 15 Nov 2023 23:25:00 +0800 Subject: [PATCH 12/13] reverted sln file --- .../System.ComponentModel.TypeConverter.sln | 26 +++++++------------ 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln b/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln index b4e7c36c84ed41..e033bb82d481ef 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln +++ b/src/libraries/System.ComponentModel.TypeConverter/System.ComponentModel.TypeConverter.sln @@ -1,8 +1,4 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.8.34112.27 -MinimumVisualStudioVersion = 10.0.40219.1 +Microsoft Visual Studio Solution File, Format Version 12.00 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{0CF88416-BA94-4C5C-8947-D21F4897C70B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Collections.NonGeneric", "..\System.Collections.NonGeneric\ref\System.Collections.NonGeneric.csproj", "{07C9AD7A-505A-4B20-81BF-A90ED3D161D3}" @@ -51,11 +47,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CD3CE573-098 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{D06EAE06-636A-4A7A-B22F-5D106F7CCC75}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{1DF76A2A-9700-4D63-8F6F-2DFCA289E70E}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "tools\gen", "{1DF76A2A-9700-4D63-8F6F-2DFCA289E70E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{82259970-A9FA-4264-A47F-7B697B317AE6}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "tools\src", "{82259970-A9FA-4264-A47F-7B697B317AE6}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "ref", "{0507C945-5BC2-4234-AF07-D766E2CCCDEC}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ref", "tools\ref", "{0507C945-5BC2-4234-AF07-D766E2CCCDEC}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{4690D584-4BB0-4C37-9887-6ED204389F4D}" EndProject @@ -151,34 +147,30 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {0CF88416-BA94-4C5C-8947-D21F4897C70B} = {E80037BD-B4E3-43B7-A7C8-F6C3C6DA4EF8} + {6A370CD8-E44B-46C9-B29A-87F847483336} = {E80037BD-B4E3-43B7-A7C8-F6C3C6DA4EF8} {07C9AD7A-505A-4B20-81BF-A90ED3D161D3} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {63D9B86C-EAA1-4FB9-8FA9-DFFCD0C267EE} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {AD743D00-58FF-4C2A-A214-28BA1BEBC4DA} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {0B2F32D4-9E58-4680-AB59-35D86E4162CE} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} - {CE7DA286-D119-4087-A953-D542727AFA02} = {CD3CE573-0987-4590-8D30-EE1F1296D931} - {6A370CD8-E44B-46C9-B29A-87F847483336} = {E80037BD-B4E3-43B7-A7C8-F6C3C6DA4EF8} {70CD261C-9771-49DF-B445-896702524E06} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {FFD73901-CE23-4002-8F31-6210280BE5C6} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {4EFC8E5E-5DAE-4B77-BB92-91DD1C86C597} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} + {AA260434-0F27-4878-8883-1B9690421BD2} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} + {CE7DA286-D119-4087-A953-D542727AFA02} = {CD3CE573-0987-4590-8D30-EE1F1296D931} {CD069E8D-AA7A-4958-B872-932CBA5EABFE} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} {113EA49F-2525-47A0-AD4D-EA510476F50B} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} {970B0849-990C-4368-B317-063BFD82CAD5} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} - {AA260434-0F27-4878-8883-1B9690421BD2} = {2F8CF65B-8616-42BC-A893-8C7C9FD990C5} {B681D74E-6DAD-4C73-9EEF-CB1C17063E32} = {D06EAE06-636A-4A7A-B22F-5D106F7CCC75} {2B00B94A-91A7-445F-A1B5-E24FA25C10E0} = {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} {FB3C7146-8622-418A-8D8B-AE819738E0B3} = {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} + {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} = {4690D584-4BB0-4C37-9887-6ED204389F4D} {945372C6-9242-441A-BFCD-4404F74FF95C} = {82259970-A9FA-4264-A47F-7B697B317AE6} {EDA39F11-6F85-4E4D-93D3-E178AC13F3FE} = {82259970-A9FA-4264-A47F-7B697B317AE6} - {C4C8D5AF-7402-4C82-BB99-B8EB18A52E8A} = {0507C945-5BC2-4234-AF07-D766E2CCCDEC} - {1DF76A2A-9700-4D63-8F6F-2DFCA289E70E} = {4690D584-4BB0-4C37-9887-6ED204389F4D} {82259970-A9FA-4264-A47F-7B697B317AE6} = {4690D584-4BB0-4C37-9887-6ED204389F4D} + {C4C8D5AF-7402-4C82-BB99-B8EB18A52E8A} = {0507C945-5BC2-4234-AF07-D766E2CCCDEC} {0507C945-5BC2-4234-AF07-D766E2CCCDEC} = {4690D584-4BB0-4C37-9887-6ED204389F4D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {1D4639D9-A4BD-410A-9491-E5F8D82DE8BF} EndGlobalSection - GlobalSection(SharedMSBuildProjectFiles) = preSolution - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{eda39f11-6f85-4e4d-93d3-e178ac13f3fe}*SharedItemsImports = 5 - ..\..\tools\illink\src\ILLink.Shared\ILLink.Shared.projitems*{fb3c7146-8622-418a-8d8b-ae819738e0b3}*SharedItemsImports = 5 - EndGlobalSection EndGlobal From 33d5a3ec9f26b6ee48545dabe70c4325475e2c73 Mon Sep 17 00:00:00 2001 From: karakasa Date: Thu, 16 Nov 2023 10:55:12 +0800 Subject: [PATCH 13/13] Skip one test on browsers ConcurrentGetProperties_ReturnsExpected is skipped on browsers because Thread.Start is unsupported. --- .../tests/TypeDescriptorTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs index 78000975fc7a25..fcde40ee1547ac 100644 --- a/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs +++ b/src/libraries/System.ComponentModel.TypeConverter/tests/TypeDescriptorTests.cs @@ -1252,6 +1252,7 @@ private void ConcurrentTest(TypeWithProperty instance) } } + [SkipOnPlatform(TestPlatforms.Browser, "Thread.Start is not supported on browsers.")] [Fact] public void ConcurrentGetProperties_ReturnsExpected() {