Custom C# locking interface and implementation
This file contains 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.Threading; | |
namespace Locking | |
{ | |
internal interface IWaitableLock | |
{ | |
/// <summary> | |
/// Acquire the lock. | |
/// </summary> | |
/// <param name="blocking">Whether to wait until acquired or return immediately</param> | |
/// <returns>true when acquired, false otherwise</returns> | |
bool Acquire(bool blocking); | |
/// <summary> | |
/// Release the lock. | |
/// </summary> | |
/// <param name="strict">Whether to assert that the lock is acquired.</param> | |
/// <exception cref="SynchronizationLockException"> | |
/// Strictly unlocking while already unlocked. | |
/// </exception> | |
/// <returns>The state of the lock before this action took place (atomically).</returns> | |
bool Release(bool strict); | |
bool IsLocked(); | |
// TODO: These two do not belong here, better use thread-safe queues. | |
/// <summary> | |
/// Return as soon as it detects the lock is acquired. Does not affect the lock. | |
/// </summary> | |
void WaitWhileUnlocked(); | |
/// <summary> | |
/// Return as soon as it detects the lock is released. Does not affect the lock. | |
/// </summary> | |
void WaitWhileLocked(); | |
/// <summary> | |
/// Event called after the lock has been acquired | |
/// </summary> | |
event Action Acquired; | |
/// <summary> | |
/// Event called after the lock has been released | |
/// </summary> | |
event Action Released; | |
} | |
/// <summary> | |
/// Simple boolean mutex, like Python's threading.Lock. | |
/// </summary> | |
internal class MyLock : IWaitableLock | |
{ | |
private int locked; | |
public event Action Acquired = delegate { }; | |
public event Action Released = delegate { }; | |
public MyLock() | |
{ | |
locked = 0; | |
} | |
public bool Acquire(bool wait) | |
{ | |
if (Interlocked.CompareExchange(ref locked, 1, 0) == 0) | |
{ | |
Acquired(); | |
return true; | |
} | |
else if (wait) | |
{ | |
WaitWhileLocked(); | |
// The lock was released, try (!) to acquire again. | |
return Acquire(wait); | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
public bool Release(bool strict) | |
{ | |
bool waslocked = (Interlocked.CompareExchange(ref locked, 0, 1) == 1); | |
if (waslocked) | |
{ | |
Released(); | |
} | |
else if (strict) | |
{ | |
throw new SynchronizationLockException("Already unlocked"); | |
} | |
return waslocked; | |
} | |
public bool IsLocked() | |
{ | |
return locked == 1; | |
} | |
public void WaitWhileUnlocked() | |
{ | |
var wait = new AutoResetEvent(false); | |
Action set = (() => wait.Set()); // ignore return value | |
Acquired += set; | |
try | |
{ | |
if (locked == 1) | |
{ | |
return; | |
} | |
wait.WaitOne(); | |
} | |
finally | |
{ | |
Acquired -= set; | |
} | |
} | |
public void WaitWhileLocked() | |
{ | |
var wait = new AutoResetEvent(false); | |
Action set = (() => wait.Set()); // ignore return value | |
Released += set; | |
try | |
{ | |
if (locked == 0) | |
{ | |
return; | |
} | |
wait.WaitOne(); | |
} | |
finally | |
{ | |
Released -= set; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment