#ifndef UDPC_THREADSAFE_LINKEDLIST_QUEUE_HPP #define UDPC_THREADSAFE_LINKEDLIST_QUEUE_HPP #include #include #include #include #include #include #include #include template 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); void push(const T &data); bool push_nb(const T &data); std::optional top(); std::optional top_nb(); bool pop(); std::optional top_and_pop(); std::optional top_and_pop_and_empty(bool *isEmpty); void clear(); bool empty(); unsigned long long size(); 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 next; std::weak_ptr prev; std::unique_ptr data; }; std::shared_ptr iterValid; std::shared_ptr iterWrapperCount; std::mutex mutex; std::shared_ptr head; std::shared_ptr tail; unsigned long long msize; }; template TSLQueue::TSLQueue() : iterValid(std::make_shared()), iterWrapperCount(std::make_shared()), head(std::make_shared()), tail(std::make_shared()), msize(0) { head->next = tail; tail->prev = head; } template TSLQueue::~TSLQueue() { } template TSLQueue::TSLQueue(TSLQueue &&other) : iterValid(std::make_shared()), iterWrapperCount(std::make_shared()) { std::lock_guard lock(other.mutex); head = std::move(other.head); tail = std::move(other.tail); msize = std::move(other.msize); } template TSLQueue & TSLQueue::operator=(TSLQueue &&other) { iterValid = std::make_shared(); iterWrapperCount = std::make_shared(); std::scoped_lock lock(mutex, other.mutex); head = std::move(other.head); tail = std::move(other.tail); msize = std::move(other.msize); } template void TSLQueue::push(const T &data) { while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); auto newNode = std::make_shared(); newNode->data = std::make_unique(data); auto last = tail->prev.lock(); assert(last); newNode->prev = last; newNode->next = tail; last->next = newNode; tail->prev = newNode; ++msize; } template bool TSLQueue::push_nb(const T &data) { if(iterWrapperCount.use_count() > 1) { return false; } else if(mutex.try_lock()) { auto newNode = std::make_shared(); newNode->data = std::make_unique(data); auto last = tail->prev.lock(); assert(last); newNode->prev = last; newNode->next = tail; last->next = newNode; tail->prev = newNode; ++msize; mutex.unlock(); return true; } else { return false; } } template std::optional TSLQueue::top() { while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); if(head->next != tail) { assert(head->next->data); return *head->next->data.get(); } else { return std::nullopt; } } template std::optional TSLQueue::top_nb() { if(iterWrapperCount.use_count() > 1) { return std::nullopt; } else if(mutex.try_lock()) { std::optional ret = std::nullopt; if(head->next != tail) { assert(head->next->data); ret = *head->next->data.get(); } mutex.unlock(); return ret; } else { return std::nullopt; } } template bool TSLQueue::pop() { while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); if(head->next == tail) { return false; } else { auto& newNext = head->next->next; newNext->prev = head; head->next = newNext; assert(msize > 0); --msize; iterValid = std::make_shared(); iterWrapperCount = std::make_shared(); return true; } } template std::optional TSLQueue::top_and_pop() { std::optional ret = std::nullopt; while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); 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; iterValid = std::make_shared(); iterWrapperCount = std::make_shared(); } return ret; } template std::optional TSLQueue::top_and_pop_and_empty(bool *isEmpty) { std::optional ret = std::nullopt; while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); if(head->next == tail) { if(isEmpty) { *isEmpty = true; } } else { assert(head->next->data); ret = *head->next->data.get(); auto& newNext = head->next->next; newNext->prev = head; head->next = newNext; assert(msize > 0); --msize; iterValid = std::make_shared(); iterWrapperCount = std::make_shared(); if(isEmpty) { *isEmpty = head->next == tail; } } return ret; } template void TSLQueue::clear() { while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); head->next = tail; tail->prev = head; msize = 0; iterValid = std::make_shared(); iterWrapperCount = std::make_shared(); } template bool TSLQueue::empty() { while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); return head->next == tail; } template unsigned long long TSLQueue::size() { while(iterWrapperCount.use_count() > 1) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); return msize; } #endif