-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make TypeDescriptor
thread safe with custom providers by enlarging lock
region
#92521
Conversation
Tagging subscribers to this area: @dotnet/area-system-componentmodel Issue DetailsFix #92394 and add a test.
|
TypeDescriptor.GetProperties
thread safeTypeDescriptor
thread safe
TypeDescriptor
thread safeTypeDescriptor
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 dotnet#92934
to wrong results when many methods are called simultaneously. The PR fixes that by extending the lock statement. Fix dotnet#92394
TypeDescriptor
thread safe with custom providersTypeDescriptor
thread safe with custom providers by enlaring lock
region
TypeDescriptor
thread safe with custom providers by enlaring lock
regionTypeDescriptor
thread safe with custom providers by enlarging lock
region
This touches the same files as the closed PR https://github.com/dotnet/runtime/pull/85156/files for issue #30024. It was closed since it didn't make sense to force this potentially risky issue late into v8.0. Both issues appear to be the same root cause around using the hashtable in Performance here will suffer quite a bit - because of the lock area expanding as well as the use of |
src/libraries/System.ComponentModel.TypeConverter/src/System/ComponentModel/TypeDescriptor.cs
Show resolved
Hide resolved
src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.ComponentModel.TypeConverter/tests/ConcurrentTypeDescriptorTests.cs
Outdated
Show resolved
Hide resolved
public int OneProperty { get; set; } | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think the tests at https://github.com/dotnet/runtime/pull/85156/files#diff-13f56d055c80679284090c59b45d396ab44378385c1f7ad0b597eec15fa5cf0fR813-R909 add any value here?
FWIW locally I added those tests to this file, and ran them in loop for 200 times are no issues were found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The other tests are not fundamentally different - but I add them if they produce errors on your computer and they don't use Thread
(compared to my tests), which is unsupported on browsers.
Nevertheless, both mine and the other tests failed on my PC. Can the issue be platform-specific?
test log
✘ System.ComponentModel.Tests.TypeDescriptorTests.GetConverterWithAddProvider_ByMultithread_Success(typeForGetConverter: typeof(System.ComponentModel.Tests.TypeDescriptorTests+MyClass), expectedConverterType: typeof(System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter))5ms
Error:
Assert.All() Failure: 1 out of 200 items in the collection did not pass.
[2]: Item: System.ComponentModel.TypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Actual: System.ComponentModel.TypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
Stack trace:
at System.ComponentModel.Tests.TypeDescriptorTests.GetConverterWithAddProvider_ByMultithread_Success(Type typeForGetConverter, Type expectedConverterType) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1370
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state) in D:\repos\karakasa\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs:line 1914
✘ System.ComponentModel.Tests.TypeDescriptorTests.GetConverterWithAddProvider_ByMultithread_Success(typeForGetConverter: typeof(System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProvider), expectedConverterType: typeof(System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter))2ms
Error:
Assert.All() Failure: 11 out of 200 items in the collection did not pass.
[11]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[10]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[9]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[8]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[7]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[6]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[5]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[4]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[3]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[2]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
[0]: Item: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
Xunit.Sdk.IsTypeException: Assert.IsType() Failure
Expected: System.ComponentModel.Tests.TypeDescriptorTests+MyInheritedClassWithCustomTypeDescriptionProviderConverter
Actual: System.ComponentModel.Tests.TypeDescriptorTests+MyTypeConverter
at Xunit.Assert.IsType(Type expectedType, Object object) in /_/src/Microsoft.DotNet.XUnitAssert/src/TypeAsserts.cs:line 140
at System.ComponentModel.Tests.TypeDescriptorTests.<>c__DisplayClass80_0.<GetConverterWithAddProvider_ByMultithread_Success>b__0(TypeConverter currentConverter) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1371
at Xunit.Assert.<>c__DisplayClass11_0`1.<All>b__0(T item, Int32 index) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 40
at Xunit.Assert.All[T](IEnumerable`1 collection, Action`2 action) in /_/src/Microsoft.DotNet.XUnitAssert/src/CollectionAsserts.cs:line 69
Stack trace:
at System.ComponentModel.Tests.TypeDescriptorTests.GetConverterWithAddProvider_ByMultithread_Success(Type typeForGetConverter, Type expectedConverterType) in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1370
at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__128_0(Object state) in D:\repos\karakasa\runtime\src\libraries\System.Private.CoreLib\src\System\Threading\Tasks\Task.cs:line 1914
✘ System.ComponentModel.Tests.TypeDescriptorTests.GetProperties_ReturnsExpected25ms
Error:
Fallback type descriptor is used. Possible race condition.
Expected: False
Actual: True
Stack trace:
at System.ComponentModel.Tests.TypeDescriptorTests.GetProperties_ReturnsExpected() in D:\repos\karakasa\runtime\src\libraries\System.ComponentModel.TypeConverter\tests\TypeDescriptorTests.cs:line 1287
at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args) in D:\repos\karakasa\runtime\src\coreclr\System.Private.CoreLib\src\System\Reflection\MethodBaseInvoker.CoreCLR.cs:line 36
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr) in D:\repos\karakasa\runtime\src\libraries\System.Private.CoreLib\src\System\Reflection\MethodBaseInvoker.cs:line 57
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments in the test file.
Note the PR is still marked as Draft. |
I am testing on adding another I will also check the extra tests, thank you.
|
Moved tests to the main testing file Adopted tests from dotnet#85156 Co-authored-by: Maximys <[email protected]>
latest benchmark with an extra hashtable to reduce locks. The perf is on par with the main repo.
|
ConcurrentGetProperties_ReturnsExpected is skipped on browsers because Thread.Start is unsupported.
Before seeing this PR I happened to be looking at this problem and made a suggestion in #30024. I think that suggestion could be considered here as a slight alternative to what's been implemented so far. |
Summary
Fix #92394 and add a test.
see comment for the reason of race conditions.
The fix is conservative as it just extends the range of
lock
statement. If the performance decrease in this PR is acceptable, I would recommend enlarginglock
region because it is unlikely to result in bugs.I did code another aggressive approach (#92548) that minimizes lock use but it seems causing stack overflow in certain conditions so I'm not gonna PR that.
Risk
There would be performance impact due to the extended use of
lock
.The bottleneck would be querying many to-be-created
TypeDescriptionNode
s at the same time - as they are forced to be created one by one - which may decrease startup perf. Otherwise the impact should be minimal because most queries quit at the firstif
block.