dimanche 20 septembre 2020

Use of C++ condition variables with atomic predicate but no mutex

I have two threads. One thread acts as a timer thread which at regular intervals of time needs to send a notification to another thread. I intend to use C++ condition variables. (There is a good article on how to use C++ condition variables along with its traps and pitfalls in the following link -

https://ift.tt/2FRGDrJ

I have the following constraints/conditions :-

  1. The notifying thread need not lock on to a mutex
  2. The notified (or the receiver) thread does some useful section but there is no critical section
  3. The receiver thread is allowed to miss a notification if and only if it is doing useful work
  4. There should be no spurious wakeups.

Using the above link as a guideline I put together the following piece of code

// conditionVariableAtomic.cpp

#include <atomic>
#include <condition_variable>
#include <iostream>
#include <thread>
#include <iostream>       // std::cout, std::endl
#include <thread>         // std::this_thread::sleep_for
#include <chrono>         // std::chrono::seconds


std::mutex mutex_;
std::condition_variable condVar;

std::atomic<bool> dataReady{false};

void waitingForWork(){
    int i = 0;
    while (i++ < 10)
    {
        std::cout << "Waiting " << std::endl;
        {
            std::unique_lock<std::mutex> lck(mutex_);
            condVar.wait(lck, []{ return dataReady.load(); });   // (1)
            dataReady = false;
        }
        std::cout << "Running " << std::endl;
        // Do useful work but no critical section.
    }
}

void setDataReady(){
    int i = 0;
    while (i++ < 10)
    {
        std::this_thread::sleep_for (std::chrono::seconds(1));
        dataReady = true;
        std::cout << "Data prepared" << std::endl;
        condVar.notify_one();
    }
}

int main(){
    
  std::cout << std::endl;

  std::thread t1(waitingForWork);
  std::thread t2(setDataReady);

  t1.join();
  t2.join();
  
  std::cout << std::endl;
  
}

I use an atomic predicate to avoid spurious wakeups, but don't use a lock_guard in the notifying thread. My question is:

  1. does the above piece of code satisfy the constraints/conditions listed above?
  2. I understand that the receiver thread cannot avoid a mutex, hence the need to use "std::unique_lockstd::mutex lck(mutex_);" in the receiver. I have however limited the scope of "std::unique_lockstd::mutex lck(mutex_);" i.e. put the following section of code
            std::unique_lock<std::mutex> lck(mutex_);
            condVar.wait(lck, []{ return dataReady.load(); });   // (1)
            dataReady = false;
    
    inside a scope block aka { .... } so that the mutex is unlocked as soon as the wait condition is over (the receiver then does some useful work but since there is no critical section, it does not need to hold on to the mutex for the entire while loop). Could there still be consequences/side effects of this limited scoping in this context ? Or does the unique_lockstd::mutex need to be locked for the entire while loop

Aucun commentaire:

Enregistrer un commentaire