-
Notifications
You must be signed in to change notification settings - Fork 850
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
Change node filters to use a concurrent list instead of a dictionary #698
Conversation
…rder can be maintained.
NBitcoin/Utils/ThreadSafeList.cs
Outdated
|
||
public IEnumerator<T> GetEnumerator() | ||
{ | ||
return _Behaviors.ToList().GetEnumerator(); |
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.
to be thread safe you need to lock Behavior, make a copy of the list, and returning the enumerator of this copy.
NBitcoin/Utils/ThreadSafeList.cs
Outdated
{ | ||
list = _Behaviors.ToList(); | ||
} | ||
return list?.GetEnumerator(); |
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.
But then, it is a performance issue because we are duplicating a list everytimes a message come. :(
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.
I think we were also doing that before (this is from ThreadSafeCollection):
public IEnumerator<T> GetEnumerator()
{
return _Behaviors.Select(k => k.Key).GetEnumerator();
}
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.
Since we are doing all this copying anyways... wondering whether we might be able to use some of the new Immutable collections like ImmutableList, been dying to find a reason to try them out 😄
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.
_Behaviors.Select(k => k.Key).GetEnumerator()
is not a copy.
An idea, is that you make an array that is a copy of |
Thanks, yes that should work. Also researching this a bit it seems that some people have implemented functionality like this using a ConcurrentDictionary with an integer (item index) as the key which may be a reasonable approach as well. |
Updated to reduce list copying for enumerator as suggested. Reran the tests and they still pass. |
NBitcoin/Utils/ThreadSafeList.cs
Outdated
} | ||
return list?.GetEnumerator(); | ||
return _EnumeratorList?.GetEnumerator(); |
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.
Please capture enumeratorList
in a lock variable so we are sure GetEnumerator never returns null.
} | ||
return _EnumeratorList?.GetEnumerator(); | ||
|
||
return enumerator; |
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.
IEnumerator<T> enumerator = _EnumeratorList?.GetEnumerator();
if (enumerator == null)
{
lock (_lock)
{
var behaviorsList = _Behaviors.ToList();
_EnumeratorList = behaviorsList;
enumerator = behaviorsList.GetEnumerator();
}
}
return enumerator;
So you lock only if really needed. And it is provable that it is impossible to get a null enumerator out of this function.
I am tempted to revert this. The build is very unstable since I merged this. |
Tried to refactor a bit, no luck. |
Sure no worries - go ahead and revert and I'll do some more testing with it with that test. |
I change back to |
I don't understand the issue. Tried replicate on my machine but that did not worked. |
@mikedennis is |
@zeptin I believe that's only .NET framework. Found the source code though, looks like it would be easy to port https://github.com/microsoft/referencesource/blob/3b1eaf5203992df69de44c783a3eda37d3d4cd10/System.ServiceModel/System/ServiceModel/SynchronizedCollection.cs |
@NicolasDorier I am unable to get that test to fail locally while using the |
yes, make a pr |
This is a prerequisite for the work I'm doing in #692
ConcurrentDictionary does not maintain insertion order so it's usage in NodeFiltersCollection does not allow for ordering of Filters.
ThreadSafeList uses a list instead of ConcurrentDictionary and the locking of the collection is done manually. Currently I've only replaced the usage in NodeFiltersCollection but I have run all the tests using ThreadSafeList isntead of ThreadSafeCollection in all occurrences and all the same tests pass. So we may be able to replace other instances as well.
ThreadSafeList will allow insertion of duplicates whereas ThreadSafeCollection will not so that is a key difference to be aware of beyond the ordering difference.