132 lines
3.9 KiB
C#
132 lines
3.9 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Threading;
|
|
using JD.Collections.Immutable;
|
|
|
|
namespace JD.Collections.Lockless
|
|
{
|
|
/// <summary>
|
|
/// A threadsafe stack.
|
|
/// </summary>
|
|
public sealed class LocklessStack<T> : IEnumerable<T>
|
|
{
|
|
#region Variables
|
|
|
|
private ImmutableStack<T> m_innerStack = new ImmutableStack<T>();
|
|
|
|
#endregion
|
|
|
|
#region Methods
|
|
|
|
#region Stack methods
|
|
|
|
/// <summary>
|
|
/// Peeks the top value on the stack.
|
|
/// </summary>
|
|
/// <param name="value">The top value of the stack at the moment of peeking.</param>
|
|
/// <returns>False if the stack was empty at the moment of peeking.</returns>
|
|
public bool TryPeek(out T value)
|
|
{
|
|
ImmutableStack<T> stack = m_innerStack;
|
|
|
|
if (!stack.IsEmpty)
|
|
{
|
|
value = stack.Peek();
|
|
return true;
|
|
}
|
|
|
|
value = default(T);
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the top value on the stack.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">If the stack is empty.</exception>
|
|
/// <returns></returns>
|
|
public T Peek()
|
|
{
|
|
return m_innerStack.Peek();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Peeks the top value on the stack.
|
|
/// </summary>
|
|
/// <param name="value">The top value of the stack at the moment of peeking.</param>
|
|
/// <returns>False if the stack was empty at the moment of peeking.</returns>
|
|
public bool TryPop(out T value)
|
|
{
|
|
bool worked = false;
|
|
ImmutableStack<T> oldValue = Assign(ref m_innerStack, delegate(ImmutableStack<T> stack)
|
|
{
|
|
worked = !stack.IsEmpty;
|
|
return worked ? stack.Pop() : stack;
|
|
});
|
|
|
|
value = worked ? oldValue.Peek() : default(T);
|
|
return worked;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a new stack with the top element removed.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">If the stack is empty.</exception>
|
|
/// <returns></returns>
|
|
public T Pop()
|
|
{
|
|
return Assign(ref m_innerStack, delegate(ImmutableStack<T> stack) { return stack.Pop(); }).Peek();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a new stack with the given <paramref name="value"/> on top.
|
|
/// </summary>
|
|
/// <param name="value"></param>
|
|
/// <returns></returns>
|
|
public LocklessStack<T> Push(T value)
|
|
{
|
|
Assign(ref m_innerStack, delegate(ImmutableStack<T> stack) { return stack.Push(value); });
|
|
return this;
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return m_innerStack.GetEnumerator();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator() {return GetEnumerator(); }
|
|
|
|
#endregion
|
|
|
|
#region General Utilities (These should probably be moved, they will work with any immutable class)
|
|
|
|
private static TImmutable Assign<TImmutable>(ref TImmutable referenceToChange, Func<TImmutable, TImmutable> operation)
|
|
where TImmutable : class
|
|
{
|
|
TImmutable oldValue;
|
|
|
|
while (!TryExchange(ref referenceToChange, operation, out oldValue))
|
|
{ }
|
|
|
|
return oldValue;
|
|
}
|
|
|
|
private static bool TryExchange<TValue>(ref TValue value, Func<TValue, TValue> operation, out TValue oldValue)
|
|
where TValue : class
|
|
{
|
|
oldValue = value;
|
|
TValue newValue = operation(oldValue);
|
|
|
|
if (oldValue == newValue)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return Interlocked.CompareExchange(ref value, newValue, oldValue) == oldValue;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
}
|
|
}
|