using System; using System.Threading; namespace Renci.SshNet.Common { /// /// Light implementation of SemaphoreSlim. /// public class SemaphoreLight : IDisposable { private readonly object _lock = new object(); private ManualResetEvent _waitHandle; private int _currentCount; /// /// Initializes a new instance of the class, specifying /// the initial number of requests that can be granted concurrently. /// /// The initial number of requests for the semaphore that can be granted concurrently. /// is a negative number. public SemaphoreLight(int initialCount) { if (initialCount < 0 ) throw new ArgumentOutOfRangeException("initialCount", "The value cannot be negative."); _currentCount = initialCount; } /// /// Gets the current count of the . /// public int CurrentCount { get { return _currentCount; } } /// /// Returns a that can be used to wait on the semaphore. /// /// /// A that can be used to wait on the semaphore. /// /// /// A successful wait on the does not imply a successful /// wait on the itself. It should be followed by a true wait /// on the semaphore. /// public WaitHandle AvailableWaitHandle { get { if (_waitHandle == null) { lock (_lock) { if (_waitHandle == null) { _waitHandle = new ManualResetEvent(_currentCount > 0); } } } return _waitHandle; } } /// /// Exits the once. /// /// The previous count of the . public int Release() { return Release(1); } /// /// Exits the a specified number of times. /// /// The number of times to exit the semaphore. /// /// The previous count of the . /// public int Release(int releaseCount) { lock (_lock) { var oldCount = _currentCount; _currentCount += releaseCount; // signal waithandle when the original semaphore count was zero if (_waitHandle != null && oldCount == 0) { _waitHandle.Set(); } Monitor.PulseAll(_lock); return oldCount; } } /// /// Blocks the current thread until it can enter the . /// public void Wait() { lock (_lock) { while (_currentCount < 1) { Monitor.Wait(_lock); } _currentCount--; // unsignal waithandle when the semaphore count reaches zero if (_waitHandle != null && _currentCount == 0) { _waitHandle.Reset(); } Monitor.PulseAll(_lock); } } /// /// Blocks the current thread until it can enter the , using a 32-bit signed /// integer that specifies the timeout. /// /// The number of milliseconds to wait, or Infinite(-1) to wait indefinitely. /// /// true if the current thread successfully entered the ; otherwise, false. /// public bool Wait(int millisecondsTimeout) { if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException("millisecondsTimeout", "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); return WaitWithTimeout(millisecondsTimeout); } /// /// Blocks the current thread until it can enter the , using a /// to specify the timeout. /// /// A that represents the number of milliseconds to wait, or a that represents -1 milliseconds to wait indefinitely. /// /// true if the current thread successfully entered the ; otherwise, false. /// public bool Wait(TimeSpan timeout) { var timeoutInMilliseconds = timeout.TotalMilliseconds; if (timeoutInMilliseconds < -1d || timeoutInMilliseconds > int.MaxValue) throw new ArgumentOutOfRangeException("timeout", "The timeout must represent a value between -1 and Int32.MaxValue, inclusive."); return WaitWithTimeout((int) timeoutInMilliseconds); } private bool WaitWithTimeout(int timeoutInMilliseconds) { lock (_lock) { if (timeoutInMilliseconds == Session.Infinite) { while (_currentCount < 1) Monitor.Wait(_lock); } else { if (_currentCount < 1) { if (timeoutInMilliseconds > 0) return false; var remainingTimeInMilliseconds = timeoutInMilliseconds; var startTicks = Environment.TickCount; while (_currentCount < 1) { if (!Monitor.Wait(_lock, remainingTimeInMilliseconds)) { return false; } var elapsed = Environment.TickCount - startTicks; remainingTimeInMilliseconds -= elapsed; if (remainingTimeInMilliseconds < 0) return false; } } } _currentCount--; // unsignal waithandle when the semaphore count is zero if (_waitHandle != null && _currentCount == 0) { _waitHandle.Reset(); } Monitor.PulseAll(_lock); return true; } } /// /// Finalizes the current . /// ~SemaphoreLight() { Dispose(false); } /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// /// Releases unmanaged and - optionally - managed resources /// /// true to release both managed and unmanaged resources; false to release only unmanaged resources. protected void Dispose(bool disposing) { if (disposing) { var waitHandle = _waitHandle; if (waitHandle != null) { waitHandle.Dispose(); _waitHandle = null; } } } } }