Skip to content

Commit a35fabc

Browse files
mdtoommarting87Matthijs den Toomclalancette
authored
[Foxy backport] Fixed memory leak in Buffer::waitForTransform (#281) (#330)
* Clear callback from transformable_callbacks_ when callback is done or not needed Co-authored-by: marting87 <[email protected]> Co-authored-by: Matthijs den Toom <[email protected]> Co-authored-by: Chris Lalancette <[email protected]>
1 parent 9aa14f2 commit a35fabc

File tree

2 files changed

+59
-10
lines changed

2 files changed

+59
-10
lines changed

tf2/src/buffer_core.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,6 +1491,7 @@ void BufferCore::testTransformableRequests()
14911491
{
14921492
const TransformableCallback& cb = it->second;
14931493
cb(req.request_handle, lookupFrameString(req.target_id), lookupFrameString(req.source_id), req.time, result);
1494+
transformable_callbacks_.erase(req.cb_handle);
14941495
}
14951496
}
14961497

tf2_ros/src/buffer.cpp

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,20 +34,40 @@
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-
4849
namespace 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+
5171
Buffer::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

Comments
 (0)