Skip to content

Commit

Permalink
MultiValueDictionary internal HashSet is not threadsafe #354
Browse files Browse the repository at this point in the history
  • Loading branch information
apobekiaris committed Mar 28, 2019
1 parent 8838df6 commit 5757104
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,7 @@ public void Attach(XpandModuleBase xpandModuleBase) {

void ApplicationOnLoggedOff(object sender, EventArgs eventArgs) {
((XafApplication)sender).LoggedOff -= ApplicationOnLoggedOff;
XpandModuleBase.CallMonitor.Remove(new KeyValuePair<string, ApplicationModulesManager>(SequenceGeneratorHelperName, _xpandModuleBase.ModuleManager));
XpandModuleBase.CallMonitor.TryRemove(new KeyValuePair<string, ApplicationModulesManager>(SequenceGeneratorHelperName, _xpandModuleBase.ModuleManager),out _);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -848,8 +848,8 @@ public void UpdateNode(IModelMemberEx node, IModelApplication application) {
node.ClearValue(ex => ex.IsCalculated);
}

public static void RemoveCall(string name, ApplicationModulesManager applicationModulesManager){
CallMonitor?.Remove(new KeyValuePair<string, ApplicationModulesManager>(name, applicationModulesManager));
public static void RemoveCall(string name, ApplicationModulesManager applicationModulesManager) {
CallMonitor?.TryRemove(new KeyValuePair<string, ApplicationModulesManager>(name, applicationModulesManager),out _);
}
}

Expand Down
118 changes: 108 additions & 10 deletions Xpand/Xpand.Utils/GeneralDataStructures/MultiValueDictionary.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,110 @@
using System.Collections;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace Xpand.Utils.GeneralDataStructures {
public class MultiValueDictionary<TKey, TValue> : ConcurrentDictionary<TKey, HashSet<TValue>>, ILookup<TKey, TValue> {
public class ConcurrentHashSet<T> : IDisposable, IEnumerable<T> {
private readonly HashSet<T> _hashSet = new HashSet<T>();
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);

public int Count {
get {
_lock.EnterReadLock();

try {
return _hashSet.Count;
}
finally {
if (_lock.IsReadLockHeld) _lock.ExitReadLock();
}
}
}

public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}

public IEnumerator<T> GetEnumerator() {
_lock.EnterWriteLock();

try {
return _hashSet.GetEnumerator();
}
finally {
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}

IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}

public bool TryAdd(T item) {
_lock.EnterWriteLock();

try {
return _hashSet.Add(item);
}
finally {
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}

public void Clear() {
_lock.EnterWriteLock();

try {
_hashSet.Clear();
}
finally {
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}

public bool Contains(T item) {
_lock.EnterReadLock();

try {
return _hashSet.Contains(item);
}
finally {
if (_lock.IsReadLockHeld) _lock.ExitReadLock();
}
}

public bool TryRemove(T item) {
_lock.EnterWriteLock();

try {
return _hashSet.Remove(item);
}
finally {
if (_lock.IsWriteLockHeld) _lock.ExitWriteLock();
}
}

public T FirstOrDefault(Func<T, bool> predicate) {
_lock.EnterReadLock();

try {
return _hashSet.FirstOrDefault(predicate);
}
finally {
if (_lock.IsReadLockHeld) _lock.ExitReadLock();
}
}

protected virtual void Dispose(bool disposing) {
if (disposing) _lock?.Dispose();
}
}

public class
MultiValueDictionary<TKey, TValue> : ConcurrentDictionary<TKey, ConcurrentHashSet<TValue>>, ILookup<TKey, TValue> {
bool ILookup<TKey, TValue>.Contains(TKey key) {
return ContainsKey(key);
}
Expand All @@ -23,11 +123,11 @@ IEnumerator IEnumerable.GetEnumerator() {

public void Add(TKey key, TValue value) {
if (!TryGetValue(key, out var container)) {
container = new HashSet<TValue>();
container = new ConcurrentHashSet<TValue>();
TryAdd(key, container);
}

container.Add(value);
container.TryAdd(value);
}

public void AddRange(TKey key, IEnumerable<TValue> values) {
Expand All @@ -44,10 +144,8 @@ public bool ContainsValue(TKey key, TValue value) {

public void Remove(TKey key, TValue value) {
if (TryGetValue(key, out var container)) {
container.Remove(value);
if (container.Count <= 0) {
TryRemove(key, out container);
}
container.TryRemove(value);
if (container.Count <= 0) TryRemove(key, out container);
}
}

Expand All @@ -59,8 +157,8 @@ public void Merge(MultiValueDictionary<TKey, TValue> toMergeWith) {
Add(pair.Key, value);
}

public HashSet<TValue> GetValues(TKey key, bool returnEmptySet) {
if (!TryGetValue(key, out var toReturn) && returnEmptySet) toReturn = new HashSet<TValue>();
public ConcurrentHashSet<TValue> GetValues(TKey key, bool returnEmptySet) {
if (!TryGetValue(key, out var toReturn) && returnEmptySet) toReturn = new ConcurrentHashSet<TValue>();
return toReturn;
}
}
Expand Down

0 comments on commit 5757104

Please sign in to comment.