/// <summary> /// Removes all the elements. /// </summary> public void Clear() { _entriesOld = null; _entriesNew = new FixedSizeSetBucket <T>(INT_DefaultCapacity, _comparer); Thread.VolatileWrite(ref _status, (int)BucketStatus.Free); Thread.VolatileWrite(ref _count, 0); _revision++; }
private bool ContainsExtracted(T item, FixedSizeSetBucket <T> entries) { for (int attempts = 0; attempts < _maxProbing; attempts++) { if (entries.Contains(item, attempts) != -1) { return(true); } } return(false); }
private bool TryGetExtracted(int index, FixedSizeSetBucket <T> entries, out T item) { item = default(T); if (entries != null) { if (entries.TryGet(index, out item)) { return(true); } } return(false); }
private bool RemoveExtracted(T item, FixedSizeSetBucket <T> entries) { if (entries != null) { for (int attempts = 0; attempts < _maxProbing; attempts++) { if (entries.Remove(item, attempts) != -1) { return(true); } } } return(false); }
/// <summary> /// Initializes a new instance of the <see cref="SetBucket{TValue}" /> class. /// </summary> /// <param name="capacity">The initial capacity.</param> /// <param name="comparer">The comparer.</param> /// <param name="maxProbing">The maximum number of steps in linear probing.</param> /// <exception cref="System.ArgumentOutOfRangeException">maxProbing;maxProbing must be greater or equal to 1 and less than capacity.</exception> public SetBucket(int capacity, IEqualityComparer <T> comparer, int maxProbing) { if (maxProbing < 1 || maxProbing >= capacity) { throw new ArgumentOutOfRangeException("maxProbing", "maxProbing must be greater or equal to 1 and less than capacity."); } else { _comparer = comparer ?? EqualityComparer <T> .Default; _entriesOld = null; _entriesNew = new FixedSizeSetBucket <T>(capacity, _comparer); _maxProbing = maxProbing; } }
//HACK private int SetExtracted(T item, FixedSizeSetBucket <T> entries, out bool isNew) { isNew = false; if (entries != null) { for (int attempts = 0; attempts < _maxProbing; attempts++) { int index = entries.Set(item, attempts, out isNew); if (index != -1) { return(index); } } } return(-1); }
private int AddExtracted(T item, FixedSizeSetBucket <T> entries, out bool isCollision) { isCollision = true; if (entries != null) { for (int attempts = 0; attempts < _maxProbing; attempts++) { int index = entries.Add(item, attempts, out isCollision); if (index != -1 || !isCollision) { return(index); } } } return(-1); }
private void CooperativeGrow() { int status; do { status = Thread.VolatileRead(ref _status); int oldStatus; switch (status) { case (int)BucketStatus.GrowRequested: // This area is only accessed by one thread, if that thread is aborted, we are doomed. // This class is not abort safe // If a thread is being aborted here it's pending operation will be lost and there is risk of a livelock var priority = Thread.CurrentThread.Priority; oldStatus = Interlocked.CompareExchange(ref _status, (int)BucketStatus.Waiting, (int)BucketStatus.GrowRequested); if (oldStatus == (int)BucketStatus.GrowRequested) { try { // The progress of other threads depend of this one, we should not allow a priority inversion. Thread.CurrentThread.Priority = ThreadPriority.Highest; //_copyPosition is set to -1. _copyPosition is incremented before it is used, so the first time it is used it will be 0. Thread.VolatileWrite(ref _copyPosition, -1); //The new capacity is twice the old capacity, the capacity must be a power of two.; var newCapacity = _entriesNew.Capacity * 2; _entriesOld = Interlocked.Exchange(ref _entriesNew, new FixedSizeSetBucket <T>(newCapacity, _comparer)); oldStatus = Interlocked.CompareExchange(ref _status, (int)BucketStatus.Copy, (int)BucketStatus.Waiting); } finally { Thread.CurrentThread.Priority = priority; _revision++; } } break; case (int)BucketStatus.Waiting: // This is the whole reason why this datastructure is not wait free. // Testing shows that it is uncommon that a thread enters here. // _status is 2 only for a short period. // Still, it happens, so this is needed for correctness. // Going completely wait-free adds complexity with deminished value. Thread.SpinWait(INT_SpinWaitHint); if (Thread.VolatileRead(ref _status) == 2) { Thread.Sleep(0); } break; case (int)BucketStatus.Copy: // It is time to cooperate to copy the old storage to the new one var old = _entriesOld; if (old != null) { // This class is not abort safe // If a thread is being aborted here it will causing lost items. _revision++; Interlocked.Increment(ref _copyingThreads); T item; int index = Interlocked.Increment(ref _copyPosition); for (; index < old.Capacity; index = Interlocked.Increment(ref _copyPosition)) { if (old.TryGet(index, out item)) { // We have read an item, so let's try to add it to the new storage bool dummy; //HACK if (SetExtracted(item, _entriesNew, out dummy) == -1) { GC.KeepAlive(dummy); } } } Interlocked.CompareExchange(ref _status, (int)BucketStatus.CopyCleanup, (int)BucketStatus.Copy); _revision++; Interlocked.Decrement(ref _copyingThreads); } break; case (int)BucketStatus.CopyCleanup: // Our copy is finished, we don't need the old storage anymore oldStatus = Interlocked.CompareExchange(ref _status, (int)BucketStatus.Waiting, (int)BucketStatus.CopyCleanup); if (oldStatus == (int)BucketStatus.CopyCleanup) { _revision++; Interlocked.Exchange(ref _entriesOld, null); Interlocked.CompareExchange(ref _status, (int)BucketStatus.Free, (int)BucketStatus.Waiting); } break; default: break; } }while (status != 0); }