#include <chrono>
#include <optional>
#include <cassert>
-
#include <list>
#include <type_traits>
private:
struct TSLQNode {
- TSLQNode() = default;
+ TSLQNode();
// disable copy
TSLQNode(TSLQNode& other) = delete;
TSLQNode& operator=(TSLQNode& other) = delete;
std::shared_ptr<TSLQNode> next;
std::weak_ptr<TSLQNode> prev;
std::unique_ptr<T> data;
+
+ enum TSLQN_Type {
+ TSLQN_NORMAL,
+ TSLQN_HEAD,
+ TSLQN_TAIL
+ };
+
+ TSLQN_Type type;
+ bool isNormal() const;
+ };
+
+ class TSLQIter {
+ public:
+ TSLQIter(std::mutex &mutex,
+ std::weak_ptr<TSLQNode> currentNode);
+ ~TSLQIter();
+
+ std::optional<T> current();
+ bool next();
+ bool prev();
+ bool remove();
+
+ private:
+ std::lock_guard<std::mutex> lock;
+ std::weak_ptr<TSLQNode> currentNode;
};
- std::shared_ptr<char> iterValid;
- std::shared_ptr<char> iterWrapperCount;
+ public:
+ TSLQIter begin();
+
+ private:
std::mutex mutex;
std::shared_ptr<TSLQNode> head;
std::shared_ptr<TSLQNode> tail;
template <typename T>
TSLQueue<T>::TSLQueue() :
- iterValid(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;
+ head->type = TSLQNode::TSLQN_Type::TSLQN_HEAD;
+ tail->type = TSLQNode::TSLQN_Type::TSLQN_TAIL;
}
template <typename T>
}
template <typename T>
-TSLQueue<T>::TSLQueue(TSLQueue &&other) :
- iterValid(std::make_shared<char>()),
- iterWrapperCount(std::make_shared<char>())
+TSLQueue<T>::TSLQueue(TSLQueue &&other)
{
std::lock_guard lock(other.mutex);
head = std::move(other.head);
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);
head = std::move(other.head);
tail = std::move(other.tail);
template <typename T>
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);
auto newNode = std::make_shared<TSLQNode>();
newNode->data = std::make_unique<T>(data);
template <typename T>
bool TSLQueue<T>::push_nb(const T &data) {
- if(iterWrapperCount.use_count() > 1) {
- return false;
- } else if(mutex.try_lock()) {
+ if(mutex.try_lock()) {
auto newNode = std::make_shared<TSLQNode>();
newNode->data = std::make_unique<T>(data);
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);
if(head->next != tail) {
assert(head->next->data);
template <typename T>
std::optional<T> TSLQueue<T>::top_nb() {
- if(iterWrapperCount.use_count() > 1) {
- return std::nullopt;
- } else if(mutex.try_lock()) {
+ if(mutex.try_lock()) {
std::optional<T> ret = std::nullopt;
if(head->next != tail) {
assert(head->next->data);
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);
if(head->next == tail) {
return false;
assert(msize > 0);
--msize;
- iterValid = std::make_shared<char>();
- iterWrapperCount = std::make_shared<char>();
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);
if(head->next != tail) {
assert(head->next->data);
head->next = newNext;
assert(msize > 0);
--msize;
-
- iterValid = std::make_shared<char>();
- iterWrapperCount = std::make_shared<char>();
}
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);
if(head->next == tail) {
if(isEmpty) {
assert(msize > 0);
--msize;
- iterValid = std::make_shared<char>();
- iterWrapperCount = std::make_shared<char>();
if(isEmpty) {
*isEmpty = head->next == tail;
}
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);
head->next = tail;
tail->prev = head;
msize = 0;
-
- iterValid = std::make_shared<char>();
- iterWrapperCount = std::make_shared<char>();
}
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);
return head->next == tail;
}
template <typename T>
unsigned long long TSLQueue<T>::size() {
- while(iterWrapperCount.use_count() > 1) {
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- }
std::lock_guard lock(mutex);
return msize;
}
+template <typename T>
+TSLQueue<T>::TSLQNode::TSLQNode() :
+type(TSLQN_Type::TSLQN_NORMAL)
+{}
+
+template <typename T>
+bool TSLQueue<T>::TSLQNode::isNormal() const {
+ return type == TSLQN_Type::TSLQN_NORMAL;
+}
+
+template <typename T>
+TSLQueue<T>::TSLQIter::TSLQIter(std::mutex &mutex,
+ std::weak_ptr<TSLQNode> currentNode) :
+lock(mutex),
+currentNode(currentNode)
+{
+}
+
+template <typename T>
+TSLQueue<T>::TSLQIter::~TSLQIter() {
+}
+
+template <typename T>
+std::optional<T> TSLQueue<T>::TSLQIter::current() {
+ std::shared_ptr<TSLQNode> currentNode = this->currentNode.lock();
+ assert(currentNode);
+ if(currentNode->isNormal()) {
+ return *currentNode->data.get();
+ } else {
+ return std::nullopt;
+ }
+}
+
+template <typename T>
+bool TSLQueue<T>::TSLQIter::next() {
+ std::shared_ptr<TSLQNode> currentNode = this->currentNode.lock();
+ assert(currentNode);
+ if(currentNode->type == TSLQNode::TSLQN_Type::TSLQN_TAIL) {
+ return false;
+ }
+
+ this->currentNode = currentNode->next;
+ return currentNode->next->type != TSLQNode::TSLQN_Type::TSLQN_TAIL;
+}
+
+template <typename T>
+bool TSLQueue<T>::TSLQIter::prev() {
+ std::shared_ptr<TSLQNode> currentNode = this->currentNode.lock();
+ assert(currentNode);
+ if(currentNode->type == TSLQNode::TSLQN_Type::TSLQN_HEAD) {
+ return false;
+ }
+
+ auto parent = currentNode->prev.lock();
+ assert(parent);
+ this->currentNode = currentNode->prev;
+ return parent->type != TSLQNode::TSLQN_Type::TSLQN_HEAD;
+}
+
+template <typename T>
+bool TSLQueue<T>::TSLQIter::remove() {
+ std::shared_ptr<TSLQNode> currentNode = this->currentNode.lock();
+ assert(currentNode);
+ if(!currentNode->isNormal()) {
+ return false;
+ }
+
+ this->currentNode = currentNode->next;
+ auto parent = currentNode->prev.lock();
+ assert(parent);
+
+ currentNode->next->prev = parent;
+ parent->next = currentNode->next;
+
+ return parent->next->isNormal();
+}
+
+template <typename T>
+typename TSLQueue<T>::TSLQIter TSLQueue<T>::begin() {
+ return TSLQIter(mutex, head->next);
+}
+
#endif
}
EXPECT_EQ(q.size(), 0);
}
+
+TEST(TSLQueue, Iterator) {
+ TSLQueue<int> q;
+
+ for(int i = 0; i < 10; ++i) {
+ q.push(i);
+ }
+
+ {
+ // iteration
+ auto iter = q.begin();
+ int i = 0;
+ auto op = iter.current();
+ while(op.has_value()) {
+ EXPECT_EQ(op.value(), i++);
+ if(i < 10) {
+ EXPECT_TRUE(iter.next());
+ } else {
+ EXPECT_FALSE(iter.next());
+ }
+ op = iter.current();
+ }
+
+ // test that lock is held by iterator
+ EXPECT_FALSE(q.push_nb(10));
+ op = q.top_nb();
+ EXPECT_FALSE(op.has_value());
+
+ // backwards iteration
+ EXPECT_TRUE(iter.prev());
+ op = iter.current();
+ while(op.has_value()) {
+ EXPECT_EQ(op.value(), --i);
+ if(i > 0) {
+ EXPECT_TRUE(iter.prev());
+ } else {
+ EXPECT_FALSE(iter.prev());
+ }
+ op = iter.current();
+ }
+ }
+
+ {
+ // iter remove
+ auto iter = q.begin();
+ EXPECT_TRUE(iter.next());
+ EXPECT_TRUE(iter.next());
+ EXPECT_TRUE(iter.next());
+ EXPECT_TRUE(iter.remove());
+
+ auto op = iter.current();
+ EXPECT_TRUE(op.has_value());
+ EXPECT_EQ(op.value(), 4);
+
+ EXPECT_TRUE(iter.prev());
+ op = iter.current();
+ EXPECT_TRUE(op.has_value());
+ EXPECT_EQ(op.value(), 2);
+ }
+
+ // check that "3" was removed from queue
+ int i = 0;
+ std::optional<int> op;
+ while(!q.empty()) {
+ op = q.top();
+ EXPECT_TRUE(op.has_value());
+ EXPECT_EQ(i++, op.value());
+ if(i == 3) {
+ ++i;
+ }
+ EXPECT_TRUE(q.pop());
+ }
+}