c++visual-studiowaitsleepdata-distribution-service

What is the best way to to wait until event happen in c++?


I am using fastdds publisher, following code to publish data. getMatchedStatus() returns publication_matched callback status

if subscriber is matched, getMatchedStatus() = 1

if no matching subscriber, getMatchedStatus() = 0

if subscriber shutdown after reading data, getMatchedStatus() = -1

void publish(){
    while (getMatchedStatus() == 0) { **// waiting for a subscriber** what is the best way to replace this sleep? 
       std::this_thread::sleep_for(std::chrono::milliseconds(250));//250ms 
    }

    while (getMatchedStatus() == 1) { 

        writer_.write(&Topic);

        if (getMatchedStatus() == -1) { // Check subscriber unmatched status
            break;
        }
    } 
}

I need a proper way or best way to wait for a matching subscriber.....

is using std::this_thread::sleep_for(std::chrono::milliseconds(250));//250ms okay? for prodcution code?

the 1st while loop should wait without making the processor busy as well...


Solution

  • I can think of three solutions: Sleeps, Condition Variables, and WaitSets. Using sleeps, however, seems to me like the less efficient one (but probably the easiest to implement):

    Using sleep

    As a general rule: it is not advisable to use sleeps to wait events. By using sleeps, you lose the control on your thread, and you depend on a fixed value rather than waiting for a real event. This makes you awake more times than required, and do not awake when needed. However, if your application/library is not real-time oriented and does not require efficiency, it is always an option.

    Using C++ STL

    Using the class std::condition_variable under <condition_variable> will provide and easy and efficient way to wait on a publication match, and awake any other thread. There is an example in Fast-DDS repository that uses this exact method in order for the publisher to wait until a subscriber is matched: https://github.com/eProsima/Fast-DDS/tree/master/examples/C%2B%2B/DDS/BasicConfigurationExample . The solution will look like this:

    // Your publish method
    void publish(){
    
        // Wait in condition variable till predicate is true
        std::unique_lock<std::mutex> lck(wait_matched_cv_mtx_); // Internal DataWriter mutex
        wait_matched_cv_.wait(lck, [this] {
                    return matched(); // Implement matched method in DataWriter or any method similar
                });
    
        // At this point it is sure that DataWriter has matched
        while (getMatchedStatus() == 1) { 
            writer_.write(&Topic);
            if (getMatchedStatus() == -1) { // Check subscriber unmatched status
                break;
            }
        } 
    }
    
    // DataWriterListener overrided method (this should be implemented by the listener passed to the DataWriter when created.
    void HelloWorldPublisher::PubListener::on_publication_matched(
            eprosima::fastdds::dds::DataWriter*,
            const eprosima::fastdds::dds::PublicationMatchedStatus& info) {
        if (info.current_count_change == 1) {
                std::unique_lock<std::mutex> lck(wait_matched_cv_mtx_);
            publisher_condition_variable_.notify_all();
        }
    }
    

    NOTE: it is strongly recommended to use mutex and predicates when using condition variables. Check following post: predicate for condition variable

    Native WaitSets

    There is a recent feature in Fast-DDS called WaitSets that are used for this exact propose. This allows to wait for an event, as a message received or an entity match. What it actually does is wrap the std::condition_variable class so it could be used with more sugary methods. Check Fast-DDS documentation in order to learn how to use them: https://fast-dds.docs.eprosima.com/en/latest/fastdds/dds_layer/core/waitsets/waitsets.html .