-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Strong detection for List/Dictionary concurrent use in Debug #17076
Conversation
@dotnet-bot test Windows_NT x64 Checked corefx_baseline |
@@ -1041,6 +1191,9 @@ ICollection IDictionary.Values | |||
{ | |||
ThrowHelper.ThrowWrongKeyTypeArgumentException(key, typeof(TKey)); | |||
} | |||
#if DEBUG |
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.
❓ Why bother with #if DEBUG
here if the target method is marked [Conditional("DEBUG")]
?
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.
If you do that it might be worth naming something like as DebugOnlyConcurrentAccessCheck so it's obvious it's debug only
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.
Wasn't sure the Jit would elide all the int version = _version;
calls (or C# delete them and not produce an error about not using it)
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.
Will change an check diffs as it reduces the number of #if
s considerably
@@ -214,14 +215,26 @@ public ValueCollection Values | |||
{ | |||
get | |||
{ | |||
#if DEBUG | |||
int version = _version; |
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.
if you're using the existing _version
field doesn't it have to be volatile or incremented with Interlocked?
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.
In DEBUG
its incremented in ConcurrentAccessWriteCheck
with
Interlocked.CompareExchange(ref _version, version + 1, version) != version
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.
Changed to
if (Interlocked.Exchange(ref _version, version + 1) != version)
{
ThrowHelper.ThrowInvalidOperationException_ConcurrentOperationsNotSupported();
}
As don't care if it stops version being updated; just that it throws if the version wasn't correct
1914ca9
to
c9646e4
Compare
@dotnet-bot test Windows_NT x64 Checked corefx_baseline |
The barriers from these interlocked operations can hide some types of the races as well. Do we have any examples of bugs in CoreFX that were caused by invalid multi-threaded Dictionary use and that were in the product for a long time because of they were hard to hit and find? It would be nice to validate the effectiveness of this on some real world examples. |
Its generating the asm for the
|
Regression in Dictionary is from incrementing Other regressions are from |
@jkotas this was motivated by https://github.com/dotnet/corefx/issues/28198. I assume there have been other cases periodically? |
b6f9740
to
3363111
Compare
Is there a way to run coreclr (checked or debug) against corefx outerloop? |
Triggered some tests that were checking version didn't change
|
@dotnet-bot test Windows_NT x64 Checked corefx_baseline |
https://github.com/dotnet/corefx/issues/28198#issuecomment-374676611 says that we do not have any tests for the buggy code. If it is the case, this would not help us to find this issue faster. |
3b28f71
to
22fb6c0
Compare
22fb6c0
to
956fdd2
Compare
@dotnet-bot test Windows_NT x64 Checked corefx_baseline |
Linux - CoreFx issues unrelated
|
@stephentoub thoughts about existence of real world examples? |
Windows_NT x64 Checked Build and Test (Jit - CoreFx) failure
|
@danmosemsft it didn't catch anything 😢 Is this change worth it? |
I figured stress runs would be more likely to catch something. As to whether it's worth it, I'm open minded, I'm curious whether @stephentoub has seen real cases. Do you understand this gain - is it worth keeping?
|
Added PR for the gains :) #17096 |
I've seen real cases, e.g. the SQL one I fixed earlier in the week, but as Jan restated this wouldn't have caught that as we didn't have any tests that exercised those code paths, nevermind ones that would have exercised it in parallel. I just did a search for "static Dictionary", "static readonly Dictionary", "readonly static Dictionary" in corefx (thankfully there's only one of the latter, as that's not the desired ordering), and did a cursory review of each's usage. A few things I noticed:
In short, across the large codebase that is corefx, it's possible this might catch something, but I wouldn't expect significant wins. |
I have opened https://github.com/dotnet/corefx/issues/30651 and https://github.com/dotnet/corefx/issues/30650 on the issues that Stephen identified by code inspection. |
Otherwise, I do not think that this is worth the extra DEBUG #ifdefs. I think that these ifdefs have about the same chance of catching problems when running debug CoreCLR build as introducing new ones. @benaadams Thanks for the suggested change anyway. |
Bit ugly, but should do the trick?
Resolves: https://github.com/dotnet/coreclr/issues/17070