ros::subscriber global variable a bad idea?

asked 2019-08-08 14:18:11 -0500

hpangels gravatar image

Is there a reason why a ROS subscriber declared as a global variable is problematic? For example, I'm finding that if you don't call shutdown() on the subscriber explicitly before exiting the program, it throws an exception during the destructor of the subscriber object. Consider the slightly modified version of the tutorial below, where I only moved the declaration of the subscriber outside of main(). (

#include "ros/ros.h"
#include "std_msgs/String.h"

 * This tutorial demonstrates simple receipt of messages over the ROS system.
void chatterCallback(const std_msgs::String::ConstPtr& msg)
  ROS_INFO("I heard: [%s]", msg->data.c_str());

ros::Subscriber sub;

int main(int argc, char **argv)
  ros::init(argc, argv, "listener");
  ros::NodeHandle n;
  /* ros::Subscriber */ sub = n.subscribe("chatter", 1000, chatterCallback);

  return 0;

This code throws a boost exception while exiting:

terminate called after throwing an instance of 'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::lock_error> >'
  what():  boost: mutex lock failed in pthread_mutex_lock: Invalid argument

The call stack shows that it is in the subscriber's destructor:!__GI_raise(int sig) (/build/glibc-Cl5G7W/glibc-2.23/sysdeps/unix/sysv/linux/raise.c:54)!__GI_abort() (/build/glibc-Cl5G7W/glibc-2.23/stdlib/abort.c:89)!__gnu_cxx::__verbose_terminate_handler() (Unknown Source:0)![Unknown/Just-In-Time compiled code] (Unknown Source:0)!__gxx_personality_v0 (Unknown Source:0)![Unknown/Just-In-Time compiled code] (Unknown Source:0)!_Unwind_Resume (Unknown Source:0)!ros::TopicManager::unsubscribe(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, boost::shared_ptr<ros::SubscriptionCallbackHelper> const&) (Unknown Source:0)!ros::Subscriber::Impl::unsubscribe() (Unknown Source:0)!ros::Subscriber::Impl::~Impl() (Unknown Source:0)!boost::detail::sp_counted_impl_pd<ros::Subscriber::Impl*, boost::detail::sp_ms_deleter<ros::Subscriber::Impl> >::dispose() (Unknown Source:0)!ros::Subscriber::~Subscriber() (Unknown Source:0)!__run_exit_handlers(int status, struct exit_function_list ** listp, _Bool run_list_atexit) (/build/glibc-Cl5G7W/glibc-2.23/stdlib/exit.c:82)!__GI_exit(int status) (/build/glibc-Cl5G7W/glibc-2.23/stdlib/exit.c:104)!__libc_start_main(int (*)(int, char **, char **) main, int argc, char ** argv, int (*)(int, char **, char **) init, void (*)(void) fini, void (*)(void) rtld_fini, void * stack_end) (/build/glibc-Cl5G7W/glibc-2.23/csu/libc-start.c:325)
_start (Unknown Source:0)

I guess what's happening is that the subscriber's default constructor is called when the global gets initialized, and then a copy constructor replaces is when n.subscribe() is called. But why should this be a problem? Colleagues tell me they've had other strange behavior when using global subscribers and just learned to avoid them, using global pointers to objects declared in main() instead. Not a great practice either, and please let's not get into the arguments why global variables are bad in the first place. I'm just curious if anyone can explain this phenomenon.


edit retag flag offensive close merge delete