Hi Vu,

 I think it would be good if we can use the policy pattern for the retry, I reworked my previous sample a bit:


#include <iostream>
#include <functional>
#include <typeinfo>
#include <unistd.h>
// only to illustrate, the existing SAF C functions with some dummy arguments
#include <stdio.h>
extern "C" int saImmOmInitialize(int i) {
  printf("saImmOmInitialize called, i = %d\n", i);
  return 2;
}

extern "C" int saImmOmSelectionObjectGet(int i, const char* str) {
  printf("saImmOmSelectionObjectGet called, i = %d, str = %s\n", i, str);
  return 3;
}

class DefaultRetryPolicy {
 public:
  static constexpr int kNumTries = 100;
  static constexpr int kRetryInterval = 10;
};

class AnotherRetryPolicy {
 public:
  static constexpr int kNumTries = 500;
  static constexpr int kRetryInterval = 12;
};

int SA_AIS_ERR_TRY_AGAIN = 0;
//
template <typename T, typename Policy = DefaultRetryPolicy> class Decorator;

template <typename T, typename Policy, typename... Args>
class Decorator<T(Args ...), Policy> {
 public:
  explicit Decorator(std::function<T(Args...)> f)
      : f_(f) {}

  T operator()(Args... args) {
    std::cout << "Call the decorated function " << std::endl;
    std::cout << "num retries: " << Policy::kNumTries << std::endl;
    std::cout << "retry interval: " << Policy::kRetryInterval << std::endl;

    int rc = f_(args...);
    int n_tries = 1;

    while (rc == SA_AIS_ERR_TRY_AGAIN &&
           n_tries < Policy::kNumTries) {
      usleep(Policy::kRetryInterval);
      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;
  }
  std::function<T(Args ...)> f_;
};


template<typename T, typename... Args>
Decorator<T(Args...)> make_decorator(T (*f)(Args...)) {
   return Decorator<T(Args...)>(std::function<T(Args...)>(f));
}

template<typename T, typename... Args>
Decorator<T(Args...), AnotherRetryPolicy> make_another_decorator(T (*f)(Args...)) {   return Decorator<T(Args...), AnotherRetryPolicy>(std::function<T(Args...)>(f));
}

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_another_decorator(saImmOmSelectionObjectGet);
    rc = saImmOmSelectionObjectGet_d(4712, "hello world!");
    std::cout << "rc: " << rc << std::endl;

    return 0;
}

/Regards HansN


On 11/30/2017 04:23 PM, Vu Minh Nguyen wrote:
Make generic C++ decorator for handling SA_AIS_ERR_TRY_AGAIN return code
of AIS APIs.
---
  src/ais/Makefile.am                        |   5 +-
  src/ais/try_again_decorator.h              | 111 +++++++++++++++++++++++++++++
  src/base/Makefile.am                       |   4 +-
  src/base/tests/try_again_decorator_test.cc |  54 ++++++++++++++
  4 files changed, 171 insertions(+), 3 deletions(-)
  create mode 100644 src/ais/try_again_decorator.h
  create mode 100644 src/base/tests/try_again_decorator_test.cc

diff --git a/src/ais/Makefile.am b/src/ais/Makefile.am
index 77ea2d8..50e4cb6 100644
--- a/src/ais/Makefile.am
+++ b/src/ais/Makefile.am
@@ -12,7 +12,7 @@
  # licensing terms.
  #
-CORE_INCLUDES += -I$(top_srcdir)/src/ais/include
+CORE_INCLUDES += -I$(top_srcdir)/src/ais/include -I$(top_srcdir)/src/ais
EXTRA_DIST += \
        src/ais/lib/libSaAmf.map \
@@ -54,7 +54,8 @@ include_HEADERS += \
        src/ais/include/saMsg.h \
        src/ais/include/saNtf.h \
        src/ais/include/saPlm.h \
-       src/ais/include/saSmf.h
+       src/ais/include/saSmf.h \
+       src/ais/try_again_decorator.h
pkgconfig_DATA += \
        src/ais/lib/opensaf-amf.pc \
