Hi Vu, Thanks, I will update and send out new version. See my replies inline.
Best Regards, ThuanTr From: Nguyen Minh Vu <vu.m.ngu...@dektech.com.au> Sent: Monday, February 17, 2020 12:29 PM To: Thuan Tran <thuan.t...@dektech.com.au>; Minh Hon Chau <minh.c...@dektech.com.au>; Thang Duc Nguyen <thang.d.ngu...@dektech.com.au>; Gary Lee <gary....@dektech.com.au> Cc: opensaf-devel@lists.sourceforge.net Subject: Re: [PATCH 1/1] base: add serial number arithmetic (RFC1982) [#3074] Hi Thuan, Thanks. See my responses inline. Regards, Vu On 2/14/20 11:48 AM, Thuan Tran wrote: Hi Vu, Thanks for comments. Please check my replies inline. Best Regards, ThuanTr From: Nguyen Minh Vu <vu.m.ngu...@dektech.com.au><mailto:vu.m.ngu...@dektech.com.au> Sent: Thursday, February 13, 2020 5:50 PM To: Thuan Tran <thuan.t...@dektech.com.au><mailto:thuan.t...@dektech.com.au>; Minh Hon Chau <minh.c...@dektech.com.au><mailto:minh.c...@dektech.com.au>; Thang Duc Nguyen <thang.d.ngu...@dektech.com.au><mailto:thang.d.ngu...@dektech.com.au>; Gary Lee <gary....@dektech.com.au><mailto:gary....@dektech.com.au> Cc: opensaf-devel@lists.sourceforge.net<mailto:opensaf-devel@lists.sourceforge.net> Subject: Re: [PATCH 1/1] base: add serial number arithmetic (RFC1982) [#3074] Hi Thuan, Ack with comments inline. Regards, Vu On 2/12/20 5:36 PM, thuan.tran wrote: - Adapt MDS with this SNA implementation. --- src/base/Makefile.am | 6 +- src/base/sna.h | 136 +++++++++++++++++++++++++++++++ src/base/tests/sna_test.cc | 117 ++++++++++++++++++++++++++ src/mds/mds_tipc_fctrl_intf.cc | 2 +- src/mds/mds_tipc_fctrl_portid.cc | 17 ++-- src/mds/mds_tipc_fctrl_portid.h | 64 +-------------- 6 files changed, 267 insertions(+), 75 deletions(-) create mode 100644 src/base/sna.h create mode 100644 src/base/tests/sna_test.cc diff --git a/src/base/Makefile.am b/src/base/Makefile.am index 025fb86a2..5082175cf 100644 --- a/src/base/Makefile.am +++ b/src/base/Makefile.am @@ -173,7 +173,8 @@ noinst_HEADERS += \ src/base/unix_client_socket.h \ src/base/unix_server_socket.h \ src/base/unix_socket.h \ - src/base/usrbuf.h + src/base/usrbuf.h \ + src/base/sna.h TESTS += bin/testleap bin/libbase_test bin/core_common_test @@ -237,7 +238,8 @@ bin_libbase_test_SOURCES = \ src/base/tests/time_compare_test.cc \ src/base/tests/time_convert_test.cc \ src/base/tests/time_subtract_test.cc \ - src/base/tests/unix_socket_test.cc + src/base/tests/unix_socket_test.cc \ + src/base/tests/sna_test.cc bin_libbase_test_LDADD = \ $(GTEST_DIR)/lib/libgtest.la \ diff --git a/src/base/sna.h b/src/base/sna.h new file mode 100644 index 000000000..fee6627bb --- /dev/null +++ b/src/base/sna.h @@ -0,0 +1,136 @@ +/* -*- OpenSAF -*- + * + * Copyright Ericsson AB 2020 - All Rights Reserved. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Reference: Serial Number Arithmetic from RFC1982 + * + */ + +#ifndef BASE_SNA_H_ +#define BASE_SNA_H_ + +#include <typeinfo> +#include <stdexcept> + +#define SNA16_MAX 65536 // 2^16 +#define SNA16_SPACE 32768 // (2^16)/2 +#define SNA32_MAX 4294967296 // 2^32 +#define SNA32_SPACE 2147483648 // (2^32)/2 [Vu] can use: #define SNA16_MAX (1 << 16) #define SNA32_MAX (1 << 32) SPACE ones probably are not necessary. See my comment for space() method below. [Thuan] Is there any benefit with 1 << 16 or 32? I think define a clear value is better. [Vu] the benefit is you won't need to add the comment e.g. // 2^16 [Thuan] OK, change as your suggestion. Btw, I will change define macro SPACE (MAX/2). About space(), it is intended because I don’t want CPU calculate every time refer to it. + +template <class T> +class _sna { [Vu] How about `class SerialNumber {}` [Thuan] OK, I will change the class name. + private: [Vu] Declaration order should start with a public: section, followed by protected:, then private: [Thuan] OK, will change order. + T i; [Vu] Should use a descriptive name. e.g: T value_ {0}; [Thuan] OK, will update the name. + uint64_t max() { + if (typeid(T) == typeid(uint64_t)) { + return SNA32_MAX; + } + if (typeid(T) == typeid(uint32_t)) { + return SNA16_MAX; + } + throw std::out_of_range("Invalid type"); [Vu] OpenSAF code does not handle exception. Should use assertion instead. e.g: assert(0 && "Invalid data type"); [Thuan] I throw to do basic test, if assert() then cannot test the case. Btw, I think throw exception without try catch will also end with terminate? [Vu] I think so; and if you you do tests on Seq16, Seq32, you probably won't reach exceptions. [Thuan] OK, change to assert() inside max()/space(), but keep other “throw” is still kept for test. + } + uint64_t space() { + if (typeid(T) == typeid(uint64_t)) { + return SNA32_SPACE; + } + if (typeid(T) == typeid(uint32_t)) { + return SNA16_SPACE; + } + throw std::out_of_range("Invalid type"); [Vu] uint64_t space() { return max()/2;} [Thuan] As explain above, I don’t want calculate every time refer it. + } + + public: + _sna(): i(0) {} + _sna(const _sna &t) { + i = t.i; + } + explicit _sna(const uint64_t &n) { + if ((n < 0) || (n > (max()-1))) + throw std::out_of_range("Invalid initial value"); [Vu] Use assert() instead of throwing exception. [Thuan] Same above. + i = n; + } + _sna& operator=(const _sna &t) { + // check for self-assignment + if (&t == this) + return *this; + i = t.i; + return *this; + } + T v() const { + return i; + } + _sna& operator+=(const uint64_t& n) { + if ((n < 0) || (n > (space() - 1))) + throw std::out_of_range("Invalid addition value"); + i = (i + n) % max(); + return *this; + } + friend _sna operator+(_sna m, const uint64_t& n) { [Vu] my suggestion: friend _sna operator+(const _sna& m, ...) { _sna s{m}; s += n; return s; } [Thuan] passing lhs by value helps optimize chained a+b+c I prefer to this link https://en.cppreference.com/w/cpp/language/operators + m += n; + return m; + } + // prefix ++ + _sna& operator++() { + *this += 1; + return *this; + } + // postfix ++ + _sna operator++(int) { + _sna tmp(*this); + operator++(); + return tmp; + } + bool operator==(const _sna& rhs) { + return i == rhs.i; + } + bool operator==(const uint32_t val) { + return i == val; + } + bool operator!=(const _sna& rhs) { + return i != rhs.i; + } + bool operator<(const _sna& rhs) { + return (i < rhs.i && rhs.i - i < space()) || \ + (i > rhs.i && i - rhs.i > space()); + } + bool operator<=(const _sna& rhs) { + return *this == rhs || *this < rhs; + } + bool operator>(const _sna& rhs) { + return (i < rhs.i && rhs.i - i > space()) || \ + (i > rhs.i && i - rhs.i < space()); [Vu] shorter version: return !(*this <= rhs); [Thuan] This statement cause the issue in Erase message which run test sometimes failure. + } + bool operator>=(const _sna& rhs) { + return *this == rhs || *this > rhs; + } [Vu] Shorter version: return !(*this < rhs); [Thuan] This statement cause the issue in Erase message which run test sometimes failure. + int64_t operator-(const _sna& rhs) { + if (*this >= rhs) { + if (i >= rhs.v()) { + return i - rhs.v(); + } else { + return (i + max()) - rhs.v(); + } + } else { + if (i < rhs.v()) { + return i - rhs.v(); + } else { + return i - (rhs.v() + max()); + } + } + } +}; + +typedef _sna<uint32_t> Seq16; +typedef _sna<uint64_t> Seq32; [Vu] another option: using Seq16 = _sna<uint32_t>; [Thuan] Is there any benefit with this instead of typedef? [Vu] 'using' is a recommendation in c++ google coding rule. https://google.github.io/styleguide/cppguide.html#Aliases [Thuan] OK, change to this style. + +#endif // BASE_SNA_H_ diff --git a/src/base/tests/sna_test.cc b/src/base/tests/sna_test.cc new file mode 100644 index 000000000..b3d2d014c --- /dev/null +++ b/src/base/tests/sna_test.cc @@ -0,0 +1,117 @@ +/* -*- OpenSAF -*- + * + * Copyright Ericsson AB 2019 - All Rights Reserved. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. This file and program are licensed + * under the GNU Lesser General Public License Version 2.1, February 1999. + * The complete license can be accessed from the following location: + * http://opensource.org/licenses/lgpl-license.php + * See the Copying file included with the OpenSAF distribution for full + * licensing terms. + * + * Reference: Serial Number Arithmetic from RFC1982 + * + */ + +#include "base/sna.h" +#include "gtest/gtest.h" + +template <class T> +int test_sna(T x) { + int rc = 1; + printf("\n============= START with x=%lu =============\n", (uint64_t)x.v()); + T y = x; + printf("x=%lu, y=%lu: check x == y++ is TRUE\n", + (uint64_t)x.v(), (uint64_t)y.v()); + if (x == y++) { + printf("now y=%lu, reset y = x\n", (uint64_t)y.v()); + y = x; + printf("x=%lu, y=%lu: check x != ++y is TRUE\n", + (uint64_t)x.v(), (uint64_t)y.v()); + if (x != ++y) { + printf("now y=%lu, reset y = x\n", (uint64_t)y.v()); + y = x; + printf("x=%lu, y=%lu: check x < ++y is TRUE\n", + (uint64_t)x.v(), (uint64_t)y.v()); + if (x < ++y) { + printf("x=%lu: check x + 1 > x and x + 1 >= x is TRUE\n", + (uint64_t)x.v()); + if ((x + 1 > x) && (x + 1 >= x)) { + printf("x=%lu: check x < x + 1 and x <= x + 1 is TRUE\n", + (uint64_t)x.v()); + y = x + 1; + printf("y = x+1 => y=%lu\n", (uint64_t)y.v()); + y = y + 1; + printf("y = y+1 => y=%lu\n", (uint64_t)y.v()); + if ((x < x + 1) && (x <= x + 1)) { + try { + printf("x=%lu: add invalid (-1)\n", (uint64_t)x.v()); + x = x + (-1); + } catch (const std::out_of_range& oor) { + printf("Expected error: %s\n", oor.what()); + try { + uint64_t max_value = 0; + if (typeid(T) == typeid(Seq16)) + max_value = SNA16_MAX; + else if (typeid(T) == typeid(Seq32)) + max_value = SNA32_MAX; + printf("x=%lu: add invalid (%lu)\n", + (uint64_t)x.v(), max_value); + x = x + max_value; + } catch (const std::out_of_range& oor) { + printf("Expected error: %s\n", oor.what()); + rc = 0; + } + } + } + } + } + } + } + printf("================ END with x=%lu ==============\n", (uint64_t)x.v()); [Vu] Should remove all printf(); Developer can add it when following macros got failures. [Thuan] Is there any problem if I keep it? [Vu] I think printf should only be using in 'debug' version. [Thuan] OK, will hide these printf(). [Vu] However, I think we should test on `Seq16` and `Seq32` data type only. [Thuan] Yes, I test Seq16 and Seq32. + return rc; +} + + +class SnaTest : public ::testing::Test { + protected: + SnaTest() {} + virtual ~SnaTest() { + // Cleanup work that doesn't throw exceptions here. + } + virtual void SetUp() { + // Code here will be called immediately after the constructor (right + // before each test) + } + virtual void TearDown() {} +}; + [Vu] Probably don't need this class. [Thuan] I just keep them as other test files in base. +TEST_F(SnaTest, unit16_sna) { + Seq16 x; + EXPECT_EQ(0, test_sna(x)); + Seq16 x1 = Seq16(1); + Seq16 x2 = Seq16(SNA16_MAX - 1); + EXPECT_EQ(2, x1 - x2); + EXPECT_EQ(-2, x2 - x1); + EXPECT_EQ(0, test_sna(x1)); + EXPECT_EQ(0, test_sna(x2)); [Vu] The production code only use Seq16 & Seq32; so I think we only do test on these data types. [Thuan] Yes, I only test Seq16 & Seq32. +} + +TEST_F(SnaTest, unit32_sna) { + Seq32 x; + EXPECT_EQ(0, test_sna(x)); + Seq32 x1 = Seq32(1); + Seq32 x2 = Seq32(SNA32_MAX - 1); + EXPECT_EQ(2, x1 - x2); + EXPECT_EQ(-2, x2 - x1); + EXPECT_EQ(0, test_sna(x1)); + EXPECT_EQ(0, test_sna(x2)); +} + + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/src/mds/mds_tipc_fctrl_intf.cc b/src/mds/mds_tipc_fctrl_intf.cc index f3883ba36..9c7d4e35c 100644 --- a/src/mds/mds_tipc_fctrl_intf.cc +++ b/src/mds/mds_tipc_fctrl_intf.cc @@ -106,7 +106,7 @@ void process_timer_event(const Event& evt) { static_cast<int>(evt.type_)); for (auto i : portid_map) { TipcPortId* portid = i.second; - + if (!portid) continue; if (evt.type_ == Event::Type::kEvtTmrTxProb) { if (portid->ReceiveTmrTxProb(kTxProbMaxRetries) == true) { txprob_restart = true; diff --git a/src/mds/mds_tipc_fctrl_portid.cc b/src/mds/mds_tipc_fctrl_portid.cc index 3562c4a00..b0f765d69 100644 --- a/src/mds/mds_tipc_fctrl_portid.cc +++ b/src/mds/mds_tipc_fctrl_portid.cc @@ -339,7 +339,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, } // update receiver sequence window - if (rcvwnd_.acked_ < Seq16(fseq) && rcvwnd_.rcv_ + Seq16(1) == Seq16(fseq)) { + if (rcvwnd_.acked_ < Seq16(fseq) && rcvwnd_.rcv_ + 1 == Seq16(fseq)) { m_MDS_LOG_DBG("FCTRL: [me] <-- [node:%x, ref:%u], " "RcvData[mseq:%u, mfrag:%u, fseq:%u], " "rcvwnd[acked:%u, rcv:%u, nacked:%" PRIu64 "]", @@ -370,7 +370,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, // It is not used for now, so ignore it. // check for transmission error - if (rcvwnd_.rcv_ + Seq16(1) < Seq16(fseq)) { + if (rcvwnd_.rcv_ + 1 < Seq16(fseq)) { if (rcvwnd_.rcv_ == 0 && rcvwnd_.acked_ == 0) { // peer does not realize that this portid reset m_MDS_LOG_ERR("FCTRL: [me] <-- [node:%x, ref:%u], " @@ -381,7 +381,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, mseq, mfrag, fseq, rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_); - rcvwnd_.rcv_ = fseq; + rcvwnd_.rcv_ = Seq16(fseq); } else { rc = NCSCC_RC_FAILURE; // msg loss @@ -393,7 +393,7 @@ uint32_t TipcPortId::ReceiveData(uint32_t mseq, uint16_t mfrag, mseq, mfrag, fseq, rcvwnd_.acked_.v(), rcvwnd_.rcv_.v(), rcvwnd_.nacked_space_); // send nack - SendNack((rcvwnd_.rcv_ + Seq16(1)).v(), svc_id); + SendNack((rcvwnd_.rcv_ + 1).v(), svc_id); } } else if (Seq16(fseq) <= rcvwnd_.rcv_) { rc = NCSCC_RC_FAILURE; @@ -456,13 +456,12 @@ void TipcPortId::ReceiveChunkAck(uint16_t fseq, uint16_t chksize) { sndwnd_.acked_.v(), sndwnd_.send_.v(), sndwnd_.nacked_space_, sndqueue_.Size()); - // fast forward the sndwnd_.acked_ sequence to fseq - sndwnd_.acked_ = fseq; - // remove a number @chksize messages out of sndqueue_ and decrease // the nacked_space_ of sender - uint64_t acked_bytes = sndqueue_.Erase(Seq16(fseq) - (chksize-1), - Seq16(fseq)); + uint64_t acked_bytes = sndqueue_.Erase(sndwnd_.acked_ + 1, Seq16(fseq)); + // fast forward the sndwnd_.acked_ sequence to fseq + sndwnd_.acked_ = Seq16(fseq); + assert(sndwnd_.nacked_space_ >= acked_bytes); sndwnd_.nacked_space_ -= acked_bytes; diff --git a/src/mds/mds_tipc_fctrl_portid.h b/src/mds/mds_tipc_fctrl_portid.h index ef74921e7..83564459b 100644 --- a/src/mds/mds_tipc_fctrl_portid.h +++ b/src/mds/mds_tipc_fctrl_portid.h @@ -24,73 +24,11 @@ #include <stdio.h> #include <unistd.h> #include <deque> +#include "base/sna.h" #include "mds/mds_tipc_fctrl_msg.h" namespace mds { -class Seq16 { - public: -#define SEQ16_MAX 65536 -#define SEQ16_SPACE 32768 - uint16_t value_; - explicit Seq16(uint16_t v) { - value_ = uint16_t((uint32_t)v % SEQ16_MAX); - } - uint16_t v() { - return value_; - } - Seq16 operator + (const Seq16 add) const { - return Seq16(((uint32_t)value_ + (uint32_t)add.value_) % SEQ16_MAX); - } - - int16_t operator - (const Seq16 sub) const { - if (value_ < sub.value_ && (sub.value_ - value_ < SEQ16_SPACE)) { - return value_ - sub.value_; - } - if (value_ > sub.value_ && (value_ - sub.value_ > SEQ16_SPACE)) { - return (int32_t)value_ + SEQ16_MAX - (int32_t)sub.value_; - } - if (value_ < sub.value_ && (sub.value_ - value_ > SEQ16_SPACE)) { - return (int32_t)value_ + SEQ16_MAX - (int32_t)sub.value_; - } - if (value_ > sub.value_ && (value_ - sub.value_ < SEQ16_SPACE)) { - return value_ - sub.value_; - } - return 0; - } - Seq16 operator - (const uint16_t sub) const { - return Seq16(((uint32_t)value_ + 65536 - sub) % SEQ16_MAX); - } - void operator ++() { - value_ = (value_ + 1) % SEQ16_MAX; - } - void operator = (const uint16_t v) { - value_ = v % SEQ16_MAX; - } - bool operator == (const Seq16& seq) const { - return value_ == seq.value_; - } - bool operator == (uint16_t val) const { - return value_ == val; - } - bool operator <= (const Seq16& seq) { - return *this == seq || *this < seq; - } - bool operator < (const Seq16& seq) { - if (value_ < seq.value_ && (seq.value_ - value_ < SEQ16_SPACE)) return true; - if (value_ > seq.value_ && (value_ - seq.value_ > SEQ16_SPACE)) return true; - return false; - } - bool operator > (const Seq16& seq) { - if (value_ < seq.value_ && (seq.value_ - value_ > SEQ16_SPACE)) return true; - if (value_ > seq.value_ && (value_ - seq.value_ < SEQ16_SPACE)) return true; - return false; - } - bool operator >= (const Seq16& seq) { - return *this == seq || *this > seq; - } -}; - class MessageQueue { public: void Queue(DataMessage* msg); _______________________________________________ Opensaf-devel mailing list Opensaf-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/opensaf-devel