1、bounded_queue.h
#include <stdint.h>
//#include <unistd.h>
#include <algorithm>
#include <atomic>
#include <cstdlib>
#include <memory>
#include "macros.h"
#include "wait_strategy.h"
template <typename T>
class BoundedQueue {
public:
using value_type = T;
using size_type = uint64_t;
public:
BoundedQueue() {}
BoundedQueue& operator=(const BoundedQueue& other) = delete;
BoundedQueue(const BoundedQueue& other) = delete;
~BoundedQueue();
bool Init(uint64_t size);
bool Init(uint64_t size, WaitStrategy* strategy);
bool Enqueue(const T& element);
bool Enqueue(T&& element);
bool WaitEnqueue(const T& element);
bool WaitEnqueue(T&& element);
bool Dequeue(T* element);
bool WaitDequeue(T* element);
uint64_t Size();
bool Empty();
void SetWaitStrategy(WaitStrategy* WaitStrategy);
void BreakAllWait();
uint64_t Head() { return head_.load(); }
uint64_t Tail() { return tail_.load(); }
uint64_t Commit() { return commit_.load(); }
private:
uint64_t GetIndex(uint64_t num);
alignas(CACHELINE_SIZE) std::atomic<uint64_t> head_ = {0};
alignas(CACHELINE_SIZE) std::atomic<uint64_t> tail_ = {1};
alignas(CACHELINE_SIZE) std::atomic<uint64_t> commit_ = {1};
// alignas(CACHELINE_SIZE) std::atomic<uint64_t> size_ = {0};
uint64_t pool_size_ = 0;
T* pool_ = nullptr;
std::unique_ptr<WaitStrategy> wait_strategy_ = nullptr;
volatile bool break_all_wait_ = false;
};
template <typename T>
BoundedQueue<T>::~BoundedQueue() {
if (wait_strategy_) {
BreakAllWait();
}
if (pool_) {
for (uint64_t i = 0; i < pool_size_; ++i) {
pool_[i].~T();
}
std::free(pool_);
}
}
template <typename T>
inline bool BoundedQueue<T>::Init(uint64_t size) {
return Init(size, new SleepWaitStrategy());
}
template <typename T>
bool BoundedQueue<T>::Init(uint64_t size, WaitStrategy* strategy) {
// Head and tail each occupy a space
pool_size_ = size + 2;
pool_ = reinterpret_cast<T*>(std::calloc(pool_size_, sizeof(T)));
if (pool_ == nullptr) {
return false;
}
for (uint64_t i = 0; i < pool_size_; ++i) {
new (&(pool_[i])) T(); //C++中的定位放置new(placement new)
}
wait_strategy_.reset(strategy);
return true;
}
template <typename T>
bool BoundedQueue<T>::Enqueue(const T& element) {
uint64_t new_tail = 0;
uint64_t old_commit = 0;
uint64_t old_tail = tail_.load(std::memory_order_acquire);
do {
new_tail = old_tail + 1;
if (GetIndex(new_tail) == GetIndex(head_.load(std::memory_order_acquire))) {
return false;
}
} while (!tail_.compare_exchange_weak(old_tail, new_tail,
std::memory_order_acq_rel,
std::memory_order_relaxed));
pool_[GetIndex(old_tail)] = element;
do {
old_commit = old_tail;
} while (unlikely(!commit_.compare_exchange_weak(old_commit, new_tail,
std::memory_order_acq_rel,
std::memory_order_relaxed)));
wait_strategy_->NotifyOne();
return true;
}
template <typename T>
bool BoundedQueue<T>::Enqueue(T&& element) {
uint64_t new_tail = 0;
uint64_t old_commit = 0;
uint64_t old_tail = tail_.load(std::memory_order_acquire);
do {
new_tail = old_tail + 1;
if (GetIndex(new_tail) == GetIndex(head_.load(std::memory_order_acquire))) {
return false;
}
} while (!tail_.compare_exchange_weak(old_tail, new_tail,
std::memory_order_acq_rel,
std::memory_order_relaxed));
pool_[GetIndex(old_tail)] = element;
do {
old_commit = old_tail;
} while (unlikely(!commit_.compare_exchange_weak(old_commit, new_tail,
std::memory_order_acq_rel,
std::memory_order_relaxed)));
wait_strategy_->NotifyOne();
return true;
}
template <typename T>
bool BoundedQueue<T>::Dequeue(T* element) {
uint64_t new_head = 0;
uint64_t old_head = head_.load(std::memory_order_acquire);
do {
new_head = old_head + 1;
if (new_head == commit_.load(std::memory_order_acquire)) {
return false;
}
*element = pool_[GetIndex(new_head)];
} while (!head_.compare_exchange_weak(old_head, new_head,
std::memory_order_acq_rel,
std::memory_order_relaxed));
return true;
}
template <typename T>
bool BoundedQueue<T>::WaitEnqueue(const T& element) {
while (!break_all_wait_) {
if (Enqueue(element)) {
return true;
}
if (wait_strategy_->EmptyWait()) {
continue;
}
// wait timeout
break;
}
return false;
}
template <typename T>
bool BoundedQueue<T>::WaitEnqueue(T&& element) {
while (!break_all_wait_) {
if (Enqueue(element)) {
return true;
}
if (wait_strategy_->EmptyWait()) {
continue;
}
// wait timeout
break;
}
return false;
}
template <typename T>
bool BoundedQueue<T>::WaitDequeue(T* element) {
while (!break_all_wait_) {
if (Dequeue(element)) {
return true;
}
if (wait_strategy_->EmptyWait()) {
continue;
}
// wait timeout
break;
}
return false;
}
template <typename T>
inline uint64_t BoundedQueue<T>::Size() {
return tail_ - head_ - 1;
}
template <typename T>
inline bool BoundedQueue<T>::Empty() {
return Size() == 0;
}
template <typename T>
inline uint64_t BoundedQueue<T>::GetIndex(uint64_t num) {
return num - (num / pool_size_) * pool_size_; // faster than %
}
template <typename T>
inline void BoundedQueue<T>::SetWaitStrategy(WaitStrategy* strategy) {
wait_strategy_.reset(strategy);
}
template <typename T>
inline void BoundedQueue<T>::BreakAllWait() {
break_all_wait_ = true;
wait_strategy_->BreakAllWait();
}
2、等待策略
#include <chrono>
#include <condition_variable>
#include <cstdlib>
#include <mutex>
#include <thread>
class WaitStrategy {
public:
virtual void NotifyOne() {}
virtual void BreakAllWait() {}
virtual bool EmptyWait() = 0;
virtual ~WaitStrategy() {}
};
class BlockWaitStrategy : public WaitStrategy {
public:
BlockWaitStrategy() {}
void NotifyOne() override { cv_.notify_one(); }
bool EmptyWait() override {
std::unique_lock<std::mutex> lock(mutex_);
cv_.wait(lock);
return true;
}
void BreakAllWait() { cv_.notify_all(); }
private:
std::mutex mutex_;
std::condition_variable cv_;
};
class SleepWaitStrategy : public WaitStrategy {
public:
SleepWaitStrategy() {}
explicit SleepWaitStrategy(uint64_t sleep_time_us)
: sleep_time_us_(sleep_time_us) {}
bool EmptyWait() override {
std::this_thread::sleep_for(std::chrono::microseconds(sleep_time_us_));
return true;
}
void SetSleepTimeMicroSeconds(uint64_t sleep_time_us) {
sleep_time_us_ = sleep_time_us;
}
private:
uint64_t sleep_time_us_ = 10000;
};
class YieldWaitStrategy : public WaitStrategy {
public:
YieldWaitStrategy() {}
bool EmptyWait() override {
std::this_thread::yield();
return true;
}
};
class BusySpinWaitStrategy : public WaitStrategy {
public:
BusySpinWaitStrategy() {}
bool EmptyWait() override { return true; }
};
class TimeoutBlockWaitStrategy : public WaitStrategy {
public:
TimeoutBlockWaitStrategy() {}
explicit TimeoutBlockWaitStrategy(uint64_t timeout)
: time_out_(std::chrono::milliseconds(timeout)) {}
void NotifyOne() override { cv_.notify_one(); }
bool EmptyWait() override {
std::unique_lock<std::mutex> lock(mutex_);
if (cv_.wait_for(lock, time_out_) == std::cv_status::timeout) {
return false;
}
return true;
}
void BreakAllWait() { cv_.notify_all(); }
void SetTimeout(uint64_t timeout) {
time_out_ = std::chrono::milliseconds(timeout);
}
private:
std::mutex mutex_;
std::condition_variable cv_;
std::chrono::milliseconds time_out_;
};
3、基础设置
#include <cstdlib>
#include <new>
#define likely(x) (x)
#define unlikely(x) (x)
#define CACHELINE_SIZE 64
#define DEFINE_TYPE_TRAIT(name, func) \
template <typename T> \
class name { \
private: \
template <typename Class> \
static char Test(decltype(&Class::func)*); \
template <typename> \
static int Test(...); \
\
public: \
static constexpr bool value = sizeof(Test<T>(nullptr)) == 1; \
}; \
\
template <typename T> \
constexpr bool name<T>::value;
//inline void cpu_relax() { asm volatile("rep; nop" ::: "memory"); }
inline void* CheckedMalloc(size_t size) {
void* ptr = std::malloc(size);
if (!ptr) {
throw std::bad_alloc();
}
return ptr;
}
inline void* CheckedCalloc(size_t num, size_t size) {
void* ptr = std::calloc(num, size);
if (!ptr) {
throw std::bad_alloc();
}
return ptr;
}
void test_BoundedQueue()
{
BoundedQueue<int> queue;
queue.Init(100);
int value = 0;
for (int i = 0; i < 100; i++) {
queue.Enqueue(i);
queue.Dequeue(&value);
std::cout << i <<" " << value << std::endl;
}
}
例子: