Should I be using a standard or recursive mutex to handle callback interruptions?

asked 2017-03-29 11:38:32 -0500

cpagravel gravatar image

updated 2017-03-29 13:46:11 -0500

I have some code to help describe my problem

void CallbackA(const type& msg)
{
  // do stuff
  WorkerFunction(data);
}

void CallbackB(const type& msg)
{
  // do stuff
  WorkerFunction(data);
}

void WorkerFunction(const type& data)
{
  // do stuff
  ros::spinOnce();
  // do more stuff
}

I am debugging a race condition where a callback function--CallbackA--is running and has not yet completed when a topic gets published causing CallbackB to run because I have a ros::spinOnce() called in a subsequent function. I want to use a mutex to ensure that the current function will finish, before letting the new callback run. This wouldn't be a problem if I did not call spinOnce inside, but I want to let other nodes do work as well.

My question is, should I use a std::mutex or std::recursive_mutex? I understand the difference, but what I don't know is how ROS handles these things. I.e. are callbacks performed with the same thread, or a new thread?

Also, as a secondary question, when are new threads created in ROS? (without specifically creating them myself)

Architectural Details

The callbacks and worker function are inside a node meant for recording information about my robot so it can publish a single, organized report. As such, it gathers information from all parts of the robot including (but not limited to): local planner, global planner, and coverage planner (a node meant for keeping track of where I've been).

Some functions inside this reporter node take a long time to compute and I want to make sure that I am not losing messages so I spawn a thread and while that thread is making calculations (the thread is localized to a single function) I will call ros::spinOnce to ensure that I keep up with the information coming in.

My problem is that I want to block certain callbacks while I'm making these calculations. I thought that I should use a mutex for this, which is why I'm asking how the threads are handled so I could decide whether to use a standard or recursive mutex.

When I say let other nodes do work as well, what I meant was that I did not want to hog cpu from other nodes since other nodes provide mission critical functions like detecting obstacles and since ROS is built off of Linux which isn't a RTOS, I need a way to tell the OS: "the current job can wait for a while, please execute other jobs and come back to this later". I thought ros::spinOnce() was doing that, but, judging by the comments I am mistaken.

edit retag flag offensive close merge delete

Comments

This wouldn't be a problem if I did not call spinOnce inside

indeed.

but I want to let other nodes do work as well.

This confuses me: are you referring to handling publish(..) 'events' here, or actual other nodes? If the former: publish(..) does not need spinning, if the latter ..

gvdhoorn gravatar image gvdhoorn  ( 2017-03-29 12:05:15 -0500 )edit

.. 'other nodes' do not share your callback queue(s) - they are completely separate processes, so they would process their own queues, irrespective of whether you call spinOnce() in yours.

Could you elaborate a bit more on the structure of your node(s)?

gvdhoorn gravatar image gvdhoorn  ( 2017-03-29 12:06:03 -0500 )edit

I.e. are callbacks performed with the same thread, or a new thread?

that completely depends on how you run/configured things. By default, queues are procesed by a single threaded spinner (ie: sequentially). Multithreaded spinners exist, but you must create them yourself. See the wiki page ..

gvdhoorn gravatar image gvdhoorn  ( 2017-03-29 12:08:05 -0500 )edit

.. about multithreading, callback queues and how they relate: roscpp/Overview/Callbacks and Spinning.

gvdhoorn gravatar image gvdhoorn  ( 2017-03-29 12:08:57 -0500 )edit

I will add some additional details in my question, from your comments, I get the impression that I might be misunderstanding what spinOnce does.

cpagravel gravatar image cpagravel  ( 2017-03-29 13:33:21 -0500 )edit

spinOnce() basically walks your callbackqueue and calls registered callbacks for any outstanding events (incoming msgs, svc reqs, timers). There is no magic. Publications are handled by the 'other threads'.

gvdhoorn gravatar image gvdhoorn  ( 2017-03-29 13:42:52 -0500 )edit

I've updated my question. If I understand you correctly, it seems that I need to use a standard mutex since a single thread will execute all of the callbacks. (i.e. each callback does not have its own thread), because I'm just using ros::spin()

cpagravel gravatar image cpagravel  ( 2017-03-29 13:48:37 -0500 )edit

(after your edit): so do you want to postpone processing events until after you've completed your 'report' (ie: sort-of snapshot), or do you want to always include the most recent msg in your report, as long as you have the time? Or are you trying to TDV multiple 'report generating' ..

gvdhoorn gravatar image gvdhoorn  ( 2017-03-29 13:50:26 -0500 )edit