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