3434
3535#include < exception>
3636#include < limits>
37+ #include < map>
3738#include < mutex>
3839#include < sstream>
3940#include < thread>
41+ #include < unordered_map>
4042
4143// TODO(tfoote replace these terrible macros)
4244#define ROS_ERROR printf
4345#define ROS_FATAL printf
4446#define ROS_INFO printf
4547#define ROS_WARN printf
4648
47-
4849namespace tf2_ros
4950{
5051
52+ // Added to backport: https://github.com/ros2/geometry2/pull/281
53+ static std::mutex g_object_map_to_cb_handle_mutex;
54+ static std::map<Buffer*, std::unordered_map<TimerHandle, tf2::TransformableCallbackHandle>> g_object_map_to_cb_handle;
55+
56+ void deleteTransformCallbackHandle (Buffer *class_ptr, const TimerHandle &timer_handle)
57+ {
58+ if (g_object_map_to_cb_handle.find (class_ptr) == g_object_map_to_cb_handle.end ())
59+ {
60+ // Return if the object map cb handle is already removed
61+ return ;
62+ }
63+
64+ g_object_map_to_cb_handle.at (class_ptr).erase (timer_handle);
65+ if (g_object_map_to_cb_handle.at (class_ptr).size () == 0 )
66+ {
67+ g_object_map_to_cb_handle.erase (class_ptr);
68+ }
69+ }
70+
5171Buffer::Buffer (rclcpp::Clock::SharedPtr clock, tf2::Duration cache_time) :
5272 BufferCore (cache_time), clock_(clock), timer_interface_(nullptr )
5373{
@@ -215,8 +235,14 @@ Buffer::waitForTransform(const std::string& target_frame, const std::string& sou
215235 for (auto it = timer_to_request_map_.begin (); it != timer_to_request_map_.end (); ++it) {
216236 if (request_handle == it->second ) {
217237 // The request handle was found, so a timeout has not occurred
218- this ->timer_interface_ ->remove (it->first );
219- this ->timer_to_request_map_ .erase (it->first );
238+ auto timer_handle = it->first ;
239+ this ->timer_interface_ ->remove (timer_handle);
240+ this ->timer_to_request_map_ .erase (timer_handle);
241+ {
242+ std::lock_guard<std::mutex> lock (g_object_map_to_cb_handle_mutex);
243+ // Remove the callback function.
244+ deleteTransformCallbackHandle (this , timer_handle);
245+ }
220246 timeout_occurred = false ;
221247 break ;
222248 }
@@ -242,19 +268,31 @@ Buffer::waitForTransform(const std::string& target_frame, const std::string& sou
242268 // Immediately transformable
243269 geometry_msgs::msg::TransformStamped msg_stamped = lookupTransform (target_frame, source_frame, time);
244270 promise->set_value (msg_stamped);
271+ removeTransformableCallback (cb_handle);
272+ callback (future);
245273 } else if (0xffffffffffffffffULL == handle) {
246274 // Never transformable
247275 promise->set_exception (std::make_exception_ptr (tf2::LookupException (
248276 " Failed to transform from " + source_frame + " to " + target_frame)));
277+ removeTransformableCallback (cb_handle);
278+ callback (future);
249279 } else {
250280 std::lock_guard<std::mutex> lock (timer_to_request_map_mutex_);
251281 auto timer_handle = timer_interface_->createTimer (
252282 clock_,
253283 timeout,
254284 std::bind (&Buffer::timerCallback, this , std::placeholders::_1, promise, future, callback));
255285
256- // Save association between timer and request handle
286+ // Save association between timer and request/callback handle
257287 timer_to_request_map_[timer_handle] = handle;
288+ {
289+ std::lock_guard<std::mutex> lock (g_object_map_to_cb_handle_mutex);
290+ if (g_object_map_to_cb_handle.find (this ) == g_object_map_to_cb_handle.end ())
291+ {
292+ g_object_map_to_cb_handle[this ];
293+ }
294+ g_object_map_to_cb_handle.at (this )[timer_handle] = cb_handle;
295+ }
258296 }
259297 return future;
260298}
@@ -266,20 +304,30 @@ Buffer::timerCallback(const TimerHandle & timer_handle,
266304 TransformReadyCallback callback)
267305{
268306 bool timer_is_valid = false ;
269- tf2::TransformableRequestHandle request_handle = 0u ;
307+ tf2::TransformableCallbackHandle callback_handle = 0u ;
270308 {
271309 std::lock_guard<std::mutex> lock (timer_to_request_map_mutex_);
272- auto timer_and_request_it = timer_to_request_map_.find (timer_handle);
273- timer_is_valid = (timer_to_request_map_.end () != timer_and_request_it);
274- if (timer_is_valid) {
275- request_handle = timer_and_request_it->second ;
310+ {
311+ std::lock_guard<std::mutex> lock (g_object_map_to_cb_handle_mutex);
312+ if (g_object_map_to_cb_handle.find (this ) != g_object_map_to_cb_handle.end ())
313+ {
314+ // Only if the map to callback handle isn't already removed.
315+ auto timer_and_callback_it = g_object_map_to_cb_handle.at (this ).find (timer_handle);
316+ timer_is_valid = (g_object_map_to_cb_handle.at (this ).end () != timer_and_callback_it);
317+
318+ if (timer_is_valid) {
319+ callback_handle = timer_and_callback_it->second ;
320+ }
321+
322+ deleteTransformCallbackHandle (this , timer_handle);
323+ }
276324 }
277325 timer_to_request_map_.erase (timer_handle);
278326 timer_interface_->remove (timer_handle);
279327 }
280328
281329 if (timer_is_valid) {
282- cancelTransformableRequest (request_handle );
330+ removeTransformableCallback (callback_handle );
283331 promise->set_exception (
284332 std::make_exception_ptr (tf2::TimeoutException (std::string (" Timed out waiting for transform" ))));
285333 callback (future);
0 commit comments