<AssemblyName>System.Collections.Concurrent</AssemblyName>
<RootNamespace>System.Collections.Concurrent</RootNamespace>
<IsPartialFacadeAssembly>true</IsPartialFacadeAssembly>
+ <NullableContextOptions>enable</NullableContextOptions>
<Configurations>netcoreapp-Unix-Debug;netcoreapp-Unix-Release;netcoreapp-Windows_NT-Debug;netcoreapp-Windows_NT-Release;uap-Windows_NT-Debug;uap-Windows_NT-Release;uapaot-Windows_NT-Debug;uapaot-Windows_NT-Release</Configurations>
</PropertyGroup>
<ItemGroup>
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
-using System.Runtime.InteropServices;
using System.Threading;
namespace System.Collections.Concurrent
[DebuggerDisplay("Count = {Count}, Type = {_collection}")]
public class BlockingCollection<T> : IEnumerable<T>, ICollection, IDisposable, IReadOnlyCollection<T>
{
- private IProducerConsumerCollection<T> _collection;
+ private IProducerConsumerCollection<T> _collection = null!;
private int _boundedCapacity;
private const int NON_BOUNDED = -1;
- private SemaphoreSlim _freeNodes;
- private SemaphoreSlim _occupiedNodes;
+ private SemaphoreSlim? _freeNodes;
+ private SemaphoreSlim _occupiedNodes = null!;
private bool _isDisposed;
- private CancellationTokenSource _consumersCancellationTokenSource;
- private CancellationTokenSource _producersCancellationTokenSource;
+ private CancellationTokenSource _consumersCancellationTokenSource = null!;
+ private CancellationTokenSource _producersCancellationTokenSource = null!;
private volatile int _currentAdders;
private const int COMPLETE_ADDING_ON_MASK = unchecked((int)0x80000000);
{
//If the _freeNodes semaphore threw OperationCanceledException then this means that CompleteAdding()
//was called concurrently with Adding which is not supported by BlockingCollection.
- CancellationTokenSource linkedTokenSource = null;
+ CancellationTokenSource? linkedTokenSource = null;
try
{
waitForSemaphoreWasSuccessful = _freeNodes.Wait(0);
/// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
/// outside of this <see
/// cref="T:System.Collections.Concurrent.BlockingCollection{T}"/> instance.</exception>
- public bool TryTake(out T item)
+ public bool TryTake(out T item) // TODO-NULLABLE-GENERIC
{
return TryTake(out item, 0, CancellationToken.None);
}
/// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
/// outside of this <see
/// cref="T:System.Collections.Concurrent.BlockingCollection{T}"/> instance.</exception>
- public bool TryTake(out T item, TimeSpan timeout)
+ public bool TryTake(out T item, TimeSpan timeout) // TODO-NULLABLE-GENERIC
{
ValidateTimeout(timeout);
return TryTakeWithNoTimeValidation(out item, (int)timeout.TotalMilliseconds, CancellationToken.None, null);
/// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
/// outside of this <see
/// cref="T:System.Collections.Concurrent.BlockingCollection{T}"/> instance.</exception>
- public bool TryTake(out T item, int millisecondsTimeout)
+ public bool TryTake(out T item, int millisecondsTimeout) // TODO-NULLABLE-GENERIC
{
ValidateMillisecondsTimeout(millisecondsTimeout);
return TryTakeWithNoTimeValidation(out item, millisecondsTimeout, CancellationToken.None, null);
/// <exception cref="T:System.InvalidOperationException">The underlying collection was modified
/// outside of this <see
/// cref="T:System.Collections.Concurrent.BlockingCollection{T}"/> instance.</exception>
- public bool TryTake(out T item, int millisecondsTimeout, CancellationToken cancellationToken)
+ public bool TryTake(out T item, int millisecondsTimeout, CancellationToken cancellationToken) // TODO-NULLABLE-GENERIC
{
ValidateMillisecondsTimeout(millisecondsTimeout);
return TryTakeWithNoTimeValidation(out item, millisecondsTimeout, cancellationToken, null);
/// <returns>False if the collection remained empty till the timeout period was exhausted. True otherwise.</returns>
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken"/> is canceled.</exception>
/// <exception cref="System.ObjectDisposedException">If the collection has been disposed.</exception>
- private bool TryTakeWithNoTimeValidation(out T item, int millisecondsTimeout, CancellationToken cancellationToken, CancellationTokenSource combinedTokenSource)
+ private bool TryTakeWithNoTimeValidation(out T item, int millisecondsTimeout, CancellationToken cancellationToken, CancellationTokenSource? combinedTokenSource) // TODO-NULLABLE-GENERIC
{
CheckDisposed();
- item = default(T);
+ item = default(T)!; // TODO-NULLABLE-GENERIC
if (cancellationToken.IsCancellationRequested)
throw new OperationCanceledException(SR.Common_OperationCanceled, cancellationToken);
bool waitForSemaphoreWasSuccessful = false;
// set the combined token source to the combinedToken parameter if it is not null (came from GetConsumingEnumerable)
- CancellationTokenSource linkedTokenSource = combinedTokenSource;
+ CancellationTokenSource? linkedTokenSource = combinedTokenSource;
try
{
waitForSemaphoreWasSuccessful = _occupiedNodes.Wait(0);
if (waitForSemaphoreWasSuccessful == false && millisecondsTimeout != 0)
{
// create the linked token if it is not created yet
- if (combinedTokenSource == null)
+ if (linkedTokenSource == null)
linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken,
_consumersCancellationTokenSource.Token);
waitForSemaphoreWasSuccessful = _occupiedNodes.Wait(millisecondsTimeout, linkedTokenSource.Token);
#else
return
#endif
- TryAddToAny(collections, item, Timeout.Infinite, CancellationToken.None);
+ TryAddToAny(collections, item, Timeout.Infinite, CancellationToken.None);
#if DEBUG
Debug.Assert((tryAddAnyReturnValue >= 0 && tryAddAnyReturnValue < collections.Length)
, "TryAddToAny() was expected to return an index within the bounds of the collections array.");
#else
return
#endif
- TryAddToAny(collections, item, Timeout.Infinite, cancellationToken);
+ TryAddToAny(collections, item, Timeout.Infinite, cancellationToken);
#if DEBUG
Debug.Assert((tryAddAnyReturnValue >= 0 && tryAddAnyReturnValue < collections.Length)
, "TryAddToAny() was expected to return an index within the bounds of the collections array.");
{
if (collections[i]._freeNodes != null)
{
- handlesList.Add(collections[i]._freeNodes.AvailableWaitHandle);
+ handlesList.Add(collections[i]._freeNodes!.AvailableWaitHandle); // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/34644
tokensList.Add(collections[i]._producersCancellationTokenSource.Token);
}
}
/// <exception cref="T:System.ArgumentOutOfRangeException">The count of <paramref name="collections"/> is greater than the maximum size of
/// 62 for STA and 63 for MTA.</exception>
/// <remarks>A call to TakeFromAny may block until an item is available to be removed.</remarks>
- public static int TakeFromAny(BlockingCollection<T>[] collections, out T item)
+ public static int TakeFromAny(BlockingCollection<T>[] collections, out T item) // TODO-NULLABLE-GENERIC
{
return TakeFromAny(collections, out item, CancellationToken.None);
}
/// <exception cref="T:System.ArgumentOutOfRangeException">The count of <paramref name="collections"/> is greater than the maximum size of
/// 62 for STA and 63 for MTA.</exception>
/// <remarks>A call to TakeFromAny may block until an item is available to be removed.</remarks>
- public static int TakeFromAny(BlockingCollection<T>[] collections, out T item, CancellationToken cancellationToken)
+ public static int TakeFromAny(BlockingCollection<T>[] collections, out T item, CancellationToken cancellationToken) // TODO-NULLABLE-GENERIC
{
int returnValue = TryTakeFromAnyCore(collections, out item, Timeout.Infinite, true, cancellationToken);
Debug.Assert((returnValue >= 0 && returnValue < collections.Length)
/// <exception cref="T:System.ArgumentOutOfRangeException">The count of <paramref name="collections"/> is greater than the maximum size of
/// 62 for STA and 63 for MTA.</exception>
/// <remarks>A call to TryTakeFromAny may block until an item is available to be removed.</remarks>
- public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item)
+ public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item) // TODO-NULLABLE-GENERIC
{
return TryTakeFromAny(collections, out item, 0);
}
/// <exception cref="T:System.ArgumentOutOfRangeException">The count of <paramref name="collections"/> is greater than the maximum size of
/// 62 for STA and 63 for MTA.</exception>
/// <remarks>A call to TryTakeFromAny may block until an item is available to be removed.</remarks>
- public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item, TimeSpan timeout)
+ public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item, TimeSpan timeout) // TODO-NULLABLE-GENERIC
{
ValidateTimeout(timeout);
return TryTakeFromAnyCore(collections, out item, (int)timeout.TotalMilliseconds, false, CancellationToken.None);
/// <exception cref="T:System.ArgumentOutOfRangeException">The count of <paramref name="collections"/> is greater than the maximum size of
/// 62 for STA and 63 for MTA.</exception>
/// <remarks>A call to TryTakeFromAny may block until an item is available to be removed.</remarks>
- public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout)
+ public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout) // TODO-NULLABLE-GENERIC
{
ValidateMillisecondsTimeout(millisecondsTimeout);
return TryTakeFromAnyCore(collections, out item, millisecondsTimeout, false, CancellationToken.None);
/// <exception cref="T:System.ArgumentOutOfRangeException">The count of <paramref name="collections"/> is greater than the maximum size of
/// 62 for STA and 63 for MTA.</exception>
/// <remarks>A call to TryTakeFromAny may block until an item is available to be removed.</remarks>
- public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout, CancellationToken cancellationToken)
+ public static int TryTakeFromAny(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout, CancellationToken cancellationToken) // TODO-NULLABLE-GENERIC
{
ValidateMillisecondsTimeout(millisecondsTimeout);
return TryTakeFromAnyCore(collections, out item, millisecondsTimeout, false, cancellationToken);
/// <exception cref="System.ArgumentException">If the collections argument is a 0-length array or contains a
/// null element. Also, if at least one of the collections has been marked complete for adds.</exception>
/// <exception cref="System.ObjectDisposedException">If at least one of the collections has been disposed.</exception>
- private static int TryTakeFromAnyCore(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout, bool isTakeOperation, CancellationToken externalCancellationToken)
+ private static int TryTakeFromAnyCore(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout, bool isTakeOperation, CancellationToken externalCancellationToken) // TODO-NULLABLE-GENERIC
{
ValidateCollectionsArray(collections, false);
/// <exception cref="System.ArgumentException">If the collections argument is a 0-length array or contains a
/// null element. Also, if at least one of the collections has been marked complete for adds.</exception>
/// <exception cref="System.ObjectDisposedException">If at least one of the collections has been disposed.</exception>
- private static int TryTakeFromAnyCoreSlow(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout, bool isTakeOperation, CancellationToken externalCancellationToken)
+ private static int TryTakeFromAnyCoreSlow(BlockingCollection<T>[] collections, out T item, int millisecondsTimeout, bool isTakeOperation, CancellationToken externalCancellationToken) // TODO-NULLABLE-GENERIC
{
const int OPERATION_FAILED = -1;
timeout = UpdateTimeOut(startTime, millisecondsTimeout);
}
- item = default(T); //case#2
+ item = default(T)!; //case#2 // TODO-NULLABLE-GENERIC
return OPERATION_FAILED;
}
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken"/> is canceled.</exception>
public IEnumerable<T> GetConsumingEnumerable(CancellationToken cancellationToken)
{
- CancellationTokenSource linkedTokenSource = null;
+ CancellationTokenSource? linkedTokenSource = null;
try
{
linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _consumersCancellationTokenSource.Token);
/// <summary>The per-bag, per-thread work-stealing queues.</summary>
private readonly ThreadLocal<WorkStealingQueue> _locals;
/// <summary>The head work stealing queue in a linked list of queues.</summary>
- private volatile WorkStealingQueue _workStealingQueues;
+ private volatile WorkStealingQueue? _workStealingQueues;
/// <summary>Number of times any list transitions from empty to non-empty.</summary>
private long _emptyToNonEmptyListTransitionCount;
_locals = new ThreadLocal<WorkStealingQueue>();
- WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: true);
+ WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: true)!;
foreach (T item in collection)
{
queue.LocalPush(item, ref _emptyToNonEmptyListTransitionCount);
/// <see cref="ConcurrentBag{T}"/>. The value can be a null reference
/// (Nothing in Visual Basic) for reference types.</param>
public void Add(T item) =>
- GetCurrentThreadWorkStealingQueue(forceCreate: true)
+ GetCurrentThreadWorkStealingQueue(forceCreate: true)!
.LocalPush(item, ref _emptyToNonEmptyListTransitionCount);
/// <summary>
/// removed from the <see cref="ConcurrentBag{T}"/> or the default value
/// of <typeparamref name="T"/> if the operation failed.</param>
/// <returns>true if an object was removed successfully; otherwise, false.</returns>
- public bool TryTake(out T result)
+ public bool TryTake(out T result) // TODO-NULLABLE-GENERIC
{
- WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: false);
+ WorkStealingQueue? queue = GetCurrentThreadWorkStealingQueue(forceCreate: false);
return (queue != null && queue.TryLocalPop(out result)) || TrySteal(out result, take: true);
}
/// the <see cref="ConcurrentBag{T}"/> or the default value of
/// <typeparamref name="T"/> if the operation failed.</param>
/// <returns>true if and object was returned successfully; otherwise, false.</returns>
- public bool TryPeek(out T result)
+ public bool TryPeek(out T result) // TODO-NULLABLE-GENERIC
{
- WorkStealingQueue queue = GetCurrentThreadWorkStealingQueue(forceCreate: false);
+ WorkStealingQueue? queue = GetCurrentThreadWorkStealingQueue(forceCreate: false);
return (queue != null && queue.TryLocalPeek(out result)) || TrySteal(out result, take: false);
}
/// <summary>Gets the work-stealing queue data structure for the current thread.</summary>
/// <param name="forceCreate">Whether to create a new queue if this thread doesn't have one.</param>
/// <returns>The local queue object, or null if the thread doesn't have one.</returns>
- private WorkStealingQueue GetCurrentThreadWorkStealingQueue(bool forceCreate) =>
+ private WorkStealingQueue? GetCurrentThreadWorkStealingQueue(bool forceCreate) => // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761
_locals.Value ??
(forceCreate ? CreateWorkStealingQueueForCurrentThread() : null);
{
lock (GlobalQueuesLock) // necessary to update _workStealingQueues, so as to synchronize with freezing operations
{
- WorkStealingQueue head = _workStealingQueues;
+ WorkStealingQueue? head = _workStealingQueues;
- WorkStealingQueue queue = head != null ? GetUnownedWorkStealingQueue() : null;
+ WorkStealingQueue? queue = head != null ? GetUnownedWorkStealingQueue() : null;
if (queue == null)
{
_workStealingQueues = queue = new WorkStealingQueue(head);
/// the bag purposefully retains its queue, as it contains data associated with the bag.
/// </summary>
/// <returns>The queue object, or null if no unowned queue could be gathered.</returns>
- private WorkStealingQueue GetUnownedWorkStealingQueue()
+ private WorkStealingQueue? GetUnownedWorkStealingQueue()
{
Debug.Assert(Monitor.IsEntered(GlobalQueuesLock));
// but if our thread ID is reused, we know that no other thread can have the same ID and thus
// no other thread can be using this queue.
int currentThreadId = Environment.CurrentManagedThreadId;
- for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
{
if (queue._ownerThreadId == currentThreadId)
{
// and try to steal from each queue until we get a result. If there is a local queue from this thread,
// then start from the next queue after it, and then iterate around back from the head to this queue,
// not including it.
- WorkStealingQueue localQueue = GetCurrentThreadWorkStealingQueue(forceCreate: false);
+ WorkStealingQueue? localQueue = GetCurrentThreadWorkStealingQueue(forceCreate: false);
bool gotItem = localQueue == null ?
TryStealFromTo(_workStealingQueues, null, out result, take) :
(TryStealFromTo(localQueue._nextQueue, null, out result, take) || TryStealFromTo(_workStealingQueues, localQueue, out result, take));
/// <summary>
/// Attempts to steal from each queue starting from <paramref name="startInclusive"/> to <paramref name="endExclusive"/>.
/// </summary>
- private bool TryStealFromTo(WorkStealingQueue startInclusive, WorkStealingQueue endExclusive, out T result, bool take)
+ private bool TryStealFromTo(WorkStealingQueue? startInclusive, WorkStealingQueue? endExclusive, out T result, bool take) // TODO-NULLABLE-GENERIC
{
- for (WorkStealingQueue queue = startInclusive; queue != endExclusive; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = startInclusive; queue != endExclusive; queue = queue._nextQueue)
{
- if (queue.TrySteal(out result, take))
+ if (queue!.TrySteal(out result, take))
{
return true;
}
}
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
Debug.Assert(Monitor.IsEntered(GlobalQueuesLock));
int i = index;
- for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
{
i += queue.DangerousCopyTo(array, i);
}
{
// If the destination is actually a T[], use the strongly-typed
// overload that doesn't allocate/copy an extra array.
- T[] szArray = array as T[];
+ T[]? szArray = array as T[];
if (szArray != null)
{
CopyTo(szArray, index);
}
// Clear the local queue.
- WorkStealingQueue local = GetCurrentThreadWorkStealingQueue(forceCreate: false);
+ WorkStealingQueue? local = GetCurrentThreadWorkStealingQueue(forceCreate: false);
if (local != null)
{
local.LocalClear();
try
{
FreezeBag(ref lockTaken);
- for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
{
T ignored;
while (queue.TrySteal(out ignored, take: true));
get
{
int count = 0;
- for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
{
checked { count += queue.DangerousCount; }
}
get
{
// Fast-path based on the current thread's local queue.
- WorkStealingQueue local = GetCurrentThreadWorkStealingQueue(forceCreate: false);
+ WorkStealingQueue? local = GetCurrentThreadWorkStealingQueue(forceCreate: false);
if (local != null)
{
// We don't need the lock to check the local queue, as no other thread
try
{
FreezeBag(ref lockTaken);
- for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
{
if (!queue.IsEmpty)
{
// while a global operation is in progress.
Debug.Assert(!Monitor.IsEntered(GlobalQueuesLock));
Monitor.Enter(GlobalQueuesLock, ref lockTaken);
- WorkStealingQueue head = _workStealingQueues; // stable at least until GlobalQueuesLock is released in UnfreezeBag
+ WorkStealingQueue? head = _workStealingQueues; // stable at least until GlobalQueuesLock is released in UnfreezeBag
// Then acquire all local queue locks, noting on each that it's been taken.
- for (WorkStealingQueue queue = head; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = head; queue != null; queue = queue._nextQueue)
{
Monitor.Enter(queue, ref queue._frozen);
}
Interlocked.MemoryBarrier(); // prevent reads of _currentOp from moving before writes to _frozen
// Finally, wait for all unsynchronized operations on each queue to be done.
- for (WorkStealingQueue queue = head; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = head; queue != null; queue = queue._nextQueue)
{
if (queue._currentOp != (int)Operation.None)
{
if (lockTaken)
{
// Release all of the individual queue locks.
- for (WorkStealingQueue queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
+ for (WorkStealingQueue? queue = _workStealingQueues; queue != null; queue = queue._nextQueue)
{
if (queue._frozen)
{
/// <summary>true if this queue's lock is held as part of a global freeze.</summary>
internal bool _frozen;
/// <summary>Next queue in the <see cref="ConcurrentBag{T}"/>'s set of thread-local queues.</summary>
- internal readonly WorkStealingQueue _nextQueue;
+ internal readonly WorkStealingQueue? _nextQueue;
/// <summary>Thread ID that owns this queue.</summary>
internal readonly int _ownerThreadId;
/// <summary>Initialize the WorkStealingQueue.</summary>
/// <param name="nextQueue">The next queue in the linked list of work-stealing queues.</param>
- internal WorkStealingQueue(WorkStealingQueue nextQueue)
+ internal WorkStealingQueue(WorkStealingQueue? nextQueue)
{
_ownerThreadId = Environment.CurrentManagedThreadId;
_nextQueue = nextQueue;
/// <summary>Remove an item from the tail of the queue.</summary>
/// <param name="result">The removed item</param>
- internal bool TryLocalPop(out T result)
+ internal bool TryLocalPop(out T result) // TODO-NULLABLE-GENERIC
{
Debug.Assert(Environment.CurrentManagedThreadId == _ownerThreadId);
int tail = _tailIndex;
if (_headIndex >= tail)
{
- result = default(T);
+ result = default(T)!;
return false;
}
{
int idx = tail & _mask;
result = _array[idx];
- _array[idx] = default(T);
+ _array[idx] = default(T)!; // TODO-NULLABLE-GENERIC
_addTakeCount--;
return true;
}
// Element still available. Take it.
int idx = tail & _mask;
result = _array[idx];
- _array[idx] = default(T);
+ _array[idx] = default(T)!;
_addTakeCount--;
return true;
}
{
// We encountered a race condition and the element was stolen, restore the tail.
_tailIndex = tail + 1;
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
}
/// <summary>Peek an item from the tail of the queue.</summary>
/// <param name="result">the peeked item</param>
/// <returns>True if succeeded, false otherwise</returns>
- internal bool TryLocalPeek(out T result)
+ internal bool TryLocalPeek(out T result) // TODO-NULLABLE-GENERIC
{
Debug.Assert(Environment.CurrentManagedThreadId == _ownerThreadId);
}
}
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
/// <summary>Steal an item from the head of the queue.</summary>
/// <param name="result">the removed item</param>
/// <param name="take">true to take the item; false to simply peek at it</param>
- internal bool TrySteal(out T result, bool take)
+ internal bool TrySteal(out T result, bool take) // TODO-NULLABLE-GENERIC
{
lock (this)
{
{
int idx = head & _mask;
result = _array[idx];
- _array[idx] = default(T);
+ _array[idx] = default(T)!; // TODO-NULLABLE-GENERIC
_stealCount++;
return true;
}
}
// The queue was empty.
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
private sealed class Enumerator : IEnumerator<T>
{
private readonly T[] _array;
- private T _current;
+ private T _current = default!;
private int _index;
public Enumerator(T[] array)
public T Current => _current;
- object IEnumerator.Current
+ object? IEnumerator.Current
{
get
{
public void Reset()
{
_index = 0;
- _current = default(T);
+ _current = default(T)!; // TODO-NULLABLE-GENERIC
}
public void Dispose() { }
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
-using System.Reflection;
using System.Runtime.CompilerServices;
using System.Threading;
/// </remarks>
[DebuggerTypeProxy(typeof(IDictionaryDebugView<,>))]
[DebuggerDisplay("Count = {Count}")]
- public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue>
+ public class ConcurrentDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IDictionary, IReadOnlyDictionary<TKey, TValue> where TKey : object
{
/// <summary>
/// Tables that hold the internal state of the ConcurrentDictionary
/// </summary>
/// <param name="comparer">The <see cref="T:System.Collections.Generic.IEqualityComparer{TKey}"/>
/// implementation to use when comparing keys.</param>
- public ConcurrentDictionary(IEqualityComparer<TKey> comparer) : this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer) { }
+ public ConcurrentDictionary(IEqualityComparer<TKey>? comparer) : this(DefaultConcurrencyLevel, DefaultCapacity, true, comparer) { }
/// <summary>
/// Initializes a new instance of the <see cref="ConcurrentDictionary{TKey,TValue}"/>
/// implementation to use when comparing keys.</param>
/// <exception cref="T:System.ArgumentNullException"><paramref name="collection"/> is a null reference
/// (Nothing in Visual Basic).</exception>
- public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
+ public ConcurrentDictionary(IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey>? comparer)
: this(comparer)
{
if (collection == null) throw new ArgumentNullException(nameof(collection));
/// </exception>
/// <exception cref="T:System.ArgumentException"><paramref name="collection"/> contains one or more duplicate keys.</exception>
public ConcurrentDictionary(
- int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey> comparer)
+ int concurrencyLevel, IEnumerable<KeyValuePair<TKey, TValue>> collection, IEqualityComparer<TKey>? comparer)
: this(concurrencyLevel, DefaultCapacity, false, comparer)
{
if (collection == null) throw new ArgumentNullException(nameof(collection));
/// <paramref name="concurrencyLevel"/> is less than 1. -or-
/// <paramref name="capacity"/> is less than 0.
/// </exception>
- public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey> comparer)
+ public ConcurrentDictionary(int concurrencyLevel, int capacity, IEqualityComparer<TKey>? comparer)
: this(concurrencyLevel, capacity, false, comparer)
{
}
- internal ConcurrentDictionary(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<TKey> comparer)
+ internal ConcurrentDictionary(int concurrencyLevel, int capacity, bool growLockArray, IEqualityComparer<TKey>? comparer)
{
if (concurrencyLevel < 1)
{
/// <returns>true if an object was removed successfully; otherwise, false.</returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
/// (Nothing in Visual Basic).</exception>
- public bool TryRemove(TKey key, out TValue value)
+ public bool TryRemove(TKey key, out TValue value) // TODO-NULLABLE-GENERIC
{
if (key == null) ThrowKeyNullException();
- return TryRemoveInternal(key, out value, false, default(TValue));
+ return TryRemoveInternal(key, out value, false, default(TValue)!); // TODO-NULLABLE-GENERIC
}
/// <summary>
/// <param name="matchValue">Whether removal of the key is conditional on its value.</param>
/// <param name="oldValue">The conditional value to compare against if <paramref name="matchValue"/> is true</param>
/// <returns></returns>
- private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue)
+ private bool TryRemoveInternal(TKey key, out TValue value, bool matchValue, TValue oldValue) // TODO-NULLABLE-GENERIC
{
int hashcode = _comparer.GetHashCode(key);
while (true)
continue;
}
- Node prev = null;
+ Node? prev = null;
for (Node curr = tables._buckets[bucketNo]; curr != null; curr = curr._next)
{
- Debug.Assert((prev == null && curr == tables._buckets[bucketNo]) || prev._next == curr);
+ Debug.Assert((prev == null && curr == tables._buckets[bucketNo]) || prev!._next == curr);
if (hashcode == curr._hashcode && _comparer.Equals(curr._key, key))
{
bool valuesMatch = EqualityComparer<TValue>.Default.Equals(oldValue, curr._value);
if (!valuesMatch)
{
- value = default(TValue);
+ value = default(TValue)!; // TODO-NULLABLE-GENERIC
return false;
}
}
}
}
- value = default(TValue);
+ value = default(TValue)!; // TODO-NULLABLE-GENERIC
return false;
}
}
/// otherwise, false.</returns>
/// <exception cref="T:System.ArgumentNullException"><paramref name="key"/> is a null reference
/// (Nothing in Visual Basic).</exception>
- public bool TryGetValue(TKey key, out TValue value)
+ public bool TryGetValue(TKey key, out TValue value) // TODO-NULLABLE-GENERIC
{
if (key == null) ThrowKeyNullException();
return TryGetValueInternal(key, _comparer.GetHashCode(key), out value);
}
- private bool TryGetValueInternal(TKey key, int hashcode, out TValue value)
+ private bool TryGetValueInternal(TKey key, int hashcode, out TValue value) // TODO-NULLABLE-GENERIC
{
Debug.Assert(_comparer.GetHashCode(key) == hashcode);
n = n._next;
}
- value = default(TValue);
+ value = default(TValue)!; // TODO-NULLABLE-GENERIC
return false;
}
}
// Try to find this key in the bucket
- Node prev = null;
+ Node? prev = null;
for (Node node = tables._buckets[bucketNo]; node != null; node = node._next)
{
- Debug.Assert((prev == null && node == tables._buckets[bucketNo]) || prev._next == node);
+ Debug.Assert((prev == null && node == tables._buckets[bucketNo]) || prev!._next == node);
if (hashcode == node._hashcode && _comparer.Equals(node._key, key))
{
if (valueComparer.Equals(node._value, comparisonValue))
}
// Try to find this key in the bucket
- Node prev = null;
+ Node? prev = null;
for (Node node = tables._buckets[bucketNo]; node != null; node = node._next)
{
- Debug.Assert((prev == null && node == tables._buckets[bucketNo]) || prev._next == node);
+ Debug.Assert((prev == null && node == tables._buckets[bucketNo]) || prev!._next == node);
if (hashcode == node._hashcode && _comparer.Equals(node._key, key))
{
// The key was found in the dictionary. If updates are allowed, update the value for that key.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- private static void ThrowIfInvalidObjectValue(object value)
+ private static void ThrowIfInvalidObjectValue(object? value)
{
if (value != null)
{
ThrowValueNullException();
}
}
- else if (default(TValue) != null)
+ else if (default(TValue)! != null) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/34757
{
ThrowValueNullException();
}
/// -or- A value with the same key already exists in the <see
/// cref="T:System.Collections.Generic.Dictionary{TKey,TValue}"/>.
/// </exception>
- void IDictionary.Add(object key, object value)
+ void IDictionary.Add(object key, object? value)
{
if (key == null) ThrowKeyNullException();
if (!(key is TKey)) throw new ArgumentException(SR.ConcurrentDictionary_TypeOfKeyIncorrect);
ThrowIfInvalidObjectValue(value);
- ((IDictionary<TKey, TValue>)this).Add((TKey)key, (TValue)value);
+ ((IDictionary<TKey, TValue>)this).Add((TKey)key, (TValue)value!);
}
/// <summary>
/// <typeparamref name="TValue"/> of the <see
/// cref="T:System.Collections.Generic.ConcurrentDictionary{TKey,TValue}"/>
/// </exception>
- object IDictionary.this[object key]
+ object? IDictionary.this[object key]
{
get
{
if (!(key is TKey)) throw new ArgumentException(SR.ConcurrentDictionary_TypeOfKeyIncorrect);
ThrowIfInvalidObjectValue(value);
- ((ConcurrentDictionary<TKey, TValue>)this)[(TKey)key] = (TValue)value;
+ ((ConcurrentDictionary<TKey, TValue>)this)[(TKey)key] = (TValue)value!; // TODO-NULLABLE: https://github.com/dotnet/csharplang/issues/538
}
}
// - an array of DictionaryEntry structs
// - an array of objects
- KeyValuePair<TKey, TValue>[] pairs = array as KeyValuePair<TKey, TValue>[];
+ KeyValuePair<TKey, TValue>[]? pairs = array as KeyValuePair<TKey, TValue>[];
if (pairs != null)
{
CopyToPairs(pairs, index);
return;
}
- DictionaryEntry[] entries = array as DictionaryEntry[];
+ DictionaryEntry[]? entries = array as DictionaryEntry[];
if (entries != null)
{
CopyToEntries(entries, index);
return;
}
- object[] objects = array as object[];
+ object[]? objects = array as object[];
if (objects != null)
{
CopyToObjects(objects, index);
get { return _enumerator.Current.Key; }
}
- public object Value
+ public object? Value
{
get { return _enumerator.Current.Value; }
}
+#pragma warning disable CS8612 // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/23268
public object Current
+#pragma warning restore CS8612
{
get { return Entry; }
}
private class Node
{
internal readonly T _value; // Value of the node.
- internal Node _next; // Next pointer.
+ internal Node? _next; // Next pointer.
/// <summary>
/// Constructs a new node with the specified value and no next node.
}
}
- private volatile Node _head; // The stack is a singly linked list, and only remembers the head.
+ private volatile Node? _head; // The stack is a singly linked list, and only remembers the head.
private const int BACKOFF_MAX_YIELDS = 8; // Arbitrary number to cap backoff.
/// <summary>
private void InitializeFromCollection(IEnumerable<T> collection)
{
// We just copy the contents of the collection to our stack.
- Node lastNode = null;
+ Node? lastNode = null;
foreach (T element in collection)
{
Node newNode = new Node(element);
// they are being dequeued. If we ever changed this (e.g. to pool nodes somehow),
// we'd need to revisit this implementation.
- for (Node curr = _head; curr != null; curr = curr._next)
+ for (Node? curr = _head; curr != null; curr = curr._next)
{
count++; //we don't handle overflow, to be consistent with existing generic collection types in CLR
}
/// the top of the <see cref="T:System.Collections.Concurrent.ConcurrentStack{T}"/> or an
/// unspecified value if the operation failed.</param>
/// <returns>true if and object was returned successfully; otherwise, false.</returns>
- public bool TryPeek(out T result)
+ public bool TryPeek(out T result) // TODO-NULLABLE-GENERIC
{
- Node head = _head;
+ Node? head = _head;
// If the stack is empty, return false; else return the element and true.
if (head == null)
{
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
else
/// <returns>true if an element was removed and returned from the top of the <see
/// cref="ConcurrentStack{T}"/>
/// successfully; otherwise, false.</returns>
- public bool TryPop(out T result)
+ public bool TryPop(out T result) // TODO-NULLABLE-GENERIC
{
- Node head = _head;
+ Node? head = _head;
//stack is empty
if (head == null)
{
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
if (Interlocked.CompareExchange(ref _head, head._next, head) == head)
if (count == 0)
return 0;
- Node poppedHead;
+ Node? poppedHead;
int nodesCount = TryPopCore(count, out poppedHead);
if (nodesCount > 0)
{
- CopyRemovedItems(poppedHead, items, startIndex, nodesCount);
+ CopyRemovedItems(poppedHead!, items, startIndex, nodesCount);
}
return nodesCount;
}
/// </summary>
/// <param name="result">The popped item</param>
/// <returns>True if succeeded, false otherwise</returns>
- private bool TryPopCore(out T result)
+ private bool TryPopCore(out T result) // TODO-NULLABLE-GENERIC
{
- Node poppedNode;
+ Node? poppedNode;
if (TryPopCore(1, out poppedNode) == 1)
{
- result = poppedNode._value;
+ result = poppedNode!._value; // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761
return true;
}
- result = default(T);
+ result = default(T)!; // TODO-NULLABLE-GENERIC
return false;
}
/// </param>
/// <returns>The number of objects successfully popped from the top of
/// the <see cref="ConcurrentStack{T}"/>.</returns>
- private int TryPopCore(int count, out Node poppedHead)
+ private int TryPopCore(int count, out Node? poppedHead) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/26761
{
SpinWait spin = new SpinWait();
// Try to CAS the head with its current next. We stop when we succeed or
// when we notice that the stack is empty, whichever comes first.
- Node head;
+ Node? head;
Node next;
int backoff = 1;
- Random r = null;
+ Random? r = null;
while (true)
{
head = _head;
/// <param name="nodesCount">The number of nodes.</param>
private static void CopyRemovedItems(Node head, T[] collection, int startIndex, int nodesCount)
{
- Node current = head;
+ Node? current = head;
for (int i = startIndex; i < startIndex + nodesCount; i++)
{
- collection[i] = current._value;
+ collection[i] = current!._value;
current = current._next;
}
}
/// cref="ConcurrentStack{T}"/>.</returns>
public T[] ToArray()
{
- Node curr = _head;
+ Node? curr = _head;
return curr == null ?
Array.Empty<T>() :
ToList(curr).ToArray();
/// Returns an array containing a snapshot of the list's contents starting at the specified node.
/// </summary>
/// <returns>A list of the stack's contents starting at the specified node.</returns>
- private List<T> ToList(Node curr)
+ private List<T> ToList(Node? curr)
{
List<T> list = new List<T>();
return GetEnumerator(_head);
}
- private IEnumerator<T> GetEnumerator(Node head)
+ private IEnumerator<T> GetEnumerator(Node? head)
{
- Node current = head;
+ Node? current = head;
while (current != null)
{
yield return current._value;
}
public void Dispose()
{
- IDisposable d = _source as IDisposable;
+ IDisposable? d = _source as IDisposable;
if (d != null)
{
d.Dispose();
return _source.Current.Value;
}
}
- object IEnumerator.Current
+ object? IEnumerator.Current
{
get
{
//deferred allocating in MoveNext() with initial value 0, to avoid false sharing
//we also use the fact that: (_currentChunkSize==null) means MoveNext is never called on this enumerator
- protected SharedInt _currentChunkSize;
+ protected SharedInt? _currentChunkSize;
//deferring allocation in MoveNext() with initial value -1, to avoid false sharing
- protected SharedInt _localOffset;
+ protected SharedInt? _localOffset;
private const int CHUNK_DOUBLING_RATE = 3; // Double the chunk size every this many grabs
private int _doublingCountdown; // Number of grabs remaining until chunk size doubles
/// <summary>
/// Get the current element in the current partition. Property required by IEnumerator interface
/// </summary>
- object IEnumerator.Current
+ object? IEnumerator.Current
{
get
{
_currentChunkSize = new SharedInt(0);
_doublingCountdown = CHUNK_DOUBLING_RATE;
}
+ Debug.Assert(_currentChunkSize != null);
if (_localOffset.Value < _currentChunkSize.Value - 1)
//attempt to grab the next element from the local chunk
private readonly IEnumerator<TSource> _sharedReader;
private SharedLong _sharedIndex;//initial value -1
- private volatile KeyValuePair<long, TSource>[] _fillBuffer; // intermediate buffer to reduce locking
+ private volatile KeyValuePair<long, TSource>[]? _fillBuffer; // intermediate buffer to reduce locking
private volatile int _fillBufferSize; // actual number of elements in _FillBuffer. Will start
// at _FillBuffer.Length, and might be reduced during the last refill
private volatile int _fillBufferCurrentPosition; //shared value to be accessed by Interlock.Increment only
// If dynamic partitioning, then _activePartitionCount == null
// If static partitioning, then it keeps track of active partition count
- private SharedInt _activePartitionCount;
+ private SharedInt? _activePartitionCount;
// records whether or not the user has requested single-chunking behavior
private readonly bool _useSingleChunking;
// making a local defensive copy of the fill buffer reference, just in case it gets nulled out
- KeyValuePair<long, TSource>[] fillBufferLocalRef = _fillBuffer;
+ KeyValuePair<long, TSource>[]? fillBufferLocalRef = _fillBuffer;
if (fillBufferLocalRef == null) return;
// first do a quick check, and give up if the current position is at the end
{
//---- fields ----
//cached local copy of the current chunk
- private KeyValuePair<long, TSource>[] _localList; //defer allocating to avoid false sharing
+ private KeyValuePair<long, TSource>[]? _localList; //defer allocating to avoid false sharing
// the values of the following two fields are passed in from
// outside(already initialized) by the constructor,
private readonly SharedBool _hasNoElementsLeft;
- private readonly SharedInt _activePartitionCount;
+ private readonly SharedInt? _activePartitionCount;
private InternalPartitionEnumerable _enumerable;
//constructor
IEnumerator<TSource> sharedReader,
SharedLong sharedIndex,
SharedBool hasNoElementsLeft,
- SharedInt activePartitionCount,
+ SharedInt? activePartitionCount,
InternalPartitionEnumerable enumerable,
bool useSingleChunking)
: base(sharedReader, sharedIndex, useSingleChunking)
#pragma warning disable 0420 // TODO: https://github.com/dotnet/corefx/issues/35022
// make the actual call to the enumerable that grabs a chunk
- return _enumerable.GrabChunk(_localList, requestedChunkSize, ref _currentChunkSize.Value);
+ return _enumerable.GrabChunk(_localList, requestedChunkSize, ref _currentChunkSize!.Value);
#pragma warning restore 0420
}
throw new InvalidOperationException(SR.PartitionerStatic_CurrentCalledBeforeMoveNext);
}
Debug.Assert(_localList != null);
- Debug.Assert(_localOffset.Value >= 0 && _localOffset.Value < _currentChunkSize.Value);
+ Debug.Assert(_localOffset!.Value >= 0 && _localOffset.Value < _currentChunkSize.Value);
return (_localList[_localOffset.Value]);
}
}
//set up local indexes.
//_currentChunkSize is always set to requestedChunkSize when source data had
//enough elements of what we requested
- _currentChunkSize.Value = (int)(newSharedIndex - oldSharedIndex);
- _localOffset.Value = -1;
+ _currentChunkSize!.Value = (int)(newSharedIndex - oldSharedIndex);
+ _localOffset!.Value = -1;
_startIndex = (int)(oldSharedIndex + 1);
return true;
}
throw new InvalidOperationException(SR.PartitionerStatic_CurrentCalledBeforeMoveNext);
}
- Debug.Assert(_localOffset.Value >= 0 && _localOffset.Value < _currentChunkSize.Value);
+ Debug.Assert(_localOffset!.Value >= 0 && _localOffset.Value < _currentChunkSize.Value);
return new KeyValuePair<long, TSource>(_startIndex + _localOffset.Value,
_sharedReader[_startIndex + _localOffset.Value]);
}
throw new InvalidOperationException(SR.PartitionerStatic_CurrentCalledBeforeMoveNext);
}
- Debug.Assert(_localOffset.Value >= 0 && _localOffset.Value < _currentChunkSize.Value);
+ Debug.Assert(_localOffset!.Value >= 0 && _localOffset.Value < _currentChunkSize.Value);
return new KeyValuePair<long, TSource>(_startIndex + _localOffset.Value,
_sharedReader[_startIndex + _localOffset.Value]);
}
}
}
- object IEnumerator.Current
+ object? IEnumerator.Current
{
get
{
// Because of the lack of typeof(T).IsValueType we need two pieces of information
// to determine this. default(T) will return a non null for Value Types, except those
// using Nullable<>, that is why we need a second condition.
- if (default(TSource) != null || Nullable.GetUnderlyingType(typeof(TSource)) != null)
+ if (default(TSource)! != null || Nullable.GetUnderlyingType(typeof(TSource)) != null) // TODO-NULLABLE: https://github.com/dotnet/roslyn/issues/34757
{
// Marshal.SizeOf fails for value types that don't have explicit layouts. We
// just fall back to some arbitrary constant in that case. Is there a better way?