diff options
author | Jan Kotas <jkotas@microsoft.com> | 2015-12-03 04:17:53 +0300 |
---|---|---|
committer | Jan Kotas <jkotas@microsoft.com> | 2015-12-03 05:15:24 +0300 |
commit | 530d048456d111fa8b4d5b2be4f15e13365c8c9c (patch) | |
tree | 969b0e1f6bb54c59c81f3421bfce2dabbb8e9ab3 /src/Common | |
parent | fe9c7754c926b6914edb842f55c12945eeb94f36 (diff) |
Move System.Private.Threading implementation files from Common
These files are very System.Threading specific, not shared by any projects and unlikely to be ever shared in future
Diffstat (limited to 'src/Common')
-rw-r--r-- | src/Common/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs | 614 | ||||
-rw-r--r-- | src/Common/src/System/Threading/Barrier.cs | 943 | ||||
-rw-r--r-- | src/Common/src/System/Threading/CDSsyncETWBCLProvider.cs | 117 | ||||
-rw-r--r-- | src/Common/src/System/Threading/CountdownEvent.cs | 585 | ||||
-rw-r--r-- | src/Common/src/System/Threading/Progress.cs | 104 | ||||
-rw-r--r-- | src/Common/src/System/Threading/ReaderWriterLockSlim.cs | 1294 | ||||
-rw-r--r-- | src/Common/src/System/Threading/TimeoutHelper.cs | 53 |
7 files changed, 0 insertions, 3710 deletions
diff --git a/src/Common/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs b/src/Common/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs deleted file mode 100644 index 3cc531e92..000000000 --- a/src/Common/src/System/Collections/Concurrent/LowLevelConcurrentQueue.cs +++ /dev/null @@ -1,614 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -#pragma warning disable 0420 - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// A lock-free, concurrent queue primitive, and its associated debugger view type. -// -// This is a stripped-down version of ConcurrentQueue, for use from within the System.Threading -// surface to eliminate a dependency on System.Collections.Concurrent. -// Please try to keep this in sync with the public ConcurrentQueue implementation. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Security; -using System.Threading; - -namespace System.Collections.Concurrent -{ - /// <summary> - /// Represents a thread-safe first-in, first-out collection of objects. - /// </summary> - /// <typeparam name="T">Specifies the type of elements in the queue.</typeparam> - /// <remarks> - /// All public and protected members of <see cref="ConcurrentQueue{T}"/> are thread-safe and may be used - /// concurrently from multiple threads. - /// </remarks> - internal class LowLevelConcurrentQueue<T> /*: IProducerConsumerCollection<T>*/ : IEnumerable<T> - { - //fields of ConcurrentQueue - private volatile Segment _head; - - private volatile Segment _tail; - - private const int SEGMENT_SIZE = 32; - - //number of snapshot takers, GetEnumerator(), ToList() and ToArray() operations take snapshot. - internal volatile int m_numSnapshotTakers = 0; - - /// <summary> - /// Initializes a new instance of the <see cref="ConcurrentQueue{T}"/> class. - /// </summary> - public LowLevelConcurrentQueue() - { - _head = _tail = new Segment(0, this); - } - - /// <summary> - /// Returns an enumerator that iterates through a collection. - /// </summary> - /// <returns>An <see cref="T:System.Collections.IEnumerator"/> that can be used to iterate through the collection.</returns> - IEnumerator IEnumerable.GetEnumerator() - { - return ((IEnumerable<T>)this).GetEnumerator(); - } - - /// <summary> - /// Gets a value that indicates whether the <see cref="ConcurrentQueue{T}"/> is empty. - /// </summary> - /// <value>true if the <see cref="ConcurrentQueue{T}"/> is empty; otherwise, false.</value> - /// <remarks> - /// For determining whether the collection contains any items, use of this property is recommended - /// rather than retrieving the number of items from the <see cref="Count"/> property and comparing it - /// to 0. However, as this collection is intended to be accessed concurrently, it may be the case - /// that another thread will modify the collection after <see cref="IsEmpty"/> returns, thus invalidating - /// the result. - /// </remarks> - public bool IsEmpty - { - get - { - Segment head = _head; - if (!head.IsEmpty) - //fast route 1: - //if current head is not empty, then queue is not empty - return false; - else if (head.Next == null) - //fast route 2: - //if current head is empty and it's the last segment - //then queue is empty - return true; - else - //slow route: - //current head is empty and it is NOT the last segment, - //it means another thread is growing new segment - { - SpinWait spin = new SpinWait(); - while (head.IsEmpty) - { - if (head.Next == null) - return true; - - spin.SpinOnce(); - head = _head; - } - return false; - } - } - } - - /// <summary> - /// Store the position of the current head and tail positions. - /// </summary> - /// <param name="head">return the head segment</param> - /// <param name="tail">return the tail segment</param> - /// <param name="headLow">return the head offset, value range [0, SEGMENT_SIZE]</param> - /// <param name="tailHigh">return the tail offset, value range [-1, SEGMENT_SIZE-1]</param> - private void GetHeadTailPositions(out Segment head, out Segment tail, - out int headLow, out int tailHigh) - { - head = _head; - tail = _tail; - headLow = head.Low; - tailHigh = tail.High; - SpinWait spin = new SpinWait(); - - //we loop until the observed values are stable and sensible. - //This ensures that any update order by other methods can be tolerated. - while ( - //if head and tail changed, retry - head != _head || tail != _tail - //if low and high pointers, retry - || headLow != head.Low || tailHigh != tail.High - //if head jumps ahead of tail because of concurrent grow and dequeue, retry - || head.m_index > tail.m_index) - { - spin.SpinOnce(); - head = _head; - tail = _tail; - headLow = head.Low; - tailHigh = tail.High; - } - } - - - /// <summary> - /// Gets the number of elements contained in the <see cref="ConcurrentQueue{T}"/>. - /// </summary> - /// <value>The number of elements contained in the <see cref="ConcurrentQueue{T}"/>.</value> - /// <remarks> - /// For determining whether the collection contains any items, use of the <see cref="IsEmpty"/> - /// property is recommended rather than retrieving the number of items from the <see cref="Count"/> - /// property and comparing it to 0. - /// </remarks> - public int Count - { - get - { - //store head and tail positions in buffer, - Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); - - if (head == tail) - { - return tailHigh - headLow + 1; - } - - //head segment - int count = SEGMENT_SIZE - headLow; - - //middle segment(s), if any, are full. - //We don't deal with overflow to be consistent with the behavior of generic types in CLR. - count += SEGMENT_SIZE * ((int)(tail.m_index - head.m_index - 1)); - - //tail segment - count += tailHigh + 1; - - return count; - } - } - - /// <summary> - /// Returns an enumerator that iterates through the <see - /// cref="ConcurrentQueue{T}"/>. - /// </summary> - /// <returns>An enumerator for the contents of the <see - /// cref="ConcurrentQueue{T}"/>.</returns> - /// <remarks> - /// The enumeration represents a moment-in-time snapshot of the contents - /// of the queue. It does not reflect any updates to the collection after - /// <see cref="GetEnumerator"/> was called. The enumerator is safe to use - /// concurrently with reads from and writes to the queue. - /// </remarks> - public IEnumerator<T> GetEnumerator() - { - // Increments the number of active snapshot takers. This increment must happen before the snapshot is - // taken. At the same time, Decrement must happen after the enumeration is over. Only in this way, can it - // eliminate race condition when Segment.TryRemove() checks whether m_numSnapshotTakers == 0. - Interlocked.Increment(ref m_numSnapshotTakers); - - // Takes a snapshot of the queue. - // A design flaw here: if a Thread.Abort() happens, we cannot decrement m_numSnapshotTakers. But we cannot - // wrap the following with a try/finally block, otherwise the decrement will happen before the yield return - // statements in the GetEnumerator (head, tail, headLow, tailHigh) method. - Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); - - //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of - // the queue is not taken when GetEnumerator is initialized but when MoveNext() is first called. - // This is inconsistent with existing generic collections. In order to prevent it, we capture the - // value of m_head in a buffer and call out to a helper method. - //The old way of doing this was to return the ToList().GetEnumerator(), but ToList() was an - // unnecessary perfomance hit. - return GetEnumerator(head, tail, headLow, tailHigh); - } - - /// <summary> - /// Helper method of GetEnumerator to seperate out yield return statement, and prevent lazy evaluation. - /// </summary> - private IEnumerator<T> GetEnumerator(Segment head, Segment tail, int headLow, int tailHigh) - { - try - { - SpinWait spin = new SpinWait(); - - if (head == tail) - { - for (int i = headLow; i <= tailHigh; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!head.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return head.m_array[i]; - } - } - else - { - //iterate on head segment - for (int i = headLow; i < SEGMENT_SIZE; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!head.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return head.m_array[i]; - } - //iterate on middle segments - Segment curr = head.Next; - while (curr != tail) - { - for (int i = 0; i < SEGMENT_SIZE; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!curr.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return curr.m_array[i]; - } - curr = curr.Next; - } - - //iterate on tail segment - for (int i = 0; i <= tailHigh; i++) - { - // If the position is reserved by an Enqueue operation, but the value is not written into, - // spin until the value is available. - spin.Reset(); - while (!tail.m_state[i].m_value) - { - spin.SpinOnce(); - } - yield return tail.m_array[i]; - } - } - } - finally - { - // This Decrement must happen after the enumeration is over. - Interlocked.Decrement(ref m_numSnapshotTakers); - } - } - - /// <summary> - /// Adds an object to the end of the <see cref="ConcurrentQueue{T}"/>. - /// </summary> - /// <param name="item">The object to add to the end of the <see - /// cref="ConcurrentQueue{T}"/>. The value can be a null reference - /// (Nothing in Visual Basic) for reference types. - /// </param> - public void Enqueue(T item) - { - SpinWait spin = new SpinWait(); - while (true) - { - Segment tail = _tail; - if (tail.TryAppend(item)) - return; - spin.SpinOnce(); - } - } - - - /// <summary> - /// Attempts to remove and return the object at the beginning of the <see - /// cref="ConcurrentQueue{T}"/>. - /// </summary> - /// <param name="result"> - /// When this method returns, if the operation was successful, <paramref name="result"/> contains the - /// object removed. If no object was available to be removed, the value is unspecified. - /// </param> - /// <returns>true if an element was removed and returned from the beggining of the <see - /// cref="ConcurrentQueue{T}"/> - /// succesfully; otherwise, false.</returns> - public bool TryDequeue(out T result) - { - while (!IsEmpty) - { - Segment head = _head; - if (head.TryRemove(out result)) - return true; - //since method IsEmpty spins, we don't need to spin in the while loop - } - result = default(T); - return false; - } - - /// <summary> - /// private class for ConcurrentQueue. - /// a queue is a linked list of small arrays, each node is called a segment. - /// A segment contains an array, a pointer to the next segment, and m_low, m_high indices recording - /// the first and last valid elements of the array. - /// </summary> - private class Segment - { - //we define two volatile arrays: m_array and m_state. Note that the accesses to the array items - //do not get volatile treatment. But we don't need to worry about loading adjacent elements or - //store/load on adjacent elements would suffer reordering. - // - Two stores: these are at risk, but CLRv2 memory model guarantees store-release hence we are safe. - // - Two loads: because one item from two volatile arrays are accessed, the loads of the array references - // are sufficient to prevent reordering of the loads of the elements. - internal volatile T[] m_array; - - // For each entry in m_array, the corresponding entry in m_state indicates whether this position contains - // a valid value. m_state is initially all false. - internal volatile VolatileBool[] m_state; - - //pointer to the next segment. null if the current segment is the last segment - private volatile Segment _next; - - //We use this zero based index to track how many segments have been created for the queue, and - //to compute how many active segments are there currently. - // * The number of currently active segments is : m_tail.m_index - m_head.m_index + 1; - // * m_index is incremented with every Segment.Grow operation. We use Int64 type, and we can safely - // assume that it never overflows. To overflow, we need to do 2^63 increments, even at a rate of 4 - // billion (2^32) increments per second, it takes 2^31 seconds, which is about 64 years. - internal readonly long m_index; - - //indices of where the first and last valid values - // - m_low points to the position of the next element to pop from this segment, range [0, infinity) - // m_low >= SEGMENT_SIZE implies the segment is disposable - // - m_high points to the position of the latest pushed element, range [-1, infinity) - // m_high == -1 implies the segment is new and empty - // m_high >= SEGMENT_SIZE-1 means this segment is ready to grow. - // and the thread who sets m_high to SEGMENT_SIZE-1 is responsible to grow the segment - // - Math.Min(m_low, SEGMENT_SIZE) > Math.Min(m_high, SEGMENT_SIZE-1) implies segment is empty - // - initially m_low =0 and m_high=-1; - private volatile int _low; - private volatile int _high; - - private volatile LowLevelConcurrentQueue<T> _source; - - /// <summary> - /// Create and initialize a segment with the specified index. - /// </summary> - internal Segment(long index, LowLevelConcurrentQueue<T> source) - { - m_array = new T[SEGMENT_SIZE]; - m_state = new VolatileBool[SEGMENT_SIZE]; //all initialized to false - _high = -1; - Debug.Assert(index >= 0); - m_index = index; - _source = source; - } - - /// <summary> - /// return the next segment - /// </summary> - internal Segment Next - { - get { return _next; } - } - - - /// <summary> - /// return true if the current segment is empty (doesn't have any element available to dequeue, - /// false otherwise - /// </summary> - internal bool IsEmpty - { - get { return (Low > High); } - } - - /// <summary> - /// Add an element to the tail of the current segment - /// exclusively called by ConcurrentQueue.InitializedFromCollection - /// InitializeFromCollection is responsible to guaratee that there is no index overflow, - /// and there is no contention - /// </summary> - /// <param name="value"></param> - internal void UnsafeAdd(T value) - { - Debug.Assert(_high < SEGMENT_SIZE - 1); - _high++; - m_array[_high] = value; - m_state[_high].m_value = true; - } - - /// <summary> - /// Create a new segment and append to the current one - /// Does not update the m_tail pointer - /// exclusively called by ConcurrentQueue.InitializedFromCollection - /// InitializeFromCollection is responsible to guaratee that there is no index overflow, - /// and there is no contention - /// </summary> - /// <returns>the reference to the new Segment</returns> - internal Segment UnsafeGrow() - { - Debug.Assert(_high >= SEGMENT_SIZE - 1); - Segment newSegment = new Segment(m_index + 1, _source); //m_index is Int64, we don't need to worry about overflow - _next = newSegment; - return newSegment; - } - - /// <summary> - /// Create a new segment and append to the current one - /// Update the m_tail pointer - /// This method is called when there is no contention - /// </summary> - internal void Grow() - { - //no CAS is needed, since there is no contention (other threads are blocked, busy waiting) - Segment newSegment = new Segment(m_index + 1, _source); //m_index is Int64, we don't need to worry about overflow - _next = newSegment; - Debug.Assert(_source._tail == this); - _source._tail = _next; - } - - - /// <summary> - /// Try to append an element at the end of this segment. - /// </summary> - /// <param name="value">the element to append</param> - /// <param name="tail">The tail.</param> - /// <returns>true if the element is appended, false if the current segment is full</returns> - /// <remarks>if appending the specified element succeeds, and after which the segment is full, - /// then grow the segment</remarks> - internal bool TryAppend(T value) - { - //quickly check if m_high is already over the boundary, if so, bail out - if (_high >= SEGMENT_SIZE - 1) - { - return false; - } - - //Now we will use a CAS to increment m_high, and store the result in newhigh. - //Depending on how many free spots left in this segment and how many threads are doing this Increment - //at this time, the returning "newhigh" can be - // 1) < SEGMENT_SIZE - 1 : we took a spot in this segment, and not the last one, just insert the value - // 2) == SEGMENT_SIZE - 1 : we took the last spot, insert the value AND grow the segment - // 3) > SEGMENT_SIZE - 1 : we failed to reserve a spot in this segment, we return false to - // Queue.Enqueue method, telling it to try again in the next segment. - - int newhigh = SEGMENT_SIZE; //initial value set to be over the boundary - - //We need do Interlocked.Increment and value/state update in a finally block to ensure that they run - //without interuption. This is to prevent anything from happening between them, and another dequeue - //thread maybe spinning forever to wait for m_state[] to be true; - try - { } - finally - { - newhigh = Interlocked.Increment(ref _high); - if (newhigh <= SEGMENT_SIZE - 1) - { - m_array[newhigh] = value; - m_state[newhigh].m_value = true; - } - - //if this thread takes up the last slot in the segment, then this thread is responsible - //to grow a new segment. Calling Grow must be in the finally block too for reliability reason: - //if thread abort during Grow, other threads will be left busy spinning forever. - if (newhigh == SEGMENT_SIZE - 1) - { - Grow(); - } - } - - //if newhigh <= SEGMENT_SIZE-1, it means the current thread successfully takes up a spot - return newhigh <= SEGMENT_SIZE - 1; - } - - - /// <summary> - /// try to remove an element from the head of current segment - /// </summary> - /// <param name="result">The result.</param> - /// <param name="head">The head.</param> - /// <returns>return false only if the current segment is empty</returns> - internal bool TryRemove(out T result) - { - SpinWait spin = new SpinWait(); - int lowLocal = Low, highLocal = High; - while (lowLocal <= highLocal) - { - //try to update m_low - if (Interlocked.CompareExchange(ref _low, lowLocal + 1, lowLocal) == lowLocal) - { - //if the specified value is not available (this spot is taken by a push operation, - // but the value is not written into yet), then spin - SpinWait spinLocal = new SpinWait(); - while (!m_state[lowLocal].m_value) - { - spinLocal.SpinOnce(); - } - result = m_array[lowLocal]; - - // If there is no other thread taking snapshot (GetEnumerator(), ToList(), etc), reset the deleted entry to null. - // It is ok if after this conditional check m_numSnapshotTakers becomes > 0, because new snapshots won't include - // the deleted entry at m_array[lowLocal]. - if (_source.m_numSnapshotTakers <= 0) - { - m_array[lowLocal] = default(T); //release the reference to the object. - } - - //if the current thread sets m_low to SEGMENT_SIZE, which means the current segment becomes - //disposable, then this thread is responsible to dispose this segment, and reset m_head - if (lowLocal + 1 >= SEGMENT_SIZE) - { - // Invariant: we only dispose the current m_head, not any other segment - // In usual situation, disposing a segment is simply seting m_head to m_head.m_next - // But there is one special case, where m_head and m_tail points to the same and ONLY - //segment of the queue: Another thread A is doing Enqueue and finds that it needs to grow, - //while the *current* thread is doing *this* Dequeue operation, and finds that it needs to - //dispose the current (and ONLY) segment. Then we need to wait till thread A finishes its - //Grow operation, this is the reason of having the following while loop - spinLocal = new SpinWait(); - while (_next == null) - { - spinLocal.SpinOnce(); - } - Debug.Assert(_source._head == this); - _source._head = _next; - } - return true; - } - else - { - //CAS failed due to contention: spin briefly and retry - spin.SpinOnce(); - lowLocal = Low; highLocal = High; - } - }//end of while - result = default(T); - return false; - } - - /// <summary> - /// return the position of the head of the current segment - /// Value range [0, SEGMENT_SIZE], if it's SEGMENT_SIZE, it means this segment is exhausted and thus empty - /// </summary> - internal int Low - { - get - { - return Math.Min(_low, SEGMENT_SIZE); - } - } - - /// <summary> - /// return the logical position of the tail of the current segment - /// Value range [-1, SEGMENT_SIZE-1]. When it's -1, it means this is a new segment and has no elemnet yet - /// </summary> - internal int High - { - get - { - //if m_high > SEGMENT_SIZE, it means it's out of range, we should return - //SEGMENT_SIZE-1 as the logical position - return Math.Min(_high, SEGMENT_SIZE - 1); - } - } - } - }//end of class Segment - - /// <summary> - /// A wrapper struct for volatile bool, please note the copy of the struct it self will not be volatile - /// for example this statement will not include in volatilness operation volatileBool1 = volatileBool2 the jit will copy the struct and will ignore the volatile - /// </summary> - internal struct VolatileBool - { - public VolatileBool(bool value) - { - m_value = value; - } - public volatile bool m_value; - } -} diff --git a/src/Common/src/System/Threading/Barrier.cs b/src/Common/src/System/Threading/Barrier.cs deleted file mode 100644 index 77c171f3a..000000000 --- a/src/Common/src/System/Threading/Barrier.cs +++ /dev/null @@ -1,943 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// A barrier allows multiple tasks to cooperatively work on some algorithm in parallel. -// A group of tasks cooperate by moving through a series of phases, where each in the group signals it has arrived at -// the barrier in a given phase and implicitly waits for all others to arrive. -// The same barrier can be used for multiple phases. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; -using System.Security; - -namespace System.Threading -{ - /// <summary> - /// The exception that is thrown when the post-phase action of a <see cref="Barrier"/> fails. - /// </summary> - public class BarrierPostPhaseException : Exception - { - /// <summary> - /// Initializes a new instance of the <see cref="BarrierPostPhaseException"/> class. - /// </summary> - public BarrierPostPhaseException() - : this((string)null) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="BarrierPostPhaseException"/> class with the specified inner exception. - /// </summary> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public BarrierPostPhaseException(Exception innerException) - : this(null, innerException) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="BarrierPostPhaseException"/> class with a specified error message. - /// </summary> - /// <param name="message">A string that describes the exception.</param> - public BarrierPostPhaseException(string message) - : this(message, null) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="BarrierPostPhaseException"/> class with a specified error message and inner exception. - /// </summary> - /// <param name="message">A string that describes the exception.</param> - /// <param name="innerException">The exception that is the cause of the current exception.</param> - public BarrierPostPhaseException(string message, Exception innerException) - : base(message == null ? SR.BarrierPostPhaseException : message, innerException) - { - } - } - - - /// <summary> - /// Enables multiple tasks to cooperatively work on an algorithm in parallel through multiple phases. - /// </summary> - /// <remarks> - /// <para> - /// A group of tasks cooperate by moving through a series of phases, where each in the group signals it - /// has arrived at the <see cref="Barrier"/> in a given phase and implicitly waits for all others to - /// arrive. The same <see cref="Barrier"/> can be used for multiple phases. - /// </para> - /// <para> - /// All public and protected members of <see cref="Barrier"/> 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="Barrier"/> have - /// completed. - /// </para> - /// </remarks> - [ComVisible(false)] - [DebuggerDisplay("Participant Count={ParticipantCount},Participants Remaining={ParticipantsRemaining}")] - public class Barrier : IDisposable - { - //This variable holds the basic barrier variables: - // 1- The current particiants count - // 2- The total participants count - // 3- The sense flag (true if the cuurrent phase is even, false otherwise) - // The first 15 bits are for the total count which means the maximum participants for the barrier is about 32K - // The 16th bit is dummy - // The next 15th bit for the current - // And the last highest bit is for the sense - private volatile int _currentTotalCount; - - // Bitmask to extract the current count - private const int CURRENT_MASK = 0x7FFF0000; - - // Bitmask to extract the total count - private const int TOTAL_MASK = 0x00007FFF; - - // Bitmask to extratc the sense flag - private const int SENSE_MASK = unchecked((int)0x80000000); - - // The maximum participants the barrier can operate = 32767 ( 2 power 15 - 1 ) - private const int MAX_PARTICIPANTS = TOTAL_MASK; - - - // The current barrier phase - // We don't need to worry about overflow, the max value is 2^63-1; If it starts from 0 at a - // rate of 4 billion increments per second, it will takes about 64 years to overflow. - private long _currentPhase; - - - // dispose flag - private bool _disposed; - - // Odd phases event - private ManualResetEventSlim _oddEvent; - - // Even phases event - private ManualResetEventSlim _evenEvent; - - // The execution context of the creator thread - private ExecutionContext _ownerThreadContext; - - // The EC callback that invokes the post phase action - [SecurityCritical] - private static ContextCallback s_invokePostPhaseAction; - - // Post phase action after each phase - private Action<Barrier> _postPhaseAction; - - // In case the post phase action throws an exception, wraps it in BarrierPostPhaseException - private Exception _exception; - - // This is the ManagedThreadID of the postPhaseAction caller thread, this is used to determine if the SignalAndWait, Dispose or Add/RemoveParticipant caller thread is - // the same thread as the postPhaseAction thread which means this method was called from the postPhaseAction which is illegal. - // This value is captured before calling the action and reset back to zero after it. - private int _actionCallerID; - - #region Properties - - /// <summary> - /// Gets the number of participants in the barrier that haven’t yet signaled - /// in the current phase. - /// </summary> - /// <remarks> - /// This could be 0 during a post-phase action delegate execution or if the - /// ParticipantCount is 0. - /// </remarks> - public int ParticipantsRemaining - { - get - { - int currentTotal = _currentTotalCount; - int total = (int)(currentTotal & TOTAL_MASK); - int current = (int)((currentTotal & CURRENT_MASK) >> 16); - return total - current; - } - } - - /// <summary> - /// Gets the total number of participants in the barrier. - /// </summary> - public int ParticipantCount - { - get { return (int)(_currentTotalCount & TOTAL_MASK); } - } - - /// <summary> - /// Gets the number of the barrier's current phase. - /// </summary> - public long CurrentPhaseNumber - { - // use the new Volatile.Read/Write method because it is cheaper than Interlocked.Read on AMD64 architecture - get { return Volatile.Read(ref _currentPhase); } - - internal set { Volatile.Write(ref _currentPhase, value); } - } - - #endregion - - /// <summary> - /// Initializes a new instance of the <see cref="Barrier"/> class. - /// </summary> - /// <param name="participantCount">The number of participating threads.</param> - /// <exception cref="ArgumentOutOfRangeException"> <paramref name="participantCount"/> is less than 0 - /// or greater than <see cref="T:System.Int16.MaxValue"/>.</exception> - public Barrier(int participantCount) - : this(participantCount, null) - { - } - - /// <summary> - /// Initializes a new instance of the <see cref="Barrier"/> class. - /// </summary> - /// <param name="participantCount">The number of participating threads.</param> - /// <param name="postPhaseAction">The <see cref="T:System.Action`1"/> to be executed after each - /// phase.</param> - /// <exception cref="T:System.ArgumentOutOfRangeException"> <paramref name="participantCount"/> is less than 0 - /// or greater than <see cref="T:System.Int32.MaxValue"/>.</exception> - /// <remarks> - /// The <paramref name="postPhaseAction"/> delegate will be executed after - /// all participants have arrived at the barrier in one phase. The participants - /// will not be released to the next phase until the postPhaseAction delegate - /// has completed execution. - /// </remarks> - public Barrier(int participantCount, Action<Barrier> postPhaseAction) - { - // the count must be non negative value - if (participantCount < 0 || participantCount > MAX_PARTICIPANTS) - { - throw new ArgumentOutOfRangeException("participantCount", participantCount, SR.Barrier_ctor_ArgumentOutOfRange); - } - _currentTotalCount = (int)participantCount; - _postPhaseAction = postPhaseAction; - - //Lazily initialize the events - _oddEvent = new ManualResetEventSlim(true); - _evenEvent = new ManualResetEventSlim(false); - - // Capture the context if the post phase action is not null - if (postPhaseAction != null) - { - _ownerThreadContext = ExecutionContext.Capture(); - } - - _actionCallerID = 0; - } - - /// <summary> - /// Extract the three variables current, total and sense from a given big variable - /// </summary> - /// <param name="currentTotal">The integer variable that contains the other three variables</param> - /// <param name="current">The current cparticipant count</param> - /// <param name="total">The total participants count</param> - /// <param name="sense">The sense flag</param> - private void GetCurrentTotal(int currentTotal, out int current, out int total, out bool sense) - { - total = (int)(currentTotal & TOTAL_MASK); - current = (int)((currentTotal & CURRENT_MASK) >> 16); - sense = (currentTotal & SENSE_MASK) == 0 ? true : false; - } - - /// <summary> - /// Write the three variables current. total and the sense to the m_currentTotal - /// </summary> - /// <param name="currentTotal">The old current total to compare</param> - /// <param name="current">The current cparticipant count</param> - /// <param name="total">The total participants count</param> - /// <param name="sense">The sense flag</param> - /// <returns>True if the CAS succeeded, false otherwise</returns> - private bool SetCurrentTotal(int currentTotal, int current, int total, bool sense) - { - int newCurrentTotal = (current << 16) | total; - - if (!sense) - { - newCurrentTotal |= SENSE_MASK; - } - -#pragma warning disable 0420 - return Interlocked.CompareExchange(ref _currentTotalCount, newCurrentTotal, currentTotal) == currentTotal; -#pragma warning restore 0420 - } - - /// <summary> - /// Notifies the <see cref="Barrier"/> that there will be an additional participant. - /// </summary> - /// <returns>The phase number of the barrier in which the new participants will first - /// participate.</returns> - /// <exception cref="T:System.InvalidOperationException"> - /// Adding a participant would cause the barrier's participant count to - /// exceed <see cref="T:System.Int16.MaxValue"/>. - /// </exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public long AddParticipant() - { - try - { - return AddParticipants(1); - } - catch (ArgumentOutOfRangeException) - { - throw new InvalidOperationException(SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange); - } - } - - /// <summary> - /// Notifies the <see cref="Barrier"/> that there will be additional participants. - /// </summary> - /// <param name="participantCount">The number of additional participants to add to the - /// barrier.</param> - /// <returns>The phase number of the barrier in which the new participants will first - /// participate.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="participantCount"/> is less than - /// 0.</exception> - /// <exception cref="T:System.ArgumentOutOfRangeException">Adding <paramref name="participantCount"/> participants would cause the - /// barrier's participant count to exceed <see cref="T:System.Int16.MaxValue"/>.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public long AddParticipants(int participantCount) - { - // check dispose - ThrowIfDisposed(); - - if (participantCount < 1) - { - throw new ArgumentOutOfRangeException("participantCount", participantCount, - SR.Barrier_AddParticipants_NonPositive_ArgumentOutOfRange); - } - else if (participantCount > MAX_PARTICIPANTS) //overflow - { - throw new ArgumentOutOfRangeException("participantCount", - SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange); - } - - // in case of this is called from the PHA - if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID) - { - throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA); - } - - SpinWait spinner = new SpinWait(); - long newPhase = 0; - while (true) - { - int currentTotal = _currentTotalCount; - int total; - int current; - bool sense; - GetCurrentTotal(currentTotal, out current, out total, out sense); - if (participantCount + total > MAX_PARTICIPANTS) //overflow - { - throw new ArgumentOutOfRangeException("participantCount", - SR.Barrier_AddParticipants_Overflow_ArgumentOutOfRange); - } - - if (SetCurrentTotal(currentTotal, current, total + participantCount, sense)) - { - // Calculating the first phase for that participant, if the current phase already finished return the nextphase else return the current phase - // To know that the current phase is the sense doesn't match the - // phase odd even, so that means it didn't yet change the phase count, so currentPhase +1 is returned, otherwise currentPhase is returned - long currPhase = CurrentPhaseNumber; - newPhase = (sense != (currPhase % 2 == 0)) ? currPhase + 1 : currPhase; - - // If this participant is going to join the next phase, which means the postPhaseAction is being running, this participants must wait until this done - // and its event is reset. - // Without that, if the postPhaseAction takes long time, this means the event ehich the current participant is goint to wait on is still set - // (FinishPPhase didn't reset it yet) so it should wait until it reset - if (newPhase != currPhase) - { - // Wait on the opposite event - if (sense) - { - _oddEvent.Wait(); - } - else - { - _evenEvent.Wait(); - } - } - - //This else to fix the racing where the current phase has been finished, m_currentPhase has been updated but the events have not been set/reset yet - // otherwise when this participant calls SignalAndWait it will wait on a set event however all other participants have not arrived yet. - else - { - if (sense && _evenEvent.IsSet) - _evenEvent.Reset(); - else if (!sense && _oddEvent.IsSet) - _oddEvent.Reset(); - } - break; - } - spinner.SpinOnce(); - } - return newPhase; - } - - /// <summary> - /// Notifies the <see cref="Barrier"/> that there will be one less participant. - /// </summary> - /// <exception cref="T:System.InvalidOperationException">The barrier already has 0 - /// participants.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void RemoveParticipant() - { - RemoveParticipants(1); - } - - /// <summary> - /// Notifies the <see cref="Barrier"/> that there will be fewer participants. - /// </summary> - /// <param name="participantCount">The number of additional participants to remove from the barrier.</param> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="participantCount"/> is less than - /// 0.</exception> - /// <exception cref="T:System.InvalidOperationException">The barrier already has 0 participants.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void RemoveParticipants(int participantCount) - { - // check dispose - ThrowIfDisposed(); - - // Validate input - if (participantCount < 1) - { - throw new ArgumentOutOfRangeException("participantCount", participantCount, - SR.Barrier_RemoveParticipants_NonPositive_ArgumentOutOfRange); - } - - // in case of this is called from the PHA - if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID) - { - throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA); - } - - SpinWait spinner = new SpinWait(); - while (true) - { - int currentTotal = _currentTotalCount; - int total; - int current; - bool sense; - GetCurrentTotal(currentTotal, out current, out total, out sense); - - if (total < participantCount) - { - throw new ArgumentOutOfRangeException("participantCount", - SR.Barrier_RemoveParticipants_ArgumentOutOfRange); - } - if (total - participantCount < current) - { - throw new InvalidOperationException(SR.Barrier_RemoveParticipants_InvalidOperation); - } - // If the remaining participats = current participants, then finish the current phase - int remaingParticipants = total - participantCount; - if (remaingParticipants > 0 && current == remaingParticipants) - { - if (SetCurrentTotal(currentTotal, 0, total - participantCount, !sense)) - { - FinishPhase(sense); - break; - } - } - else - { - if (SetCurrentTotal(currentTotal, current, total - participantCount, sense)) - { - break; - } - } - spinner.SpinOnce(); - } - } - - /// <summary> - /// Signals that a participant has reached the <see cref="Barrier"/> and waits for all other - /// participants to reach the barrier as well. - /// </summary> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, - /// or the barrier is being used by more threads than are registered as participants. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public void SignalAndWait() - { - SignalAndWait(new CancellationToken()); - } - - /// <summary> - /// Signals that a participant has reached the <see cref="Barrier"/> and waits for all other - /// participants to reach the barrier, while observing a <see - /// cref="T:System.Threading.CancellationToken"/>. - /// </summary> - /// <param name="cancellationToken">The <see cref="T:System.Threading.CancellationToken"/> to - /// observe.</param> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, - /// or the barrier is being used by more threads than are registered as participants. - /// </exception> - /// <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 SignalAndWait(CancellationToken cancellationToken) - { -#if DEBUG - bool result = -#endif - SignalAndWait(Timeout.Infinite, cancellationToken); -#if DEBUG - Debug.Assert(result); -#endif - } - - /// <summary> - /// Signals that a participant has reached the <see cref="Barrier"/> and waits for all other - /// participants to reach the barrier as well, 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 all other participants reached the barrier; 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 it is greater than - /// <see cref="T:System.Int32.MaxValue"/>.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, - /// or the barrier is being used by more threads than are registered as participants. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public Boolean SignalAndWait(TimeSpan timeout) - { - return SignalAndWait(timeout, new CancellationToken()); - } - - /// <summary> - /// Signals that a participant has reached the <see cref="Barrier"/> and waits for all other - /// participants to reach the barrier as well, 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 all other participants reached the barrier; 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.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, - /// or the barrier is being used by more threads than are registered as participants. - /// </exception> - /// <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 Boolean SignalAndWait(TimeSpan timeout, CancellationToken cancellationToken) - { - Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) - { - throw new System.ArgumentOutOfRangeException("timeout", timeout, - SR.Barrier_SignalAndWait_ArgumentOutOfRange); - } - return SignalAndWait((int)timeout.TotalMilliseconds, cancellationToken); - } - - /// <summary> - /// Signals that a participant has reached the <see cref="Barrier"/> and waits for all other - /// participants to reach the barrier as well, 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 all other participants reached the barrier; otherwise, false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a - /// negative number other than -1, which represents an infinite time-out.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, - /// or the barrier is being used by more threads than are registered as participants. - /// </exception> - /// <exception cref="T:System.ObjectDisposedException">The current instance has already been - /// disposed.</exception> - public bool SignalAndWait(int millisecondsTimeout) - { - return SignalAndWait(millisecondsTimeout, new CancellationToken()); - } - - /// <summary> - /// Signals that a participant has reached the barrier and waits for all other participants to reach - /// the barrier as well, 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 all other participants reached the barrier; otherwise, false.</returns> - /// <exception cref="T:System.ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a - /// negative number other than -1, which represents an infinite time-out.</exception> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action, the barrier currently has 0 participants, - /// or the barrier is being used by more threads than are registered as participants. - /// </exception> - /// <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 bool SignalAndWait(int millisecondsTimeout, CancellationToken cancellationToken) - { - ThrowIfDisposed(); - cancellationToken.ThrowIfCancellationRequested(); - - if (millisecondsTimeout < -1) - { - throw new System.ArgumentOutOfRangeException("millisecondsTimeout", millisecondsTimeout, - SR.Barrier_SignalAndWait_ArgumentOutOfRange); - } - - // in case of this is called from the PHA - if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID) - { - throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA); - } - - // local variables to extract the basic barrier variable and update them - // The are declared here instead of inside the loop body because the will be used outside the loop - bool sense; // The sense of the barrier *before* the phase associated with this SignalAndWait call completes - int total; - int current; - int currentTotal; - long phase; - SpinWait spinner = new SpinWait(); - while (true) - { - currentTotal = _currentTotalCount; - GetCurrentTotal(currentTotal, out current, out total, out sense); - phase = CurrentPhaseNumber; - // throw if zero participants - if (total == 0) - { - throw new InvalidOperationException(SR.Barrier_SignalAndWait_InvalidOperation_ZeroTotal); - } - // Try to detect if the number of threads for this phase exceeded the total number of participants or not - // This can be detected if the current is zero which means all participants for that phase has arrived and the phase number is not changed yet - if (current == 0 && sense != (CurrentPhaseNumber % 2 == 0)) - { - throw new InvalidOperationException(SR.Barrier_SignalAndWait_InvalidOperation_ThreadsExceeded); - } - //This is the last thread, finish the phase - if (current + 1 == total) - { - if (SetCurrentTotal(currentTotal, 0, total, !sense)) - { - if (CdsSyncEtwBCLProvider.Log.IsEnabled()) - { - CdsSyncEtwBCLProvider.Log.Barrier_PhaseFinished(sense, CurrentPhaseNumber); - } - FinishPhase(sense); - return true; - } - } - else if (SetCurrentTotal(currentTotal, current + 1, total, sense)) - { - break; - } - - spinner.SpinOnce(); - } - - // ** Perform the real wait ** - // select the correct event to wait on, based on the current sense. - ManualResetEventSlim eventToWaitOn = (sense) ? _evenEvent : _oddEvent; - - bool waitWasCanceled = false; - bool waitResult = false; - try - { - waitResult = DiscontinuousWait(eventToWaitOn, millisecondsTimeout, cancellationToken, phase); - } - catch (OperationCanceledException) - { - waitWasCanceled = true; - } - catch (ObjectDisposedException)// in case a race happen where one of the thread returned from SignalAndWait and the current thread calls Wait on a disposed event - { - // make sure the current phase for this thread is already finished, otherwise propagate the exception - if (phase < CurrentPhaseNumber) - waitResult = true; - else - throw; - } - - - - if (!waitResult) - { - //reset the spinLock to prepare it for the next loop - spinner.Reset(); - - //If the wait timeout expired and all other thread didn't reach the barrier yet, update the current count back - while (true) - { - bool newSense; - currentTotal = _currentTotalCount; - GetCurrentTotal(currentTotal, out current, out total, out newSense); - // If the timeout expired and the phase has just finished, return true and this is considered as succeeded SignalAndWait - //otherwise the timeout expired and the current phase has not been finished yet, return false - //The phase is finished if the phase member variable is changed (incremented) or the sense has been changed - // we have to use the statements in the comparison below for two cases: - // 1- The sense is changed but the last thread didn't update the phase yet - // 2- The phase is already incremented but the sense flipped twice due to the termination of the next phase - if (phase < CurrentPhaseNumber || sense != newSense) - { - // The current phase has been finished, but we shouldn't return before the events are set/reset otherwise this thread could start - // next phase and the appropriate event has not reset yet which could make it return immediately from the next phase SignalAndWait - // before waiting other threads - WaitCurrentPhase(eventToWaitOn, phase); - Debug.Assert(phase < CurrentPhaseNumber); - break; - } - //The phase has not been finished yet, try to update the current count. - if (SetCurrentTotal(currentTotal, current - 1, total, sense)) - { - //if here, then the attempt to backout was successful. - //throw (a fresh) oce if cancellation woke the wait - //or return false if it was the timeout that woke the wait. - // - if (waitWasCanceled) - throw new OperationCanceledException(SR.Common_OperationCanceled, cancellationToken); - else - return false; - } - spinner.SpinOnce(); - } - } - - if (_exception != null) - throw new BarrierPostPhaseException(_exception); - - return true; - } - - /// <summary> - /// Finish the phase by invoking the post phase action, and setting the event, this must be called by the - /// last arrival thread - /// </summary> - /// <param name="observedSense">The current phase sense</param> - [SecuritySafeCritical] - private void FinishPhase(bool observedSense) - { - // Execute the PHA in try/finally block to reset the variables back in case of it threw an exception - if (_postPhaseAction != null) - { - try - { - // Capture the caller thread ID to check if the Add/RemoveParticipant(s) is called from the PHA - _actionCallerID = Environment.CurrentManagedThreadId; - if (_ownerThreadContext != null) - { - var currentContext = _ownerThreadContext; - - ContextCallback handler = s_invokePostPhaseAction; - if (handler == null) - { - s_invokePostPhaseAction = handler = InvokePostPhaseAction; - } - ExecutionContext.Run(_ownerThreadContext, handler, this); - } - else - { - _postPhaseAction(this); - } - - _exception = null; // reset the exception if it was set previously - } - catch (Exception ex) - { - _exception = ex; - } - finally - { - _actionCallerID = 0; - SetResetEvents(observedSense); - if (_exception != null) - throw new BarrierPostPhaseException(_exception); - } - } - else - { - SetResetEvents(observedSense); - } - } - - /// <summary> - /// Helper method to call the post phase action - /// </summary> - /// <param name="obj"></param> - [SecurityCritical] - private static void InvokePostPhaseAction(object obj) - { - var thisBarrier = (Barrier)obj; - thisBarrier._postPhaseAction(thisBarrier); - } - - /// <summary> - /// Sets the current phase event and reset the next phase event - /// </summary> - /// <param name="observedSense">The current phase sense</param> - private void SetResetEvents(bool observedSense) - { - // Increment the phase count using Volatile class because m_currentPhase is 64 bit long type, that could cause torn write on 32 bit machines - CurrentPhaseNumber = CurrentPhaseNumber + 1; - if (observedSense) - { - _oddEvent.Reset(); - _evenEvent.Set(); - } - else - { - _evenEvent.Reset(); - _oddEvent.Set(); - } - } - - /// <summary> - /// Wait until the current phase finishes completely by spinning until either the event is set, - /// or the phase count is incremented more than one time - /// </summary> - /// <param name="currentPhaseEvent">The current phase event</param> - /// <param name="observedPhase">The current phase for that thread</param> - private void WaitCurrentPhase(ManualResetEventSlim currentPhaseEvent, long observedPhase) - { - //spin until either of these two conditions succeeds - //1- The event is set - //2- the phase count is incremented more than one time, this means the next phase is finished as well, - //but the event will be reset again, so we check the phase count instead - SpinWait spinner = new SpinWait(); - while (!currentPhaseEvent.IsSet && CurrentPhaseNumber - observedPhase <= 1) - { - spinner.SpinOnce(); - } - } - - /// <summary> - /// The reason of discontinuous waiting instead of direct waiting on the event is to avoid the race where the sense is - /// changed twice because the next phase is finished (due to either RemoveParticipant is called or another thread joined - /// the next phase instead of the current thread) so the current thread will be stuck on the event because it is reset back - /// The maxwait and the shift numbers are arbitrarily choosen, there were no references picking them - /// </summary> - /// <param name="currentPhaseEvent">The current phase event</param> - /// <param name="totalTimeout">wait timeout in milliseconds</param> - /// <param name="token">cancellation token passed to SignalAndWait</param> - /// <param name="observedPhase">The current phase number for this thread</param> - /// <returns>True if the event is set or the phasenumber changed, false if the timeout expired</returns> - private bool DiscontinuousWait(ManualResetEventSlim currentPhaseEvent, int totalTimeout, CancellationToken token, long observedPhase) - { - int maxWait = 100; // 100 ms - int waitTimeCeiling = 10000; // 10 seconds - while (observedPhase == CurrentPhaseNumber) - { - // the next wait time, the min of the maxWait and the totalTimeout - int waitTime = totalTimeout == Timeout.Infinite ? maxWait : Math.Min(maxWait, totalTimeout); - - if (currentPhaseEvent.Wait(waitTime, token)) - return true; - - //update the total wait time - if (totalTimeout != Timeout.Infinite) - { - totalTimeout -= waitTime; - if (totalTimeout <= 0) - return false; - } - - //if the maxwait exceeded 10 seconds then we will stop increasing the maxWait time and keep it 10 seconds, otherwise keep doubling it - maxWait = maxWait >= waitTimeCeiling ? waitTimeCeiling : Math.Min(maxWait << 1, waitTimeCeiling); - } - - //if we exited the loop because the observed phase doesn't match the current phase, then we have to spin to mske sure - //the event is set or the next phase is finished - WaitCurrentPhase(currentPhaseEvent, observedPhase); - - return true; - } - - /// <summary> - /// Releases all resources used by the current instance of <see cref="Barrier"/>. - /// </summary> - /// <exception cref="T:System.InvalidOperationException"> - /// The method was invoked from within a post-phase action. - /// </exception> - /// <remarks> - /// Unlike most of the members of <see cref="Barrier"/>, Dispose is not thread-safe and may not be - /// used concurrently with other members of this instance. - /// </remarks> - public void Dispose() - { - // in case of this is called from the PHA - if (_actionCallerID != 0 && Environment.CurrentManagedThreadId == _actionCallerID) - { - throw new InvalidOperationException(SR.Barrier_InvalidOperation_CalledFromPHA); - } - Dispose(true); - GC.SuppressFinalize(this); - } - - /// <summary> - /// When overridden in a derived class, releases the unmanaged resources used by the - /// <see cref="Barrier"/>, 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="Barrier"/>, 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 (!_disposed) - { - if (disposing) - { - _oddEvent.Dispose(); - _evenEvent.Dispose(); - } - _disposed = true; - } - } - - /// <summary> - /// Throw ObjectDisposedException if the barrier is disposed - /// </summary> - private void ThrowIfDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException("Barrier", SR.Barrier_Dispose); - } - } - } -} diff --git a/src/Common/src/System/Threading/CDSsyncETWBCLProvider.cs b/src/Common/src/System/Threading/CDSsyncETWBCLProvider.cs deleted file mode 100644 index b42450aaa..000000000 --- a/src/Common/src/System/Threading/CDSsyncETWBCLProvider.cs +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// A helper class for firing ETW events related to the Coordination Data Structure -// sync primitives. This provider is used by CDS sync primitives in both mscorlib.dll -// and system.dll. The purpose of sharing the provider class is to be able to enable -// ETW tracing on all CDS sync types with a single ETW provider GUID, and to minimize -// the number of providers in use. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Collections.Generic; -using System.Text; -using System.Security; -using System.Diagnostics.Tracing; - -namespace System.Threading -{ - /// <summary>Provides an event source for tracing CDS synchronization information.</summary> - [EventSource( - Name = "System.Threading.SynchronizationEventSource", - Guid = "EC631D38-466B-4290-9306-834971BA0217" - //TODO:(TFS455853):Add support for reading localized string in the EventSource il2il transform - //,LocalizationResources = "mscorlib" - )] - internal sealed class CdsSyncEtwBCLProvider : EventSource - { - /// <summary> - /// Defines the singleton instance for the CDS Sync ETW provider. - /// The CDS Sync Event provider GUID is {EC631D38-466B-4290-9306-834971BA0217}. - /// </summary> - public static CdsSyncEtwBCLProvider Log = new CdsSyncEtwBCLProvider(); - /// <summary>Prevent external instantiation. All logging should go through the Log instance.</summary> - private CdsSyncEtwBCLProvider() { } - - /// <summary>Enabled for all keywords.</summary> - private const EventKeywords ALL_KEYWORDS = (EventKeywords)(-1); - - //----------------------------------------------------------------------------------- - // - // CDS Synchronization Event IDs (must be unique) - // - - private const int SPINLOCK_FASTPATHFAILED_ID = 1; - private const int SPINWAIT_NEXTSPINWILLYIELD_ID = 2; - private const int BARRIER_PHASEFINISHED_ID = 3; - - ///////////////////////////////////////////////////////////////////////////////////// - // - // SpinLock Events - // - - [Event(SPINLOCK_FASTPATHFAILED_ID, Level = EventLevel.Warning)] - public void SpinLock_FastPathFailed(int ownerID) - { - if (IsEnabled(EventLevel.Warning, ALL_KEYWORDS)) - { - WriteEvent(SPINLOCK_FASTPATHFAILED_ID, ownerID); - } - } - - ///////////////////////////////////////////////////////////////////////////////////// - // - // SpinWait Events - // - - [Event(SPINWAIT_NEXTSPINWILLYIELD_ID, Level = EventLevel.Informational)] - public void SpinWait_NextSpinWillYield() - { - if (IsEnabled(EventLevel.Informational, ALL_KEYWORDS)) - { - WriteEvent(SPINWAIT_NEXTSPINWILLYIELD_ID); - } - } - - - // - // Events below this point are used by the CDS types in System.dll - // - - ///////////////////////////////////////////////////////////////////////////////////// - // - // Barrier Events - // - - [SecuritySafeCritical] - [Event(BARRIER_PHASEFINISHED_ID, Level = EventLevel.Verbose, Version = 1)] - public void Barrier_PhaseFinished(bool currentSense, long phaseNum) - { - if (IsEnabled(EventLevel.Verbose, ALL_KEYWORDS)) - { - // WriteEvent(BARRIER_PHASEFINISHED_ID, currentSense, phaseNum); - - // There is no explicit WriteEvent() overload matching this event's bool+long fields. - // Therefore calling WriteEvent() would hit the "params" overload, which leads to an - // object allocation every time this event is fired. To prevent that problem we will - // call WriteEventCore(), which works with a stack based EventData array populated with - // the event fields. - unsafe - { - EventData* eventPayload = stackalloc EventData[2]; - - Int32 senseAsInt32 = currentSense ? 1 : 0; // write out Boolean as Int32 - eventPayload[0].Size = sizeof(int); - eventPayload[0].DataPointer = ((IntPtr)(&senseAsInt32)); - eventPayload[1].Size = sizeof(long); - eventPayload[1].DataPointer = ((IntPtr)(&phaseNum)); - - WriteEventCore(BARRIER_PHASEFINISHED_ID, 2, eventPayload); - } - } - } - } -} diff --git a/src/Common/src/System/Threading/CountdownEvent.cs b/src/Common/src/System/Threading/CountdownEvent.cs deleted file mode 100644 index d27caa9f8..000000000 --- a/src/Common/src/System/Threading/CountdownEvent.cs +++ /dev/null @@ -1,585 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -// -// A simple coordination data structure that we use for fork/join style parallelism. -// -// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- - -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Threading; - -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> - [ComVisible(false)] - [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 _initialCount; // The original # of signals the latch was instantiated with. - private volatile int _currentCount; // The # of outstanding signals before the latch transitions to a signaled state. - private ManualResetEventSlim _event; // An event used to manage blocking and signaling. - private volatile bool _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("initialCount"); - } - - _initialCount = initialCount; - _currentCount = initialCount; - - // Allocate a thin event, which internally defers creation of an actual Win32 event. - _event = new ManualResetEventSlim(); - - // If the latch was created with a count of 0, then it's already in the signaled state. - if (initialCount == 0) - { - _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 = _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 _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 (_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 _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) - { - _event.Dispose(); - _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(_event != null); - - if (_currentCount <= 0) - { - throw new InvalidOperationException(SR.CountdownEvent_Decrement_BelowZero); - } -#pragma warning disable 0420 - int newCount = Interlocked.Decrement(ref _currentCount); -#pragma warning restore 0420 - if (newCount == 0) - { - _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(SR.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("signalCount"); - } - - ThrowIfDisposed(); - Debug.Assert(_event != null); - - int observedCount; - SpinWait spin = new SpinWait(); - while (true) - { - observedCount = _currentCount; - - // If the latch is already signaled, we will fail. - if (observedCount < signalCount) - { - throw new InvalidOperationException(SR.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 _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) - { - _event.Set(); - return true; - } - - Debug.Assert(_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(SR.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("signalCount"); - } - - ThrowIfDisposed(); - - // Loop around until we successfully increment the count. - int observedCount; - SpinWait spin = new SpinWait(); - while (true) - { - observedCount = _currentCount; - - if (observedCount <= 0) - { - return false; - } - else if (observedCount > (Int32.MaxValue - signalCount)) - { - throw new InvalidOperationException(SR.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 _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(_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("count"); - } - - _currentCount = count; - _initialCount = count; - - if (count == 0) - { - _event.Set(); - } - else - { - _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("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("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("millisecondsTimeout"); - } - - ThrowIfDisposed(); - cancellationToken.ThrowIfCancellationRequested(); - - bool returnValue = IsSet; - - // If not completed yet, wait on the event. - if (!returnValue) - { - // ** the actual wait - returnValue = _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 (_disposed) - { - throw new ObjectDisposedException("CountdownEvent"); - } - } - } -} diff --git a/src/Common/src/System/Threading/Progress.cs b/src/Common/src/System/Threading/Progress.cs deleted file mode 100644 index 4b1f64f1c..000000000 --- a/src/Common/src/System/Threading/Progress.cs +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Threading; -using System.Diagnostics; - -namespace System -{ - /// <summary> - /// Provides an IProgress{T} that invokes callbacks for each reported progress value. - /// </summary> - /// <typeparam name="T">Specifies the type of the progress report value.</typeparam> - /// <remarks> - /// Any handler provided to the constructor or event handlers registered with - /// the <see cref="ProgressChanged"/> event are invoked through a - /// <see cref="System.Threading.SynchronizationContext"/> instance captured - /// when the instance is constructed. If there is no current SynchronizationContext - /// at the time of construction, the callbacks will be invoked on the ThreadPool. - /// </remarks> - public class Progress<T> : IProgress<T> - { - /// <summary>The synchronization context captured upon construction. This will never be null.</summary> - private readonly SynchronizationContext _synchronizationContext; - /// <summary>The handler specified to the constructor. This may be null.</summary> - private readonly Action<T> _handler; - /// <summary>A cached delegate used to post invocation to the synchronization context.</summary> - private readonly SendOrPostCallback _invokeHandlers; - - /// <summary>Initializes the <see cref="Progress{T}"/>.</summary> - public Progress() - { - // Capture the current synchronization context. - // If there is no current context, we use a default instance targeting the ThreadPool. - _synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext; - Debug.Assert(_synchronizationContext != null); - _invokeHandlers = new SendOrPostCallback(InvokeHandlers); - } - - /// <summary>Initializes the <see cref="Progress{T}"/> with the specified callback.</summary> - /// <param name="handler"> - /// A handler to invoke for each reported progress value. This handler will be invoked - /// in addition to any delegates registered with the <see cref="ProgressChanged"/> event. - /// Depending on the <see cref="System.Threading.SynchronizationContext"/> instance captured by - /// the <see cref="Progress"/> at construction, it's possible that this handler instance - /// could be invoked concurrently with itself. - /// </param> - /// <exception cref="System.ArgumentNullException">The <paramref name="handler"/> is null (Nothing in Visual Basic).</exception> - public Progress(Action<T> handler) : this() - { - if (handler == null) throw new ArgumentNullException("handler"); - _handler = handler; - } - - /// <summary>Raised for each reported progress value.</summary> - /// <remarks> - /// Handlers registered with this event will be invoked on the - /// <see cref="System.Threading.SynchronizationContext"/> captured when the instance was constructed. - /// </remarks> - public event EventHandler<T> ProgressChanged; - - /// <summary>Reports a progress change.</summary> - /// <param name="value">The value of the updated progress.</param> - protected virtual void OnReport(T value) - { - // If there's no handler, don't bother going through the sync context. - // Inside the callback, we'll need to check again, in case - // an event handler is removed between now and then. - Action<T> handler = _handler; - EventHandler<T> changedEvent = ProgressChanged; - if (handler != null || changedEvent != null) - { - // Post the processing to the sync context. - // (If T is a value type, it will get boxed here.) - _synchronizationContext.Post(_invokeHandlers, value); - } - } - - /// <summary>Reports a progress change.</summary> - /// <param name="value">The value of the updated progress.</param> - void IProgress<T>.Report(T value) { OnReport(value); } - - /// <summary>Invokes the action and event callbacks.</summary> - /// <param name="state">The progress value.</param> - private void InvokeHandlers(object state) - { - T value = (T)state; - - Action<T> handler = _handler; - EventHandler<T> changedEvent = ProgressChanged; - - if (handler != null) handler(value); - if (changedEvent != null) changedEvent(this, value); - } - } - - /// <summary>Holds static values for <see cref="Progress{T}"/>.</summary> - /// <remarks>This avoids one static instance per type T.</remarks> - internal static class ProgressStatics - { - /// <summary>A default synchronization context that targets the ThreadPool.</summary> - internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext(); - } -} diff --git a/src/Common/src/System/Threading/ReaderWriterLockSlim.cs b/src/Common/src/System/Threading/ReaderWriterLockSlim.cs deleted file mode 100644 index b5c761e18..000000000 --- a/src/Common/src/System/Threading/ReaderWriterLockSlim.cs +++ /dev/null @@ -1,1294 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Diagnostics; // for TraceInformation -using System.Threading; -using System.Runtime.CompilerServices; - -namespace System.Threading -{ - public enum LockRecursionPolicy - { - NoRecursion = 0, - SupportsRecursion = 1, - } - - // - // ReaderWriterCount tracks how many of each kind of lock is held by each thread. - // We keep a linked list for each thread, attached to a ThreadStatic field. - // These are reused wherever possible, so that a given thread will only - // allocate N of these, where N is the maximum number of locks held simultaneously - // by that thread. - // - internal class ReaderWriterCount - { - // Which lock does this object belong to? This is a numeric ID for two reasons: - // 1) We don't want this field to keep the lock object alive, and a WeakReference would - // be too expensive. - // 2) Setting the value of a long is faster than setting the value of a reference. - // The "hot" paths in ReaderWriterLockSlim are short enough that this actually - // matters. - public long lockID; - - // How many reader locks does this thread hold on this ReaderWriterLockSlim instance? - public int readercount; - - // Ditto for writer/upgrader counts. These are only used if the lock allows recursion. - // But we have to have the fields on every ReaderWriterCount instance, because - // we reuse it for different locks. - public int writercount; - public int upgradecount; - - // Next RWC in this thread's list. - public ReaderWriterCount next; - } - - /// <summary> - /// A reader-writer lock implementation that is intended to be simple, yet very - /// efficient. In particular only 1 interlocked operation is taken for any lock - /// operation (we use spin locks to achieve this). The spin lock is never held - /// for more than a few instructions (in particular, we never call event APIs - /// or in fact any non-trivial API while holding the spin lock). - /// </summary> - public class ReaderWriterLockSlim : IDisposable - { - //Specifying if locked can be reacquired recursively. - private bool _fIsReentrant; - - // Lock specifiation for myLock: This lock protects exactly the local fields associted - // instance of ReaderWriterLockSlim. It does NOT protect the memory associted with the - // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). - private int _myLock; - - //The variables controlling spinning behaviior of Mylock(which is a spin-lock) - - private const int LockSpinCycles = 20; - private const int LockSpinCount = 10; - private const int LockSleep0Count = 5; - - // These variables allow use to avoid Setting events (which is expensive) if we don't have to. - private uint _numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent - private uint _numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent - private uint _numWriteUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). - private uint _numUpgradeWaiters; - - //Variable used for quick check when there are no waiters. - private bool _fNoWaiters; - - private int _upgradeLockOwnerId; - private int _writeLockOwnerId; - - // conditions we wait on. - private EventWaitHandle _writeEvent; // threads waiting to aquire a write lock go here. - private EventWaitHandle _readEvent; // threads waiting to aquire a read lock go here (will be released in bulk) - private EventWaitHandle _upgradeEvent; // thread waiting to acquire the upgrade lock - private EventWaitHandle _waitUpgradeEvent; // thread waiting to upgrade from the upgrade lock to a write lock go here (at most one) - - // Every lock instance has a unique ID, which is used by ReaderWriterCount to associate itself with the lock - // without holding a reference to it. - private static long s_nextLockID; - private long _lockID; - - // See comments on ReaderWriterCount. - [ThreadStatic] - private static ReaderWriterCount t_rwc; - - private bool _fUpgradeThreadHoldingRead; - - private const int MaxSpinCount = 20; - - //The uint, that contains info like if the writer lock is held, num of - //readers etc. - private uint _owners; - - //Various R/W masks - //Note: - //The Uint is divided as follows: - // - //Writer-Owned Waiting-Writers Waiting Upgraders Num-REaders - // 31 30 29 28.......0 - // - //Dividing the uint, allows to vastly simplify logic for checking if a - //reader should go in etc. Setting the writer bit, will automatically - //make the value of the uint much larger than the max num of readers - //allowed, thus causing the check for max_readers to fail. - - private const uint WRITER_HELD = 0x80000000; - private const uint WAITING_WRITERS = 0x40000000; - private const uint WAITING_UPGRADER = 0x20000000; - - //The max readers is actually one less then it's theoretical max. - //This is done in order to prevent reader count overflows. If the reader - //count reaches max, other readers will wait. - private const uint MAX_READER = 0x10000000 - 2; - - private const uint READER_MASK = 0x10000000 - 1; - - private bool _fDisposed; - - private void InitializeThreadCounts() - { - _upgradeLockOwnerId = -1; - _writeLockOwnerId = -1; - } - - public ReaderWriterLockSlim() - : this(LockRecursionPolicy.NoRecursion) - { - } - - public ReaderWriterLockSlim(LockRecursionPolicy recursionPolicy) - { - if (recursionPolicy == LockRecursionPolicy.SupportsRecursion) - { - _fIsReentrant = true; - } - InitializeThreadCounts(); - _fNoWaiters = true; - _lockID = Interlocked.Increment(ref s_nextLockID); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static bool IsRWEntryEmpty(ReaderWriterCount rwc) - { - if (rwc.lockID == 0) - return true; - else if (rwc.readercount == 0 && rwc.writercount == 0 && rwc.upgradecount == 0) - return true; - else - return false; - } - - private bool IsRwHashEntryChanged(ReaderWriterCount lrwc) - { - return lrwc.lockID != _lockID; - } - - /// <summary> - /// This routine retrieves/sets the per-thread counts needed to enforce the - /// various rules related to acquiring the lock. - /// - /// DontAllocate is set to true if the caller just wants to get an existing - /// entry for this thread, but doesn't want to add one if an existing one - /// could not be found. - /// </summary> - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ReaderWriterCount GetThreadRWCount(bool dontAllocate) - { - ReaderWriterCount rwc = t_rwc; - ReaderWriterCount empty = null; - while (rwc != null) - { - if (rwc.lockID == _lockID) - return rwc; - - if (!dontAllocate && empty == null && IsRWEntryEmpty(rwc)) - empty = rwc; - - rwc = rwc.next; - } - - if (dontAllocate) - return null; - - if (empty == null) - { - empty = new ReaderWriterCount(); - empty.next = t_rwc; - t_rwc = empty; - } - - empty.lockID = _lockID; - return empty; - } - - public void EnterReadLock() - { - TryEnterReadLock(-1); - } - - // - // Common timeout support - // - private struct TimeoutTracker - { - private int _total; - private int _start; - - public TimeoutTracker(TimeSpan timeout) - { - long ltm = (long)timeout.TotalMilliseconds; - if (ltm < -1 || ltm > (long)Int32.MaxValue) - throw new ArgumentOutOfRangeException("timeout"); - _total = (int)ltm; - if (_total != -1 && _total != 0) - _start = Environment.TickCount; - else - _start = 0; - } - - public TimeoutTracker(int millisecondsTimeout) - { - if (millisecondsTimeout < -1) - throw new ArgumentOutOfRangeException("millisecondsTimeout"); - _total = millisecondsTimeout; - if (_total != -1 && _total != 0) - _start = Environment.TickCount; - else - _start = 0; - } - - public int RemainingMilliseconds - { - get - { - if (_total == -1 || _total == 0) - return _total; - - int elapsed = Environment.TickCount - _start; - // elapsed may be negative if TickCount has overflowed by 2^31 milliseconds. - if (elapsed < 0 || elapsed >= _total) - return 0; - - return _total - elapsed; - } - } - - public bool IsExpired - { - get - { - return RemainingMilliseconds == 0; - } - } - } - - public bool TryEnterReadLock(TimeSpan timeout) - { - return TryEnterReadLock(new TimeoutTracker(timeout)); - } - - public bool TryEnterReadLock(int millisecondsTimeout) - { - return TryEnterReadLock(new TimeoutTracker(millisecondsTimeout)); - } - - private bool TryEnterReadLock(TimeoutTracker timeout) - { - return TryEnterReadLockCore(timeout); - } - - private bool TryEnterReadLockCore(TimeoutTracker timeout) - { - if (_fDisposed) - throw new ObjectDisposedException(null); - - ReaderWriterCount lrwc = null; - int id = Environment.CurrentManagedThreadId; - - if (!_fIsReentrant) - { - if (id == _writeLockOwnerId) - { - //Check for AW->AR - throw new LockRecursionException(SR.LockRecursionException_ReadAfterWriteNotAllowed); - } - - EnterMyLock(); - - lrwc = GetThreadRWCount(false); - - //Check if the reader lock is already acquired. Note, we could - //check the presence of a reader by not allocating rwc (But that - //would lead to two lookups in the common case. It's better to keep - //a count in the struucture). - if (lrwc.readercount > 0) - { - ExitMyLock(); - throw new LockRecursionException(SR.LockRecursionException_RecursiveReadNotAllowed); - } - else if (id == _upgradeLockOwnerId) - { - //The upgrade lock is already held. - //Update the global read counts and exit. - - lrwc.readercount++; - _owners++; - ExitMyLock(); - return true; - } - } - else - { - EnterMyLock(); - lrwc = GetThreadRWCount(false); - if (lrwc.readercount > 0) - { - lrwc.readercount++; - ExitMyLock(); - return true; - } - else if (id == _upgradeLockOwnerId) - { - //The upgrade lock is already held. - //Update the global read counts and exit. - lrwc.readercount++; - _owners++; - ExitMyLock(); - _fUpgradeThreadHoldingRead = true; - return true; - } - else if (id == _writeLockOwnerId) - { - //The write lock is already held. - //Update global read counts here, - lrwc.readercount++; - _owners++; - ExitMyLock(); - return true; - } - } - - bool retVal = true; - - int spincount = 0; - - for (; ;) - { - // We can enter a read lock if there are only read-locks have been given out - // and a writer is not trying to get in. - - if (_owners < MAX_READER) - { - // Good case, there is no contention, we are basically done - _owners++; // Indicate we have another reader - lrwc.readercount++; - break; - } - - if (spincount < MaxSpinCount) - { - ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); - EnterMyLock(); - //The per-thread structure may have been recycled as the lock is acquired (due to message pumping), load again. - if (IsRwHashEntryChanged(lrwc)) - lrwc = GetThreadRWCount(false); - continue; - } - - // Drat, we need to wait. Mark that we have waiters and wait. - if (_readEvent == null) // Create the needed event - { - LazyCreateEvent(ref _readEvent, false); - if (IsRwHashEntryChanged(lrwc)) - lrwc = GetThreadRWCount(false); - continue; // since we left the lock, start over. - } - - retVal = WaitOnEvent(_readEvent, ref _numReadWaiters, timeout); - if (!retVal) - { - return false; - } - if (IsRwHashEntryChanged(lrwc)) - lrwc = GetThreadRWCount(false); - } - - ExitMyLock(); - return retVal; - } - - public void EnterWriteLock() - { - TryEnterWriteLock(-1); - } - - public bool TryEnterWriteLock(TimeSpan timeout) - { - return TryEnterWriteLock(new TimeoutTracker(timeout)); - } - - public bool TryEnterWriteLock(int millisecondsTimeout) - { - return TryEnterWriteLock(new TimeoutTracker(millisecondsTimeout)); - } - - private bool TryEnterWriteLock(TimeoutTracker timeout) - { - return TryEnterWriteLockCore(timeout); - } - - private bool TryEnterWriteLockCore(TimeoutTracker timeout) - { - if (_fDisposed) - throw new ObjectDisposedException(null); - - int id = Environment.CurrentManagedThreadId; - ReaderWriterCount lrwc; - bool upgradingToWrite = false; - - if (!_fIsReentrant) - { - if (id == _writeLockOwnerId) - { - //Check for AW->AW - throw new LockRecursionException(SR.LockRecursionException_RecursiveWriteNotAllowed); - } - else if (id == _upgradeLockOwnerId) - { - //AU->AW case is allowed once. - upgradingToWrite = true; - } - - EnterMyLock(); - lrwc = GetThreadRWCount(true); - - //Can't acquire write lock with reader lock held. - if (lrwc != null && lrwc.readercount > 0) - { - ExitMyLock(); - throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); - } - } - else - { - EnterMyLock(); - lrwc = GetThreadRWCount(false); - - if (id == _writeLockOwnerId) - { - lrwc.writercount++; - ExitMyLock(); - return true; - } - else if (id == _upgradeLockOwnerId) - { - upgradingToWrite = true; - } - else if (lrwc.readercount > 0) - { - //Write locks may not be acquired if only read locks have been - //acquired. - ExitMyLock(); - throw new LockRecursionException(SR.LockRecursionException_WriteAfterReadNotAllowed); - } - } - - int spincount = 0; - bool retVal = true; - - for (; ;) - { - if (IsWriterAcquired()) - { - // Good case, there is no contention, we are basically done - SetWriterAcquired(); - break; - } - - //Check if there is just one upgrader, and no readers. - //Assumption: Only one thread can have the upgrade lock, so the - //following check will fail for all other threads that may sneak in - //when the upgrading thread is waiting. - - if (upgradingToWrite) - { - uint readercount = GetNumReaders(); - - if (readercount == 1) - { - //Good case again, there is just one upgrader, and no readers. - SetWriterAcquired(); // indicate we have a writer. - break; - } - else if (readercount == 2) - { - if (lrwc != null) - { - if (IsRwHashEntryChanged(lrwc)) - lrwc = GetThreadRWCount(false); - - if (lrwc.readercount > 0) - { - //This check is needed for EU->ER->EW case, as the owner count will be two. - Debug.Assert(_fIsReentrant); - Debug.Assert(_fUpgradeThreadHoldingRead); - - //Good case again, there is just one upgrader, and no readers. - SetWriterAcquired(); // indicate we have a writer. - break; - } - } - } - } - - if (spincount < MaxSpinCount) - { - ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); - EnterMyLock(); - continue; - } - - if (upgradingToWrite) - { - if (_waitUpgradeEvent == null) // Create the needed event - { - LazyCreateEvent(ref _waitUpgradeEvent, true); - continue; // since we left the lock, start over. - } - - Debug.Assert(_numWriteUpgradeWaiters == 0, "There can be at most one thread with the upgrade lock held."); - - retVal = WaitOnEvent(_waitUpgradeEvent, ref _numWriteUpgradeWaiters, timeout); - - //The lock is not held in case of failure. - if (!retVal) - return false; - } - else - { - // Drat, we need to wait. Mark that we have waiters and wait. - if (_writeEvent == null) // create the needed event. - { - LazyCreateEvent(ref _writeEvent, true); - continue; // since we left the lock, start over. - } - - retVal = WaitOnEvent(_writeEvent, ref _numWriteWaiters, timeout); - //The lock is not held in case of failure. - if (!retVal) - return false; - } - } - - Debug.Assert((_owners & WRITER_HELD) > 0); - - if (_fIsReentrant) - { - if (IsRwHashEntryChanged(lrwc)) - lrwc = GetThreadRWCount(false); - lrwc.writercount++; - } - - ExitMyLock(); - - _writeLockOwnerId = id; - - return true; - } - - public void EnterUpgradeableReadLock() - { - TryEnterUpgradeableReadLock(-1); - } - - public bool TryEnterUpgradeableReadLock(TimeSpan timeout) - { - return TryEnterUpgradeableReadLock(new TimeoutTracker(timeout)); - } - - public bool TryEnterUpgradeableReadLock(int millisecondsTimeout) - { - return TryEnterUpgradeableReadLock(new TimeoutTracker(millisecondsTimeout)); - } - - private bool TryEnterUpgradeableReadLock(TimeoutTracker timeout) - { - return TryEnterUpgradeableReadLockCore(timeout); - } - - private bool TryEnterUpgradeableReadLockCore(TimeoutTracker timeout) - { - if (_fDisposed) - throw new ObjectDisposedException(null); - - int id = Environment.CurrentManagedThreadId; - ReaderWriterCount lrwc; - - if (!_fIsReentrant) - { - if (id == _upgradeLockOwnerId) - { - //Check for AU->AU - throw new LockRecursionException(SR.LockRecursionException_RecursiveUpgradeNotAllowed); - } - else if (id == _writeLockOwnerId) - { - //Check for AU->AW - throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterWriteNotAllowed); - } - - EnterMyLock(); - lrwc = GetThreadRWCount(true); - //Can't acquire upgrade lock with reader lock held. - if (lrwc != null && lrwc.readercount > 0) - { - ExitMyLock(); - throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); - } - } - else - { - EnterMyLock(); - lrwc = GetThreadRWCount(false); - - if (id == _upgradeLockOwnerId) - { - lrwc.upgradecount++; - ExitMyLock(); - return true; - } - else if (id == _writeLockOwnerId) - { - //Write lock is already held, Just update the global state - //to show presence of upgrader. - Debug.Assert((_owners & WRITER_HELD) > 0); - _owners++; - _upgradeLockOwnerId = id; - lrwc.upgradecount++; - if (lrwc.readercount > 0) - _fUpgradeThreadHoldingRead = true; - ExitMyLock(); - return true; - } - else if (lrwc.readercount > 0) - { - //Upgrade locks may not be acquired if only read locks have been - //acquired. - ExitMyLock(); - throw new LockRecursionException(SR.LockRecursionException_UpgradeAfterReadNotAllowed); - } - } - - bool retVal = true; - - int spincount = 0; - - for (; ;) - { - //Once an upgrade lock is taken, it's like having a reader lock held - //until upgrade or downgrade operations are performed. - - if ((_upgradeLockOwnerId == -1) && (_owners < MAX_READER)) - { - _owners++; - _upgradeLockOwnerId = id; - break; - } - - if (spincount < MaxSpinCount) - { - ExitMyLock(); - if (timeout.IsExpired) - return false; - spincount++; - SpinWait(spincount); - EnterMyLock(); - continue; - } - - // Drat, we need to wait. Mark that we have waiters and wait. - if (_upgradeEvent == null) // Create the needed event - { - LazyCreateEvent(ref _upgradeEvent, true); - continue; // since we left the lock, start over. - } - - //Only one thread with the upgrade lock held can proceed. - retVal = WaitOnEvent(_upgradeEvent, ref _numUpgradeWaiters, timeout); - if (!retVal) - return false; - } - - if (_fIsReentrant) - { - //The lock may have been dropped getting here, so make a quick check to see whether some other - //thread did not grab the entry. - if (IsRwHashEntryChanged(lrwc)) - lrwc = GetThreadRWCount(false); - lrwc.upgradecount++; - } - - ExitMyLock(); - - return true; - } - - public void ExitReadLock() - { - ReaderWriterCount lrwc = null; - - EnterMyLock(); - - lrwc = GetThreadRWCount(true); - - if (lrwc == null || lrwc.readercount < 1) - { - //You have to be holding the read lock to make this call. - ExitMyLock(); - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedRead); - } - - if (_fIsReentrant) - { - if (lrwc.readercount > 1) - { - lrwc.readercount--; - ExitMyLock(); - return; - } - - if (Environment.CurrentManagedThreadId == _upgradeLockOwnerId) - { - _fUpgradeThreadHoldingRead = false; - } - } - - Debug.Assert(_owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken"); - - --_owners; - - Debug.Assert(lrwc.readercount == 1); - lrwc.readercount--; - - ExitAndWakeUpAppropriateWaiters(); - } - - public void ExitWriteLock() - { - ReaderWriterCount lrwc; - if (!_fIsReentrant) - { - if (Environment.CurrentManagedThreadId != _writeLockOwnerId) - { - //You have to be holding the write lock to make this call. - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); - } - EnterMyLock(); - } - else - { - EnterMyLock(); - lrwc = GetThreadRWCount(false); - - if (lrwc == null) - { - ExitMyLock(); - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); - } - - if (lrwc.writercount < 1) - { - ExitMyLock(); - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedWrite); - } - - lrwc.writercount--; - - if (lrwc.writercount > 0) - { - ExitMyLock(); - return; - } - } - - Debug.Assert((_owners & WRITER_HELD) > 0, "Calling ReleaseWriterLock when no write lock is held"); - - ClearWriterAcquired(); - - _writeLockOwnerId = -1; - - ExitAndWakeUpAppropriateWaiters(); - } - - public void ExitUpgradeableReadLock() - { - ReaderWriterCount lrwc; - if (!_fIsReentrant) - { - if (Environment.CurrentManagedThreadId != _upgradeLockOwnerId) - { - //You have to be holding the upgrade lock to make this call. - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); - } - EnterMyLock(); - } - else - { - EnterMyLock(); - lrwc = GetThreadRWCount(true); - - if (lrwc == null) - { - ExitMyLock(); - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); - } - - if (lrwc.upgradecount < 1) - { - ExitMyLock(); - throw new SynchronizationLockException(SR.SynchronizationLockException_MisMatchedUpgrade); - } - - lrwc.upgradecount--; - - if (lrwc.upgradecount > 0) - { - ExitMyLock(); - return; - } - - _fUpgradeThreadHoldingRead = false; - } - - _owners--; - _upgradeLockOwnerId = -1; - - ExitAndWakeUpAppropriateWaiters(); - } - - /// <summary> - /// A routine for lazily creating a event outside the lock (so if errors - /// happen they are outside the lock and that we don't do much work - /// while holding a spin lock). If all goes well, reenter the lock and - /// set 'waitEvent' - /// </summary> - private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) - { -#if DEBUG - Debug.Assert(MyLockHeld); - Debug.Assert(waitEvent == null); -#endif - ExitMyLock(); - EventWaitHandle newEvent; - if (makeAutoResetEvent) - newEvent = new AutoResetEvent(false); - else - newEvent = new ManualResetEvent(false); - EnterMyLock(); - if (waitEvent == null) // maybe someone snuck in. - waitEvent = newEvent; - else - newEvent.Dispose(); - } - - /// <summary> - /// Waits on 'waitEvent' with a timeout - /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. - /// </summary> - private bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, TimeoutTracker timeout) - { -#if DEBUG - Debug.Assert(MyLockHeld); -#endif - waitEvent.Reset(); - numWaiters++; - _fNoWaiters = false; - - //Setting these bits will prevent new readers from getting in. - if (_numWriteWaiters == 1) - SetWritersWaiting(); - if (_numWriteUpgradeWaiters == 1) - SetUpgraderWaiting(); - - bool waitSuccessful = false; - ExitMyLock(); // Do the wait outside of any lock - - try - { - waitSuccessful = waitEvent.WaitOne(timeout.RemainingMilliseconds); - } - finally - { - EnterMyLock(); - --numWaiters; - - if (_numWriteWaiters == 0 && _numWriteUpgradeWaiters == 0 && _numUpgradeWaiters == 0 && _numReadWaiters == 0) - _fNoWaiters = true; - - if (_numWriteWaiters == 0) - ClearWritersWaiting(); - if (_numWriteUpgradeWaiters == 0) - ClearUpgraderWaiting(); - - if (!waitSuccessful) // We may also be aboutto throw for some reason. Exit myLock. - ExitMyLock(); - } - return waitSuccessful; - } - - /// <summary> - /// Determines the appropriate events to set, leaves the locks, and sets the events. - /// </summary> - private void ExitAndWakeUpAppropriateWaiters() - { -#if DEBUG - Debug.Assert(MyLockHeld); -#endif - if (_fNoWaiters) - { - ExitMyLock(); - return; - } - - ExitAndWakeUpAppropriateWaitersPreferringWriters(); - } - - private void ExitAndWakeUpAppropriateWaitersPreferringWriters() - { - bool setUpgradeEvent = false; - bool setReadEvent = false; - uint readercount = GetNumReaders(); - - //We need this case for EU->ER->EW case, as the read count will be 2 in - //that scenario. - if (_fIsReentrant) - { - if (_numWriteUpgradeWaiters > 0 && _fUpgradeThreadHoldingRead && readercount == 2) - { - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). - return; - } - } - - if (readercount == 1 && _numWriteUpgradeWaiters > 0) - { - //We have to be careful now, as we are droppping the lock. - //No new writes should be allowed to sneak in if an upgrade - //was pending. - - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - _waitUpgradeEvent.Set(); // release all upgraders (however there can be at most one). - } - else if (readercount == 0 && _numWriteWaiters > 0) - { - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - _writeEvent.Set(); // release one writer. - } - else if (readercount >= 0) - { - if (_numReadWaiters != 0 || _numUpgradeWaiters != 0) - { - if (_numReadWaiters != 0) - setReadEvent = true; - - if (_numUpgradeWaiters != 0 && _upgradeLockOwnerId == -1) - { - setUpgradeEvent = true; - } - - ExitMyLock(); // Exit before signaling to improve efficiency (wakee will need the lock) - - if (setReadEvent) - _readEvent.Set(); // release all readers. - - if (setUpgradeEvent) - _upgradeEvent.Set(); //release one upgrader. - } - else - ExitMyLock(); - } - else - ExitMyLock(); - } - - private bool IsWriterAcquired() - { - return (_owners & ~WAITING_WRITERS) == 0; - } - - private void SetWriterAcquired() - { - _owners |= WRITER_HELD; // indicate we have a writer. - } - - private void ClearWriterAcquired() - { - _owners &= ~WRITER_HELD; - } - - private void SetWritersWaiting() - { - _owners |= WAITING_WRITERS; - } - - private void ClearWritersWaiting() - { - _owners &= ~WAITING_WRITERS; - } - - private void SetUpgraderWaiting() - { - _owners |= WAITING_UPGRADER; - } - - private void ClearUpgraderWaiting() - { - _owners &= ~WAITING_UPGRADER; - } - - private uint GetNumReaders() - { - return _owners & READER_MASK; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void EnterMyLock() - { - if (Interlocked.CompareExchange(ref _myLock, 1, 0) != 0) - EnterMyLockSpin(); - } - - private void EnterMyLockSpin() - { - int pc = Environment.ProcessorCount; - for (int i = 0; ; i++) - { - if (i < LockSpinCount && pc > 1) - { - Helpers.Spin(LockSpinCycles * (i + 1)); // Wait a few dozen instructions to let another processor release lock. - } - else if (i < (LockSpinCount + LockSleep0Count)) - { - Helpers.Sleep(0); // Give up my quantum. - } - else - { - Helpers.Sleep(1); // Give up my quantum. - } - - if (_myLock == 0 && Interlocked.CompareExchange(ref _myLock, 1, 0) == 0) - return; - } - } - - private void ExitMyLock() - { - Debug.Assert(_myLock != 0, "Exiting spin lock that is not held"); - Volatile.Write(ref _myLock, 0); - } - -#if DEBUG - private bool MyLockHeld { get { return _myLock != 0; } } -#endif - - private static void SpinWait(int SpinCount) - { - //Exponential backoff - if ((SpinCount < 5) && (Environment.ProcessorCount > 1)) - { - Helpers.Spin(LockSpinCycles * SpinCount); - } - else if (SpinCount < MaxSpinCount - 3) - { - Helpers.Sleep(0); - } - else - { - Helpers.Sleep(1); - } - } - - public void Dispose() - { - Dispose(true); - } - - private void Dispose(bool disposing) - { - if (disposing) - { - if (_fDisposed) - throw new ObjectDisposedException(null); - - if (WaitingReadCount > 0 || WaitingUpgradeCount > 0 || WaitingWriteCount > 0) - throw new SynchronizationLockException(SR.SynchronizationLockException_IncorrectDispose); - - if (IsReadLockHeld || IsUpgradeableReadLockHeld || IsWriteLockHeld) - throw new SynchronizationLockException(SR.SynchronizationLockException_IncorrectDispose); - - if (_writeEvent != null) - { - _writeEvent.Dispose(); - _writeEvent = null; - } - - if (_readEvent != null) - { - _readEvent.Dispose(); - _readEvent = null; - } - - if (_upgradeEvent != null) - { - _upgradeEvent.Dispose(); - _upgradeEvent = null; - } - - if (_waitUpgradeEvent != null) - { - _waitUpgradeEvent.Dispose(); - _waitUpgradeEvent = null; - } - - _fDisposed = true; - } - } - - public bool IsReadLockHeld - { - get - { - if (RecursiveReadCount > 0) - return true; - else - return false; - } - } - - public bool IsUpgradeableReadLockHeld - { - get - { - if (RecursiveUpgradeCount > 0) - return true; - else - return false; - } - } - - public bool IsWriteLockHeld - { - get - { - if (RecursiveWriteCount > 0) - return true; - else - return false; - } - } - - public LockRecursionPolicy RecursionPolicy - { - get - { - if (_fIsReentrant) - { - return LockRecursionPolicy.SupportsRecursion; - } - else - { - return LockRecursionPolicy.NoRecursion; - } - } - } - - public int CurrentReadCount - { - get - { - int numreaders = (int)GetNumReaders(); - - if (_upgradeLockOwnerId != -1) - return numreaders - 1; - else - return numreaders; - } - } - - - public int RecursiveReadCount - { - get - { - int count = 0; - ReaderWriterCount lrwc = GetThreadRWCount(true); - if (lrwc != null) - count = lrwc.readercount; - - return count; - } - } - - public int RecursiveUpgradeCount - { - get - { - if (_fIsReentrant) - { - int count = 0; - - ReaderWriterCount lrwc = GetThreadRWCount(true); - if (lrwc != null) - count = lrwc.upgradecount; - - return count; - } - else - { - if (Environment.CurrentManagedThreadId == _upgradeLockOwnerId) - return 1; - else - return 0; - } - } - } - - public int RecursiveWriteCount - { - get - { - if (_fIsReentrant) - { - int count = 0; - - ReaderWriterCount lrwc = GetThreadRWCount(true); - if (lrwc != null) - count = lrwc.writercount; - - return count; - } - else - { - if (Environment.CurrentManagedThreadId == _writeLockOwnerId) - return 1; - else - return 0; - } - } - } - - public int WaitingReadCount - { - get - { - return (int)_numReadWaiters; - } - } - - public int WaitingUpgradeCount - { - get - { - return (int)_numUpgradeWaiters; - } - } - - public int WaitingWriteCount - { - get - { - return (int)_numWriteWaiters; - } - } - } -} diff --git a/src/Common/src/System/Threading/TimeoutHelper.cs b/src/Common/src/System/Threading/TimeoutHelper.cs deleted file mode 100644 index 598641437..000000000 --- a/src/Common/src/System/Threading/TimeoutHelper.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System.Diagnostics; - -namespace System.Threading -{ - /// <summary> - /// A helper class to capture a start time using Environment.TickCout as a time in milliseconds, also updates a given timeout bu subtracting the current time from - /// the start time - /// </summary> - internal static class TimeoutHelper - { - /// <summary> - /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days - /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days - /// </summary> - /// <returns></returns> - public static uint GetTime() - { - return (uint)Environment.TickCount; - } - - /// <summary> - /// Helper function to measure and update the elapsed time - /// </summary> - /// <param name="startTime"> The first time (in milliseconds) observed when the wait started</param> - /// <param name="originalWaitMillisecondsTimeout">The orginal wait timeoutout in milliseconds</param> - /// <returns>The new wait time in milliseconds, -1 if the time expired</returns> - public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout) - { - // The function must be called in case the time out is not infinite - Debug.Assert(originalWaitMillisecondsTimeout != Timeout.Infinite); - - uint elapsedMilliseconds = (GetTime() - startTime); - - // Check the elapsed milliseconds is greater than max int because this property is uint - if (elapsedMilliseconds > int.MaxValue) - { - return 0; - } - - // Subtract the elapsed time from the current wait time - int currentWaitTimeout = originalWaitMillisecondsTimeout - (int)elapsedMilliseconds; ; - if (currentWaitTimeout <= 0) - { - return 0; - } - - return currentWaitTimeout; - } - } -} |