From 10899ffaabeb6bfc1890461c1ae1c9374f80f62d Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Tue, 29 Oct 2019 20:33:16 +0900 Subject: [PATCH] Replace std::list with own doubly-linked-list TODO iterator --- cpp_impl/src/TSLQueue.hpp | 344 +++++++++-------------------- cpp_impl/src/test/TestTSLQueue.cpp | 317 +++++++++----------------- 2 files changed, 214 insertions(+), 447 deletions(-) diff --git a/cpp_impl/src/TSLQueue.hpp b/cpp_impl/src/TSLQueue.hpp index 7d03f0c..eaa73c2 100644 --- a/cpp_impl/src/TSLQueue.hpp +++ b/cpp_impl/src/TSLQueue.hpp @@ -6,12 +6,11 @@ #include #include #include +#include #include #include -// definition - template class TSLQueue { public: @@ -25,7 +24,7 @@ class TSLQueue { 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 top(); std::optional top_nb(); @@ -35,64 +34,41 @@ class TSLQueue { void clear(); bool empty(); - - template - class TSLQIterWrapper { - public: - TSLQIterWrapper( - std::conditional_t, std::list> *container, - std::weak_ptr iterValid, - std::shared_ptr iterWrapperCount - ); - - bool isValid() const; - - bool next(); - bool prev(); - std::optional current(); - /// can only be used when isRev is false - bool remove(); - - TSLQIterWrapper& operator++(); - TSLQIterWrapper& operator--(); - - bool set(T &&newValue); - - private: - std::conditional_t, std::list> - *containerPtr; - std::conditional_t::const_reverse_iterator, - typename std::list::reverse_iterator>, - std::conditional_t::const_iterator, - typename std::list::iterator>> - iter; - - std::weak_ptr iterValid; - std::shared_ptr iterWrapperCount; - }; - - TSLQIterWrapper iter(); - TSLQIterWrapper riter(); - TSLQIterWrapper citer(); - TSLQIterWrapper criter(); + 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::list container; + std::shared_ptr head; + std::shared_ptr tail; + unsigned long long msize; }; -// implementation - template TSLQueue::TSLQueue() : iterValid(std::make_shared()), - iterWrapperCount(std::make_shared()) + iterWrapperCount(std::make_shared()), + head(std::make_shared()), + tail(std::make_shared()), + msize(0) { + head->next = tail; + tail->prev = head; } template @@ -105,23 +81,38 @@ TSLQueue::TSLQueue(TSLQueue &&other) : iterWrapperCount(std::make_shared()) { 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 TSLQueue & TSLQueue::operator=(TSLQueue &&other) { + iterValid = std::make_shared(); + iterWrapperCount = std::make_shared(); 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 -bool TSLQueue::push(const T &data) { +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); - container.push_back(data); - return true; + 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 @@ -129,7 +120,18 @@ bool TSLQueue::push_nb(const T &data) { if(iterWrapperCount.use_count() > 1) { return false; } else if(mutex.try_lock()) { - container.push_back(data); + 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 { @@ -143,10 +145,11 @@ std::optional TSLQueue::top() { 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; } } @@ -155,7 +158,11 @@ std::optional TSLQueue::top_nb() { if(iterWrapperCount.use_count() > 1) { return std::nullopt; } else if(mutex.try_lock()) { - std::optional ret = container.front(); + std::optional ret = std::nullopt; + if(head->next != tail) { + assert(head->next->data); + ret = *head->next->data.get(); + } mutex.unlock(); return ret; } else { @@ -169,10 +176,15 @@ bool TSLQueue::pop() { 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(); iterWrapperCount = std::make_shared(); return true; @@ -186,9 +198,16 @@ std::optional TSLQueue::top_and_pop() { 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(); iterWrapperCount = std::make_shared(); } @@ -202,17 +221,24 @@ std::optional TSLQueue::top_and_pop_and_empty(bool *isEmpty) { 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(); iterWrapperCount = std::make_shared(); if(isEmpty) { - *isEmpty = container.empty(); + *isEmpty = head->next == tail; } } return ret; @@ -224,7 +250,11 @@ void TSLQueue::clear() { 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(); iterWrapperCount = std::make_shared(); } @@ -235,180 +265,16 @@ bool TSLQueue::empty() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } std::lock_guard lock(mutex); - return container.empty(); + return head->next == tail; } template -template -TSLQueue::TSLQIterWrapper::TSLQIterWrapper( - std::conditional_t, std::list> *container, - std::weak_ptr iterValid, - std::shared_ptr 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(); - } +unsigned long long TSLQueue::size() { + while(iterWrapperCount.use_count() > 1) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); } -} - -template -template -bool TSLQueue::TSLQIterWrapper::isValid() const { - return !iterValid.expired(); -} - -template -template -bool TSLQueue::TSLQIterWrapper::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 -template -bool TSLQueue::TSLQIterWrapper::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 -template -std::optional TSLQueue::TSLQIterWrapper::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 -template -bool TSLQueue::TSLQIterWrapper::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 -template -typename TSLQueue::template TSLQIterWrapper& TSLQueue::TSLQIterWrapper::operator++() { - next(); - return *this; -} - -template -template -typename TSLQueue::template TSLQIterWrapper& TSLQueue::TSLQIterWrapper::operator--() { - prev(); - return *this; -} - -template -template -bool TSLQueue::TSLQIterWrapper::set(T &&newValue) { - if constexpr(isConst) { - return false; - } else { - if(!isValid()) { - return false; - } - *iter = std::forward(newValue); - return true; - } -} - -template -typename TSLQueue::template TSLQIterWrapper TSLQueue::iter() { std::lock_guard lock(mutex); - return TSLQIterWrapper(&container, iterValid, iterWrapperCount); -} - -template -typename TSLQueue::template TSLQIterWrapper TSLQueue::riter() { - std::lock_guard lock(mutex); - return TSLQIterWrapper(&container, iterValid, iterWrapperCount); -} - -template -typename TSLQueue::template TSLQIterWrapper TSLQueue::citer() { - std::lock_guard lock(mutex); - return TSLQIterWrapper(&container, iterValid, iterWrapperCount); -} - -template -typename TSLQueue::template TSLQIterWrapper TSLQueue::criter() { - std::lock_guard lock(mutex); - return TSLQIterWrapper(&container, iterValid, iterWrapperCount); + return msize; } #endif diff --git a/cpp_impl/src/test/TestTSLQueue.cpp b/cpp_impl/src/test/TestTSLQueue.cpp index d5b97bc..cd96104 100644 --- a/cpp_impl/src/test/TestTSLQueue.cpp +++ b/cpp_impl/src/test/TestTSLQueue.cpp @@ -1,217 +1,118 @@ #include -#include +#include +#include #include "TSLQueue.hpp" -TEST(TSLQueue, Usage) { +TEST(TSLQueue, PushTopPopSize) { TSLQueue q; - bool isEmpty; - std::optional opt; - // init + EXPECT_FALSE(q.top().has_value()); + + for(int i = 0; i < 10; ++i) { + EXPECT_EQ(i, q.size()); + q.push(i); + } + + 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); + EXPECT_FALSE(q.pop()); - - opt = q.top_and_pop(); - EXPECT_FALSE(opt.has_value()); - - opt = q.top_and_pop_and_empty(&isEmpty); - EXPECT_FALSE(opt.has_value()); - EXPECT_TRUE(isEmpty); - - EXPECT_TRUE(q.empty()); - - // push 1, 2, 3 - q.push(1); - EXPECT_FALSE(q.empty()); - opt = q.top(); - ASSERT_TRUE(opt.has_value()); - EXPECT_EQ(opt.value(), 1); - - q.push_nb(2); - EXPECT_FALSE(q.empty()); - opt = q.top_nb(); - ASSERT_TRUE(opt.has_value()); - EXPECT_EQ(opt.value(), 1); - - q.push(3); - EXPECT_FALSE(q.empty()); - opt = q.top(); - ASSERT_TRUE(opt.has_value()); - EXPECT_EQ(opt.value(), 1); - - // 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()); - } - { - 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()); - } - { - // 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()); - } - { - // 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); - } - - { - // 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()); - } - - { - // 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()); - } - - { - // 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()); - } - - { - auto iter = q.iter(); - opt = iter.current(); - EXPECT_FALSE(opt.has_value()); - } - { - auto riter = q.riter(); - opt = riter.current(); - EXPECT_FALSE(opt.has_value()); - } +} + +TEST(TSLQueue, PushNB_TopNB_TopAndPop_Size) { + TSLQueue q; + + for(int i = 0; i < 10; ++i) { + EXPECT_EQ(q.size(), i); + EXPECT_TRUE(q.push_nb(i)); + } + + 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); + } + + { + auto v = q.top_nb(); + ASSERT_FALSE(v.has_value()); + } + { + auto v = q.top_and_pop(); + ASSERT_FALSE(v.has_value()); + } + EXPECT_EQ(q.size(), 0); +} + +TEST(TSLQueue, Push_TopAndPopAndEmpty_Size) { + TSLQueue q; + + for(int i = 0; i < 10; ++i) { + EXPECT_EQ(q.size(), i); + q.push(i); + } + + 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); +} + +TEST(TSLQueue, PushClearEmptySize) { + TSLQueue q; + + for(int i = 0; i < 10; ++i) { + EXPECT_EQ(q.size(), i); + q.push(i); + } + EXPECT_EQ(q.size(), 10); + + EXPECT_FALSE(q.empty()); + q.clear(); + EXPECT_TRUE(q.empty()); + EXPECT_EQ(q.size(), 0); +} + +TEST(TSLQueue, Concurrent) { + TSLQueue q; + + const auto add_fn = [] (TSLQueue *q, int i) -> void { + q->push(i); + }; + + std::future futures[100]; + for(int i = 0; i < 100; ++i) { + futures[i] = std::async(std::launch::async, add_fn, &q, i); + } + 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); }