/* This file is part of Patchage. * Copyright 2007-2014 David Robillard * * Patchage is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) * any later version. * * Patchage is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. * * You should have received a copy of the GNU General Public License * along with Patchage. If not, see . */ #ifndef QUEUE_HPP_INCLUDED #define QUEUE_HPP_INCLUDED #include #ifdef __APPLE__ # include #endif /** Realtime-safe single-reader single-writer queue. This is a RingBuffer but templated for fixed sized objects which makes its use a bit more efficient and cleaner in C++ than a traditional byte oriented ring buffer. */ template class Queue { public: /** @param size Size in number of elements */ explicit Queue(uint32_t size); ~Queue(); // Any thread: inline uint32_t capacity() const { return _size - 1; } // Write thread(s): inline bool full() const; inline bool push(const T& obj); // Read thread: inline bool empty() const; inline T& front() const; inline void pop(); private: Queue(const Queue&); ///< Undefined (noncopyable) Queue& operator=(const Queue&); ///< Undefined (noncopyable) static inline void barrier() { #if defined(__APPLE__) OSMemoryBarrier(); #elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) __sync_synchronize(); #else # warning Memory barriers unsupported, possible bugs on SMP systems #endif } int _front; ///< Index to front of queue int _back; ///< Index to back of queue (1 past last element) const uint32_t _size; ///< Size of _objects (1 more than can be stored) T* const _objects; ///< Circular array containing queued elements }; template Queue::Queue(uint32_t size) : _front(0) , _back(0) , _size(size + 1) , _objects(new T[_size]) { assert(size > 1); } template Queue::~Queue() { delete[] _objects; } /** Return whether or not the queue is empty. */ template inline bool Queue::empty() const { return (_back == _front); } /** Return whether or not the queue is full. */ template inline bool Queue::full() const { return (((_front - _back + _size) % _size) == 1); } /** Return the element at the front of the queue without removing it */ template inline T& Queue::front() const { return _objects[_front]; } /** Push an item onto the back of the Queue - realtime-safe, not thread-safe. * * @returns true if `elem` was successfully pushed onto the queue, * false otherwise (queue is full). */ template inline bool Queue::push(const T& elem) { if (full()) { return false; } else { const int back = _back; _objects[back] = elem; barrier(); _back = (back + 1) % _size; return true; } } /** Pop an item off the front of the queue - realtime-safe, not thread-safe. * * It is a fatal error to call pop() when the queue is empty. * * @returns the element popped. */ template inline void Queue::pop() { assert(!empty()); assert(_size > 0); barrier(); _front = (_front + 1) % (_size); } #endif // QUEUE_HPP_INCLUDED