316 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			316 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C#
		
	
	
using System.Runtime.CompilerServices;
 | 
						|
using System.Runtime.InteropServices;
 | 
						|
using System.Threading;
 | 
						|
 | 
						|
namespace dotNetty_kcp.thread
 | 
						|
{
 | 
						|
    public class RingBuffer<T>
 | 
						|
    {
 | 
						|
        private readonly T[] _entries;
 | 
						|
        private readonly int _modMask;
 | 
						|
        private Volatile.PaddedLong _consumerCursor = new Volatile.PaddedLong();
 | 
						|
        private Volatile.PaddedLong _producerCursor = new Volatile.PaddedLong();
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Creates a new RingBuffer with the given capacity
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="capacity">The capacity of the buffer</param>
 | 
						|
        /// <remarks>Only a single thread may attempt to consume at any one time</remarks>
 | 
						|
        public RingBuffer(int capacity)
 | 
						|
        {
 | 
						|
            capacity = NextPowerOfTwo(capacity);
 | 
						|
            _modMask = capacity - 1;
 | 
						|
            _entries = new T[capacity];
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// The maximum number of items that can be stored
 | 
						|
        /// </summary>
 | 
						|
        public int Capacity
 | 
						|
        {
 | 
						|
            get { return _entries.Length; }
 | 
						|
        }
 | 
						|
 | 
						|
        public T this[long index]
 | 
						|
        {
 | 
						|
            get
 | 
						|
            {
 | 
						|
                unchecked
 | 
						|
                {
 | 
						|
                    return _entries[index & _modMask];
 | 
						|
                }
 | 
						|
            }
 | 
						|
            set
 | 
						|
            {
 | 
						|
                unchecked
 | 
						|
                {
 | 
						|
                    _entries[index & _modMask] = value;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Removes an item from the buffer.
 | 
						|
        /// </summary>
 | 
						|
        /// <returns>The next available item</returns>
 | 
						|
        public T Dequeue()
 | 
						|
        {
 | 
						|
            var next = _consumerCursor.ReadAcquireFence() + 1;
 | 
						|
            while (_producerCursor.ReadAcquireFence() < next
 | 
						|
            ) // makes sure we read the data from _entries after we have read the producer cursor
 | 
						|
            {
 | 
						|
                Thread.SpinWait(1);
 | 
						|
            }
 | 
						|
 | 
						|
            var result = this[next];
 | 
						|
            _consumerCursor
 | 
						|
                .WriteReleaseFence(
 | 
						|
                    next); // makes sure we read the data from _entries before we update the consumer cursor
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Attempts to remove an items from the queue
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="obj">the items</param>
 | 
						|
        /// <returns>True if successful</returns>
 | 
						|
        public bool TryDequeue(out T obj)
 | 
						|
        {
 | 
						|
            var next = _consumerCursor.ReadAcquireFence() + 1;
 | 
						|
 | 
						|
            if (_producerCursor.ReadAcquireFence() < next)
 | 
						|
            {
 | 
						|
                obj = default(T);
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            obj = Dequeue();
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Add an item to the buffer
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="item"></param>
 | 
						|
        public void Enqueue(T item)
 | 
						|
        {
 | 
						|
            var next = _producerCursor.ReadAcquireFence() + 1;
 | 
						|
 | 
						|
            long wrapPoint = next - _entries.Length;
 | 
						|
            long min = _consumerCursor.ReadAcquireFence();
 | 
						|
 | 
						|
            while (wrapPoint > min)
 | 
						|
            {
 | 
						|
                min = _consumerCursor.ReadAcquireFence();
 | 
						|
                Thread.SpinWait(1);
 | 
						|
            }
 | 
						|
 | 
						|
            this[next] = item;
 | 
						|
            _producerCursor
 | 
						|
                .WriteReleaseFence(
 | 
						|
                    next); // makes sure we write the data in _entries before we update the producer cursor
 | 
						|
        }
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// Add an item to the buffer
 | 
						|
        /// </summary>
 | 
						|
        /// <param name="item"></param>
 | 
						|
        public bool tryEnqueue(T item)
 | 
						|
        {
 | 
						|
            var next = _producerCursor.ReadAcquireFence() + 1;
 | 
						|
 | 
						|
            long wrapPoint = next - _entries.Length;
 | 
						|
            long min = _consumerCursor.ReadAcquireFence();
 | 
						|
 | 
						|
            if (wrapPoint>min)
 | 
						|
            {
 | 
						|
                return false;
 | 
						|
            }
 | 
						|
 | 
						|
            this[next] = item;
 | 
						|
            _producerCursor
 | 
						|
                .WriteReleaseFence(
 | 
						|
                    next); // makes sure we write the data in _entries before we update the producer cursor
 | 
						|
            return true;
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        /// <summary>
 | 
						|
        /// The number of items in the buffer
 | 
						|
        /// </summary>
 | 
						|
        /// <remarks>for indicative purposes only, may contain stale data</remarks>
 | 
						|
        public int Count
 | 
						|
        {
 | 
						|
            get { return (int) (_producerCursor.ReadFullFence() - _consumerCursor.ReadFullFence()); }
 | 
						|
        }
 | 
						|
 | 
						|
        private static int NextPowerOfTwo(int x)
 | 
						|
        {
 | 
						|
            var result = 2;
 | 
						|
            while (result < x)
 | 
						|
            {
 | 
						|
                result <<= 1;
 | 
						|
            }
 | 
						|
 | 
						|
            return result;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public static class Volatile
 | 
						|
    {
 | 
						|
        private const int CacheLineSize = 64;
 | 
						|
 | 
						|
        [StructLayout(LayoutKind.Explicit, Size = CacheLineSize * 2)]
 | 
						|
        public struct PaddedLong
 | 
						|
        {
 | 
						|
            [FieldOffset(CacheLineSize)] private long _value;
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Create a new <see cref="PaddedLong"/> with the given initial value.
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="value">Initial value</param>
 | 
						|
            public PaddedLong(long value)
 | 
						|
            {
 | 
						|
                _value = value;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Read the value without applying any fence
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>The current value</returns>
 | 
						|
            public long ReadUnfenced()
 | 
						|
            {
 | 
						|
                return _value;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Read the value applying acquire fence semantic
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>The current value</returns>
 | 
						|
            public long ReadAcquireFence()
 | 
						|
            {
 | 
						|
                var value = _value;
 | 
						|
                Thread.MemoryBarrier();
 | 
						|
                return value;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Read the value applying full fence semantic
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>The current value</returns>
 | 
						|
            public long ReadFullFence()
 | 
						|
            {
 | 
						|
                Thread.MemoryBarrier();
 | 
						|
                return _value;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Read the value applying a compiler only fence, no CPU fence is applied
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>The current value</returns>
 | 
						|
            [MethodImpl(MethodImplOptions.NoOptimization)]
 | 
						|
            public long ReadCompilerOnlyFence()
 | 
						|
            {
 | 
						|
                return _value;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Write the value applying release fence semantic
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="newValue">The new value</param>
 | 
						|
            public void WriteReleaseFence(long newValue)
 | 
						|
            {
 | 
						|
                Thread.MemoryBarrier();
 | 
						|
                _value = newValue;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Write the value applying full fence semantic
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="newValue">The new value</param>
 | 
						|
            public void WriteFullFence(long newValue)
 | 
						|
            {
 | 
						|
                Thread.MemoryBarrier();
 | 
						|
                _value = newValue;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Write the value applying a compiler fence only, no CPU fence is applied
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="newValue">The new value</param>
 | 
						|
            [MethodImpl(MethodImplOptions.NoOptimization)]
 | 
						|
            public void WriteCompilerOnlyFence(long newValue)
 | 
						|
            {
 | 
						|
                _value = newValue;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Write without applying any fence
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="newValue">The new value</param>
 | 
						|
            public void WriteUnfenced(long newValue)
 | 
						|
            {
 | 
						|
                _value = newValue;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Atomically set the value to the given updated value if the current value equals the comparand
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="newValue">The new value</param>
 | 
						|
            /// <param name="comparand">The comparand (expected value)</param>
 | 
						|
            /// <returns></returns>
 | 
						|
            public bool AtomicCompareExchange(long newValue, long comparand)
 | 
						|
            {
 | 
						|
                return Interlocked.CompareExchange(ref _value, newValue, comparand) == comparand;
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Atomically set the value to the given updated value
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="newValue">The new value</param>
 | 
						|
            /// <returns>The original value</returns>
 | 
						|
            public long AtomicExchange(long newValue)
 | 
						|
            {
 | 
						|
                return Interlocked.Exchange(ref _value, newValue);
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Atomically add the given value to the current value and return the sum
 | 
						|
            /// </summary>
 | 
						|
            /// <param name="delta">The value to be added</param>
 | 
						|
            /// <returns>The sum of the current value and the given value</returns>
 | 
						|
            public long AtomicAddAndGet(long delta)
 | 
						|
            {
 | 
						|
                return Interlocked.Add(ref _value, delta);
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Atomically increment the current value and return the new value
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>The incremented value.</returns>
 | 
						|
            public long AtomicIncrementAndGet()
 | 
						|
            {
 | 
						|
                return Interlocked.Increment(ref _value);
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Atomically increment the current value and return the new value
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>The decremented value.</returns>
 | 
						|
            public long AtomicDecrementAndGet()
 | 
						|
            {
 | 
						|
                return Interlocked.Decrement(ref _value);
 | 
						|
            }
 | 
						|
 | 
						|
            /// <summary>
 | 
						|
            /// Returns the string representation of the current value.
 | 
						|
            /// </summary>
 | 
						|
            /// <returns>the string representation of the current value.</returns>
 | 
						|
            public override string ToString()
 | 
						|
            {
 | 
						|
                var value = ReadFullFence();
 | 
						|
                return value.ToString();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |