diff options
Diffstat (limited to 'src/mscorlib/src/System/Threading/CountdownEvent.cs')
-rw-r--r-- | src/mscorlib/src/System/Threading/CountdownEvent.cs | 589 |
1 files changed, 0 insertions, 589 deletions
diff --git a/src/mscorlib/src/System/Threading/CountdownEvent.cs b/src/mscorlib/src/System/Threading/CountdownEvent.cs deleted file mode 100644 index af055e347e..0000000000 --- a/src/mscorlib/src/System/Threading/CountdownEvent.cs +++ /dev/null @@ -1,589 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// -// -// A simple coordination data structure that we use for fork/join style parallelism. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Runtime.InteropServices; -using System.Threading; -using System.Diagnostics; -using System.Diagnostics.Contracts; - -namespace System.Threading -{ - - /// <summary> - /// Represents a synchronization primitive that is signaled when its count reaches zero. - /// </summary> - /// <remarks> - /// All public and protected members of <see cref="CountdownEvent"/> are thread-safe and may be used - /// concurrently from multiple threads, with the exception of Dispose, which - /// must only be used when all other operations on the <see cref="CountdownEvent"/> have - /// completed, and Reset, which should only be used when no other threads are - /// accessing the event. - /// </remarks> - [DebuggerDisplay("Initial Count={InitialCount}, Current Count={CurrentCount}")] - public class CountdownEvent : IDisposable - { - // CountdownEvent is a simple synchronization primitive used for fork/join parallelism. We create a - // latch with a count of N; threads then signal the latch, which decrements N by 1; other threads can - // wait on the latch at any point; when the latch count reaches 0, all threads are woken and - // subsequent waiters return without waiting. The implementation internally lazily creates a true - // Win32 event as needed. We also use some amount of spinning on MP machines before falling back to a - // wait. - - private int m_initialCount; // The original # of signals the latch was instantiated with. - private volatile int m_currentCount; // The # of outstanding signals before the latch transitions to a signaled state. - private ManualResetEventSlim m_event; // An event used to manage blocking and signaling. - private volatile bool m_disposed; // Whether the latch has been disposed. - - /// <summary> - /// Initializes a new instance of <see cref="T:System.Threading.CountdownEvent"/> class with the - /// specified count. - /// </summary> - /// <param name="initialCount">The number of signals required to set the <see - /// cref="T:System.Threading.CountdownEvent"/>.</param> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="initialCount"/> is less - /// than 0.</exception> - public CountdownEvent(int initialCount) - { - if (initialCount < 0) - { - throw new ArgumentOutOfRangeException(nameof(initialCount)); - } - - m_initialCount = initialCount; - m_currentCount = initialCount; - - // Allocate a thin event, which internally defers creation of an actual Win32 event. - m_event = new ManualResetEventSlim(); - - // If the latch was created with a count of 0, then it's already in the signaled state. - if (initialCount == 0) - { - m_event.Set(); - } - } - - /// <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 - { - int observedCount = m_currentCount; - return observedCount < 0 ? 0 : observedCount; - } - } - - /// <summary> - /// Gets the numbers of signals initially required to set the event. - /// </summary> - /// <value> - /// The number of signals initially required to set the event. - /// </value> - public int InitialCount - { - get { return m_initialCount; } - } - - /// <summary> - /// Determines whether the event is set. - /// </summary> - /// <value>true if the event is set; otherwise, false.</value> - public bool IsSet - { - get - { - // The latch is "completed" if its current count has reached 0. Note that this is NOT - // the same thing is checking the event's IsCompleted property. There is a tiny window - // of time, after the final decrement of the current count to 0 and before setting the - // event, where the two values are out of sync. - return (m_currentCount <= 0); - } - } - - /// <summary> - /// Gets a <see cref="T:System.Threading.WaitHandle"/> that is used to wait for the event to be set. - /// </summary> - /// <value>A <see cref="T:System.Threading.WaitHandle"/> that is used to wait for the event to be set.</value> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been disposed.</exception> - /// <remarks> - /// <see cref="WaitHandle"/> should only be used if it's needed for integration with code bases - /// that rely on having a WaitHandle. If all that's needed is to wait for the <see cref="CountdownEvent"/> - /// to be set, the <see cref="Wait()"/> method should be preferred. - /// </remarks> - public WaitHandle WaitHandle - { - get - { - ThrowIfDisposed(); - return m_event.WaitHandle; - } - } - - /// <summary> - /// Releases all resources used by the current instance of <see cref="T:System.Threading.CountdownEvent"/>. - /// </summary> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, <see cref="Dispose()"/> is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - public void Dispose() - { - // Gets rid of this latch's associated resources. This can consist of a Win32 event - // which is (lazily) allocated by the underlying thin event. This method is not safe to - // call concurrently -- i.e. a caller must coordinate to ensure only one thread is using - // the latch at the time of the call to Dispose. - - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// When overridden in a derived class, releases the unmanaged resources used by the - /// <see cref="T:System.Threading.CountdownEvent"/>, and optionally releases the managed resources. - /// </summary> - /// <param name="disposing">true to release both managed and unmanaged resources; false to release - /// only unmanaged resources.</param> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, <see cref="Dispose()"/> is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - protected virtual void Dispose(bool disposing) - { - if (disposing) - { - m_event.Dispose(); - m_disposed = true; - } - } - - /// <summary> - /// Registers a signal with the <see cref="T:System.Threading.CountdownEvent"/>, decrementing its - /// count. - /// </summary> - /// <returns>true if the signal caused the count to reach zero and the event was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.InvalidOperationException">The current instance is already set. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Signal() - { - ThrowIfDisposed(); - Debug.Assert(m_event != null); - - if (m_currentCount <= 0) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); - } -#pragma warning disable 0420 - int newCount = Interlocked.Decrement(ref m_currentCount); -#pragma warning restore 0420 - if (newCount == 0) - { - m_event.Set(); - return true; - } - else if (newCount < 0) - { - //if the count is decremented below zero, then throw, it's OK to keep the count negative, and we shouldn't set the event here - //because there was a thread already which decremented it to zero and set the event - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); - } - - return false; - } - - /// <summary> - /// Registers multiple signals with the <see cref="T:System.Threading.CountdownEvent"/>, - /// decrementing its count by the specified amount. - /// </summary> - /// <param name="signalCount">The number of signals to register.</param> - /// <returns>true if the signals caused the count to reach zero and the event was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.InvalidOperationException"> - /// The current instance is already set. -or- Or <paramref name="signalCount"/> is greater than <see - /// cref="CurrentCount"/>. - /// </exception> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less - /// than 1.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Signal(int signalCount) - { - if (signalCount <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalCount)); - } - - ThrowIfDisposed(); - Debug.Assert(m_event != null); - - int observedCount; - SpinWait spin = new SpinWait(); - while (true) - { - observedCount = m_currentCount; - - // If the latch is already signaled, we will fail. - if (observedCount < signalCount) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Decrement_BelowZero")); - } - - // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning - // for this statement. This warning is clearly senseless for Interlocked operations. -#pragma warning disable 0420 - if (Interlocked.CompareExchange(ref m_currentCount, observedCount - signalCount, observedCount) == observedCount) -#pragma warning restore 0420 - { - break; - } - - // The CAS failed. Spin briefly and try again. - spin.SpinOnce(); - } - - // If we were the last to signal, set the event. - if (observedCount == signalCount) - { - m_event.Set(); - return true; - } - - Debug.Assert(m_currentCount >= 0, "latch was decremented below zero"); - return false; - } - - /// <summary> - /// Increments the <see cref="T:System.Threading.CountdownEvent"/>'s current count by one. - /// </summary> - /// <exception cref="T:System.InvalidOperationException">The current instance is already - /// set.</exception> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException"> - /// The current instance has already been disposed. - /// </exception> - public void AddCount() - { - AddCount(1); - } - - /// <summary> - /// Attempts to increment the <see cref="T:System.Threading.CountdownEvent"/>'s current count by one. - /// </summary> - /// <returns>true if the increment succeeded; otherwise, false. If <see cref="CurrentCount"/> is - /// already at zero. this will return false.</returns> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool TryAddCount() - { - return TryAddCount(1); - } - - /// <summary> - /// Increments the <see cref="T:System.Threading.CountdownEvent"/>'s current count by a specified - /// value. - /// </summary> - /// <param name="signalCount">The value by which to increase <see cref="CurrentCount"/>.</param> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less than - /// 0.</exception> - /// <exception cref="T:System.InvalidOperationException">The current instance is already - /// set.</exception> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void AddCount(int signalCount) - { - if (!TryAddCount(signalCount)) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyZero")); - } - } - - /// <summary> - /// Attempts to increment the <see cref="T:System.Threading.CountdownEvent"/>'s current count by a - /// specified value. - /// </summary> - /// <param name="signalCount">The value by which to increase <see cref="CurrentCount"/>.</param> - /// <returns>true if the increment succeeded; otherwise, false. If <see cref="CurrentCount"/> is - /// already at zero this will return false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="signalCount"/> is less - /// than 0.</exception> - /// <exception cref="T:System.InvalidOperationException">The current instance is already - /// set.</exception> - /// <exception cref="T:System.InvalidOperationException"><see cref="CurrentCount"/> is equal to <see - /// cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool TryAddCount(int signalCount) - { - if (signalCount <= 0) - { - throw new ArgumentOutOfRangeException(nameof(signalCount)); - } - - ThrowIfDisposed(); - - // Loop around until we successfully increment the count. - int observedCount; - SpinWait spin = new SpinWait(); - while (true) - { - observedCount = m_currentCount; - - if (observedCount <= 0) - { - return false; - } - else if (observedCount > (Int32.MaxValue - signalCount)) - { - throw new InvalidOperationException(Environment.GetResourceString("CountdownEvent_Increment_AlreadyMax")); - } - - // This disables the "CS0420: a reference to a volatile field will not be treated as volatile" warning - // for this statement. This warning is clearly senseless for Interlocked operations. -#pragma warning disable 0420 - if (Interlocked.CompareExchange(ref m_currentCount, observedCount + signalCount, observedCount) == observedCount) -#pragma warning restore 0420 - { - break; - } - - // The CAS failed. Spin briefly and try again. - spin.SpinOnce(); - } - - return true; - } - - /// <summary> - /// Resets the <see cref="CurrentCount"/> to the value of <see cref="InitialCount"/>. - /// </summary> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, Reset is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed..</exception> - public void Reset() - { - Reset(m_initialCount); - } - - /// <summary> - /// Resets the <see cref="CurrentCount"/> to a specified value. - /// </summary> - /// <param name="count">The number of signals required to set the <see - /// cref="T:System.Threading.CountdownEvent"/>.</param> - /// <remarks> - /// Unlike most of the members of <see cref="CountdownEvent"/>, Reset is not - /// thread-safe and may not be used concurrently with other members of this instance. - /// </remarks> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="count"/> is - /// less than 0.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has alread been disposed.</exception> - public void Reset(int count) - { - ThrowIfDisposed(); - - if (count < 0) - { - throw new ArgumentOutOfRangeException(nameof(count)); - } - - m_currentCount = count; - m_initialCount = count; - - if (count == 0) - { - m_event.Set(); - } - else - { - m_event.Reset(); - } - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set. - /// </summary> - /// <remarks> - /// The caller of this method blocks indefinitely until the current instance is set. The caller will - /// return immediately if the event is currently in a set state. - /// </remarks> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void Wait() - { - Wait(Timeout.Infinite, new CancellationToken()); - } - - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, while - /// observing a <see cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <remarks> - /// The caller of this method blocks indefinitely until the current instance is set. The caller will - /// return immediately if the event is currently in a set state. If the - /// <see cref="T:System.Threading.CancellationToken">CancellationToken</see> being observed - /// is canceled during the wait operation, an <see cref="T:System.OperationCanceledException"/> - /// will be thrown. - /// </remarks> - /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has been - /// canceled.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void Wait(CancellationToken cancellationToken) - { - Wait(Timeout.Infinite, cancellationToken); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a - /// <see cref="T:System.TimeSpan"/> to measure the time interval. - /// </summary> - /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of - /// milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to - /// wait indefinitely.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative - /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than <see cref="System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Wait(TimeSpan timeout) - { - long totalMilliseconds = (long)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } - - return Wait((int)totalMilliseconds, new CancellationToken()); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using - /// a <see cref="T:System.TimeSpan"/> to measure the time interval, while observing a - /// <see cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="timeout">A <see cref="T:System.TimeSpan"/> that represents the number of - /// milliseconds to wait, or a <see cref="T:System.TimeSpan"/> that represents -1 milliseconds to - /// wait indefinitely.</param> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="timeout"/> is a negative - /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater - /// than <see cref="System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has - /// been canceled.</exception> - public bool Wait(TimeSpan timeout, CancellationToken cancellationToken) - { - long totalMilliseconds = (long)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new ArgumentOutOfRangeException(nameof(timeout)); - } - - return Wait((int)totalMilliseconds, cancellationToken); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a - /// 32-bit signed integer to measure the time interval. - /// </summary> - /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see - /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a - /// negative number other than -1, which represents an infinite time-out.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool Wait(int millisecondsTimeout) - { - return Wait(millisecondsTimeout, new CancellationToken()); - } - - /// <summary> - /// Blocks the current thread until the <see cref="T:System.Threading.CountdownEvent"/> is set, using a - /// 32-bit signed integer to measure the time interval, while observing a - /// <see cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see - /// cref="Timeout.Infinite"/>(-1) to wait indefinitely.</param> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <returns>true if the <see cref="System.Threading.CountdownEvent"/> was set; otherwise, - /// false.</returns> - /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a - /// negative number other than -1, which represents an infinite time-out.</exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - /// <exception cref="T:System.OperationCanceledException"><paramref name="cancellationToken"/> has - /// been canceled.</exception> - public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken) - { - if (millisecondsTimeout < -1) - { - throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); - } - - ThrowIfDisposed(); - cancellationToken.ThrowIfCancellationRequested(); - - bool returnValue = IsSet; - - // If not completed yet, wait on the event. - if (!returnValue) - { - // ** the actual wait - returnValue = m_event.Wait(millisecondsTimeout, cancellationToken); - //the Wait will throw OCE itself if the token is canceled. - } - - return returnValue; - } - - // -------------------------------------- - // Private methods - - - /// <summary> - /// Throws an exception if the latch has been disposed. - /// </summary> - private void ThrowIfDisposed() - { - if (m_disposed) - { - throw new ObjectDisposedException("CountdownEvent"); - } - } - } -} |