1919#include < memory>
2020#include < thread>
2121
22+ #include " rmw/impl/cpp/demangle.hpp"
23+
2224#include " rclcpp/contexts/default_context.hpp"
25+ #include " rclcpp/detail/cpp_callback_trampoline.hpp"
2326#include " rclcpp/exceptions.hpp"
24-
27+ # include " rclcpp/logging.hpp "
2528#include " rcutils/logging_macros.h"
2629
2730using rclcpp::TimerBase;
@@ -71,7 +74,9 @@ TimerBase::TimerBase(
7174}
7275
7376TimerBase::~TimerBase ()
74- {}
77+ {
78+ clear_on_reset_callback ();
79+ }
7580
7681void
7782TimerBase::cancel ()
@@ -96,7 +101,11 @@ TimerBase::is_canceled()
96101void
97102TimerBase::reset ()
98103{
99- rcl_ret_t ret = rcl_timer_reset (timer_handle_.get ());
104+ rcl_ret_t ret = RCL_RET_OK;
105+ {
106+ std::lock_guard<std::recursive_mutex> lock (callback_mutex_);
107+ ret = rcl_timer_reset (timer_handle_.get ());
108+ }
100109 if (ret != RCL_RET_OK) {
101110 rclcpp::exceptions::throw_from_rcl_error (ret, " Couldn't reset timer" );
102111 }
@@ -138,3 +147,73 @@ TimerBase::exchange_in_use_by_wait_set_state(bool in_use_state)
138147{
139148 return in_use_by_wait_set_.exchange (in_use_state);
140149}
150+
151+ void
152+ TimerBase::set_on_reset_callback (std::function<void (size_t )> callback)
153+ {
154+ if (!callback) {
155+ throw std::invalid_argument (
156+ " The callback passed to set_on_reset_callback "
157+ " is not callable." );
158+ }
159+
160+ auto new_callback =
161+ [callback, this ](size_t reset_calls) {
162+ try {
163+ callback (reset_calls);
164+ } catch (const std::exception & exception) {
165+ RCLCPP_ERROR_STREAM (
166+ rclcpp::get_logger (" rclcpp" ),
167+ " rclcpp::TimerBase@" << this <<
168+ " caught " << rmw::impl::cpp::demangle (exception) <<
169+ " exception in user-provided callback for the 'on reset' callback: " <<
170+ exception.what ());
171+ } catch (...) {
172+ RCLCPP_ERROR_STREAM (
173+ rclcpp::get_logger (" rclcpp" ),
174+ " rclcpp::TimerBase@" << this <<
175+ " caught unhandled exception in user-provided callback " <<
176+ " for the 'on reset' callback" );
177+ }
178+ };
179+
180+ std::lock_guard<std::recursive_mutex> lock (callback_mutex_);
181+
182+ // Set it temporarily to the new callback, while we replace the old one.
183+ // This two-step setting, prevents a gap where the old std::function has
184+ // been replaced but rcl hasn't been told about the new one yet.
185+ set_on_reset_callback (
186+ rclcpp::detail::cpp_callback_trampoline<
187+ decltype (new_callback), const void *, size_t >,
188+ static_cast <const void *>(&new_callback));
189+
190+ // Store the std::function to keep it in scope, also overwrites the existing one.
191+ on_reset_callback_ = new_callback;
192+
193+ // Set it again, now using the permanent storage.
194+ set_on_reset_callback (
195+ rclcpp::detail::cpp_callback_trampoline<
196+ decltype (on_reset_callback_), const void *, size_t >,
197+ static_cast <const void *>(&on_reset_callback_));
198+ }
199+
200+ void
201+ TimerBase::clear_on_reset_callback ()
202+ {
203+ std::lock_guard<std::recursive_mutex> lock (callback_mutex_);
204+
205+ if (on_reset_callback_) {
206+ set_on_reset_callback (nullptr , nullptr );
207+ on_reset_callback_ = nullptr ;
208+ }
209+ }
210+
211+ void
212+ TimerBase::set_on_reset_callback (rcl_event_callback_t callback, const void * user_data)
213+ {
214+ rcl_ret_t ret = rcl_timer_set_on_reset_callback (timer_handle_.get (), callback, user_data);
215+
216+ if (ret != RCL_RET_OK) {
217+ rclcpp::exceptions::throw_from_rcl_error (ret, " Failed to set timer on reset callback" );
218+ }
219+ }
0 commit comments