diff --git a/src/ais/try_again_decorator.h b/src/ais/try_again_decorator.h
new file mode 100644
index 0000000..a7967e9
--- /dev/null
+++ b/src/ais/try_again_decorator.h
@@ -0,0 +1,111 @@
+/*      -*- 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 AIS_TRY_AGAIN_DECORATOR_H_
+#define AIS_TRY_AGAIN_DECORATOR_H_
+
+#include <iostream>
+#include <functional>
+#include <saAis.h>
+#include "base/time.h"
+
+namespace base {
+
+//>
+// 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:
+//
+// auto saClmInitialize = base::TryAgainDecorator(::saClmInitialize);
+// if (saClmInitialize(handle, cbs, version) != SA_AIS_OK) {
+//    // error handling
+// }
+//
+// 2) If user wants using other retry control than default ones,
+// such as interval = 10ms, timeout = 10second, then do:
+//
+// class MyOwnTryAgain : public base::RetryControl {
+//  public:
+//   constexpr uint64_t interval_ms() const { return 10;  }
+//   constexpr uint64_t timeout_ms()  const { return 10 * 1000; }
+// };
+//
+// const MyOwnTryAgain my_own_retry {};
+// auto saClmInitialize = TryAgainDecorator(::saClmInitialize, my_own_retry);
+// if (saClmInitialize(handle, cbs, version) != SA_AIS_OK) {
+//    // error handling
+// }
+//
+//<
+
+// Default sleep time b/w retries (ms)
+static const uint64_t kInterval = 40ull;
+// Default maximum time for retry loop (ms)
+static const uint64_t kTimeout  = 10 * 1000ull;
+
+class RetryControl {
+ public:
+  virtual uint64_t interval_ms() const = 0;
+  virtual uint64_t timeout_ms()  const = 0;
+};
+
+class DefaultRetryControl : public RetryControl {
+ public:
+  constexpr uint64_t interval_ms() const { return  kInterval; }
+  constexpr uint64_t timeout_ms()  const { return kTimeout;  }
+};
+
+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},
+        timeout_ms_{ctrl.timeout_ms()},
+        interval_ms_{ctrl.interval_ms()} {}
+
+  T operator()(Args ... args) {
+    T ais_error;
+    timespec interval;
+    interval.tv_sec = 0;
+    interval.tv_nsec = (interval_ms_ * 1000 * 1000);
+
+    base::Timer wtime(timeout_ms_);
+    while (wtime.is_timeout() == false) {
+      ais_error = f_(args...);
+      if (ais_error != SA_AIS_ERR_TRY_AGAIN) break;
+      base::Sleep(interval);
+    }
+    return ais_error;
+  }
+
+ private:
+  const std::function<T(Args ...)> f_;
+  const uint64_t timeout_ms_;
+  const uint64_t interval_ms_;
+};
+
+const static DefaultRetryControl default_retry {}; // NOLINT(*)
+template<class T, class... Args> Decorator<T(Args...)>
+TryAgainDecorator(T (*f)(Args ...), const RetryControl& ctrl = default_retry) {
+  return Decorator<T(Args...)>(std::function<T(Args...)>(f), ctrl);
+}
+
+}  // namespace base
+
+#endif  //< AIS_TRY_AGAIN_DECORATOR_H_
diff --git a/src/base/Makefile.am b/src/base/Makefile.am
index 956cce6..feafdde 100644
--- a/src/base/Makefile.am
+++ b/src/base/Makefile.am
@@ -208,7 +208,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..d711710
--- /dev/null
+++ b/src/base/tests/try_again_decorator_test.cc
@@ -0,0 +1,54 @@
+/*      -*- 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 <try_again_decorator.h>
+#include "gtest/gtest.h"
+
+namespace {
+unsigned counter = 0;
+};
+
+extern "C" SaAisErrorT TestMethod() {
+  ++counter;
+  return SA_AIS_ERR_TRY_AGAIN;
+}
+
+TEST(TryAgainDecorator, DefaultControl) {
+  // default interval = 40ms, timeout = 10 * 1000ms
+  auto DecorTestMethod = base::TryAgainDecorator(::TestMethod);
+
+  EXPECT_EQ(DecorTestMethod(), SA_AIS_ERR_TRY_AGAIN);
+  EXPECT_GE(counter, 200);
+  EXPECT_LE(counter, 250);
+  counter = 0;
+}
+
+TEST(TryAgainDecorator, GivenRetryControl) {
+  class MyTryAgain : public base::RetryControl {
+   public:
+    constexpr uint64_t interval_ms() const { return 10;  }
+    constexpr uint64_t timeout_ms()  const { return 100; }
+  };
+
+  const MyTryAgain my_try_again {};
+  auto DecorTestMethod = base::TryAgainDecorator(::TestMethod, my_try_again);
+
+  EXPECT_EQ(DecorTestMethod(), SA_AIS_ERR_TRY_AGAIN);
+  EXPECT_GE(counter, 5);
+  EXPECT_LE(counter, 10);
+  counter = 0;
+}


------------------------------------------------------------------------------
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
[email protected]
https://lists.sourceforge.net/lists/listinfo/opensaf-devel

Reply via email to