JD2022-TU1/main/tools/framework/JD.Collections/Lockless/LocklessStack.cs

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
}
}