|
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
namespace Mubble.Threading
{
/// <summary>
/// Defines a lock "collection" allowing for named locks
/// </summary>
/// <typeparam name="T">
/// The type of "name" to use. Must be usable as a Dictionary key.
/// </typeparam>
public class NamedLock<T>
{
Dictionary<T, ReferenceCount> lockCollection = new Dictionary<T, ReferenceCount>();
/// <summary>
/// Acquires a lock for the specified name. It's best to use this
/// in a "using" statement
/// </summary>
/// <param name="name">The name to lock</param>
/// <returns>A disposable lock token</returns>
public IDisposable Lock(T name)
{
return Lock(name, Timeout.Infinite);
}
/// <summary>
/// Acquires a lock for the specified name. Times out after the specified number of milliseconds.
/// It's best to use this in a "using" statement
/// </summary>
/// <param name="name">The name to lock</param>
/// <param name="timeout">Number of milliseconds to wait before timing out</param>
/// <returns>A disposable lock token</returns>
public IDisposable Lock(T name, int timeout)
{
Monitor.Enter(lockCollection);
ReferenceCount obj = null;
lockCollection.TryGetValue(name, out obj);
if (obj == null)
{
obj = new ReferenceCount();
Monitor.Enter(obj);
lockCollection.Add(name, obj);
Monitor.Exit(lockCollection);
}
else
{
obj.AddRef();
Monitor.Exit(lockCollection);
if (!Monitor.TryEnter(obj, timeout))
{
throw new TimeoutException(
string.Format("Timeout while waiting for lock on {0}", name)
);
}
}
return new Token<T>(this, name);
}
/// <summary>
/// Unlocks the specified name. This method shouldn't be used directly, disposing of the
/// lock token is a much better route
/// </summary>
/// <param name="name">The name to unlock</param>
public void Unlock(T name)
{
lock (lockCollection)
{
ReferenceCount obj = null;
lockCollection.TryGetValue(name, out obj);
if (obj != null)
{
Monitor.Exit(obj);
if (0 == obj.Release())
{
lockCollection.Remove(name);
}
}
}
}
class Token<S> : IDisposable
{
NamedLock<S> parent;
S name;
public Token(NamedLock<S> parent, S name)
{
this.parent = parent;
this.name = name;
}
#region IDisposable Members
public void Dispose()
{
parent.Unlock(name);
}
#endregion
}
class ReferenceCount
{
public int count = 0;
public ReferenceCount()
{
AddRef();
}
public int AddRef()
{
return Interlocked.Increment(ref count);
}
public int Release()
{
return Interlocked.Decrement(ref count);
}
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.