using System;
using System.Threading;
namespace JD.Threading
{
///
/// Implementation of Dijkstra's PV Semaphore based on the Monitor class.
/// See http://en.wikipedia.org/wiki/Semaphore_(programming)
///
public sealed class PVSemaphore
{
#region Variables
///
/// The number of units alloted by this semaphore.
///
private int m_count;
///
/// Lock for the semaphore.
///
private readonly object m_semaphoreLock = new object();
#endregion
#region Constructors
///
/// Initialize the semaphore as a binary semaphore.
///
public PVSemaphore()
: this(1)
{
}
///
/// Initialize the semaphore as a counting semaphore.
///
/// Initial number of threads that can take out units from this semaphore.
/// Thrown if the count argument is less than 0.
public PVSemaphore(int count)
{
if (count < 0)
{
throw new ArgumentOutOfRangeException("count", count, "Semaphore must have a count of at least 0.");
}
m_count = count;
}
#endregion
#region Methods
///
/// V the semaphore (add 1 unit to it).
///
public void AddOne()
{
// Lock so we can work in peace. This works because lock is actually
// built around Monitor.
lock (m_semaphoreLock)
{
// Release our hold on the unit of control. Then tell everyone
// waiting on this object that there is a unit available.
m_count++;
Monitor.Pulse(m_semaphoreLock);
}
}
///
/// P the semaphore (wait and take out 1 unit from it).
///
public void WaitOne()
{
// Lock so we can work in peace. This works because lock is actually
// built around Monitor.
lock (m_semaphoreLock)
{
// Wait until a unit becomes available. We need to wait
// in a loop in case someone else wakes up before us. This could
// happen if the Monitor.Pulse statements were changed to Monitor.PulseAll
// statements in order to introduce some randomness into the order
// in which threads are woken.
while (m_count <= 0)
{
Monitor.Wait(m_semaphoreLock, Timeout.Infinite);
}
m_count--;
}
}
///
/// Resets the semaphore to the specified count. Should be used cautiously.
///
public void Reset(int count)
{
lock (m_semaphoreLock)
{
m_count = count;
}
}
#endregion
}
}