// thread_safe_queue.h
#ifndef THREAD_SAFE_QUEUE_H
#define THREAD_SAFE_QUEUE_H
#include <string>
#include <memory>
#include <mutex>
#include <atomic>
#include <sstream>
#include <iostream>
#include <condition_variable>
template<typename T>
class ThreadSafeQueue {
private:
struct node {
std::shared_ptr<T> data;
std::shared_ptr<node> next;
};
std::mutex head_mutex_;
std::shared_ptr<node> head_;
std::mutex tail_mutex_;
std::shared_ptr<node> tail_;
std::condition_variable data_cond_;
std::atomic<int> size_;
public:
ThreadSafeQueue(): head_(new node), tail_(head_), size_(0) {}
ThreadSafeQueue(const ThreadSafeQueue& other) = delete;
ThreadSafeQueue& operator=(const ThreadSafeQueue& other) = delete;
bool empty();
void push(T new_value);
void push(std::shared_ptr<T> new_value_ptr);
bool try_pop(T& value);
std::shared_ptr<T> try_pop();
bool wait_and_pop(T& value);
std::shared_ptr<T> wait_and_pop();
int size() {return size_; };
std::string debug_info();
private:
std::shared_ptr<node> get_tail() {
std::lock_guard<std::mutex> tail_lock(tail_mutex_);
return tail_;
}
std::shared_ptr<node> pop_head() {
std::shared_ptr<node> old_head = head_;
head_ = old_head->next;
size_--;
return old_head;
}
std::unique_lock<std::mutex> wait_for_data() {
std::unique_lock<std::mutex> head_lock(head_mutex_);
data_cond_.wait(head_lock, [&]{return head_!=get_tail();});
return std::move(head_lock);
}
std::shared_ptr<node> wait_pop_head() {
std::unique_lock<std::mutex> head_lock(wait_for_data());
return pop_head();
}
std::shared_ptr<node> wait_pop_head(T& value) {
std::unique_lock<std::mutex> head_lock(wait_for_data());
value = std::move(*head_->data);
return pop_head();
}
std::shared_ptr<node> try_pop_head() {
std::lock_guard<std::mutex> head_lock(head_mutex_);
if (head_ == get_tail()) {
return std::shared_ptr<node>();
}
return pop_head();
}
std::shared_ptr<node> try_pop_head(T& value) {
std::lock_guard<std::mutex> head_lock(head_mutex_);
if (head_ == get_tail()) {
return std::shared_ptr<node>();
}
value = std::move(*head_->data);
return pop_head();
}
};
template<typename T>
bool ThreadSafeQueue<T>::empty() {
std::lock_guard<std::mutex> head_lock(head_mutex_);
return (head_==get_tail());
}
template<typename T>
void ThreadSafeQueue<T>::push(T new_value) {
std::shared_ptr<T> new_data(std::make_shared<T>(std::move(new_value)));
push(new_data);
}
template<typename T>
void ThreadSafeQueue<T>::push(std::shared_ptr<T> new_data) {
std::shared_ptr<node> new_tail(new node);
{
std::lock_guard<std::mutex> tail_lock(tail_mutex_);
tail_->data = new_data;
tail_->next = new_tail;
tail_ = new_tail;
size_++;
}
data_cond_.notify_one();
}
template<typename T>
bool ThreadSafeQueue<T>::try_pop(T& value) {
std::shared_ptr<node> old_head = try_pop_head(value);
return old_head;
}
template<typename T>
std::shared_ptr<T> ThreadSafeQueue<T>::try_pop() {
std::shared_ptr<node> old_head = try_pop_head();
return old_head? old_head->data: std::shared_ptr<T>();
}
template<typename T>
bool ThreadSafeQueue<T>::wait_and_pop(T& value) {
std::shared_ptr<node> const old_head = wait_pop_head(value);
}
template<typename T>
std::shared_ptr<T> ThreadSafeQueue<T>::wait_and_pop() {
std::shared_ptr<node> const old_head = wait_pop_head();
return old_head->data;
}
template<typename T>
std::string ThreadSafeQueue<T>::debug_info() {
std::ostringstream oss;
std::unique_lock<std::mutex> lock_head(head_mutex_, std::defer_lock);
std::unique_lock<std::mutex> lock_tail(tail_mutex_, std::defer_lock);
std::lock(lock_head, lock_tail);
std::shared_ptr<node> p = head_;
int num = 0;
int num_limit = 100;
bool cross_limit = false;
oss << "size[" << size() << "] ";
oss << "[head] -> ";
while (p && p->data) {
if (++num <= num_limit) {
oss << *p->data << " -> ";
if (num % 20 == 0) {
oss << std::endl;
}
} else {
if (!cross_limit) {
cross_limit = true;
oss << "... ..." << " -> ";
}
}
p = p->next;
}
oss << "[tail]" << " [" << num << "]";
if (num != size()) {
oss << " [error: num do not match size]";
}
return oss.str();
}
#endif /* THREAD_SAFE_QUEUE_H */
// test.c
#include "thread_safe_queue.h"
int main() {
ThreadSafeQueue<std::string> myQueue;
myQueue.push("001");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("002");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("003");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("004");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("005");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("006");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("007");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("008");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("009");
std::cout << myQueue.debug_info() << std::endl;
myQueue.push("010");
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.try_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.wait_and_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.try_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.try_pop();
std::cout << myQueue.debug_info() << std::endl;
myQueue.try_pop();
std::cout << myQueue.debug_info() << std::endl;
return 0;
}
$ g++ test.cpp -o test && ./test
size[1] [head] -> 001 -> [tail] [1]
size[2] [head] -> 001 -> 002 -> [tail] [2]
size[3] [head] -> 001 -> 002 -> 003 -> [tail] [3]
size[4] [head] -> 001 -> 002 -> 003 -> 004 -> [tail] [4]
size[5] [head] -> 001 -> 002 -> 003 -> 004 -> 005 -> [tail] [5]
size[6] [head] -> 001 -> 002 -> 003 -> 004 -> 005 -> 006 -> [tail] [6]
size[7] [head] -> 001 -> 002 -> 003 -> 004 -> 005 -> 006 -> 007 -> [tail] [7]
size[8] [head] -> 001 -> 002 -> 003 -> 004 -> 005 -> 006 -> 007 -> 008 -> [tail] [8]
size[9] [head] -> 001 -> 002 -> 003 -> 004 -> 005 -> 006 -> 007 -> 008 -> 009 -> [tail] [9]
size[10] [head] -> 001 -> 002 -> 003 -> 004 -> 005 -> 006 -> 007 -> 008 -> 009 -> 010 -> [tail] [10]
size[9] [head] -> 002 -> 003 -> 004 -> 005 -> 006 -> 007 -> 008 -> 009 -> 010 -> [tail] [9]
size[8] [head] -> 003 -> 004 -> 005 -> 006 -> 007 -> 008 -> 009 -> 010 -> [tail] [8]
size[7] [head] -> 004 -> 005 -> 006 -> 007 -> 008 -> 009 -> 010 -> [tail] [7]
size[6] [head] -> 005 -> 006 -> 007 -> 008 -> 009 -> 010 -> [tail] [6]
size[5] [head] -> 006 -> 007 -> 008 -> 009 -> 010 -> [tail] [5]
size[4] [head] -> 007 -> 008 -> 009 -> 010 -> [tail] [4]
size[3] [head] -> 008 -> 009 -> 010 -> [tail] [3]
size[2] [head] -> 009 -> 010 -> [tail] [2]
size[1] [head] -> 010 -> [tail] [1]
size[0] [head] -> [tail] [0]
size[0] [head] -> [tail] [0]
size[0] [head] -> [tail] [0]
size[0] [head] -> [tail] [0]