Make generic C++ python-like decorator handling SA_AIS_ERR_TRY_AGAIN
return code of AIS APIs.
---
 src/base/Makefile.am                       |   5 +-
 src/base/tests/try_again_decorator_test.cc |  69 +++++++++++++++
 src/base/try_again_decorator.h             | 131 +++++++++++++++++++++++++++++
 3 files changed, 204 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..50ddf15
--- /dev/null
+++ b/src/base/tests/try_again_decorator_test.cc
@@ -0,0 +1,69 @@
+/*      -*- 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
+ *
+ */
+
+#define private public
+#include "base/try_again_decorator.h"
+#include "gtest/gtest.h"
+
+static unsigned count = 0;
+
+SaAisErrorT TestMethod() {
+  ++count;
+  return SA_AIS_ERR_TRY_AGAIN;
+}
+
+TEST(TryAgainDecorator, DecoratorFunction) {
+  auto DecorTestMethod = base::TryAgainDecorator(TestMethod);
+  DecorTestMethod();
+  EXPECT_GE(count, 200);
+  EXPECT_LE(count, 250);
+  count = 0;
+}
+
+TEST(TryAgainDecorator, DefaultRetryControl) {
+  auto DecorTestMethod = base::TryAgainDecorator(TestMethod);
+  timespec interval = DecorTestMethod.retry_ctrl_->interval;
+  uint64_t timeout = DecorTestMethod.retry_ctrl_->timeout;
+
+  EXPECT_EQ(interval.tv_sec, 0);
+  EXPECT_EQ(interval.tv_nsec, 40*1000*1000);
+  EXPECT_EQ(timeout, 10*1000);
+}
+
+TEST(TryAgainDecorator, UseLocalRetryControl) {
+  base::RetryControl ctrl({0, 60*1000}, 60);
+  auto DecorTestMethod = base::TryAgainDecorator(TestMethod, ctrl);
+  timespec interval = DecorTestMethod.retry_ctrl_->interval;
+  uint64_t timeout = DecorTestMethod.retry_ctrl_->timeout;
+
+  EXPECT_EQ(interval.tv_sec, 0);
+  EXPECT_EQ(interval.tv_nsec, 60*1000);
+  EXPECT_EQ(timeout, 60);
+}
+
+TEST(TryAgainDecorator, ChangeGlobalRetryControl) {
+  base::RetryControl ctrl({0, 100*1000}, 200);
+  auto DecorTestMethod = base::TryAgainDecorator(TestMethod);
+
+  base::ChangeGlobalRetryControl(ctrl);
+  timespec interval = DecorTestMethod.retry_ctrl_->interval;
+  uint64_t timeout = DecorTestMethod.retry_ctrl_->timeout;
+
+  EXPECT_EQ(interval.tv_sec, 0);
+  EXPECT_EQ(interval.tv_nsec, 100*1000);
+  EXPECT_EQ(timeout, 200);
+}
diff --git a/src/base/try_again_decorator.h b/src/base/try_again_decorator.h
new file mode 100644
index 0000000..1b648f4
--- /dev/null
+++ b/src/base/try_again_decorator.h
@@ -0,0 +1,131 @@
+/*      -*- 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 "base/time.h"
+#include "ais/include/saAis.h"
+
+namespace base {
+
+struct RetryControl {
+  // Sleep time b/w retries
+  timespec interval;
+  // Maximum time for retries (ms)
+  uint64_t timeout;
+
+  RetryControl() {
+    interval = {0, 40*1000*1000};  // 40 miliseconds
+    timeout  = 10*1000;            // 10 seconds
+  }
+
+  explicit RetryControl(timespec i, uint64_t t) {
+    interval = i;
+    timeout  = t;
+  }
+
+  explicit RetryControl(const RetryControl& ctrl) {
+    interval = ctrl.interval;
+    timeout  = ctrl.timeout;
+  }
+
+  RetryControl& operator=(const RetryControl& ctrl) {
+    interval = ctrl.interval;
+    timeout  = ctrl.timeout;
+    return *this;
+  }
+};
+
+
+//>
+// Python-like C++ decorator which escapsulates try again handling.
+//
+// E.g:
+// 1) When 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) != 0) {
+//    // error handling
+// }
+//
+// 2) When user wants other retry control than default ones, pass
+// your owned retry control information to decorator.
+//
+// const base::RetryControl ctrl({0, 60*1000*1000}, 60*1000);
+// auto saClmInitialize = TryAgainDecorator(::saClmInitialize, ctrl);
+// if (saClmInitialize(handle, cbs, version) != 0) {
+//    // error handling
+// }
+//
+//<
+
+static RetryControl global_retry_ctrl;
+
+// Change default retry control information to given one.
+// Any call to decorators after this call will take the given
+// retry control.
+inline void ChangeGlobalRetryControl(const RetryControl& ctrl) {
+  global_retry_ctrl = ctrl;
+}
+
+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)
+      : Decorator(f, global_retry_ctrl) {}
+
+  ~Decorator() { }
+
+  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) {
+        base::Sleep(retry_ctrl_->interval);
+        continue;
+      }
+      break;
+    }
+    return ais_error;
+  }
+
+ private:
+  const std::function<T(Args ...)> f_;
+  const RetryControl* retry_ctrl_;
+};
+
+template<class T, class... Args>
+Decorator<T(Args...)> TryAgainDecorator(T (*f)(Args ...)) {
+  return Decorator<T(Args...)>(std::function<T(Args...)>(f));
+}
+
+template<class T, class... Args> Decorator<T(Args...)>
+TryAgainDecorator(T (*f)(Args ...), const RetryControl& ctrl) {
+  return Decorator<T(Args...)>(std::function<T(Args...)>(f), ctrl);
+}
+
+}  // namespace base
+
+#endif  //< BASE_TRY_AGAIN_DECORATOR_H_
-- 
1.9.1


------------------------------------------------------------------------------
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