From ece17e1aca5eb2701f63c26bea6c22a1a8ee8a54 Mon Sep 17 00:00:00 2001 From: Stephen Seo Date: Wed, 11 Dec 2019 20:00:48 +0900 Subject: [PATCH] Drop support from C++17 to C++11 std::optional replaced with Entry in TSLQueue (which is basically a poor man's optional). --- CMakeLists.txt | 2 +- src/TSLQueue.hpp | 149 +++++++++++++++++++++++++++----------- src/UDPConnection.cpp | 121 ++++++++++++++++--------------- src/test/TestTSLQueue.cpp | 30 ++++---- 4 files changed, 187 insertions(+), 115 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa4c321..f63fec6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ add_library(UDPConnection ${UDPConnection_SOURCES}) set_target_properties(UDPConnection PROPERTIES VERSION ${UDPConnection_VERSION}) -target_compile_features(UDPConnection PUBLIC cxx_std_17) +target_compile_features(UDPConnection PUBLIC cxx_std_11) target_link_libraries(UDPConnection PUBLIC pthread) if(WIN32) if(MINGW) diff --git a/src/TSLQueue.hpp b/src/TSLQueue.hpp index d368f39..7ef865c 100644 --- a/src/TSLQueue.hpp +++ b/src/TSLQueue.hpp @@ -23,14 +23,40 @@ class TSLQueue { TSLQueue(TSLQueue &&other); TSLQueue &operator=(TSLQueue &&other); + // in place of std::optional which is only available in C++17 + struct Entry { + Entry(); + Entry(const T& value); + Entry(T&& rvalue); + + // enable copy + Entry(const Entry& other) = default; + Entry &operator =(const Entry& other) = default; + + // enable move + Entry(Entry&& other) = default; + Entry &operator =(Entry&& other) = default; + + enum Type { + NONE, + SOME + } type; + T value; + + bool has_value() const; + operator bool () const; + T& operator *(); + const T& operator *() const; + }; + void push(const T &data); bool push_nb(const T &data); - std::optional top(); - std::optional top_nb(); + Entry top(); + Entry top_nb(); bool pop(); - std::optional top_and_pop(); - std::optional top_and_pop_and_empty(bool *isEmpty); - std::optional top_and_pop_and_rsize(unsigned long *rsize); + Entry top_and_pop(); + Entry top_and_pop_and_empty(bool *isEmpty); + Entry top_and_pop_and_rsize(unsigned long *rsize); void clear(); bool empty(); @@ -62,18 +88,18 @@ class TSLQueue { class TSLQIter { public: - TSLQIter(std::mutex &mutex, + TSLQIter(std::mutex *mutex, std::weak_ptr currentNode, unsigned long *msize); ~TSLQIter(); - std::optional current(); + Entry current(); bool next(); bool prev(); bool remove(); private: - std::lock_guard lock; + std::mutex *mutex; std::weak_ptr currentNode; unsigned long *const msize; @@ -108,7 +134,7 @@ TSLQueue::~TSLQueue() { template TSLQueue::TSLQueue(TSLQueue &&other) { - std::lock_guard lock(other.mutex); + std::lock_guard lock(other.mutex); head = std::move(other.head); tail = std::move(other.tail); msize = std::move(other.msize); @@ -116,15 +142,54 @@ TSLQueue::TSLQueue(TSLQueue &&other) template TSLQueue & TSLQueue::operator=(TSLQueue &&other) { - std::scoped_lock lock(mutex, other.mutex); + std::lock_guard lock(mutex); + std::lock_guard otherLock(other.mutex); head = std::move(other.head); tail = std::move(other.tail); msize = std::move(other.msize); } +template +TSLQueue::Entry::Entry() : + type(Type::NONE), + value() +{} + +template +TSLQueue::Entry::Entry(const T& value) : + type(Type::SOME), + value(value) +{} + +template +TSLQueue::Entry::Entry(T&& value) : + type(Type::SOME), + value(std::forward(value)) +{} + +template +bool TSLQueue::Entry::has_value() const { + return type == Type::SOME; +} + +template +TSLQueue::Entry::operator bool() const { + return has_value(); +} + +template +T& TSLQueue::Entry::operator *() { + return value; +} + +template +const T& TSLQueue::Entry::operator *() const { + return value; +} + template void TSLQueue::push(const T &data) { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); auto newNode = std::make_shared(); newNode->data = std::make_unique(data); @@ -161,34 +226,34 @@ bool TSLQueue::push_nb(const T &data) { } template -std::optional TSLQueue::top() { - std::lock_guard lock(mutex); +typename TSLQueue::Entry TSLQueue::top() { + std::lock_guard lock(mutex); if(head->next != tail) { assert(head->next->data); - return *head->next->data.get(); + return Entry(*head->next->data.get()); } else { - return std::nullopt; + return Entry(); } } template -std::optional TSLQueue::top_nb() { +typename TSLQueue::Entry TSLQueue::top_nb() { if(mutex.try_lock()) { - std::optional ret = std::nullopt; + Entry ret; if(head->next != tail) { assert(head->next->data); - ret = *head->next->data.get(); + ret = Entry(*head->next->data.get()); } mutex.unlock(); return ret; } else { - return std::nullopt; + return Entry(); } } template bool TSLQueue::pop() { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); if(head->next == tail) { return false; } else { @@ -203,12 +268,12 @@ bool TSLQueue::pop() { } template -std::optional TSLQueue::top_and_pop() { - std::optional ret = std::nullopt; - std::lock_guard lock(mutex); +typename TSLQueue::Entry TSLQueue::top_and_pop() { + Entry ret; + std::lock_guard lock(mutex); if(head->next != tail) { assert(head->next->data); - ret = *head->next->data.get(); + ret = Entry(*head->next->data.get()); auto& newNext = head->next->next; newNext->prev = head; @@ -220,16 +285,16 @@ std::optional TSLQueue::top_and_pop() { } template -std::optional TSLQueue::top_and_pop_and_empty(bool *isEmpty) { - std::optional ret = std::nullopt; - std::lock_guard lock(mutex); +typename TSLQueue::Entry TSLQueue::top_and_pop_and_empty(bool *isEmpty) { + Entry ret; + std::lock_guard lock(mutex); if(head->next == tail) { if(isEmpty) { *isEmpty = true; } } else { assert(head->next->data); - ret = *head->next->data.get(); + ret = Entry(*head->next->data.get()); auto& newNext = head->next->next; newNext->prev = head; @@ -245,16 +310,16 @@ std::optional TSLQueue::top_and_pop_and_empty(bool *isEmpty) { } template -std::optional TSLQueue::top_and_pop_and_rsize(unsigned long *rsize) { - std::optional ret = std::nullopt; - std::lock_guard lock(mutex); +typename TSLQueue::Entry TSLQueue::top_and_pop_and_rsize(unsigned long *rsize) { + Entry ret; + std::lock_guard lock(mutex); if(head->next == tail) { if(rsize) { *rsize = 0; } } else { assert(head->next->data); - ret = *head->next->data.get(); + ret = Entry(*head->next->data.get()); auto& newNext = head->next->next; newNext->prev = head; @@ -271,7 +336,7 @@ std::optional TSLQueue::top_and_pop_and_rsize(unsigned long *rsize) { template void TSLQueue::clear() { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); head->next = tail; tail->prev = head; @@ -280,13 +345,13 @@ void TSLQueue::clear() { template bool TSLQueue::empty() { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); return head->next == tail; } template unsigned long TSLQueue::size() { - std::lock_guard lock(mutex); + std::lock_guard lock(mutex); return msize; } @@ -301,27 +366,29 @@ bool TSLQueue::TSLQNode::isNormal() const { } template -TSLQueue::TSLQIter::TSLQIter(std::mutex &mutex, +TSLQueue::TSLQIter::TSLQIter(std::mutex *mutex, std::weak_ptr currentNode, unsigned long *msize) : -lock(mutex), +mutex(mutex), currentNode(currentNode), msize(msize) { + mutex->lock(); } template TSLQueue::TSLQIter::~TSLQIter() { + mutex->unlock(); } template -std::optional TSLQueue::TSLQIter::current() { +typename TSLQueue::Entry TSLQueue::TSLQIter::current() { std::shared_ptr currentNode = this->currentNode.lock(); assert(currentNode); if(currentNode->isNormal()) { - return *currentNode->data.get(); + return Entry(*currentNode->data.get()); } else { - return std::nullopt; + return Entry(); } } @@ -374,7 +441,7 @@ bool TSLQueue::TSLQIter::remove() { template typename TSLQueue::TSLQIter TSLQueue::begin() { - return TSLQIter(mutex, head->next, &msize); + return TSLQIter(&mutex, head->next, &msize); } #endif diff --git a/src/UDPConnection.cpp b/src/UDPConnection.cpp index c2501c7..d754b35 100644 --- a/src/UDPConnection.cpp +++ b/src/UDPConnection.cpp @@ -265,7 +265,7 @@ void UDPC::Context::update_impl() { do { auto optE = internalEvents.top_and_pop(); if(optE.has_value()) { - switch(optE.value().type) { + switch(optE.value.type) { case UDPC_ET_REQUEST_CONNECT: { unsigned char *sk = nullptr; @@ -277,11 +277,11 @@ void UDPC::Context::update_impl() { UDPC::ConnectionData newCon( false, this, - optE.value().conId.addr, - optE.value().conId.scope_id, - optE.value().conId.port, + optE.value.conId.addr, + optE.value.conId.scope_id, + optE.value.conId.port, #ifdef UDPC_LIBSODIUM_ENABLED - flags.test(2) && optE.value().v.enableLibSodium != 0, + flags.test(2) && optE.value.v.enableLibSodium != 0, sk, pk); #else false, @@ -292,9 +292,9 @@ void UDPC::Context::update_impl() { UDPC_LoggingType::UDPC_ERROR, "Failed to init ConnectionData instance (libsodium " "init fail) while client establishing connection with ", - UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr), + UDPC_atostr((UDPC_HContext)this, optE.value.conId.addr), " port ", - optE.value().conId.port); + optE.value.conId.port); continue; } newCon.sent = std::chrono::steady_clock::now() - UDPC::INIT_PKT_INTERVAL_DT; @@ -314,34 +314,34 @@ void UDPC::Context::update_impl() { #endif } - if(conMap.find(optE.value().conId) == conMap.end()) { + if(conMap.find(optE.value.conId) == conMap.end()) { conMap.insert(std::make_pair( - optE.value().conId, + optE.value.conId, std::move(newCon))); - auto addrConIter = addrConMap.find(optE.value().conId.addr); + auto addrConIter = addrConMap.find(optE.value.conId.addr); if(addrConIter == addrConMap.end()) { auto insertResult = addrConMap.insert(std::make_pair( - optE.value().conId.addr, + optE.value.conId.addr, std::unordered_set{})); assert(insertResult.second && "new connection insert into addrConMap must not fail"); addrConIter = insertResult.first; } - addrConIter->second.insert(optE.value().conId); + addrConIter->second.insert(optE.value.conId); UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_INFO, "Client initiating connection to ", - UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr), + UDPC_atostr((UDPC_HContext)this, optE.value.conId.addr), " port ", - optE.value().conId.port, + optE.value.conId.port, " ..."); } else { UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_WARNING, "Client initiate connection, already connected to peer ", - UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr), + UDPC_atostr((UDPC_HContext)this, optE.value.conId.addr), " port ", - optE.value().conId.port); + optE.value.conId.port); } } break; @@ -358,9 +358,9 @@ void UDPC::Context::update_impl() { UDPC::ConnectionData newCon( false, this, - optE.value().conId.addr, - optE.value().conId.scope_id, - optE.value().conId.port, + optE.value.conId.addr, + optE.value.conId.scope_id, + optE.value.conId.port, #ifdef UDPC_LIBSODIUM_ENABLED true, sk, pk); @@ -368,18 +368,18 @@ void UDPC::Context::update_impl() { false, sk, pk); assert(!"compiled without libsodium support"); - delete[] optE.value().v.pk; + delete[] optE.value.v.pk; break; #endif if(newCon.flags.test(5)) { - delete[] optE.value().v.pk; + delete[] optE.value.v.pk; UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_ERROR, "Failed to init ConnectionData instance (libsodium " "init fail) while client establishing connection with ", - UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr), + UDPC_atostr((UDPC_HContext)this, optE.value.conId.addr), " port ", - optE.value().conId.port); + optE.value.conId.port); continue; } newCon.sent = std::chrono::steady_clock::now() - UDPC::INIT_PKT_INTERVAL_DT; @@ -401,48 +401,48 @@ void UDPC::Context::update_impl() { // set peer public key std::memcpy( newCon.peer_pk, - optE.value().v.pk, + optE.value.v.pk, crypto_sign_PUBLICKEYBYTES); newCon.flags.set(7); } - delete[] optE.value().v.pk; + delete[] optE.value.v.pk; - if(conMap.find(optE.value().conId) == conMap.end()) { + if(conMap.find(optE.value.conId) == conMap.end()) { conMap.insert(std::make_pair( - optE.value().conId, + optE.value.conId, std::move(newCon))); - auto addrConIter = addrConMap.find(optE.value().conId.addr); + auto addrConIter = addrConMap.find(optE.value.conId.addr); if(addrConIter == addrConMap.end()) { auto insertResult = addrConMap.insert(std::make_pair( - optE.value().conId.addr, + optE.value.conId.addr, std::unordered_set{})); assert(insertResult.second && "new connection insert into addrConMap must not fail"); addrConIter = insertResult.first; } - addrConIter->second.insert(optE.value().conId); + addrConIter->second.insert(optE.value.conId); UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_INFO, "Client initiating connection to ", - UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr), + UDPC_atostr((UDPC_HContext)this, optE.value.conId.addr), " port ", - optE.value().conId.port, + optE.value.conId.port, " ..."); } else { UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_WARNING, "Client initiate connection, already connected to peer ", - UDPC_atostr((UDPC_HContext)this, optE.value().conId.addr), + UDPC_atostr((UDPC_HContext)this, optE.value.conId.addr), " port ", - optE.value().conId.port); + optE.value.conId.port); } } break; case UDPC_ET_REQUEST_DISCONNECT: - if(optE.value().v.dropAllWithAddr != 0) { + if(optE.value.v.dropAllWithAddr != 0) { // drop all connections with same address - auto addrConIter = addrConMap.find(optE.value().conId.addr); + auto addrConIter = addrConMap.find(optE.value.conId.addr); if(addrConIter != addrConMap.end()) { for(auto identIter = addrConIter->second.begin(); identIter != addrConIter->second.end(); @@ -463,14 +463,14 @@ void UDPC::Context::update_impl() { } } else { // drop only specific connection with addr and port - auto iter = conMap.find(optE.value().conId); + auto iter = conMap.find(optE.value.conId); if(iter != conMap.end()) { if(iter->second.flags.test(4)) { idMap.erase(iter->second.id); } - auto addrConIter = addrConMap.find(optE.value().conId.addr); + auto addrConIter = addrConMap.find(optE.value.conId.addr); if(addrConIter != addrConMap.end()) { - addrConIter->second.erase(optE.value().conId); + addrConIter->second.erase(optE.value.conId); if(addrConIter->second.empty()) { addrConMap.erase(addrConIter); } @@ -607,18 +607,18 @@ void UDPC::Context::update_impl() { while(true) { auto next = sendIter.current(); if(next) { - if(auto iter = conMap.find(next.value().receiver); - iter != conMap.end()) { + auto iter = conMap.find(next.value.receiver); + if(iter != conMap.end()) { if(iter->second.sendPkts.size() >= UDPC_QUEUED_PKTS_MAX_SIZE) { - if(notQueued.find(next.value().receiver) == notQueued.end()) { - notQueued.insert(next.value().receiver); + if(notQueued.find(next.value.receiver) == notQueued.end()) { + notQueued.insert(next.value.receiver); UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_DEBUG, "Not queueing packet to ", UDPC_atostr((UDPC_HContext)this, - next.value().receiver.addr), + next.value.receiver.addr), ", port = ", - next.value().receiver.port, + next.value.receiver.port, ", connection's queue reached max size"); } if(sendIter.next()) { @@ -627,24 +627,24 @@ void UDPC::Context::update_impl() { break; } } - iter->second.sendPkts.push_back(next.value()); + iter->second.sendPkts.push_back(next.value); if(sendIter.remove()) { continue; } else { break; } } else { - if(dropped.find(next.value().receiver) == dropped.end()) { + if(dropped.find(next.value.receiver) == dropped.end()) { UDPC_CHECK_LOG(this, UDPC_LoggingType::UDPC_WARNING, "Dropped queued packets to ", UDPC_atostr( (UDPC_HContext)this, - next.value().receiver.addr), + next.value.receiver.addr), ", port = ", - next.value().receiver.port, + next.value.receiver.port, " due to connection not existing"); - dropped.insert(next.value().receiver); + dropped.insert(next.value.receiver); } if(sendIter.remove()) { continue; @@ -1589,17 +1589,22 @@ UDPC::Context *UDPC::verifyContext(UDPC_HContext ctx) { } bool UDPC::isBigEndian() { - static std::optional isBigEndian = std::nullopt; - if(isBigEndian) { - return *isBigEndian; + /* + * 0 - unset + * 1 - is big endian + * 2 - is not big endian + */ + static char isBigEndian = 0; + if(isBigEndian != 0) { + return isBigEndian == 1; } union { uint32_t i; char c[4]; } bint = {0x01020304}; - isBigEndian = (bint.c[0] == 1); - return *isBigEndian; + isBigEndian = (bint.c[0] == 1 ? 1 : 2); + return isBigEndian; } void UDPC::preparePacket( @@ -1923,8 +1928,8 @@ void UDPC_destroy(UDPC_HContext ctx) { #endif while(!UDPC_ctx->internalEvents.empty()) { auto optE = UDPC_ctx->internalEvents.top_and_pop(); - if(optE.has_value() && optE.value().type == UDPC_ET_REQUEST_CONNECT_PK) { - delete[] optE.value().v.pk; + if(optE.has_value() && optE.value.type == UDPC_ET_REQUEST_CONNECT_PK) { + delete[] optE.value.v.pk; } } UDPC_ctx->_contextIdentifier = 0; @@ -2141,7 +2146,7 @@ UDPC_Event UDPC_get_event(UDPC_HContext ctx, unsigned long *remaining) { auto optE = c->externalEvents.top_and_pop_and_rsize(remaining); if(optE) { - return optE.value(); + return optE.value; } else { return UDPC_Event{UDPC_ET_NONE, UDPC_create_id_anyaddr(0), 0}; } diff --git a/src/test/TestTSLQueue.cpp b/src/test/TestTSLQueue.cpp index 2ac41a5..4bb3647 100644 --- a/src/test/TestTSLQueue.cpp +++ b/src/test/TestTSLQueue.cpp @@ -18,7 +18,7 @@ TEST(TSLQueue, PushTopPopSize) { for(int i = 0; i < 10; ++i) { auto v = q.top(); ASSERT_TRUE(v.has_value()); - EXPECT_EQ(v.value(), i); + EXPECT_EQ(v.value, i); EXPECT_EQ(10 - i, q.size()); EXPECT_TRUE(q.pop()); } @@ -38,11 +38,11 @@ TEST(TSLQueue, PushNB_TopNB_TopAndPop_Size) { for(int i = 0; i < 10; ++i) { auto v = q.top_nb(); ASSERT_TRUE(v.has_value()); - EXPECT_EQ(v.value(), i); + 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); + EXPECT_EQ(v.value, i); } { @@ -69,7 +69,7 @@ TEST(TSLQueue, Push_TopAndPopAndEmpty_Size) { 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(v.value, i); EXPECT_EQ(i == 9, isEmpty); } EXPECT_EQ(q.size(), 0); @@ -110,8 +110,8 @@ TEST(TSLQueue, Concurrent) { 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_GE(v.value, 0); + EXPECT_LE(v.value, 100); EXPECT_EQ(i == 99, q.empty()); } EXPECT_EQ(q.size(), 0); @@ -131,7 +131,7 @@ TEST(TSLQueue, Iterator) { int i = 0; auto op = iter.current(); while(op.has_value()) { - EXPECT_EQ(op.value(), i++); + EXPECT_EQ(op.value, i++); if(i < 10) { EXPECT_TRUE(iter.next()); } else { @@ -149,7 +149,7 @@ TEST(TSLQueue, Iterator) { EXPECT_TRUE(iter.prev()); op = iter.current(); while(op.has_value()) { - EXPECT_EQ(op.value(), --i); + EXPECT_EQ(op.value, --i); if(i > 0) { EXPECT_TRUE(iter.prev()); } else { @@ -169,22 +169,22 @@ TEST(TSLQueue, Iterator) { auto op = iter.current(); EXPECT_TRUE(op.has_value()); - EXPECT_EQ(op.value(), 4); + EXPECT_EQ(op.value, 4); EXPECT_TRUE(iter.prev()); op = iter.current(); EXPECT_TRUE(op.has_value()); - EXPECT_EQ(op.value(), 2); + EXPECT_EQ(op.value, 2); } EXPECT_EQ(q.size(), 9); // check that "3" was removed from queue int i = 0; - std::optional op; + TSLQueue::Entry op; while(!q.empty()) { op = q.top(); EXPECT_TRUE(op.has_value()); - EXPECT_EQ(i++, op.value()); + EXPECT_EQ(i++, op.value); if(i == 3) { ++i; } @@ -206,7 +206,7 @@ TEST(TSLQueue, Iterator) { while(!q.empty()) { op = q.top(); EXPECT_TRUE(op.has_value()); - EXPECT_EQ(i++, op.value()); + EXPECT_EQ(i++, op.value); EXPECT_TRUE(q.pop()); } @@ -222,7 +222,7 @@ TEST(TSLQueue, Iterator) { EXPECT_TRUE(iter.next()); op = iter.current(); EXPECT_TRUE(op.has_value()); - if(op.value() == 3) { + if(op.value == 3) { EXPECT_FALSE(iter.remove()); break; } @@ -233,7 +233,7 @@ TEST(TSLQueue, Iterator) { while(!q.empty()) { op = q.top(); EXPECT_TRUE(op.has_value()); - EXPECT_EQ(i++, op.value()); + EXPECT_EQ(i++, op.value); EXPECT_TRUE(q.pop()); if(i == 3) { EXPECT_TRUE(q.empty());