Replace std::list with own doubly-linked-list

TODO iterator
This commit is contained in:
Stephen Seo 2019-10-29 20:33:16 +09:00
parent 3dc12683e8
commit 10899ffaab
2 changed files with 214 additions and 447 deletions

View file

@ -6,12 +6,11 @@
#include <thread>
#include <chrono>
#include <optional>
#include <cassert>
#include <list>
#include <type_traits>
// definition
template <typename T>
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<T> top();
std::optional<T> top_nb();
@ -35,64 +34,41 @@ class TSLQueue {
void clear();
bool empty();
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;
};
TSLQIterWrapper<false, false> iter();
TSLQIterWrapper<false, true> riter();
TSLQIterWrapper<true, false> citer();
TSLQIterWrapper<true, true> 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<TSLQNode> next;
std::weak_ptr<TSLQNode> prev;
std::unique_ptr<T> data;
};
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>
@ -105,23 +81,38 @@ TSLQueue<T>::TSLQueue(TSLQueue &&other) :
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>
@ -129,7 +120,18 @@ bool TSLQueue<T>::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<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 {
@ -143,10 +145,11 @@ std::optional<T> TSLQueue<T>::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<T> TSLQueue<T>::top_nb() {
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 {
@ -169,10 +176,15 @@ bool TSLQueue<T>::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<char>();
iterWrapperCount = std::make_shared<char>();
return true;
@ -186,9 +198,16 @@ std::optional<T> TSLQueue<T>::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<char>();
iterWrapperCount = std::make_shared<char>();
}
@ -202,17 +221,24 @@ std::optional<T> TSLQueue<T>::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<char>();
iterWrapperCount = std::make_shared<char>();
if(isEmpty) {
*isEmpty = container.empty();
*isEmpty = head->next == tail;
}
}
return ret;
@ -224,7 +250,11 @@ void TSLQueue<T>::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<char>();
iterWrapperCount = std::make_shared<char>();
}
@ -235,180 +265,16 @@ bool TSLQueue<T>::empty() {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
std::lock_guard lock(mutex);
return container.empty();
return head->next == tail;
}
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();
}
unsigned long long TSLQueue<T>::size() {
while(iterWrapperCount.use_count() > 1) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
}
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;
}
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;
}
}
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

View file

@ -1,217 +1,118 @@
#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.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<int> 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<int> 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<int> 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<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);
}
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);
}