--- Semaphore-snap.cs 2008-01-03 20:32:46.000000000 +1100
+++ Semaphore.cs 2008-01-04 10:17:34.000000000 +1100
@@ -26,57 +26,96 @@
{
internal class Semaphore : WaitHandle
{
+ AutoResetEvent autoEvent = null;
+ object countLock = new object();
+
+ int availableCount = 0;
+ int capacityCount = 0;
+
+ ///
+ /// Initializes a new Semaphore
+ ///
+ /// Initial tickets in this Semaphore instance
+ /// Capacity of this Semaphore instance
public Semaphore(int initialCount, int maximumCount)
{
- SECURITY_ATTRIBUTES sa = new SECURITY_ATTRIBUTES();
- IntPtr handle = CreateSemaphore(ref sa, initialCount,
- maximumCount, null);
- if (handle.Equals(IntPtr.Zero))
+ autoEvent = new AutoResetEvent(false);
+
+ availableCount = initialCount;
+ capacityCount = maximumCount;
+ }
+
+ ///
+ /// Releases one Semaphore ticket
+ ///
+ /// The count on the Semaphore before the Release method was called.
+ public int Release()
+ {
+ return Release(1);
+ }
+
+ ///
+ /// Releases a given number of Semaphore tickets
+ ///
+ /// Amount of tickets to release
+ /// The count on the Semaphore before the Release method was called.
+ public int Release(int releaseCount)
+ {
+ int previousCount = availableCount;
+
+ lock (countLock)
{
- throw new Exception("Unable to create semaphore");
+ if (releaseCount <= 0 || (availableCount + releaseCount > capacityCount))
+ throw new InvalidOperationException("Unable to release Semaphore");
+
+ availableCount += releaseCount;
}
- base.Handle = handle;
+
+ // Pulse the amount of threads for tickets we have released
+ for (int i = 0; i < releaseCount; i++)
+ autoEvent.Set();
+
+ return previousCount;
}
- public int Release()
+ ///
+ /// Attempt to take a ticket from the counter.
+ /// If we have a ticket available, take it.
+ ///
+ /// Whether a ticket was taken.
+ bool TryTakeTicket()
{
- IntPtr previous = IntPtr.Zero;
- if (!ReleaseSemaphore(base.Handle, 1, previous))
- throw new Exception("Unable to release semaphore");
- return previous.ToInt32();
+ lock (countLock)
+ {
+ if (availableCount > 0)
+ {
+ availableCount--;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
}
public override bool WaitOne(int millisecondsTimeout, bool exitContext)
{
- if ((millisecondsTimeout < 0) && (millisecondsTimeout != -1))
+ if ((millisecondsTimeout < 0) && (millisecondsTimeout != Timeout.Infinite))
throw new ArgumentOutOfRangeException("millisecondsTimeout");
if (exitContext)
throw new ArgumentException(null, "exitContext");
- int result = WaitForSingleObject(Handle, millisecondsTimeout);
- if (0 == result) return true;
- return false;
- }
-
- [DllImport("kernel32.dll")]
- static extern bool ReleaseSemaphore(IntPtr hSemaphore,
- int lReleaseCount, IntPtr lpPreviousCount);
-
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern IntPtr CreateSemaphore(
- ref SECURITY_ATTRIBUTES securityAttributes, int initialCount,
- int maximumCount, string name);
+ // Is there a ticket free? Take one if there is and return.
+ if (TryTakeTicket())
+ return true;
- [DllImport("kernel32.dll", SetLastError = true)]
- private static extern int WaitForSingleObject(IntPtr handle, int millis);
- }
+ // We have no tickets right now, lets wait for one.
+ autoEvent.WaitOne(millisecondsTimeout, false);
- [StructLayout(LayoutKind.Sequential)]
- internal struct SECURITY_ATTRIBUTES
- {
- public int nLength;
- public IntPtr lpSecurityDescriptor;
- public int bInheritHandle;
+ // Try one last time to retrieve a free ticket
+ return TryTakeTicket();
+ }
}
}