#if FAT using System; using System.Collections.Generic; using System.Linq; using System.Threading; using LinqInternal.Core; using LinqInternal.Threading.Needles; namespace LinqInternal.Collections.ThreadSafe { /// /// Represent a thread-safe wait-free fixed size bucket with lazy initialization. /// /// The type of the item. /// THe type of the needles /// /// Consider wrapping this class to implement or any other desired interface. /// [Serializable] internal sealed class NeedleBucket : IEnumerable, IBucket where TNeedle : class, IRecyclableNeedle { private readonly FixedSizeBucket _entries; private readonly Func _needleFactory; private readonly Func _needleIndexFactory; [NonSerialized] private NeedleReservoir _reservoir; /// /// Initializes a new instance of the class. /// /// The delegate that is invoked to do the lazy initialization of the items given their index. /// The delegate that is invoked to create a needle /// The capacity. /// /// is null. public NeedleBucket(Func valueFactory, Func needleFactory, int capacity) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } if (needleFactory == null) { throw new ArgumentNullException("needleFactory"); } _needleFactory = needleFactory; _reservoir = new NeedleReservoir(_needleFactory); _needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure(valueFactory, index).InvokeReturn()); _entries = new FixedSizeBucket(capacity); } /// /// Initializes a new instance of the class. /// /// The delegate that is invoked to do the lazy initialization of the items. /// The delegate that is invoked to create a needle /// The capacity. /// /// is null. public NeedleBucket(Func valueFactory, Func needleFactory, int capacity) { if (valueFactory == null) { throw new ArgumentNullException("valueFactory"); } if (needleFactory == null) { throw new ArgumentNullException("needleFactory"); } _needleFactory = needleFactory; _reservoir = new NeedleReservoir(_needleFactory); _needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure(valueFactory).InvokeReturn()); _entries = new FixedSizeBucket(capacity); } /// /// Gets the capacity. /// public int Capacity { get { return _entries.Capacity; } } /// /// Gets the number of items actually contained. /// public int Count { get { return _entries.Count; } } public NeedleReservoir Reservoir { get { var found = Interlocked.CompareExchange(ref _reservoir, null, null); if (found != null) { return found; } var created = new NeedleReservoir(_needleFactory); found = Interlocked.CompareExchange(ref _reservoir, created, null); return found ?? created; } } /// /// Copies the items to a compatible one-dimensional array, starting at the specified index of the target array. /// /// The array. /// Index of the array. /// array /// arrayIndex;Non-negative number is required. /// array;The array can not contain the number of elements. public void CopyTo(T[] array, int arrayIndex) { _entries.ToArray().CopyTo(array, arrayIndex); } /// /// Sets the item at the specified index. /// /// The index. /// The item. /// The previous item in the specified index. /// /// true if the item was new; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool Exchange(int index, T item, out T previous) { TNeedle found; if (_entries.Exchange(index, Reservoir.GetNeedle(item), out found)) { previous = default(T); return true; } // TryGetValue is null resistant found.TryGetValue(out previous); // This is a needle that is no longer referenced, we donate it Reservoir.DonateNeedle(found); return false; } /// /// Sets the needle at the specified index. /// /// The index. /// The needle. /// The previous item in the specified index. /// /// true if the item was new; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool ExchangeNeedle(int index, TNeedle needle, out TNeedle previous) { // This may allow null to enter, this may also give null as previous if null was allowed to enter // We don't donate because we return the needle return _entries.Exchange(index, needle, out previous); } /// /// Retrieve or creates a new item at the specified index. /// /// The index. /// The value. /// index;index must be greater or equal to 0 and less than capacity public T Get(int index) { if (index < 0 || index >= _entries.Capacity) { throw new ArgumentOutOfRangeException("index", "index must be greater or equal to 0 and less than capacity"); } // Using TryGetValue first just avoid wasting a needle TNeedle found; if (_entries.TryGetInternal(index, out found)) { // Null resistant T previous; found.TryGetValue(out previous); return previous; // We don't donate because we didn't remove } var newNeedle = _needleIndexFactory(index); if (_entries.InsertInternal(index, newNeedle, out found)) { return newNeedle.Value; } // we just failed to insert, meaning that we created a usless needle // donate it Reservoir.DonateNeedle(newNeedle); return found.Value; } /// /// Returns an enumerator that iterates through a collection. /// /// /// An object that can be used to iterate through the collection. /// public IEnumerator GetEnumerator() { foreach (var needle in _entries) { T item; if (needle.TryGetValue(out item)) { yield return item; } } } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Retrieve or creates a new needle at the specified index. /// /// The index. /// The needle. /// index;index must be greater or equal to 0 and less than capacity public TNeedle GetNeedle(int index) { var newNeedle = _needleIndexFactory(index); TNeedle found; if (_entries.Insert(index, newNeedle, out found)) { return newNeedle; } // we just failed to insert, meaning that we created a usless needle // donate it Reservoir.DonateNeedle(newNeedle); return found; } /// /// Inserts the item at the specified index. /// /// The index. /// The item. /// /// true if the item was inserted; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity. /// /// The insertion can fail if the index is already used or is being written by another thread. /// If the index is being written it can be understood that the insert operation happened before but the item was overwritten or removed. /// public bool Insert(int index, T item) { // Only succeeds if there was nothing there before // meaning that if this succeeds it replaced nothing // If this fails whatever was there is still there var newNeedle = Reservoir.GetNeedle(item); if (_entries.Insert(index, newNeedle)) { return true; } Reservoir.DonateNeedle(newNeedle); return false; } /// /// Inserts the item at the specified index. /// /// The index. /// The item. /// The previous item in the specified index. /// /// true if the item was inserted; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity /// /// The insertion can fail if the index is already used or is being written by another thread. /// If the index is being written it can be understood that the insert operation happened before but the item was overwritten or removed. /// public bool Insert(int index, T item, out T previous) { // Only succeeds if there was nothing there before // meaning that if this succeeds it replaced nothing // If this fails whatever was there is still there var newNeedle = Reservoir.GetNeedle(item); TNeedle found; if (_entries.Insert(index, newNeedle, out found)) { previous = default(T); return true; } Reservoir.DonateNeedle(newNeedle); // TryGetValue is null resistant found.TryGetValue(out previous); return false; } /// /// Inserts the needle at the specified index. /// /// The index. /// The needle. /// /// true if the needle was inserted; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity. /// /// The insertion can fail if the index is already used or is being written by another thread. /// If the index is being written it can be understood that the insert operation happened before but the needle was overwritten or removed. /// public bool InsertNeedle(int index, TNeedle needle) { // This may allow null to enter return _entries.Insert(index, needle); } /// /// Inserts the needle at the specified index. /// /// The index. /// The needle. /// The previous needle in the specified index. /// /// true if the needle was inserted; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity /// /// The insertion can fail if the index is already used or is being written by another thread. /// If the index is being written it can be understood that the insert operation happened before but the needle was overwritten or removed. /// public bool InsertNeedle(int index, TNeedle needle, out TNeedle previous) { // This may allow null to enter, this may also give null as previous if null was allowed to enter return _entries.Insert(index, needle, out previous); } /// /// Removes the item at the specified index. /// /// The index. /// /// true if the item was removed; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool RemoveAt(int index) { return _entries.RemoveAt(index); } /// /// Removes the item at the specified index. /// /// The index. /// The previous item in the specified index. /// /// true if the item was removed; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool RemoveAt(int index, out T previous) { TNeedle found; if (_entries.RemoveAt(index, out found)) { // TryGetValue is null resistant found.TryGetValue(out previous); // Donate it Reservoir.DonateNeedle(found); return true; } previous = default(T); return false; } public bool RemoveAt(int index, Predicate check) { if (check == null) { throw new ArgumentNullException("check"); } TNeedle found = null; Predicate replacementCheck = needle => { found = needle; return check(needle.Value); }; if (_entries.RemoveAt(index, replacementCheck)) { // Donate it Reservoir.DonateNeedle(found); return true; } return false; } /// /// Removes the needle at the specified index. /// /// The index. /// The previous needle in the specified index. /// /// true if the needle was removed; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool RemoveNeedleAt(int index, out TNeedle previous) { return _entries.RemoveAt(index, out previous); } /// /// Sets the item at the specified index. /// /// The index. /// The item. /// if set to true the index was not previously used. /// index;index must be greater or equal to 0 and less than capacity public void Set(int index, T item, out bool isNew) { // This may have replaced something _entries.Set(index, Reservoir.GetNeedle(item), out isNew); } /// /// Sets the needle at the specified index. /// /// The index. /// The needle. /// if set to true the index was not previously used. /// index;index must be greater or equal to 0 and less than capacity public void SetNeedle(int index, TNeedle needle, out bool isNew) { // This may allow null to enter _entries.Set(index, needle, out isNew); } /// /// Tries to retrieve the item at the specified index. /// /// The index. /// The value. /// /// true if the item was retrieved; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool TryGet(int index, out T value) { TNeedle found; if (_entries.TryGet(index, out found)) { // Null resistant found.TryGetValue(out value); // We don't donate because we didn't remove return true; } value = default(T); return false; } /// /// Tries to retrieve the needle at the specified index. /// /// The index. /// The value. /// /// true if the needle was retrieved; otherwise, false. /// /// index;index must be greater or equal to 0 and less than capacity public bool TryGetNeedle(int index, out TNeedle value) { return _entries.TryGet(index, out value); } public bool Update(int index, Func itemUpdateFactory, Predicate check, out bool isEmpty) { if (itemUpdateFactory == null) { throw new ArgumentNullException("check"); } if (check == null) { throw new ArgumentNullException("check"); } TNeedle newNeedle = null; Func replacementFactory = needle => newNeedle = Reservoir.GetNeedle(itemUpdateFactory(needle.Value)); Predicate replacementCheck = needle => check(needle.Value); if (_entries.Update(index, replacementFactory, replacementCheck, out isEmpty)) { return true; } if (newNeedle != null) { Reservoir.DonateNeedle(newNeedle); } return false; } public IEnumerable Where(Predicate check) { if (check == null) { throw new ArgumentNullException("check"); } foreach (var needle in _entries.Where(needle => check(needle.Value))) { yield return needle.Value; } } } } #endif