In the General forum, @tareq97 asked me to share some dubious code patterns (code smells?) I have observed over the years in various ROS projects. He is working on a static analysis tool, and adding checks for such patterns would be a nice addition to his tool.
I thought such a discussion would fit better in the QA forum, and so I will share here a small list of code patterns along with my reasoning for finding them questionable. Some of them are found in public repositories, but I will refrain from citing the sources (blaming, or shaming), unless asked to. Feel free to tell me whether you agree or disagree with my assessment, as well as adding code patterns you have observed yourself.
Pattern: Creating one thread to process callbacks, while the main thread stays idle.
ros::AsyncSpinner spinner(1);
spinner.start();
while (!gShutdownRequest) { ros::Duration(0.05).sleep(); }
Reasoning: Why create a new thread? Seems like a remnant from code refactoring. This pattern can be replaced with:
ros::spin();
Pattern: Using a Rate
object just to receive callbacks.
ros::Rate r(100);
while (ros::ok()) {
ros::spinOnce();
r.sleep();
}
Reasoning: This is only justifiable if one wants to limit the rate at which callbacks are processed by some degree, at the risk of possibly dropping some messages. This pattern can be replaced with:
ros::spin();
Pattern: Calling spinOnce
at the end of the loop.
while (ros::ok())
{
/** ... */
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
}
Reasoning: If the published message is based on the received data, spinOnce
should be called at the start of the loop, so that the newest data is processed as fast as possible.
Pattern: One thread processes callbacks, another publishes messages. They share internal state, but lack thread synchronization.
void MyClass::onInit() {
update_thread_.start(&MyClass::update, *this);
}
void MyClass::topicCB(const std_msgs::Int8ConstPtr msg) {
var1_ = ...
var2_ = ...
}
void MyClass::update() {
ros::Rate spin_rate(10);
while (!shutdown_requested_ && ros::ok()) {
if (var1_ || var2_) { ... }
spin_rate.sleep();
}
}
Reasoning: Lack of thread synchronization means that variable updates on one thread might not be visible to the other thread. That is, updates to var1_
or var2_
in topicCB()
might never make it to the thread that runs update()
.