You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
5.5 KiB
C#

#if !FEATURE_THREAD_COUNTDOWNEVENT
using System;
using System.Threading;
namespace Renci.SshNet.Common
{
/// <summary>
/// Represents a synchronization primitive that is signaled when its count reaches zero.
/// </summary>
internal class CountdownEvent : IDisposable
{
private int _count;
private ManualResetEvent _event;
private bool _disposed;
/// <summary>
/// Initializes a new instance of <see cref="CountdownEvent"/> class with the specified count.
/// </summary>
/// <param name="initialCount">The number of signals initially required to set the <see cref="CountdownEvent"/>.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="initialCount"/> is less than zero.</exception>
/// <remarks>
/// If <paramref name="initialCount"/> is <c>zero</c>, the event is created in a signaled state.
/// </remarks>
public CountdownEvent(int initialCount)
{
if (initialCount < 0)
{
throw new ArgumentOutOfRangeException("initialCount");
}
_count = initialCount;
var initialState = _count == 0;
_event = new ManualResetEvent(initialState);
}
/// <summary>
/// Gets the number of remaining signals required to set the event.
/// </summary>
/// <value>
/// The number of remaining signals required to set the event.
/// </value>
public int CurrentCount
{
get { return _count; }
}
/// <summary>
/// Indicates whether the <see cref="CountdownEvent"/>'s current count has reached zero.
/// </summary>
/// <value>
/// <c>true</c> if the current count is zero; otherwise, <c>false</c>.
/// </value>
public bool IsSet
{
get { return _count == 0; }
}
/// <summary>
/// Registers a signal with the <see cref="CountdownEvent"/>, decrementing the value of <see cref="CurrentCount"/>.
/// </summary>
/// <returns>
/// <c>true</c> if the signal caused the count to reach zero and the event was set; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
/// <exception cref="InvalidOperationException">The current instance is already set.</exception>
public bool Signal()
{
EnsureNotDisposed();
if (_count <= 0)
throw new InvalidOperationException("Invalid attempt made to decrement the event's count below zero.");
var newCount = Interlocked.Decrement(ref _count);
if (newCount == 0)
{
_event.Set();
return true;
}
return false;
}
/// <summary>
/// Increments the <see cref="CountdownEvent"/>'s current count by one.
/// </summary>
/// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
/// <exception cref="InvalidOperationException">The current instance is already set.</exception>
/// <exception cref="InvalidOperationException"><see cref="CurrentCount"/> is equal to or greather than <see cref="int.MaxValue"/>.</exception>
public void AddCount()
{
EnsureNotDisposed();
if (_count == int.MaxValue)
throw new InvalidOperationException("TODO");
Interlocked.Increment(ref _count);
}
/// <summary>
/// Blocks the current thread until the <see cref="CountdownEvent"/> is set, using a <see cref="TimeSpan"/>
/// to measure the timeout.
/// </summary>
/// <param name="timeout">A <see cref="TimeSpan"/> that represents the number of milliseconds to wait, or a <see cref="TimeSpan"/> that represents -1 milliseconds to wait indefinitely.</param>
/// <returns>
/// <c>true</c> if the <see cref="CountdownEvent"/> was set; otherwise, <c>false</c>.
/// </returns>
/// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception>
public bool Wait(TimeSpan timeout)
{
EnsureNotDisposed();
return _event.WaitOne(timeout);
}
/// <summary>
/// Releases all resources used by the current instance of the <see cref="CountdownEvent"/> class.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases the unmanaged resources used by the <see cref="CountdownEvent"/>, and optionally releases the managed resources.
/// </summary>
/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
var theEvent = _event;
if (theEvent != null)
{
_event = null;
theEvent.Dispose();
}
_disposed = true;
}
}
private void EnsureNotDisposed()
{
if (_disposed)
throw new ObjectDisposedException(GetType().Name);
}
}
}
#endif // FEATURE_THREAD_COUNTDOWNEVENT