Is this a reasonable way to achieve a "self-destructing" Timer?

asked 2021-02-01 08:44:49 -0500

xkortex gravatar image

I want to be able to fire off one-shot Timer events and not worry about cleaning up after the reference to the Timer object, which deletes the callback if it falls out of scope. I hadn't been able to find anything helpful on how to achieve this, so I came up with this pattern.

/// just for pretty-printing
std::string to_string(ros::Timer &t) {
    std::stringstream ss;
    ss << "Timer@" << &t << "(s=" << t.hasStarted() << ", p=" << t.hasPending() << ")";
    return ss.str();
}

 /// Create an anonymous timer callback. A reference to ros::Timer must exist in order for the callback
/// to trip. If the Timer is destructed, the callback is popped from the event queue
/// NodeHandle is ROS's god class. It handles the event queue, firing callbacks, message bus, etc.
void anonymous_timer(ros::NodeHandlePtr const &nhp, double duration) {
    if (!ros::ok()) return;
    auto yolo = std::make_shared<ros::Timer>();
    auto func = [nhp, yolo](const ros::TimerEvent &event) {
        ROS_INFO("inside callback %s", to_string(*yolo).c_str());
        /// (yolo) falls out of scope here which should fully destruct the shared pointer
    };
    *yolo = nhp->createTimer(ros::Duration(duration), func, true, true);
    /// (*yolo) would normally fall out of scope, but since we retained a shared_ptr (yolo) to it in the lambda, it survives
}

This achieves the behavior I want, and I've stressed tested this by allocating large strings to check for memory leaks, but I'm not sure that this isn't going to result in anything unexpected, such as a memory leak due to circular references preventing that shared_ptr from destructing. Gonna also try to valgrind it once I figure out how to valgrind a ros node, will update with results if I can.

Thanks,

Edit 1: I just ran a node which uses this mechanism to re-issue an event every second. After running for 8 hours, it's taking up to 12 seconds between firing, so something's definitely amiss. I want to be able to check the size or contents of the callback queue, but it seems like the API doesn't give me that level of access.

edit retag flag offensive close merge delete