-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Thread-safety implications of using EqualityComparer<TValue>.Default in ConcurrentDictionary #10852
Comments
Tagging subscribers to this area: @dotnet/area-system-collections |
This looks like a variation on dotnet/runtime#84163, so I don't think there is much we could do that wouldn't be a breaking change. FWIW mutable types and optimistic transactions don't mix in my experience, I would probably have switched the example to use an immutable queue as its value type. |
Using an |
But in the example the developer is not doing so. The dev is calling AddOrUpdate, which needs to do a comparison, and is concurrently mutating the value that needs to be compared. It's no different than if you replaced the AddOrUpdate with a direct call to SequenceEqual or to the value's Equal, outside of the custom lock on the instance that's protecting the mutation but not the read. So I'm not sure what this issue is trying to achieve. Are you asking for the docs to be more explicit about AddOrUpdate comparing the value? |
Mr. Toub the purpose of this issue is to raise awareness that |
Thanks. Making the docs more explicit about AddOrUpdate needing to compare the values would be fine. |
Hi. Some time ago I posted an issue about the performance implications of using the default
TValue
comparer internally in theConcurrentDictionary<TKey,TValue>
collection. Recently I discovered that there is a thread-safety implication as well, so I would like to report it. The issue emerges under these conditions:TValue
is a mutable type, and implements theIEquatable<T>
interface or overrides theobject.Equals
method.Equals
implementation depends on mutable fields of the type.TValue
instance is mutated on one thread, while a concurrentAddOrUpdate
operation runs on another thread.In this case the
Equals
might see theTValue
instance in a transitional/invalid state, and throw an exception (or other undefined behavior).Below is a demonstration of this issue. The
TValue
is aGenome
class that derives from theQueue<char>
, and compares itself with otherGenome
instances with theSequenceEqual
method. Two threads are using theAddOrUpdate
to replaceGenome
instances in the dictionary, and then they take an exclusivelock
on the returnedGenome
instance and mutate it. The result is anInvalidOperationException
:Output:
Online demo.
This looks to me like a valid/intended use of the
ConcurrentDictionary<TKey,TValue>
collection, but I might be wrong.I experimented with the
ImmutableDictionary<TKey,TValue>
as well, expecting to be unaffected from this issue, but to my surprise it is affected as well (demo). This collection also makes calls to theEqualityComparer<TValue>.Default.Equals
when it is updated atomically with theImmutableInterlocked.AddOrUpdate
API. At least the immutable dictionary is equipped with theWithComparers
method, that allows to customize thevalueComparer
that is used internally by the collection.I should clarify that the above example is contrived. I haven't been personally affected by this issue.
The text was updated successfully, but these errors were encountered: