I have two comments:

1) I think this should be put under src/ais (but not src/ais/include) instead of putting it in src/base.

2) Could you think about if it is possible to make the retry time and interval constexpr?

regards,

Anders Widell


On 11/30/2017 01:01 PM, Hans Nordebäck wrote:
Hi Vu,

please see some comments inlined below/Regards HansN


On 11/28/2017 02:13 PM, Vu Minh Nguyen wrote:
Make generic C++ decorator for handling SA_AIS_ERR_TRY_AGAIN return code
of AIS APIs.
---
  src/base/Makefile.am                       |   5 +-
  src/base/tests/try_again_decorator_test.cc |  48 +++++++++++++
  src/base/try_again_decorator.h             | 109 +++++++++++++++++++++++++++++
  3 files changed, 161 insertions(+), 1 deletion(-)
  create mode 100644 src/base/tests/try_again_decorator_test.cc
  create mode 100644 src/base/try_again_decorator.h

diff --git a/src/base/Makefile.am b/src/base/Makefile.am
index 956cce6..f11b738 100644
--- a/src/base/Makefile.am
+++ b/src/base/Makefile.am
@@ -150,6 +150,7 @@ noinst_HEADERS += \
      src/base/unix_client_socket.h \
      src/base/unix_server_socket.h \
      src/base/unix_socket.h \
+    src/base/try_again_decorator.h \
      src/base/usrbuf.h
    TESTS += bin/testleap bin/libbase_test bin/core_common_test
@@ -208,7 +209,9 @@ 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/try_again_decorator_test.cc
+
    bin_libbase_test_LDADD = \
      $(GTEST_DIR)/lib/libgtest.la \
diff --git a/src/base/tests/try_again_decorator_test.cc b/src/base/tests/try_again_decorator_test.cc
new file mode 100644
index 0000000..ac8595d
--- /dev/null
+++ b/src/base/tests/try_again_decorator_test.cc
@@ -0,0 +1,48 @@
+/*      -*- OpenSAF  -*-
+ *
+ * (C) Copyright 2017 The OpenSAF Foundation
+ *
+ * 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.
+ *
+ * Author(s): Ericsson AB
+ *
+ */
+
+#include "base/try_again_decorator.h"
+#include "gtest/gtest.h"
+
+static unsigned counter = 0;
[HansN] use an unnamed namespace instead of static:
namespace {
  unsigned counter = 0;
}

+
+SaAisErrorT TestMethod() {
+  ++counter;
+  return SA_AIS_ERR_TRY_AGAIN;
+}
[HansN] shouldn't this function be declared extern "C" ?

+
+TEST(TryAgainDecorator, DefaultRetryControl) {
+  // default interval = 40ms, timeout = 10 * 1000ms
+  using DefaultDecorator = base::TryAgainDecorator<>;
+  auto DecorTestMethod = DefaultDecorator::DecorateFunction(TestMethod);
+
+  DecorTestMethod();

[HansN] the return code from the DecorTestMethod should be validated
+  EXPECT_GE(counter, 200);
+  EXPECT_LE(counter, 250);
+  counter = 0;
+}
+
+TEST(TryAgainDecorator, GivenRetryControl) {
+  // interval = 10ms, timeout = 1000ms
+  using TryAgainDecorator = base::TryAgainDecorator<10, 1 * 1000>;
+  auto DecorTestMethod  = TryAgainDecorator::DecorateFunction(TestMethod);
+
+  DecorTestMethod();

[HansN] the return code from the DecorTestMethod should be validated
+  EXPECT_GE(counter, 50);
+  EXPECT_LE(counter, 100);
+  counter = 0;
+}
diff --git a/src/base/try_again_decorator.h b/src/base/try_again_decorator.h
new file mode 100644
index 0000000..e676697
--- /dev/null
+++ b/src/base/try_again_decorator.h
@@ -0,0 +1,109 @@
+/*      -*- OpenSAF  -*-
+ *
+ * Copyright Ericsson AB 2017 - 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.
+ *
+ */
+
+#ifndef BASE_TRY_AGAIN_DECORATOR_H_
+#define BASE_TRY_AGAIN_DECORATOR_H_
+
+#include <iostream>
+#include <functional>
+#include <saAis.h>
+#include "base/time.h"
+
+namespace base {
+
[HansN] perhaps we can use a strategy/policy like pattern instead? What do you say about something like the following:


struct RetryStrategy {

  virtual int nTries() = 0;

  virtual int retryInterval() = 0;

};

struct DefaultRetryStrategy : public RetryStrategy {

  int nTries() {return 10;}

  int retryInterval() {return 10;}

};

struct AnotherRetryStrategy : public RetryStrategy {

  int nTries() {return 100;}

  int retryInterval() {return 100;}

};

DefaultRetryStrategy DfltStrategy;

AnotherRetryStrategy AnotherStrategy;

int SA_AIS_ERR_TRY_AGAIN = 0;

//

template <typename> class Decorator;

template <typename T, typename... Args>

class Decorator<T(Args ...)> {

 public:

  explicit Decorator(std::function<T(Args...)> f, RetryStrategy &strategy)

      : f_(f), strategy_(strategy) {}

  T operator()(Args... args) {

    std::cout << "Call the decorated function" << std::endl;

    int rc = f_(args...);

    int n_tries = 1;

    while (rc == SA_AIS_ERR_TRY_AGAIN &&

           n_tries < strategy_.nTries()) {

      base::Sleep(strategy_.retryInterval());

      rc = f_(args...);

      n_tries++;

    }

    std::cout << "return code is " << rc << std::endl;

    std::cout << "add all common retry logic here, etc " << std::endl;

    return rc;

  }

  RetryStrategy &strategy_;

  std::function<T(Args ...)> f_;

};

template<typename T, typename... Args>

Decorator<T(Args...)> make_decorator(T (*f)(Args...), RetryStrategy &strategy=DfltStrategy) {

  return Decorator<T(Args...)>(std::function<T(Args...)>(f), strategy);

}

int main() {

  int rc = 0;

    auto saImmOmInitialize_d = make_decorator(saImmOmInitialize);

    std::cout << typeid(saImmOmInitialize_d).name() << std::endl;

    rc = saImmOmInitialize_d(4711);

    std::cout << "rc: " << rc << std::endl;

    auto saImmOmSelectionObjectGet_d = make_decorator(saImmOmSelectionObjectGet, AnotherStrategy);

    rc = saImmOmSelectionObjectGet_d(4712, "hello world!");

   :


+//>
+// C++ decorator which escapsulates try again handling.
+//
+// E.g:
+// 1) If user wants to call saClmInitialize() which has try again
+// handling inside using this decorator, do this:
+//
+// using TryAgainDecorator = base::TryAgainDecorator<>;
+// auto saClmInitialize = TryAgainDecorator(::saClmInitialize);
+// if (saClmInitialize(handle, cbs, version) != SA_AIS_OK) {
+//    // error handling
+// }
+//
+// 2) If user wants other retry control than default ones,
+// such as interval = 10ms, timeout = 10second, do this:
+//
+// using TryAgainDecorator = base::TryAgainDecorator<
+//                                  {0, 10 * 1000 * 1000},
+//                                  10 * 1000
+//                                  >
+// auto saClmInitialize = TryAgainDecorator(::saClmInitialize);
+// if (saClmInitialize(handle, cbs, version) != SA_AIS_OK) {
+//    // error handling
+// }
+//
+//<
+
+// Default sleep time b/w retries {second, ns}
+static const timespec kInterval = {0, 40 * 1000 * 1000};
+// Default maximum time for retry loop (ms)
+static const uint64_t kTimeout  = 10 * 1000;
+
+struct RetryControl {
+  RetryControl() : interval(kInterval), timeout(kTimeout) {}
+  RetryControl(timespec i, uint64_t t) : interval(i), timeout(t) {}
+  RetryControl(const RetryControl& ctrl)
+      : interval(ctrl.interval), timeout(ctrl.timeout) {}
+
+  // Sleep time b/w retries
+  timespec interval;
+  // Maximum time for retry loop
+  uint64_t timeout;
+};
+
+template <class> class Decorator;
+template <class T, class... Args>
+class Decorator<T(Args ...)> {
+ public:
+  explicit Decorator(const std::function<T(Args ...)>& f,
+                     const RetryControl& ctrl)
+      : f_{f}, retry_ctrl_{ctrl} {}
+
+  explicit Decorator(const std::function<T(Args ...)>& f) : f_{f} {}
+
+  T operator()(Args ... args) {
+    T ais_error;
+    base::Timer wtime(retry_ctrl_.timeout);
+    while (wtime.is_timeout() == false) {
+      ais_error = f_(args...);
+      if (ais_error != SA_AIS_ERR_TRY_AGAIN) break;
+      base::Sleep(retry_ctrl_.interval);
+    }
+    return ais_error;
+  }
+
+ private:
+  const std::function<T(Args ...)> f_;
+  RetryControl retry_ctrl_;
+};
+
+template <uint64_t interval_ms = 40, uint64_t timeout_ms = 10 * 1000>
+class TryAgainDecorator {
+ public:
+  template<class T, class... Args>
+  static Decorator<T(Args...)> DecorateFunction(T (*f)(Args ...)) {
+    RetryControl ctrl({0, interval_ms * 1000 * 1000}, timeout_ms);
+    return Decorator<T(Args...)>
+        (std::function<T(Args...)>(f), ctrl);
+  }
+};
+
+}  // namespace base
+
+#endif  //< BASE_TRY_AGAIN_DECORATOR_H_




------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Opensaf-devel mailing list
Opensaf-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to