#include <thread>
#include <chrono>
#include <optional>
+#include <cassert>
#include <list>
#include <type_traits>
-// definition
-
template <typename T>
class TSLQueue {
public:
TSLQueue(TSLQueue &&other);
TSLQueue &operator=(TSLQueue &&other);
- bool push(const T &data);
+ void push(const T &data);
bool push_nb(const T &data);
std::optional<T> top();
std::optional<T> top_nb();
void clear();
bool empty();
+ unsigned long long size();
- template <bool isConst, bool isRev>
- class TSLQIterWrapper {
- public:
- TSLQIterWrapper(
- std::conditional_t<isConst, const std::list<T>, std::list<T>> *container,
- std::weak_ptr<void> iterValid,
- std::shared_ptr<char> iterWrapperCount
- );
-
- bool isValid() const;
-
- bool next();
- bool prev();
- std::optional<T> current();
- /// can only be used when isRev is false
- bool remove();
-
- TSLQIterWrapper<isConst, isRev>& operator++();
- TSLQIterWrapper<isConst, isRev>& operator--();
-
- bool set(T &&newValue);
-
- private:
- std::conditional_t<isConst, const std::list<T>, std::list<T>>
- *containerPtr;
- std::conditional_t<isRev,
- std::conditional_t<isConst,
- typename std::list<T>::const_reverse_iterator,
- typename std::list<T>::reverse_iterator>,
- std::conditional_t<isConst,
- typename std::list<T>::const_iterator,
- typename std::list<T>::iterator>>
- iter;
-
- std::weak_ptr<void> iterValid;
- std::shared_ptr<char> iterWrapperCount;
+ 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;
};
- TSLQIterWrapper<false, false> iter();
- TSLQIterWrapper<false, true> riter();
- TSLQIterWrapper<true, false> citer();
- TSLQIterWrapper<true, true> criter();
-
- private:
std::shared_ptr<char> iterValid;
std::shared_ptr<char> iterWrapperCount;
std::mutex mutex;
- std::list<T> container;
+ std::shared_ptr<TSLQNode> head;
+ std::shared_ptr<TSLQNode> tail;
+ unsigned long long msize;
};
-// implementation
-
template <typename T>
TSLQueue<T>::TSLQueue() :
iterValid(std::make_shared<char>()),
- iterWrapperCount(std::make_shared<char>())
+ iterWrapperCount(std::make_shared<char>()),
+ head(std::make_shared<TSLQNode>()),
+ tail(std::make_shared<TSLQNode>()),
+ msize(0)
{
+ head->next = tail;
+ tail->prev = head;
}
template <typename T>
iterWrapperCount(std::make_shared<char>())
{
std::lock_guard lock(other.mutex);
- container = std::move(other.container);
+ head = std::move(other.head);
+ tail = std::move(other.tail);
+ msize = std::move(other.msize);
}
template <typename T>
TSLQueue<T> & TSLQueue<T>::operator=(TSLQueue &&other) {
+ iterValid = std::make_shared<char>();
+ iterWrapperCount = std::make_shared<char>();
std::scoped_lock lock(mutex, other.mutex);
- container = std::move(other.container);
+ head = std::move(other.head);
+ tail = std::move(other.tail);
+ msize = std::move(other.msize);
}
template <typename T>
-bool TSLQueue<T>::push(const T &data) {
+void TSLQueue<T>::push(const T &data) {
while(iterWrapperCount.use_count() > 1) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- container.push_back(data);
- return true;
+ 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;
}
template <typename T>
if(iterWrapperCount.use_count() > 1) {
return false;
} else if(mutex.try_lock()) {
- container.push_back(data);
+ 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;
+
mutex.unlock();
return true;
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- if(container.empty()) {
- return std::nullopt;
+ if(head->next != tail) {
+ assert(head->next->data);
+ return *head->next->data.get();
} else {
- return container.front();
+ return std::nullopt;
}
}
if(iterWrapperCount.use_count() > 1) {
return std::nullopt;
} else if(mutex.try_lock()) {
- std::optional<T> ret = container.front();
+ std::optional<T> ret = std::nullopt;
+ if(head->next != tail) {
+ assert(head->next->data);
+ ret = *head->next->data.get();
+ }
mutex.unlock();
return ret;
} else {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- if(container.empty()) {
+ if(head->next == tail) {
return false;
} else {
- container.pop_front();
+ auto& newNext = head->next->next;
+ newNext->prev = head;
+ head->next = newNext;
+ assert(msize > 0);
+ --msize;
+
iterValid = std::make_shared<char>();
iterWrapperCount = std::make_shared<char>();
return true;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- if(!container.empty()) {
- ret = container.front();
- container.pop_front();
+ 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<char>();
iterWrapperCount = std::make_shared<char>();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- if(container.empty()) {
+ if(head->next == tail) {
if(isEmpty) {
*isEmpty = true;
}
} else {
- ret = container.front();
- container.pop_front();
+ 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<char>();
iterWrapperCount = std::make_shared<char>();
if(isEmpty) {
- *isEmpty = container.empty();
+ *isEmpty = head->next == tail;
}
}
return ret;
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- container.clear();
+
+ head->next = tail;
+ tail->prev = head;
+ msize = 0;
+
iterValid = std::make_shared<char>();
iterWrapperCount = std::make_shared<char>();
}
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
- return container.empty();
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::TSLQIterWrapper(
- std::conditional_t<isConst, const std::list<T>, std::list<T>> *container,
- std::weak_ptr<void> iterValid,
- std::shared_ptr<char> iterWrapperCount) :
- containerPtr(container),
- iterValid(iterValid),
- iterWrapperCount(iterWrapperCount) {
- if constexpr (isRev) {
- if constexpr (isConst) {
- iter = containerPtr->crbegin();
- } else {
- iter = containerPtr->rbegin();
- }
- } else {
- if constexpr (isConst) {
- iter = containerPtr->cbegin();
- } else {
- iter = containerPtr->begin();
- }
- }
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-bool TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::isValid() const {
- return !iterValid.expired();
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-bool TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::next() {
- if(!isValid()) {
- return false;
- }
-
- if constexpr (isRev) {
- if(containerPtr->rend() == iter) {
- iterValid.reset();
- return false;
- } else {
- ++iter;
- if(containerPtr->rend() == iter) {
- return false;
- }
- }
- } else {
- if(containerPtr->end() == iter) {
- iterValid.reset();
- return false;
- } else {
- ++iter;
- if(containerPtr->end() == iter) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-bool TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::prev() {
- if(!isValid()) {
- return false;
- }
-
- if constexpr (isRev) {
- if(containerPtr->rbegin() == iter) {
- iterValid.reset();
- return false;
- } else {
- --iter;
- }
- } else {
- if(containerPtr->begin() == iter) {
- iterValid.reset();
- return false;
- } else {
- --iter;
- }
- }
-
- return true;
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-std::optional<T> TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::current() {
- if(!isValid()) {
- return std::nullopt;
- } else if constexpr (isRev) {
- if(containerPtr->rend() == iter) {
- return std::nullopt;
- }
- } else {
- if(containerPtr->end() == iter) {
- return std::nullopt;
- }
- }
- return *iter;
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-bool TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::remove() {
- if(!isValid()) {
- return false;
- } else if constexpr(isRev) {
- return false;
- } else {
- if(containerPtr->end() == iter) {
- return false;
- }
- }
- iter = containerPtr->erase(iter);
- return true;
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-typename TSLQueue<T>::template TSLQIterWrapper<isConst, isRev>& TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::operator++() {
- next();
- return *this;
+ return head->next == tail;
}
template <typename T>
-template <bool isConst, bool isRev>
-typename TSLQueue<T>::template TSLQIterWrapper<isConst, isRev>& TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::operator--() {
- prev();
- return *this;
-}
-
-template <typename T>
-template <bool isConst, bool isRev>
-bool TSLQueue<T>::TSLQIterWrapper<isConst, isRev>::set(T &&newValue) {
- if constexpr(isConst) {
- return false;
- } else {
- if(!isValid()) {
- return false;
- }
- *iter = std::forward<T>(newValue);
- return true;
+unsigned long long TSLQueue<T>::size() {
+ while(iterWrapperCount.use_count() > 1) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
-}
-
-template <typename T>
-typename TSLQueue<T>::template TSLQIterWrapper<false, false> TSLQueue<T>::iter() {
- std::lock_guard lock(mutex);
- return TSLQIterWrapper<false, false>(&container, iterValid, iterWrapperCount);
-}
-
-template <typename T>
-typename TSLQueue<T>::template TSLQIterWrapper<false, true> TSLQueue<T>::riter() {
- std::lock_guard lock(mutex);
- return TSLQIterWrapper<false, true>(&container, iterValid, iterWrapperCount);
-}
-
-template <typename T>
-typename TSLQueue<T>::template TSLQIterWrapper<true, false> TSLQueue<T>::citer() {
- std::lock_guard lock(mutex);
- return TSLQIterWrapper<true, false>(&container, iterValid, iterWrapperCount);
-}
-
-template <typename T>
-typename TSLQueue<T>::template TSLQIterWrapper<true, true> TSLQueue<T>::criter() {
std::lock_guard lock(mutex);
- return TSLQIterWrapper<true, true>(&container, iterValid, iterWrapperCount);
+ return msize;
}
#endif
#include <gtest/gtest.h>
-#include <iostream>
+#include <future>
+#include <functional>
#include "TSLQueue.hpp"
-TEST(TSLQueue, Usage) {
+TEST(TSLQueue, PushTopPopSize) {
TSLQueue<int> q;
- bool isEmpty;
- std::optional<int> opt;
-
- // init
- EXPECT_FALSE(q.pop());
- opt = q.top_and_pop();
- EXPECT_FALSE(opt.has_value());
+ EXPECT_FALSE(q.top().has_value());
- opt = q.top_and_pop_and_empty(&isEmpty);
- EXPECT_FALSE(opt.has_value());
- EXPECT_TRUE(isEmpty);
-
- EXPECT_TRUE(q.empty());
+ for(int i = 0; i < 10; ++i) {
+ EXPECT_EQ(i, q.size());
+ q.push(i);
+ }
- // push 1, 2, 3
- q.push(1);
- EXPECT_FALSE(q.empty());
- opt = q.top();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
+ for(int i = 0; i < 10; ++i) {
+ auto v = q.top();
+ ASSERT_TRUE(v.has_value());
+ EXPECT_EQ(v.value(), i);
+ EXPECT_EQ(10 - i, q.size());
+ EXPECT_TRUE(q.pop());
+ }
+ EXPECT_EQ(q.size(), 0);
- q.push_nb(2);
- EXPECT_FALSE(q.empty());
- opt = q.top_nb();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
+ EXPECT_FALSE(q.pop());
+}
- q.push(3);
- EXPECT_FALSE(q.empty());
- opt = q.top();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
+TEST(TSLQueue, PushNB_TopNB_TopAndPop_Size) {
+ TSLQueue<int> q;
- // iterators
- {
- auto citer = q.citer();
- opt = citer.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
- EXPECT_FALSE(citer.set(111));
-
- EXPECT_TRUE(citer.next());
- opt = citer.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 2);
-
- EXPECT_TRUE(citer.next());
- opt = citer.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
-
- EXPECT_FALSE(citer.next());
- opt = citer.current();
- EXPECT_FALSE(opt.has_value());
-
- EXPECT_TRUE(citer.isValid());
- EXPECT_FALSE(citer.next());
- EXPECT_FALSE(citer.isValid());
+ for(int i = 0; i < 10; ++i) {
+ EXPECT_EQ(q.size(), i);
+ EXPECT_TRUE(q.push_nb(i));
}
- {
- auto criter = q.criter();
- opt = criter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
- EXPECT_FALSE(criter.set(333));
-
- EXPECT_TRUE(criter.next());
- opt = criter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 2);
-
- EXPECT_TRUE(criter.next());
- opt = criter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
-
- EXPECT_FALSE(criter.next());
- opt = criter.current();
- EXPECT_FALSE(opt.has_value());
+
+ for(int i = 0; i < 10; ++i) {
+ auto v = q.top_nb();
+ ASSERT_TRUE(v.has_value());
+ EXPECT_EQ(v.value(), i);
+ EXPECT_EQ(q.size(), 10 - i);
+ v = q.top_and_pop();
+ ASSERT_TRUE(v.has_value());
+ EXPECT_EQ(v.value(), i);
}
+
{
- // values changed to 10, 20, 30
- auto iter = q.iter();
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
- EXPECT_TRUE(iter.set(10));
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 10);
-
- EXPECT_TRUE(iter.next());
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 2);
- EXPECT_TRUE(iter.set(20));
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 20);
-
- EXPECT_TRUE(iter.next());
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
- EXPECT_TRUE(iter.set(30));
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 30);
-
- EXPECT_FALSE(iter.next());
- opt = iter.current();
- EXPECT_FALSE(opt.has_value());
+ auto v = q.top_nb();
+ ASSERT_FALSE(v.has_value());
}
{
- // values changed to 1, 2, 3
- auto riter = q.riter();
- opt = riter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 30);
- EXPECT_TRUE(riter.set(3));
- opt = riter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
-
- EXPECT_TRUE(riter.next());
- opt = riter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 20);
- EXPECT_TRUE(riter.set(2));
- opt = riter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 2);
-
- EXPECT_TRUE(riter.next());
- opt = riter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 10);
- EXPECT_TRUE(riter.set(1));
- opt = riter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
+ auto v = q.top_and_pop();
+ ASSERT_FALSE(v.has_value());
}
+ EXPECT_EQ(q.size(), 0);
+}
- {
- // remove center (2), result: 1, 3
- auto iter = q.iter();
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
-
- EXPECT_TRUE(iter.next());
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 2);
-
- EXPECT_TRUE(iter.remove());
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
-
- EXPECT_FALSE(iter.next());
- opt = iter.current();
- ASSERT_FALSE(opt.has_value());
- EXPECT_FALSE(iter.remove());
+TEST(TSLQueue, Push_TopAndPopAndEmpty_Size) {
+ TSLQueue<int> q;
+
+ for(int i = 0; i < 10; ++i) {
+ EXPECT_EQ(q.size(), i);
+ q.push(i);
}
- {
- // remove first (1), result: 3
- auto iter = q.iter();
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 1);
-
- EXPECT_TRUE(iter.remove());
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
-
- EXPECT_FALSE(iter.next());
- opt = iter.current();
- ASSERT_FALSE(opt.has_value());
- EXPECT_FALSE(iter.remove());
+ bool isEmpty;
+ for(int i = 0; i < 10; ++i) {
+ EXPECT_EQ(q.size(), 10 - i);
+ auto v = q.top_and_pop_and_empty(&isEmpty);
+ ASSERT_TRUE(v.has_value());
+ EXPECT_EQ(v.value(), i);
+ EXPECT_EQ(i == 9, isEmpty);
}
+ EXPECT_EQ(q.size(), 0);
+}
- {
- // remove (3), result: empty
- auto iter = q.iter();
- opt = iter.current();
- ASSERT_TRUE(opt.has_value());
- EXPECT_EQ(opt.value(), 3);
-
- EXPECT_TRUE(iter.remove());
- opt = iter.current();
- EXPECT_FALSE(opt.has_value());
- EXPECT_FALSE(iter.remove());
+TEST(TSLQueue, PushClearEmptySize) {
+ TSLQueue<int> q;
+
+ for(int i = 0; i < 10; ++i) {
+ EXPECT_EQ(q.size(), i);
+ q.push(i);
}
+ EXPECT_EQ(q.size(), 10);
- {
- auto iter = q.iter();
- opt = iter.current();
- EXPECT_FALSE(opt.has_value());
+ EXPECT_FALSE(q.empty());
+ q.clear();
+ EXPECT_TRUE(q.empty());
+ EXPECT_EQ(q.size(), 0);
+}
+
+TEST(TSLQueue, Concurrent) {
+ TSLQueue<int> q;
+
+ const auto add_fn = [] (TSLQueue<int> *q, int i) -> void {
+ q->push(i);
+ };
+
+ std::future<void> futures[100];
+ for(int i = 0; i < 100; ++i) {
+ futures[i] = std::async(std::launch::async, add_fn, &q, i);
}
- {
- auto riter = q.riter();
- opt = riter.current();
- EXPECT_FALSE(opt.has_value());
+ for(int i = 0; i < 100; ++i) {
+ futures[i].wait();
+ }
+
+ EXPECT_FALSE(q.empty());
+ for(int i = 0; i < 100; ++i) {
+ EXPECT_EQ(q.size(), 100 - i);
+ auto v = q.top_and_pop();
+ ASSERT_TRUE(v.has_value());
+ EXPECT_GE(v.value(), 0);
+ EXPECT_LE(v.value(), 100);
+ EXPECT_EQ(i == 99, q.empty());
}
+ EXPECT_EQ(q.size(), 0);
}