123 lines
3.1 KiB
C++
123 lines
3.1 KiB
C++
#pragma once
|
|
#ifndef _NB_UTIL_THREADSAFE_QUEUE
|
|
#define _NB_UTIL_THREADSAFE_QUEUE
|
|
|
|
#include <condition_variable>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <type_traits>
|
|
|
|
namespace nb {
|
|
|
|
template<typename T, bool isPoly>
|
|
class ThreadsafeQueue_impl;
|
|
|
|
// Non-polymorphic
|
|
template<typename T>
|
|
class ThreadsafeQueue_impl<T, false> {
|
|
public:
|
|
bool empty() const {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return _queue.empty();
|
|
}
|
|
unsigned int size() const {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return _queue.size();
|
|
}
|
|
|
|
template<typename U>
|
|
void push(U&& val) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
_queue.push(std::forward<U>(val));
|
|
_cond.notify_one();
|
|
}
|
|
bool pop(std::shared_ptr<T>& dest) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
if (_queue.empty()) { return false; }
|
|
dest = std::make_shared<T>(std::move(_queue.front()));
|
|
_queue.pop();
|
|
return true;
|
|
}
|
|
std::shared_ptr<T> popBlock() {
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
_cond.wait(lock, [&]{ return !_queue.empty(); });
|
|
auto ret = std::make_shared<T>(std::move(_queue.front()));
|
|
_queue.pop();
|
|
return ret;
|
|
}
|
|
|
|
protected:
|
|
ThreadsafeQueue_impl() {}
|
|
|
|
std::queue<T> _queue;
|
|
std::condition_variable _cond;
|
|
mutable std::mutex _mutex;
|
|
|
|
};
|
|
|
|
// Polymorphic
|
|
template<typename T>
|
|
class ThreadsafeQueue_impl<T, true> {
|
|
public:
|
|
bool empty() const {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return _queue.empty();
|
|
}
|
|
unsigned int size() const {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
return _queue.size();
|
|
}
|
|
|
|
template<typename U>
|
|
void push(U&& val) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
std::shared_ptr<T> newval = std::make_shared<T>(std::forward<U>(val));
|
|
_queue.push(newval);
|
|
_cond.notify_one();
|
|
}
|
|
bool pop(std::shared_ptr<T>& dest) {
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
if (_queue.empty()) { return false; }
|
|
dest = std::move(_queue.front());
|
|
_queue.pop();
|
|
return true;
|
|
}
|
|
std::shared_ptr<T> popBlock() {
|
|
std::unique_lock<std::mutex> lock(_mutex);
|
|
_cond.wait(lock, [&]{ return !_queue.empty(); });
|
|
auto ret = std::move(_queue.front());
|
|
_queue.pop();
|
|
return ret;
|
|
}
|
|
|
|
protected:
|
|
ThreadsafeQueue_impl() {}
|
|
|
|
std::queue<std::shared_ptr<T>> _queue;
|
|
std::condition_variable _cond;
|
|
mutable std::mutex _mutex;
|
|
|
|
};
|
|
|
|
template<typename T>
|
|
class ThreadsafeQueue : public ThreadsafeQueue_impl<T, std::is_polymorphic<T>::value> {
|
|
public:
|
|
using Implementation = ThreadsafeQueue_impl<T, std::is_polymorphic<T>::value>;
|
|
|
|
ThreadsafeQueue() = default;
|
|
~ThreadsafeQueue() = default;
|
|
|
|
ThreadsafeQueue(ThreadsafeQueue& rhs) = delete;
|
|
ThreadsafeQueue& operator=(ThreadsafeQueue& rhs) = delete;
|
|
ThreadsafeQueue(ThreadsafeQueue&& rhs) = delete;
|
|
ThreadsafeQueue& operator=(ThreadsafeQueue&& rhs) = delete;
|
|
|
|
operator bool() const { return !Implementation::empty(); }
|
|
|
|
};
|
|
|
|
} // namespace nb
|
|
|
|
|
|
#endif // _NB_UTIL_THREADSAFE_QUEUE
|