Closed
Description
Description
If multiple threads call TypeDescriptor.GetProperties(object instance) at the same time, they can get different results if the instance has a TypeDescriptorProvider attribute. I believe this is because of missing locks around reading the WeakHashTables.
Reproduction Steps
using System.ComponentModel;
using System.Threading;
namespace TypeDescriptionProvider_GetProperties_repro
{
public class SomeTypeProvider : TypeDescriptionProvider
{
public static ThreadLocal<bool> Constructed = new ThreadLocal<bool>();
public static ThreadLocal<bool> GetPropertiesCalled = new ThreadLocal<bool>();
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))]
public sealed class SomeType
{
public int SomeProperty { get; set; }
}
public static class Program
{
public static void ConcurrentTest(SomeType instance)
{
var properties = TypeDescriptor.GetProperties(instance);
Console.WriteLine($"Constructed = {SomeTypeProvider.Constructed.Value} GetPropertiesCalled={SomeTypeProvider.GetPropertiesCalled.Value} properties.Count = {properties.Count} (should be 0)");
}
public static void Main()
{
const int threadCount = 10;
// Uncomment and it will get the correct properties for all threads
// TypeDescriptor.GetProperties(new SomeType());
using (var finished = new CountdownEvent(threadCount))
{
SomeType[] instances = new SomeType[threadCount];
for (var i = 0; i < threadCount; i++)
{
instances[i] = new SomeType();
}
for (var i = 0; i < threadCount; i++)
{
int _i = i;
new Thread(() =>
{
ConcurrentTest(instances[_i]);
finished.Signal();
}).Start();
}
finished.Wait();
}
}
}
}
### Expected behavior
Every thread should call the provider and get an empty list of properties.
### Actual behavior
Some threads will not call the provider and get the default properties based on reflection
### Regression?
Not that I know.
### Known Workarounds
_No response_
### Configuration
_No response_
### Other information
_No response_