Skip to content

NH-3885 - Replace ThreadSafeDictionary wrapper with Framework's ConcurrentDictionary #498

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

Merged
merged 2 commits into from
Dec 4, 2016

Conversation

ngbrown
Copy link
Contributor

@ngbrown ngbrown commented Aug 10, 2016

This should address NH-3885 by replacing all uses of the custom ThreadSafeDictionary wrapper with the .NET Framework provided ConcurrentDictionary.

In most places the IDictionary<,> interface is kept as it provides the expected exception throwing when adding existing keys, otherwise the caller would be responsible for error handling on the result of TryAdd().

In SessionFactoryImpl specifically, I update the code to use the thread-safe GetOrAdd() as that was what the intention of the code was.

This removes ThreadSafeDictionary from the project.

@@ -247,7 +248,7 @@ public SessionFactoryImpl(Configuration cfg, IMapping mapping, Settings settings
if (cache != null)
{
caches.Add(cacheRegion, cache);
allCacheRegions.Add(cache.RegionName, cache.Cache);
((IDictionary<string, ICache>)allCacheRegions).Add(cache.RegionName, cache.Cache);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the explicit interface implementations of the ConcurrentDictionary are not thread safe (that's why they are explicitly implemented). I had a similar solution on my machine. Currently I'm in holidays. I'll compare yours to mine when I'm back.

Copy link
Contributor Author

@ngbrown ngbrown Aug 11, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@stefan-steinegger I had that same concern too, but I looked at the Microsoft Reference Source and the IDictonary implementation is a very simple wrapper around the main TryAdd and it also throws if not added (TryAdd returns false). This throw code could easily be moved into the application, but since it is in the library, in the explicit IDictionary implementations, I left it there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation, however, states:
All public and protected members of ConcurrentDictionary<TKey, TValue> are thread-safe and may be used concurrently from multiple threads. However, members accessed through one of the interfaces the ConcurrentDictionary<TKey, TValue> implements, including extension methods, are not guaranteed to be thread safe and may need to be synchronized by the caller.

We shouldn't rely on behavior the documentation doesn't promise.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we simply use TryAdd() instead?

@oskarb oskarb added this to the 5.0.0 milestone Nov 19, 2016
with Framework's ConcurrentDictionary.
Copy link
Member

@oskarb oskarb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM except for the exception messages.

((IDictionary<string, ICache>)allCacheRegions).Add(cache.RegionName, cache.Cache);
if (!allCacheRegions.TryAdd(cache.RegionName, cache.Cache))
{
throw new HibernateException("Key already existed");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per guidelines, exception messages are supposed to be proper sentences, which includes a period at the end. (Multiple locations.)

Only use ConcurrentDictionary through the public methods, not the
interface methods.
@ngbrown
Copy link
Contributor Author

ngbrown commented Dec 4, 2016

@oskarb I updated the messages to a bit more than the standard .Net framework Dictionary exception sentence, including the dictionary variable name.

@oskarb oskarb merged commit 3906356 into nhibernate:master Dec 4, 2016
@ngbrown ngbrown deleted the NH-3885 branch December 4, 2016 21:54
@lnu
Copy link
Member

lnu commented Jan 3, 2017

I'm doing some tests with the new version and it seems NHibernate.Envers relies on the ThreadSafeDictionnary class.
Maybe it's a bit too radical to remove it, could we mark it as obsolete?

In the meantime I've created a pull request for envers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants