Skip to content

Instantly share code, notes, and snippets.

@xanathar
Last active August 29, 2015 14:02
Show Gist options
  • Save xanathar/1f07acf3db24d451c6d6 to your computer and use it in GitHub Desktop.
Save xanathar/1f07acf3db24d451c6d6 to your computer and use it in GitHub Desktop.
Wrapper of Monitor class for easier deadlock debug
// original source is likely to be https://github.com/thinkpixellab/bot/blob/master/net40-client/Core/LockHelper.cs although I can't verify it anymore.
/// <summary>
/// Provides services of Monitor static class, but while allowing better debugging of deadlocks.
/// </summary>
public class LockHelper
{
/// <summary>
/// Creates a new instance of LockHelper.
/// </summary>
/// <param name="name">The name to give the helper. Cannot be null or empty.</param>
/// <exception cref="ArgumentOutOfRangeException">If name is null or empty.</exception>
public LockHelper(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentOutOfRangeException("name");
}
m_name = name;
}
/// <summary>
/// Aquires the lock.
/// </summary>
/// <returns>An IDisposable that can be used in a C# 'lock' block.</returns>
/// <example>using(lockHelper.GetLock(){ //do work here }</example>
public IDisposable GetLock()
{
Enter();
return new Unlocker(this);
}
/// <summary>
/// Aquires the lock using a Monitor.Enter like semantic.
/// </summary>
public void Enter()
{
Monitor.Enter(m_lockObject);
Thread currentThread = Thread.CurrentThread;
m_threadStack.Push(currentThread);
if (string.IsNullOrEmpty(currentThread.Name))
{
m_owningThreadName = string.Format("Unnamed - ManagedThreadId:{0}", currentThread.ManagedThreadId);
}
else
{
m_owningThreadName = currentThread.Name;
}
}
/// <summary>
/// Exits the lock using a Monitor.Exit like semantic.
/// </summary>
public void Exit()
{
unlock();
}
/// <summary>
/// Proxy for the Monitor.Pulse method on the current instance.
/// </summary>
public void Pulse()
{
Monitor.Pulse(m_lockObject);
}
/// <summary>
/// Proxy for the Monitor.Wait method on the current instance.
/// </summary>
public void Wait()
{
Monitor.Wait(m_lockObject);
}
/// <summary>
/// Check to see if the calling thread hold this lock on this LockHelper.
/// </summary>
/// <returns>True if it holds the lock. False otherwise.</returns>
public bool CheckAccess()
{
return (m_threadStack.Count > 0) &&
(Thread.CurrentThread == m_threadStack.Peek());
}
/// <summary>
/// Throws an exception if CheckAccess() returns false.
/// </summary>
/// <exception cref="CommonException">If CheckAccess would throw an exception.</exception>
public void VerifyAccess()
{
if (!CheckAccess())
{
throw new Exception("Code was run that does not have the nessesary lock.");
}
}
private void unlock()
{
VerifyAccess();
//ideally, this work would be done 'after' Exit, to make sure 'Exit' succeeds
//BUT, it must be done while the object is locked, or we'll have conflicts
//with the code that is run after Monitor.Enter
m_threadStack.Pop();
if (m_threadStack.Count > 0)
{
Thread currentThread = m_threadStack.Peek();
if (string.IsNullOrEmpty(currentThread.Name))
{
m_owningThreadName = string.Format("Unnamed - ManagedThreadId:{0}", currentThread.ManagedThreadId);
}
else
{
m_owningThreadName = currentThread.Name;
}
}
else
{
m_owningThreadName = null;
}
Monitor.Exit(m_lockObject);
}
private string m_owningThreadName;
private readonly Stack<Thread> m_threadStack = new Stack<Thread>();
private readonly object m_lockObject = new object();
private readonly string m_name;
private class Unlocker : IDisposable
{
public Unlocker(LockHelper owner)
{
if (owner == null)
{
throw new ArgumentNullException("owner");
}
m_owner = owner;
}
public void Dispose()
{
lock (m_lockObject)
{
if (m_owner != null)
{
m_owner.unlock();
//do this after the unlock to ensure that it succeeds
m_owner = null;
}
else
{
throw new Exception("Cannot Dispose the object returned from GetLock more than once.");
}
}
}
private readonly object m_lockObject = new object();
private LockHelper m_owner;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment