Last active
August 6, 2019 20:20
-
-
Save pmunin/3f20d1051fb74a09d07fe8bc6310d4a5 to your computer and use it in GitHub Desktop.
ReadWriteLockableValue.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Concurrent; | |
using System.Collections.Generic; | |
using System.Threading; | |
namespace ConcurrentLocking | |
{ | |
public class ConcurrentLockDictionary<TKey> | |
{ | |
public class KeyLock | |
{ | |
public int RefCount; | |
public TKey Key { get; set; } | |
public ReaderWriterLockSlim RWLock { get; } = new ReaderWriterLockSlim(); | |
} | |
public ConcurrentLockDictionary(IEqualityComparer<TKey> comparer = null) | |
{ | |
this.EqualityComparer = comparer; | |
} | |
ConcurrentDictionary<TKey, Lazy<KeyLock>> lockByKey; | |
public ConcurrentDictionary<TKey, Lazy<KeyLock>> LockByKey | |
{ | |
get | |
{ | |
if (lockByKey == null) | |
{ | |
lockByKey = EqualityComparer == null | |
? new ConcurrentDictionary<TKey, Lazy<KeyLock>>() | |
: new ConcurrentDictionary<TKey, Lazy<KeyLock>>(EqualityComparer); | |
} | |
return lockByKey; | |
} | |
} | |
public IEqualityComparer<TKey> EqualityComparer { get; } | |
public IDisposable BeginUsingKey(TKey key, out KeyLock keyLock) | |
{ | |
var _keyLock = keyLock = LockByKey.GetOrAdd(key, new Lazy<KeyLock>(() => new KeyLock() { Key = key})).Value; | |
Interlocked.Increment(ref _keyLock.RefCount); | |
return Disposable.For(() => | |
{ | |
lock (_keyLock) | |
{ | |
_keyLock.RefCount--; | |
if (_keyLock.RefCount == 0) | |
{ | |
LockByKey.TryRemove(key, out var __); | |
_keyLock.RWLock.Dispose(); | |
} | |
} | |
}); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ReaderWriterLockExtensions | |
{ | |
public static IDisposable ReadUpgradable(this ReaderWriterLockSlim locker, int? timeoutMs=null, Action onTimeout=null) | |
{ | |
var successful = true; | |
if (timeoutMs == null) | |
{ | |
locker.EnterUpgradeableReadLock(); | |
} | |
else | |
{ | |
successful = locker.TryEnterUpgradeableReadLock(timeoutMs.Value); | |
if (!successful) | |
{ | |
if (onTimeout != null) | |
{ | |
onTimeout(); | |
} | |
else | |
{ | |
throw new TimeoutException( | |
"ReaderWriterLockSlim.TryEnterUpgradeableReadLock failed with timeout"); | |
} | |
} | |
} | |
return Disposable.For(() => | |
{ | |
if (successful) | |
{ | |
locker.ExitUpgradeableReadLock(); | |
} | |
}); | |
} | |
public static IDisposable Read(this ReaderWriterLockSlim locker, int? timeoutMs = null, Action onTimeout = null) | |
{ | |
var timeout = false; | |
if (timeoutMs != null) | |
{ | |
timeout = !locker.TryEnterReadLock(timeoutMs.Value); | |
} | |
else | |
{ | |
locker.EnterReadLock(); | |
} | |
if (timeout) | |
{ | |
if (onTimeout == null) | |
{ | |
throw new TimeoutException("ReaderWriterLockSlim.TryEnterReadLock failed with timeout"); | |
} | |
else | |
{ | |
onTimeout(); | |
} | |
} | |
return Disposable.For(() => | |
{ | |
if (timeout) | |
{ | |
locker.ExitReadLock(); | |
} | |
}); | |
} | |
public static IDisposable Write(this ReaderWriterLockSlim locker, int? timeoutMs = null, Action onTimeout = null) | |
{ | |
var timeout = false; | |
if (timeoutMs != null) | |
{ | |
timeout = !locker.TryEnterWriteLock(timeoutMs.Value); | |
} | |
else | |
{ | |
locker.EnterWriteLock(); | |
} | |
if (timeout) | |
{ | |
if (onTimeout == null) | |
{ | |
throw new TimeoutException("ReaderWriterLockSlim.TryEnterWriteLock failed with timeout"); | |
} | |
else | |
{ | |
onTimeout(); | |
} | |
} | |
return Disposable.For(() => | |
{ | |
if (timeout) | |
{ | |
locker.ExitWriteLock(); | |
} | |
}); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class ReadWriteLockableValue<T> : IDisposable | |
{ | |
public ReadWriteLockableValue(T initialValue=default(T), LockRecursionPolicy recursion=LockRecursionPolicy.SupportsRecursion) | |
{ | |
this.value = initialValue; | |
this.locker = new ReaderWriterLockSlim(recursion); | |
} | |
T value; | |
ReaderWriterLockSlim locker; | |
public void Dispose() | |
{ | |
this.locker.Dispose(); | |
} | |
public bool Read(Action<T> onRead, bool upgradable=false, TimeSpan? timeout=null) | |
{ | |
var isTimeout = false; | |
if (upgradable) | |
{ | |
if (timeout == null) | |
{ | |
this.locker.EnterUpgradeableReadLock(); | |
} | |
else | |
{ | |
isTimeout = !this.locker.TryEnterUpgradeableReadLock(timeout.Value); | |
} | |
} | |
else | |
{ | |
if (timeout==null) | |
{ | |
this.locker.EnterReadLock(); | |
} | |
else | |
{ | |
isTimeout = !this.locker.TryEnterReadLock(timeout.Value); | |
} | |
} | |
if (isTimeout) {return false;} | |
try | |
{ | |
onRead(this.value); | |
} | |
finally | |
{ | |
if (upgradable) | |
{ | |
this.locker.ExitUpgradeableReadLock(); | |
} | |
else | |
{ | |
this.locker.ExitReadLock(); | |
} | |
} | |
return true; | |
} | |
public bool Update(Func<T, T> onUpdate, TimeSpan? timeout = null) | |
{ | |
var isTimeout = false; | |
if (timeout == null) | |
{ | |
this.locker.EnterWriteLock(); | |
} | |
else | |
{ | |
isTimeout = this.locker.TryEnterWriteLock(timeout.Value); | |
} | |
if (isTimeout) {return false;} | |
try | |
{ | |
this.value = onUpdate(this.value); | |
} | |
finally | |
{ | |
this.locker.ExitWriteLock(); | |
} | |
return true; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment