Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Shared variables in two threads

I’m writing a ros node with two threads to support client dynamic reconfiguration in C++. One thread is responsible for subscribing to a topic /error which is float, taking the sum of value sum_of_error, and incrementing batch_size. Another thread is responsible for taking the average of the value if batch_size reaches BATCH and then resetting sum_of_error and batch_size to 0. Both batch_size and sum_of_error are shared variables between two threads so I use mutex to prevent data race. The thread for subscription of a topic works well but the thread for taking the average and resetting batch_size and sum_of_error is stuck. I notice that the process gets stuck at while loop in the function get_value(). I think it has something to do with race conditions. Any idea? How can I change my code to solve this problem.

#include <ros/ros.h>
#include <string>
#include <vector>
#include <std_msgs/Float32.h>
#include <dynamic_reconfigure/client.h>
#include <calibration/HcNodeConfig.h>
#include <mutex>
#include <thread>

using namespace std;


mutex m;
int batch_size = 0;
float sum_of_error = 0;

int BATCH = 10;


float get_value() {
    while (batch_size < BATCH) {
    }
    float res = sum_of_error / batch_size;
    m.lock();
    sum_of_error = 0.0;
    batch_size = 0;
    m.unlock();
    return res;
}


void adjuster_callback(const hc::HcNodeConfig& data) {
    
}


void listener_callback(const std_msgs::Float32ConstPtr& msg) {
    float current_error = msg->data;
    m.lock();
    batch_size++;
    sum_of_error += current_error;
    m.unlock();
}


void start_listener(ros::NodeHandle n) {
    ros::Subscriber listener;
    ros::Rate loop_rate(BATCH);
    listener = n.subscribe("/error", BATCH, listener_callback);
    while (ros::ok()) {
        ros::spinOnce();
        loop_rate.sleep();
    }
}


void start_adjuster(ros::NodeHandle n) {
    ros::Rate loop_rate(BATCH);
    hc::HcNodeConfig config;
    dynamic_reconfigure::Client<hc::HcNodeConfig> client("/lidar_front_left_hc", adjuster_callback);
    ros::Duration d(2);
    client.getDefaultConfiguration(config, d);
    while (ros::ok()) {
        float current_error = get_value();
        if (current_error > 0.0) {
             config.angle = 0.0;
             client.setConfiguration(config);
        }
        ros::spinOnce();
        loop_rate.sleep();
    }
}


int main(int argc, char ** argv) {
    ros::init(argc, argv, "adjuster");
    ros::NodeHandle n;

    thread t1(start_listener, n);
    thread t2(start_adjuster, n);

    t1.join();
    t2.join();
    return 0;
}

>Solution :

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

    while (batch_size < BATCH) {
    }

The apparent intent of this is to wait until another execution thread increments batch_size until it reaches BATCH.

However because this access to batch_size is not synchronized, at all, this results in undefined behavior.

Other execution threads synchronize modifications to batch_size using the mutex. This execution thread must do the same. Something like this:

int get_batch_size()
{
    m.lock();

    int s=batch_size;

    m.unlock();

    return s;
}

// ...

while (get_batch_size() < BATCH)
   ;

This is, of course, busy-polling and would be horribly inefficient. A condition variable should be employed, together with this mutex, for optimal performance and results.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading