blob: a08dd41a4af73cac4c0872a98aff1b71ac71e94d [file]
/*
Copyright 2014 DataStax
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Note:
A combination of the algorithms described by the circular buffers
documentation found in the Linux kernel, and the bounded MPMC queue
by Dmitry Vyukov[1]. Implemented in pure C++11. Should work across
most CPU architectures.
[1]
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
*/
#ifndef __CASS_SPSC_QUEUE_HPP_INCLUDED__
#define __CASS_SPSC_QUEUE_HPP_INCLUDED__
#include <assert.h>
#include "third_party/boost/boost/atomic.hpp"
#include "third_party/boost/boost/type_traits/alignment_of.hpp"
#include "third_party/boost/boost/aligned_storage.hpp"
namespace cass {
template <typename T>
class SPSCQueue {
public:
typedef T EntryType;
SPSCQueue(size_t size)
: _size(size)
, _mask(size - 1)
, _buffer(reinterpret_cast<T*>(
// need one extra element for a guard
new SPSCQueueAlignedEntry[_size + 1]))
, _head(0)
, _tail(0) {
// make sure it's a power of 2
assert((_size != 0) && ((_size & (~_size + 1)) == _size));
}
~SPSCQueue() { delete[] _buffer; }
bool enqueue(const T& input) {
const size_t head = _head.load(boost::memory_order_relaxed);
if (((_tail.load(boost::memory_order_acquire) - (head + 1)) & _mask) >= 1) {
_buffer[head & _mask] = input;
_head.store(head + 1, boost::memory_order_release);
return true;
}
return false;
}
bool dequeue(T& output) {
const size_t tail = _tail.load(boost::memory_order_relaxed);
if (((_head.load(boost::memory_order_acquire) - tail) & _mask) >= 1) {
output = _buffer[_tail & _mask];
_tail.store(tail + 1, boost::memory_order_release);
return true;
}
return false;
}
private:
typedef typename boost::aligned_storage<
sizeof(T), boost::alignment_of<T>::value>::type SPSCQueueAlignedEntry;
typedef char cache_line_pad_t[64];
cache_line_pad_t _pad0;
const size_t _size;
const size_t _mask;
T* const _buffer;
cache_line_pad_t _pad1;
boost::atomic<size_t> _head;
cache_line_pad_t _pad2;
boost::atomic<size_t> _tail;
SPSCQueue(const SPSCQueue&) {}
void operator=(const SPSCQueue&) {}
};
} // namespace cass
#endif