From 59c642add8fb292ea26704a6f1c2da67b6ad88a5 Mon Sep 17 00:00:00 2001 From: Sai Kishor Kothakota Date: Thu, 12 Sep 2024 13:55:38 +0200 Subject: [PATCH 1/4] [HW_IF] Prepare the handles for async operations --- .../include/hardware_interface/handle.hpp | 84 +++++++++++++++++-- .../loaned_command_interface.hpp | 19 ++++- .../loaned_state_interface.hpp | 11 ++- 3 files changed, 106 insertions(+), 8 deletions(-) diff --git a/hardware_interface/include/hardware_interface/handle.hpp b/hardware_interface/include/hardware_interface/handle.hpp index 6fe4f25663..2ea3310cee 100644 --- a/hardware_interface/include/hardware_interface/handle.hpp +++ b/hardware_interface/include/hardware_interface/handle.hpp @@ -17,7 +17,9 @@ #include #include +#include #include +#include #include #include "hardware_interface/hardware_info.hpp" @@ -69,13 +71,57 @@ class Handle { } - Handle(const Handle & other) = default; + Handle(const Handle & other) noexcept + { + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); + prefix_name_ = other.prefix_name_; + interface_name_ = other.interface_name_; + handle_name_ = other.handle_name_; + value_ = other.value_; + value_ptr_ = other.value_ptr_; + } - Handle(Handle && other) = default; + Handle(Handle && other) noexcept + { + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); + prefix_name_ = std::move(other.prefix_name_); + interface_name_ = std::move(other.interface_name_); + handle_name_ = std::move(other.handle_name_); + value_ = std::move(other.value_); + value_ptr_ = std::move(other.value_ptr_); + } - Handle & operator=(const Handle & other) = default; + Handle & operator=(const Handle & other) + { + if (this != &other) + { + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); + prefix_name_ = other.prefix_name_; + interface_name_ = other.interface_name_; + handle_name_ = other.handle_name_; + value_ = other.value_; + value_ptr_ = other.value_ptr_; + } + return *this; + } - Handle & operator=(Handle && other) = default; + Handle & operator=(Handle && other) + { + if (this != &other) + { + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); + prefix_name_ = std::move(other.prefix_name_); + interface_name_ = std::move(other.interface_name_); + handle_name_ = std::move(other.handle_name_); + value_ = std::move(other.value_); + value_ptr_ = std::move(other.value_ptr_); + } + return *this; + } virtual ~Handle() = default; @@ -95,8 +141,14 @@ class Handle const std::string & get_prefix_name() const { return prefix_name_; } + [[deprecated("Use bool get_value(double & value) instead to retrieve the value.")]] double get_value() const { + std::unique_lock lock(handle_mutex_, std::try_to_lock); + if (!lock.owns_lock()) + { + return std::numeric_limits::quiet_NaN(); + } // BEGIN (Handle export change): for backward compatibility // TODO(Manuel) return value_ if old functionality is removed THROW_ON_NULLPTR(value_ptr_); @@ -104,12 +156,33 @@ class Handle // END } - void set_value(double value) + [[nodiscard]] bool get_value(double & value) const + { + std::unique_lock lock(handle_mutex_, std::try_to_lock); + if (!lock.owns_lock()) + { + return false; + } + // BEGIN (Handle export change): for backward compatibility + // TODO(Manuel) set value directly if old functionality is removed + THROW_ON_NULLPTR(value_ptr_); + value = *value_ptr_; + return true; + // END + } + + [[nodiscard]] bool set_value(double value) { + std::unique_lock lock(handle_mutex_, std::try_to_lock); + if (!lock.owns_lock()) + { + return false; + } // BEGIN (Handle export change): for backward compatibility // TODO(Manuel) set value_ directly if old functionality is removed THROW_ON_NULLPTR(this->value_ptr_); *this->value_ptr_ = value; + return true; // END } @@ -122,6 +195,7 @@ class Handle // TODO(Manuel) redeclare as HANDLE_DATATYPE * value_ptr_ if old functionality is removed double * value_ptr_; // END + mutable std::recursive_mutex handle_mutex_; }; class StateInterface : public Handle diff --git a/hardware_interface/include/hardware_interface/loaned_command_interface.hpp b/hardware_interface/include/hardware_interface/loaned_command_interface.hpp index aa306870a1..0015ad7d35 100644 --- a/hardware_interface/include/hardware_interface/loaned_command_interface.hpp +++ b/hardware_interface/include/hardware_interface/loaned_command_interface.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "hardware_interface/handle.hpp" @@ -70,9 +71,23 @@ class LoanedCommandInterface const std::string & get_prefix_name() const { return command_interface_.get_prefix_name(); } - void set_value(double val) { command_interface_.set_value(val); } + void set_value(double val) + { + while (!command_interface_.set_value(val)) + { + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + } - double get_value() const { return command_interface_.get_value(); } + double get_value() const + { + double value; + while (!command_interface_.get_value(value)) + { + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + return value; + } protected: CommandInterface & command_interface_; diff --git a/hardware_interface/include/hardware_interface/loaned_state_interface.hpp b/hardware_interface/include/hardware_interface/loaned_state_interface.hpp index 96cc3e89df..5f1475c4e9 100644 --- a/hardware_interface/include/hardware_interface/loaned_state_interface.hpp +++ b/hardware_interface/include/hardware_interface/loaned_state_interface.hpp @@ -17,6 +17,7 @@ #include #include +#include #include #include "hardware_interface/handle.hpp" @@ -75,7 +76,15 @@ class LoanedStateInterface const std::string & get_prefix_name() const { return state_interface_.get_prefix_name(); } - double get_value() const { return state_interface_.get_value(); } + double get_value() const + { + double value; + while (!state_interface_.get_value(value)) + { + std::this_thread::sleep_for(std::chrono::microseconds(10)); + } + return value; + } protected: const StateInterface & state_interface_; From f0847f33ab9fcfc412175732da8dde83d1d0e7bf Mon Sep 17 00:00:00 2001 From: Sai Kishor Kothakota Date: Mon, 14 Oct 2024 19:25:30 +0200 Subject: [PATCH 2/4] update Loaned command and state handles with statistics, templated set and get value method with some statistics on misses and timeouts --- .../loaned_command_interface.hpp | 89 +++++++++++++++++-- .../loaned_state_interface.hpp | 56 +++++++++++- 2 files changed, 137 insertions(+), 8 deletions(-) diff --git a/hardware_interface/include/hardware_interface/loaned_command_interface.hpp b/hardware_interface/include/hardware_interface/loaned_command_interface.hpp index 0015ad7d35..58db14dc63 100644 --- a/hardware_interface/include/hardware_interface/loaned_command_interface.hpp +++ b/hardware_interface/include/hardware_interface/loaned_command_interface.hpp @@ -16,11 +16,13 @@ #define HARDWARE_INTERFACE__LOANED_COMMAND_INTERFACE_HPP_ #include +#include #include #include #include #include "hardware_interface/handle.hpp" +#include "rclcpp/logging.hpp" namespace hardware_interface { @@ -33,17 +35,23 @@ class LoanedCommandInterface CommandInterface & command_interface) : LoanedCommandInterface(command_interface, nullptr) { + get_value_statistics_.reset(); + set_value_statistics_.reset(); } [[deprecated("Replaced by the new version using shared_ptr")]] LoanedCommandInterface( CommandInterface & command_interface, Deleter && deleter) : command_interface_(command_interface), deleter_(std::forward(deleter)) { + get_value_statistics_.reset(); + set_value_statistics_.reset(); } LoanedCommandInterface(CommandInterface::SharedPtr command_interface, Deleter && deleter) : command_interface_(*command_interface), deleter_(std::forward(deleter)) { + get_value_statistics_.reset(); + set_value_statistics_.reset(); } LoanedCommandInterface(const LoanedCommandInterface & other) = delete; @@ -52,6 +60,27 @@ class LoanedCommandInterface virtual ~LoanedCommandInterface() { + auto logger = rclcpp::get_logger(command_interface_.get_name()); + RCLCPP_WARN_EXPRESSION( + rclcpp::get_logger(get_name()), + (get_value_statistics_.failed_counter > 0 || get_value_statistics_.timeout_counter > 0), + "LoanedCommandInterface %s has %u (%f %%) timeouts and %u (%f %%) missed calls out of %u " + "get_value calls", + get_name().c_str(), get_value_statistics_.timeout_counter, + (get_value_statistics_.timeout_counter * 100.0) / get_value_statistics_.total_counter, + get_value_statistics_.failed_counter, + (get_value_statistics_.failed_counter * 100.0) / get_value_statistics_.total_counter, + get_value_statistics_.total_counter); + RCLCPP_WARN_EXPRESSION( + rclcpp::get_logger(get_name()), + (set_value_statistics_.failed_counter > 0 || set_value_statistics_.timeout_counter > 0), + "LoanedCommandInterface %s has %u (%f %%) timeouts and %u (%f %%) missed calls out of %u " + "set_value calls", + get_name().c_str(), set_value_statistics_.timeout_counter, + (set_value_statistics_.timeout_counter * 100.0) / set_value_statistics_.total_counter, + set_value_statistics_.failed_counter, + (set_value_statistics_.failed_counter * 100.0) / set_value_statistics_.total_counter, + set_value_statistics_.total_counter); if (deleter_) { deleter_(); @@ -71,27 +100,77 @@ class LoanedCommandInterface const std::string & get_prefix_name() const { return command_interface_.get_prefix_name(); } - void set_value(double val) + template + [[nodiscard]] bool set_value(T value, unsigned int max_tries = 10) { - while (!command_interface_.set_value(val)) + unsigned int nr_tries = 0; + set_value_statistics_.total_counter++; + while (!command_interface_.set_value(value)) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); + set_value_statistics_.failed_counter++; + ++nr_tries; + if (nr_tries == max_tries) + { + set_value_statistics_.timeout_counter++; + return false; + } + std::this_thread::yield(); } + return true; } double get_value() const { double value; + if (get_value(value)) + { + return value; + } + else + { + return std::numeric_limits::quiet_NaN(); + } + } + + template + [[nodiscard]] bool get_value(T & value, unsigned int max_tries = 10) const + { + unsigned int nr_tries = 0; + get_value_statistics_.total_counter++; while (!command_interface_.get_value(value)) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); + get_value_statistics_.failed_counter++; + ++nr_tries; + if (nr_tries == max_tries) + { + get_value_statistics_.timeout_counter++; + return false; + } + std::this_thread::yield(); } - return value; + return true; } protected: CommandInterface & command_interface_; Deleter deleter_; + +private: + struct HandleRTStatistics + { + unsigned int total_counter; + unsigned int failed_counter; + unsigned int timeout_counter; + + void reset() + { + total_counter = 0; + failed_counter = 0; + timeout_counter = 0; + } + }; + mutable HandleRTStatistics get_value_statistics_; + HandleRTStatistics set_value_statistics_; }; } // namespace hardware_interface diff --git a/hardware_interface/include/hardware_interface/loaned_state_interface.hpp b/hardware_interface/include/hardware_interface/loaned_state_interface.hpp index 5f1475c4e9..a4f32305db 100644 --- a/hardware_interface/include/hardware_interface/loaned_state_interface.hpp +++ b/hardware_interface/include/hardware_interface/loaned_state_interface.hpp @@ -16,12 +16,13 @@ #define HARDWARE_INTERFACE__LOANED_STATE_INTERFACE_HPP_ #include +#include #include #include #include #include "hardware_interface/handle.hpp" - +#include "rclcpp/logging.hpp" namespace hardware_interface { class LoanedStateInterface @@ -57,6 +58,17 @@ class LoanedStateInterface virtual ~LoanedStateInterface() { + auto logger = rclcpp::get_logger(state_interface_.get_name()); + RCLCPP_WARN_EXPRESSION( + logger, + (get_value_statistics_.failed_counter > 0 || get_value_statistics_.timeout_counter > 0), + "LoanedStateInterface %s has %u (%.2f %%) timeouts and %u (%.2f %%) missed calls out of %u " + "get_value calls", + state_interface_.get_name().c_str(), get_value_statistics_.timeout_counter, + (get_value_statistics_.timeout_counter * 100.0) / get_value_statistics_.total_counter, + get_value_statistics_.failed_counter, + (get_value_statistics_.failed_counter * 100.0) / get_value_statistics_.total_counter, + get_value_statistics_.total_counter); if (deleter_) { deleter_(); @@ -79,16 +91,54 @@ class LoanedStateInterface double get_value() const { double value; + if (get_value(value)) + { + return value; + } + else + { + return std::numeric_limits::quiet_NaN(); + } + } + + template + [[nodiscard]] bool get_value(T & value, unsigned int max_tries = 10) const + { + unsigned int nr_tries = 0; + get_value_statistics_.total_counter++; while (!state_interface_.get_value(value)) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); + get_value_statistics_.failed_counter++; + ++nr_tries; + if (nr_tries == max_tries) + { + get_value_statistics_.timeout_counter++; + return false; + } + std::this_thread::yield(); } - return value; + return true; } protected: const StateInterface & state_interface_; Deleter deleter_; + +private: + struct HandleRTStatistics + { + unsigned int total_counter; + unsigned int failed_counter; + unsigned int timeout_counter; + + void reset() + { + total_counter = 0; + failed_counter = 0; + timeout_counter = 0; + } + }; + mutable HandleRTStatistics get_value_statistics_; }; } // namespace hardware_interface From 466a1071bc6945e66e7ca6caef4645e2993ab0da Mon Sep 17 00:00:00 2001 From: Sai Kishor Kothakota Date: Fri, 25 Oct 2024 10:44:16 +0200 Subject: [PATCH 3/4] Use shared_mutex for better performance with the StateInterfaces --- .../include/hardware_interface/handle.hpp | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/hardware_interface/include/hardware_interface/handle.hpp b/hardware_interface/include/hardware_interface/handle.hpp index 2ea3310cee..1dfd499c2c 100644 --- a/hardware_interface/include/hardware_interface/handle.hpp +++ b/hardware_interface/include/hardware_interface/handle.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -73,8 +74,8 @@ class Handle Handle(const Handle & other) noexcept { - std::unique_lock lock(other.handle_mutex_); - std::unique_lock lock_this(handle_mutex_); + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); prefix_name_ = other.prefix_name_; interface_name_ = other.interface_name_; handle_name_ = other.handle_name_; @@ -84,8 +85,8 @@ class Handle Handle(Handle && other) noexcept { - std::unique_lock lock(other.handle_mutex_); - std::unique_lock lock_this(handle_mutex_); + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); prefix_name_ = std::move(other.prefix_name_); interface_name_ = std::move(other.interface_name_); handle_name_ = std::move(other.handle_name_); @@ -97,8 +98,8 @@ class Handle { if (this != &other) { - std::unique_lock lock(other.handle_mutex_); - std::unique_lock lock_this(handle_mutex_); + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); prefix_name_ = other.prefix_name_; interface_name_ = other.interface_name_; handle_name_ = other.handle_name_; @@ -112,8 +113,8 @@ class Handle { if (this != &other) { - std::unique_lock lock(other.handle_mutex_); - std::unique_lock lock_this(handle_mutex_); + std::unique_lock lock(other.handle_mutex_); + std::unique_lock lock_this(handle_mutex_); prefix_name_ = std::move(other.prefix_name_); interface_name_ = std::move(other.interface_name_); handle_name_ = std::move(other.handle_name_); @@ -144,7 +145,7 @@ class Handle [[deprecated("Use bool get_value(double & value) instead to retrieve the value.")]] double get_value() const { - std::unique_lock lock(handle_mutex_, std::try_to_lock); + std::shared_lock lock(handle_mutex_, std::try_to_lock); if (!lock.owns_lock()) { return std::numeric_limits::quiet_NaN(); @@ -158,7 +159,7 @@ class Handle [[nodiscard]] bool get_value(double & value) const { - std::unique_lock lock(handle_mutex_, std::try_to_lock); + std::shared_lock lock(handle_mutex_, std::try_to_lock); if (!lock.owns_lock()) { return false; @@ -173,7 +174,7 @@ class Handle [[nodiscard]] bool set_value(double value) { - std::unique_lock lock(handle_mutex_, std::try_to_lock); + std::unique_lock lock(handle_mutex_, std::try_to_lock); if (!lock.owns_lock()) { return false; @@ -195,7 +196,7 @@ class Handle // TODO(Manuel) redeclare as HANDLE_DATATYPE * value_ptr_ if old functionality is removed double * value_ptr_; // END - mutable std::recursive_mutex handle_mutex_; + mutable std::shared_mutex handle_mutex_; }; class StateInterface : public Handle From 132c821af239338d36584676b0e066e3ebc05743 Mon Sep 17 00:00:00 2001 From: Sai Kishor Kothakota Date: Tue, 29 Oct 2024 23:28:43 +0100 Subject: [PATCH 4/4] Fix the missing reset in LoanedStateInterface and also fix the final statistical prints --- .../loaned_command_interface.hpp | 43 +++++++------------ .../loaned_state_interface.hpp | 23 ++++------ 2 files changed, 23 insertions(+), 43 deletions(-) diff --git a/hardware_interface/include/hardware_interface/loaned_command_interface.hpp b/hardware_interface/include/hardware_interface/loaned_command_interface.hpp index 58db14dc63..6013dea778 100644 --- a/hardware_interface/include/hardware_interface/loaned_command_interface.hpp +++ b/hardware_interface/include/hardware_interface/loaned_command_interface.hpp @@ -35,23 +35,17 @@ class LoanedCommandInterface CommandInterface & command_interface) : LoanedCommandInterface(command_interface, nullptr) { - get_value_statistics_.reset(); - set_value_statistics_.reset(); } [[deprecated("Replaced by the new version using shared_ptr")]] LoanedCommandInterface( CommandInterface & command_interface, Deleter && deleter) : command_interface_(command_interface), deleter_(std::forward(deleter)) { - get_value_statistics_.reset(); - set_value_statistics_.reset(); } LoanedCommandInterface(CommandInterface::SharedPtr command_interface, Deleter && deleter) : command_interface_(*command_interface), deleter_(std::forward(deleter)) { - get_value_statistics_.reset(); - set_value_statistics_.reset(); } LoanedCommandInterface(const LoanedCommandInterface & other) = delete; @@ -64,22 +58,22 @@ class LoanedCommandInterface RCLCPP_WARN_EXPRESSION( rclcpp::get_logger(get_name()), (get_value_statistics_.failed_counter > 0 || get_value_statistics_.timeout_counter > 0), - "LoanedCommandInterface %s has %u (%f %%) timeouts and %u (%f %%) missed calls out of %u " - "get_value calls", + "LoanedCommandInterface %s has %u (%.4f %%) timeouts and %u (~ %.4f %%) missed calls out of " + "%u get_value calls", get_name().c_str(), get_value_statistics_.timeout_counter, (get_value_statistics_.timeout_counter * 100.0) / get_value_statistics_.total_counter, get_value_statistics_.failed_counter, - (get_value_statistics_.failed_counter * 100.0) / get_value_statistics_.total_counter, + (get_value_statistics_.failed_counter * 10.0) / get_value_statistics_.total_counter, get_value_statistics_.total_counter); RCLCPP_WARN_EXPRESSION( rclcpp::get_logger(get_name()), (set_value_statistics_.failed_counter > 0 || set_value_statistics_.timeout_counter > 0), - "LoanedCommandInterface %s has %u (%f %%) timeouts and %u (%f %%) missed calls out of %u " - "set_value calls", + "LoanedCommandInterface %s has %u (%.4f %%) timeouts and %u (~ %.4f %%) missed calls out of " + "%u set_value calls", get_name().c_str(), set_value_statistics_.timeout_counter, (set_value_statistics_.timeout_counter * 100.0) / set_value_statistics_.total_counter, set_value_statistics_.failed_counter, - (set_value_statistics_.failed_counter * 100.0) / set_value_statistics_.total_counter, + (set_value_statistics_.failed_counter * 10.0) / set_value_statistics_.total_counter, set_value_statistics_.total_counter); if (deleter_) { @@ -104,14 +98,14 @@ class LoanedCommandInterface [[nodiscard]] bool set_value(T value, unsigned int max_tries = 10) { unsigned int nr_tries = 0; - set_value_statistics_.total_counter++; + ++set_value_statistics_.total_counter; while (!command_interface_.set_value(value)) { - set_value_statistics_.failed_counter++; + ++set_value_statistics_.failed_counter; ++nr_tries; if (nr_tries == max_tries) { - set_value_statistics_.timeout_counter++; + ++set_value_statistics_.timeout_counter; return false; } std::this_thread::yield(); @@ -136,14 +130,14 @@ class LoanedCommandInterface [[nodiscard]] bool get_value(T & value, unsigned int max_tries = 10) const { unsigned int nr_tries = 0; - get_value_statistics_.total_counter++; + ++get_value_statistics_.total_counter; while (!command_interface_.get_value(value)) { - get_value_statistics_.failed_counter++; + ++get_value_statistics_.failed_counter; ++nr_tries; if (nr_tries == max_tries) { - get_value_statistics_.timeout_counter++; + ++get_value_statistics_.timeout_counter; return false; } std::this_thread::yield(); @@ -158,16 +152,9 @@ class LoanedCommandInterface private: struct HandleRTStatistics { - unsigned int total_counter; - unsigned int failed_counter; - unsigned int timeout_counter; - - void reset() - { - total_counter = 0; - failed_counter = 0; - timeout_counter = 0; - } + unsigned int total_counter = 0; + unsigned int failed_counter = 0; + unsigned int timeout_counter = 0; }; mutable HandleRTStatistics get_value_statistics_; HandleRTStatistics set_value_statistics_; diff --git a/hardware_interface/include/hardware_interface/loaned_state_interface.hpp b/hardware_interface/include/hardware_interface/loaned_state_interface.hpp index a4f32305db..3ebc8c7ca0 100644 --- a/hardware_interface/include/hardware_interface/loaned_state_interface.hpp +++ b/hardware_interface/include/hardware_interface/loaned_state_interface.hpp @@ -62,12 +62,12 @@ class LoanedStateInterface RCLCPP_WARN_EXPRESSION( logger, (get_value_statistics_.failed_counter > 0 || get_value_statistics_.timeout_counter > 0), - "LoanedStateInterface %s has %u (%.2f %%) timeouts and %u (%.2f %%) missed calls out of %u " + "LoanedStateInterface %s has %u (%.4f %%) timeouts and %u (%.4f %%) missed calls out of %u " "get_value calls", state_interface_.get_name().c_str(), get_value_statistics_.timeout_counter, (get_value_statistics_.timeout_counter * 100.0) / get_value_statistics_.total_counter, get_value_statistics_.failed_counter, - (get_value_statistics_.failed_counter * 100.0) / get_value_statistics_.total_counter, + (get_value_statistics_.failed_counter * 10.0) / get_value_statistics_.total_counter, get_value_statistics_.total_counter); if (deleter_) { @@ -105,14 +105,14 @@ class LoanedStateInterface [[nodiscard]] bool get_value(T & value, unsigned int max_tries = 10) const { unsigned int nr_tries = 0; - get_value_statistics_.total_counter++; + ++get_value_statistics_.total_counter; while (!state_interface_.get_value(value)) { - get_value_statistics_.failed_counter++; + ++get_value_statistics_.failed_counter; ++nr_tries; if (nr_tries == max_tries) { - get_value_statistics_.timeout_counter++; + ++get_value_statistics_.timeout_counter; return false; } std::this_thread::yield(); @@ -127,16 +127,9 @@ class LoanedStateInterface private: struct HandleRTStatistics { - unsigned int total_counter; - unsigned int failed_counter; - unsigned int timeout_counter; - - void reset() - { - total_counter = 0; - failed_counter = 0; - timeout_counter = 0; - } + unsigned int total_counter = 0; + unsigned int failed_counter = 0; + unsigned int timeout_counter = 0; }; mutable HandleRTStatistics get_value_statistics_; };