I'm implementing a "live analytics" feature and I need to always keep track of the last N items that were added to a collection so I figured I'd make a circular buffer since I couldn't find one.
I tried to figure out to use Interlocked but couldn't find out how to use Increment or CompareExchange in an obviously correct way - so I took the lock.
Review/advice is appreciated.
public class CircularBuffer<T>
{
private readonly T[] _data;
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim();
private int _i = 0;
private bool _isOneCycleFinished = false;
private readonly int _size;
public CircularBuffer(int size)
{
if (size < 0)
{
throw new ArgumentException($"{nameof(size)} cannot be negative");
}
_i = 0;
_data = new T[size];
_size = size;
}
public IEnumerable<T> Latest()
{
try
{
_lock.EnterReadLock();
if (!_isOneCycleFinished)
{
return _data.Take(_i).ToList();
}
return _data.Concat(_data).Skip(_i).Take(_size).ToList();
}
finally
{
_lock.ExitReadLock();
}
}
public void Add(T t)
{
try
{
_lock.EnterWriteLock();
_i = (_i + 1)%_size;
if (!_isOneCycleFinished && _i == 0)
{
_isOneCycleFinished = true;
}
_data[_i] = t;
}
finally
{
_lock.ExitWriteLock();
}
}
}
Interlocked.Exchange(ref _i, (_i + 1) % _size);and get rid of the local i variable. \$\endgroup\$_isOneCycleFinished? \$\endgroup\$_iitems or I need to return the_sizeitems starting ati. If the buffer is of ints and of size 4 - It starts as0000, I add 1 and 2 and get1200- withLatestI expect to get1,2. Now if I add3456in - the buffer is at5634, I expect to get3456. \$\endgroup\$Add()'s write to_isOneCycleFinishedandLatest()'s read of_isOneCycleFinishedwere not synchronised, nor were the writing and reading of_i. \$\endgroup\$