#pragma once
#include<memory>
#include<mutex>
#include<condition_variable>
template<typename T>
class threadsafe_queue
{
using namespace std;
private:
struct node
{
std::shared_ptr<T> data;
std::unique_ptr<node> next;
};
std::mutex mx_head;
std::mutex mx_tail;
std::unique_ptr<node> head;
node* tail;
std::condition_variable data_cond;
public:
threadsafe_queue() :head(new node), tail(head.get()) {}
threadsafe_queue(const threadsafe_queue&) = delete;
threadsafe_queue& operator =(const threadsafe_queue&) = delete;
std::shared_ptr<T> try_pop();
bool try_pop(T& value);
std::shared_ptr<T> wait_and_pop();
void wait_and_pop(T& value);
void push(T NewValue);
void empty();
private:
node* get_tail()
{
std::lock_guard<std::mutex> lk(mx_tail);
return tail;
}
std::unique_ptr<node> pop_head()
{
std::unique_ptr<node> old_head = std::move(head);
head = std::move(old_head->next);
return old_head;
}
std::unique_ptr<node> try_pop_head()
{
std::lock_guard<mutex>headlock(mx_head);
if (head.get() == get_tail())
return unique_ptr<node>();
return pop_head();
}
std::unique_ptr<node> try_pop_head(T&value)
{
std::lock_guard<mutex>headlock(mx_head);
if (head.get() == get_tail())
return unique_ptr<node>();
value = std::move(*head->data);
return pop_head();
}
std::unique_lock<std::mutex> wait_for_data()
{
std::unique_lock<std::mutex> headlock(mx_head);
data_cond.wait(&headlock, [this]{ return head.get() != get_tail(); });
return std::move(headlock);
}
std::unique_ptr<node> wait_pop_head()
{
std::unique_lock<std::mutex> headlock(wait_for_data());
return pop_head();
}
std::unique_ptr<node> wait_pop_head(T&value)
{
std::unique_lock<std::mutex> headlock(wait_for_data());
value = std::move(*head->data);
return pop_head();
}
};
template<typename T>
inline std::shared_ptr<T> threadsafe_queue<T>::try_pop()
{
std::unique_ptr<node>old_head=try_pop_head();
return old_head ? old_head->data : std::shared_ptr<T>();
}
template<typename T>
inline bool threadsafe_queue<T>::try_pop(T& value)
{
std::unique_ptr<node>old_head = try_pop_head(value);
return old_head;
}
template<typename T>
inline std::shared_ptr<T> threadsafe_queue<T>::wait_and_pop()
{
const std::unique_ptr<node> old_head = wait_pop_head();
return old_head->data;
}
template<typename T>
inline void threadsafe_queue<T>::wait_and_pop(T& value)
{
const std::unique_ptr<node> old_head = wait_pop_head(value);
}
template<typename T>
inline void threadsafe_queue<T>::push(T NewValue)
{
std::shared_ptr<T> data(std::make_shared(std::move(NewValue)));
std::unique_ptr<node> pNode = std::make_unique();
{
std::lock_guard<std::mutex> lk(mx_tail);
Node*const Newtail = pNode.get();
tail->data = data;
tail->next = std::move(pNode);
tail = Newtail;
}
data_cond.notify_one();
}
template<typename T>
inline void threadsafe_queue<T>::empty()
{
std::lock_guard<std::mutex>headlock(mx_head);
return (head.get() == get_tail());
}