UmberHubManager/api/lib/BrainStem2/LocklessQueue_SPSC.h

120 lines
4.2 KiB
C++

/////////////////////////////////////////////////////////////////////
// //
// file: LockLessQueue_SPSC.h //
// //
/////////////////////////////////////////////////////////////////////
// //
// description: Template implementation of a thread-safe single //
// producer, single consumer lock-less queue. //
// //
// build number: source //
// //
/////////////////////////////////////////////////////////////////////
// //
// Copyright (c) 2022 Acroname Inc. - All Rights Reserved //
// //
// This file is part of the BrainStem release. See the license.txt //
// file included with this package or go to //
// https://acroname.com/software/brainstem-development-kit //
// for full license details. //
/////////////////////////////////////////////////////////////////////
#ifndef _LocklessQueue_SPSC_H_
#define _LocklessQueue_SPSC_H_
#if defined(__cplusplus)
#include <atomic>
namespace Acroname {
/// Template class of a thread-safe queue when used as a single producer,
/// single consumer queue.
template <class T>
class LocklessQueue_SPSC {
public:
/// Constructor - Creates a queue with the desired buffer size.
/// Note: Actually buffer size is always buffer
/// \param bufferSize - Size of queue to create.
/// Actual size is always bufferSize - 1 because of internal overhead.
LocklessQueue_SPSC(uint32_t bufferSize = 256) :
_buffer(new T[bufferSize]),
_bufferSize(bufferSize),
_head(0),
_tail(0)
{ }
/// Destructor - Recovers internal memory.
~LocklessQueue_SPSC() {
delete[] _buffer;
_buffer = nullptr;
}
/// Pushes data into the queue.
/// Should only be called by one thread context
/// \param value Element to be queued.
/// \return True on success; False if the buffer is full
bool push(T value);
/// Pops data off of the queue
/// Should only be called by one thread context.
/// \param value Element to be fill from the queue.
/// \return True on success; False if the buffer is empty
bool pop(T* value);
/// Queries the number of elements in the queue.
/// \return Number of element in queue.
uint16_t size();
private:
T* _buffer;
const uint16_t _bufferSize;
std::atomic<uint16_t> _head;
std::atomic<uint16_t> _tail;
inline uint16_t _increment(uint16_t value);
};
template <class T>
bool LocklessQueue_SPSC<T>::push(T value) {
uint16_t newHead = _increment(_head);
if(newHead != _tail) {
_buffer[newHead] = value;
_head = newHead;
return true;
}
else { return false; }
}
template <class T>
bool LocklessQueue_SPSC<T>::pop(T* value) {
if(_tail != _head) {
_tail = _increment(_tail);
*value = _buffer[_tail];
return true;
}
else { return false; }
}
template <class T>
uint16_t LocklessQueue_SPSC<T>::size() {
uint16_t head = _head;
uint16_t tail = _tail;
if(head >= tail) { return head - tail; }
else { return (_bufferSize - tail) + head; }
}
template <class T>
inline uint16_t LocklessQueue_SPSC<T>::_increment(uint16_t value) {
return (((value + 1) < _bufferSize) ? (value + 1) : (0));
}
}
#endif//defined(__cplusplus)
#endif //_LocklessQueue_SPSC_H_