You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
512 lines
21 KiB
512 lines
21 KiB
5 years ago
|
#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
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Represent a thread-safe wait-free fixed size bucket with lazy initialization.
|
||
|
/// </summary>
|
||
|
/// <typeparam name="T">The type of the item.</typeparam>
|
||
|
/// <typeparam name="TNeedle">THe type of the needles</typeparam>
|
||
|
/// <remarks>
|
||
|
/// Consider wrapping this class to implement <see cref="ICollection{T}" /> or any other desired interface.
|
||
|
/// </remarks>
|
||
|
[Serializable]
|
||
|
internal sealed class NeedleBucket<T, TNeedle> : IEnumerable<T>, IBucket<T>
|
||
|
where TNeedle : class, IRecyclableNeedle<T>
|
||
|
{
|
||
|
private readonly FixedSizeBucket<TNeedle> _entries;
|
||
|
private readonly Func<T, TNeedle> _needleFactory;
|
||
|
private readonly Func<int, TNeedle> _needleIndexFactory;
|
||
|
|
||
|
[NonSerialized]
|
||
|
private NeedleReservoir<T, TNeedle> _reservoir;
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="NeedleBucket{T, TNeedle}" /> class.
|
||
|
/// </summary>
|
||
|
/// <param name = "valueFactory">The delegate that is invoked to do the lazy initialization of the items given their index.</param>
|
||
|
/// <param name="needleFactory">The delegate that is invoked to create a needle</param>
|
||
|
/// <param name="capacity">The capacity.</param>
|
||
|
/// <exception cref="InvalidOperationException"></exception>
|
||
|
/// <exception cref="ArgumentNullException"><paramref name="valueFactory"/> is <c>null</c>.</exception>
|
||
|
public NeedleBucket(Func<int, T> valueFactory, Func<T, TNeedle> needleFactory, int capacity)
|
||
|
{
|
||
|
if (valueFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("valueFactory");
|
||
|
}
|
||
|
if (needleFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("needleFactory");
|
||
|
}
|
||
|
_needleFactory = needleFactory;
|
||
|
_reservoir = new NeedleReservoir<T, TNeedle>(_needleFactory);
|
||
|
_needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure<int, T>(valueFactory, index).InvokeReturn());
|
||
|
_entries = new FixedSizeBucket<TNeedle>(capacity);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Initializes a new instance of the <see cref="NeedleBucket{T, TNeedle}" /> class.
|
||
|
/// </summary>
|
||
|
/// <param name = "valueFactory">The delegate that is invoked to do the lazy initialization of the items.</param>
|
||
|
/// <param name="needleFactory">The delegate that is invoked to create a needle</param>
|
||
|
/// <param name="capacity">The capacity.</param>
|
||
|
/// <exception cref="InvalidOperationException"></exception>
|
||
|
/// <exception cref="ArgumentNullException"><paramref name="valueFactory"/> is <c>null</c>.</exception>
|
||
|
public NeedleBucket(Func<T> valueFactory, Func<T, TNeedle> needleFactory, int capacity)
|
||
|
{
|
||
|
if (valueFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("valueFactory");
|
||
|
}
|
||
|
if (needleFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("needleFactory");
|
||
|
}
|
||
|
_needleFactory = needleFactory;
|
||
|
_reservoir = new NeedleReservoir<T, TNeedle>(_needleFactory);
|
||
|
_needleIndexFactory = index => Reservoir.GetNeedle(new ValueFuncClosure<T>(valueFactory).InvokeReturn());
|
||
|
_entries = new FixedSizeBucket<TNeedle>(capacity);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the capacity.
|
||
|
/// </summary>
|
||
|
public int Capacity
|
||
|
{
|
||
|
get { return _entries.Capacity; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets the number of items actually contained.
|
||
|
/// </summary>
|
||
|
public int Count
|
||
|
{
|
||
|
get { return _entries.Count; }
|
||
|
}
|
||
|
|
||
|
public NeedleReservoir<T, TNeedle> Reservoir
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
var found = Interlocked.CompareExchange(ref _reservoir, null, null);
|
||
|
if (found != null)
|
||
|
{
|
||
|
return found;
|
||
|
}
|
||
|
var created = new NeedleReservoir<T, TNeedle>(_needleFactory);
|
||
|
found = Interlocked.CompareExchange(ref _reservoir, created, null);
|
||
|
return found ?? created;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Copies the items to a compatible one-dimensional array, starting at the specified index of the target array.
|
||
|
/// </summary>
|
||
|
/// <param name="array">The array.</param>
|
||
|
/// <param name="arrayIndex">Index of the array.</param>
|
||
|
/// <exception cref="System.ArgumentNullException">array</exception>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">arrayIndex;Non-negative number is required.</exception>
|
||
|
/// <exception cref="System.ArgumentException">array;The array can not contain the number of elements.</exception>
|
||
|
public void CopyTo(T[] array, int arrayIndex)
|
||
|
{
|
||
|
_entries.ToArray().CopyTo(array, arrayIndex);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="item">The item.</param>
|
||
|
/// <param name="previous">The previous item in the specified index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was new; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="needle">The needle.</param>
|
||
|
/// <param name="previous">The previous item in the specified index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was new; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Retrieve or creates a new item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <returns>The value.</returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an enumerator that iterates through a collection.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// An <see cref="System.Collections.Generic.IEnumerator{T}" /> object that can be used to iterate through the collection.
|
||
|
/// </returns>
|
||
|
public IEnumerator<T> 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();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Retrieve or creates a new needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <returns>The needle.</returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Inserts the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="item">The item.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was inserted; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity.</exception>
|
||
|
/// <remarks>
|
||
|
/// 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.
|
||
|
/// </remarks>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Inserts the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="item">The item.</param>
|
||
|
/// <param name="previous">The previous item in the specified index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was inserted; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
/// <remarks>
|
||
|
/// 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.
|
||
|
/// </remarks>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Inserts the needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="needle">The needle.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the needle was inserted; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity.</exception>
|
||
|
/// <remarks>
|
||
|
/// 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.
|
||
|
/// </remarks>
|
||
|
public bool InsertNeedle(int index, TNeedle needle)
|
||
|
{
|
||
|
// This may allow null to enter
|
||
|
return _entries.Insert(index, needle);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Inserts the needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="needle">The needle.</param>
|
||
|
/// <param name="previous">The previous needle in the specified index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the needle was inserted; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
/// <remarks>
|
||
|
/// 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.
|
||
|
/// </remarks>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was removed; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
public bool RemoveAt(int index)
|
||
|
{
|
||
|
return _entries.RemoveAt(index);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="previous">The previous item in the specified index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was removed; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
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<T> check)
|
||
|
{
|
||
|
if (check == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
TNeedle found = null;
|
||
|
Predicate<TNeedle> replacementCheck = needle =>
|
||
|
{
|
||
|
found = needle;
|
||
|
return check(needle.Value);
|
||
|
};
|
||
|
if (_entries.RemoveAt(index, replacementCheck))
|
||
|
{
|
||
|
// Donate it
|
||
|
Reservoir.DonateNeedle(found);
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes the needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="previous">The previous needle in the specified index.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the needle was removed; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
public bool RemoveNeedleAt(int index, out TNeedle previous)
|
||
|
{
|
||
|
return _entries.RemoveAt(index, out previous);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="item">The item.</param>
|
||
|
/// <param name="isNew">if set to <c>true</c> the index was not previously used.</param>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
public void Set(int index, T item, out bool isNew)
|
||
|
{
|
||
|
// This may have replaced something
|
||
|
_entries.Set(index, Reservoir.GetNeedle(item), out isNew);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Sets the needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="needle">The needle.</param>
|
||
|
/// <param name="isNew">if set to <c>true</c> the index was not previously used.</param>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
public void SetNeedle(int index, TNeedle needle, out bool isNew)
|
||
|
{
|
||
|
// This may allow null to enter
|
||
|
_entries.Set(index, needle, out isNew);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Tries to retrieve the item at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="value">The value.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the item was retrieved; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Tries to retrieve the needle at the specified index.
|
||
|
/// </summary>
|
||
|
/// <param name="index">The index.</param>
|
||
|
/// <param name="value">The value.</param>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the needle was retrieved; otherwise, <c>false</c>.
|
||
|
/// </returns>
|
||
|
/// <exception cref="System.ArgumentOutOfRangeException">index;index must be greater or equal to 0 and less than capacity</exception>
|
||
|
public bool TryGetNeedle(int index, out TNeedle value)
|
||
|
{
|
||
|
return _entries.TryGet(index, out value);
|
||
|
}
|
||
|
|
||
|
public bool Update(int index, Func<T, T> itemUpdateFactory, Predicate<T> check, out bool isEmpty)
|
||
|
{
|
||
|
if (itemUpdateFactory == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
if (check == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
TNeedle newNeedle = null;
|
||
|
Func<TNeedle, TNeedle> replacementFactory = needle => newNeedle = Reservoir.GetNeedle(itemUpdateFactory(needle.Value));
|
||
|
Predicate<TNeedle> 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<T> Where(Predicate<T> check)
|
||
|
{
|
||
|
if (check == null)
|
||
|
{
|
||
|
throw new ArgumentNullException("check");
|
||
|
}
|
||
|
foreach (var needle in _entries.Where(needle => check(needle.Value)))
|
||
|
{
|
||
|
yield return needle.Value;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif
|