2019-10-22 11:24:23 +00:00
|
|
|
#ifndef UDPC_THREADSAFE_LINKEDLIST_QUEUE_HPP
|
|
|
|
#define UDPC_THREADSAFE_LINKEDLIST_QUEUE_HPP
|
|
|
|
|
|
|
|
#include <memory>
|
|
|
|
#include <mutex>
|
2019-10-24 08:49:28 +00:00
|
|
|
#include <thread>
|
|
|
|
#include <chrono>
|
2019-10-22 11:24:23 +00:00
|
|
|
#include <optional>
|
2019-10-29 11:33:16 +00:00
|
|
|
#include <cassert>
|
2019-10-22 11:24:23 +00:00
|
|
|
|
|
|
|
#include <list>
|
|
|
|
#include <type_traits>
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
class TSLQueue {
|
|
|
|
public:
|
|
|
|
TSLQueue();
|
|
|
|
~TSLQueue();
|
|
|
|
|
|
|
|
// disable copy
|
|
|
|
TSLQueue(const TSLQueue &other) = delete;
|
|
|
|
TSLQueue &operator=(const TSLQueue &other) = delete;
|
|
|
|
// enable move
|
|
|
|
TSLQueue(TSLQueue &&other);
|
|
|
|
TSLQueue &operator=(TSLQueue &&other);
|
|
|
|
|
2019-10-29 11:33:16 +00:00
|
|
|
void push(const T &data);
|
2019-10-24 08:49:28 +00:00
|
|
|
bool push_nb(const T &data);
|
2019-10-22 11:24:23 +00:00
|
|
|
std::optional<T> top();
|
2019-10-24 08:49:28 +00:00
|
|
|
std::optional<T> top_nb();
|
2019-10-22 11:24:23 +00:00
|
|
|
bool pop();
|
|
|
|
std::optional<T> top_and_pop();
|
|
|
|
std::optional<T> top_and_pop_and_empty(bool *isEmpty);
|
|
|
|
void clear();
|
|
|
|
|
|
|
|
bool empty();
|
2019-10-29 11:33:16 +00:00
|
|
|
unsigned long long size();
|
2019-10-22 11:24:23 +00:00
|
|
|
|
2019-10-29 11:33:16 +00:00
|
|
|
private:
|
|
|
|
struct TSLQNode {
|
|
|
|
TSLQNode() = default;
|
|
|
|
// disable copy
|
|
|
|
TSLQNode(TSLQNode& other) = delete;
|
|
|
|
TSLQNode& operator=(TSLQNode& other) = delete;
|
|
|
|
// enable move
|
|
|
|
TSLQNode(TSLQNode&& other) = default;
|
|
|
|
TSLQNode& operator=(TSLQNode&& other) = default;
|
|
|
|
|
|
|
|
std::shared_ptr<TSLQNode> next;
|
|
|
|
std::weak_ptr<TSLQNode> prev;
|
|
|
|
std::unique_ptr<T> data;
|
2019-10-22 11:24:23 +00:00
|
|
|
};
|
|
|
|
|
2019-10-24 11:25:41 +00:00
|
|
|
std::shared_ptr<char> iterValid;
|
|
|
|
std::shared_ptr<char> iterWrapperCount;
|
2019-10-24 08:49:28 +00:00
|
|
|
std::mutex mutex;
|
2019-10-29 11:33:16 +00:00
|
|
|
std::shared_ptr<TSLQNode> head;
|
|
|
|
std::shared_ptr<TSLQNode> tail;
|
|
|
|
unsigned long long msize;
|
2019-10-22 11:24:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
template <typename T>
|
2019-10-24 08:49:28 +00:00
|
|
|
TSLQueue<T>::TSLQueue() :
|
2019-10-24 11:25:41 +00:00
|
|
|
iterValid(std::make_shared<char>()),
|
2019-10-29 11:33:16 +00:00
|
|
|
iterWrapperCount(std::make_shared<char>()),
|
|
|
|
head(std::make_shared<TSLQNode>()),
|
|
|
|
tail(std::make_shared<TSLQNode>()),
|
|
|
|
msize(0)
|
2019-10-24 08:49:28 +00:00
|
|
|
{
|
2019-10-29 11:33:16 +00:00
|
|
|
head->next = tail;
|
|
|
|
tail->prev = head;
|
2019-10-22 11:24:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
TSLQueue<T>::~TSLQueue() {
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2019-10-24 08:49:28 +00:00
|
|
|
TSLQueue<T>::TSLQueue(TSLQueue &&other) :
|
2019-10-24 11:25:41 +00:00
|
|
|
iterValid(std::make_shared<char>()),
|
|
|
|
iterWrapperCount(std::make_shared<char>())
|
2019-10-24 08:49:28 +00:00
|
|
|
{
|
|
|
|
std::lock_guard lock(other.mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
head = std::move(other.head);
|
|
|
|
tail = std::move(other.tail);
|
|
|
|
msize = std::move(other.msize);
|
2019-10-22 11:24:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
TSLQueue<T> & TSLQueue<T>::operator=(TSLQueue &&other) {
|
2019-10-29 11:33:16 +00:00
|
|
|
iterValid = std::make_shared<char>();
|
|
|
|
iterWrapperCount = std::make_shared<char>();
|
2019-10-24 08:49:28 +00:00
|
|
|
std::scoped_lock lock(mutex, other.mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
head = std::move(other.head);
|
|
|
|
tail = std::move(other.tail);
|
|
|
|
msize = std::move(other.msize);
|
2019-10-24 08:49:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2019-10-29 11:33:16 +00:00
|
|
|
void TSLQueue<T>::push(const T &data) {
|
2019-10-24 08:49:28 +00:00
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
auto newNode = std::make_shared<TSLQNode>();
|
|
|
|
newNode->data = std::make_unique<T>(data);
|
|
|
|
|
|
|
|
auto last = tail->prev.lock();
|
|
|
|
assert(last);
|
|
|
|
|
|
|
|
newNode->prev = last;
|
|
|
|
newNode->next = tail;
|
|
|
|
last->next = newNode;
|
|
|
|
tail->prev = newNode;
|
|
|
|
++msize;
|
2019-10-24 08:49:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool TSLQueue<T>::push_nb(const T &data) {
|
|
|
|
if(iterWrapperCount.use_count() > 1) {
|
|
|
|
return false;
|
|
|
|
} else if(mutex.try_lock()) {
|
2019-10-29 11:33:16 +00:00
|
|
|
auto newNode = std::make_shared<TSLQNode>();
|
|
|
|
newNode->data = std::make_unique<T>(data);
|
|
|
|
|
|
|
|
auto last = tail->prev.lock();
|
|
|
|
assert(last);
|
|
|
|
|
|
|
|
newNode->prev = last;
|
|
|
|
newNode->next = tail;
|
|
|
|
last->next = newNode;
|
|
|
|
tail->prev = newNode;
|
|
|
|
++msize;
|
|
|
|
|
2019-10-24 08:49:28 +00:00
|
|
|
mutex.unlock();
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
std::optional<T> TSLQueue<T>::top() {
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
if(head->next != tail) {
|
|
|
|
assert(head->next->data);
|
|
|
|
return *head->next->data.get();
|
2019-10-24 08:49:28 +00:00
|
|
|
} else {
|
2019-10-29 11:33:16 +00:00
|
|
|
return std::nullopt;
|
2019-10-24 08:49:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
std::optional<T> TSLQueue<T>::top_nb() {
|
|
|
|
if(iterWrapperCount.use_count() > 1) {
|
|
|
|
return std::nullopt;
|
|
|
|
} else if(mutex.try_lock()) {
|
2019-10-29 11:33:16 +00:00
|
|
|
std::optional<T> ret = std::nullopt;
|
|
|
|
if(head->next != tail) {
|
|
|
|
assert(head->next->data);
|
|
|
|
ret = *head->next->data.get();
|
|
|
|
}
|
2019-10-24 08:49:28 +00:00
|
|
|
mutex.unlock();
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
bool TSLQueue<T>::pop() {
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
if(head->next == tail) {
|
2019-10-24 08:49:28 +00:00
|
|
|
return false;
|
|
|
|
} else {
|
2019-10-29 11:33:16 +00:00
|
|
|
auto& newNext = head->next->next;
|
|
|
|
newNext->prev = head;
|
|
|
|
head->next = newNext;
|
|
|
|
assert(msize > 0);
|
|
|
|
--msize;
|
|
|
|
|
2019-10-24 11:25:41 +00:00
|
|
|
iterValid = std::make_shared<char>();
|
|
|
|
iterWrapperCount = std::make_shared<char>();
|
2019-10-24 08:49:28 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
std::optional<T> TSLQueue<T>::top_and_pop() {
|
|
|
|
std::optional<T> ret = std::nullopt;
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
if(head->next != tail) {
|
|
|
|
assert(head->next->data);
|
|
|
|
ret = *head->next->data.get();
|
|
|
|
|
|
|
|
auto& newNext = head->next->next;
|
|
|
|
newNext->prev = head;
|
|
|
|
head->next = newNext;
|
|
|
|
assert(msize > 0);
|
|
|
|
--msize;
|
|
|
|
|
2019-10-24 11:25:41 +00:00
|
|
|
iterValid = std::make_shared<char>();
|
|
|
|
iterWrapperCount = std::make_shared<char>();
|
2019-10-24 08:49:28 +00:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
std::optional<T> TSLQueue<T>::top_and_pop_and_empty(bool *isEmpty) {
|
|
|
|
std::optional<T> ret = std::nullopt;
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
if(head->next == tail) {
|
2019-10-24 08:49:28 +00:00
|
|
|
if(isEmpty) {
|
|
|
|
*isEmpty = true;
|
|
|
|
}
|
|
|
|
} else {
|
2019-10-29 11:33:16 +00:00
|
|
|
assert(head->next->data);
|
|
|
|
ret = *head->next->data.get();
|
|
|
|
|
|
|
|
auto& newNext = head->next->next;
|
|
|
|
newNext->prev = head;
|
|
|
|
head->next = newNext;
|
|
|
|
assert(msize > 0);
|
|
|
|
--msize;
|
|
|
|
|
2019-10-24 11:25:41 +00:00
|
|
|
iterValid = std::make_shared<char>();
|
|
|
|
iterWrapperCount = std::make_shared<char>();
|
2019-10-24 08:49:28 +00:00
|
|
|
if(isEmpty) {
|
2019-10-29 11:33:16 +00:00
|
|
|
*isEmpty = head->next == tail;
|
2019-10-24 08:49:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
|
|
|
void TSLQueue<T>::clear() {
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
|
|
|
|
head->next = tail;
|
|
|
|
tail->prev = head;
|
|
|
|
msize = 0;
|
|
|
|
|
2019-10-24 11:25:41 +00:00
|
|
|
iterValid = std::make_shared<char>();
|
|
|
|
iterWrapperCount = std::make_shared<char>();
|
2019-10-22 11:24:23 +00:00
|
|
|
}
|
|
|
|
|
2019-10-24 08:51:40 +00:00
|
|
|
template <typename T>
|
|
|
|
bool TSLQueue<T>::empty() {
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
return head->next == tail;
|
2019-10-24 08:58:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T>
|
2019-10-29 11:33:16 +00:00
|
|
|
unsigned long long TSLQueue<T>::size() {
|
|
|
|
while(iterWrapperCount.use_count() > 1) {
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
2019-10-24 11:25:41 +00:00
|
|
|
}
|
|
|
|
std::lock_guard lock(mutex);
|
2019-10-29 11:33:16 +00:00
|
|
|
return msize;
|
2019-10-24 08:49:28 +00:00
|
|
|
}
|
|
|
|
|
2019-10-22 11:24:23 +00:00
|
|
|
#endif
|