--- 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(); + } } }