0

Could you please help me to implement a simple latch arrive_and_wait using std::condition_variable? My project uses C++17 and so I can not use C++20 std::latch. So, I am trying to implement a simple latch myself.

Here is the simplest example. The expected result is that "done 1", "done 2", and "done 3" should be printed simultaneously.

/******************************************************************************/
#include <atomic>
#include <condition_variable>
#include <limits>

#include <chrono>
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <future>
#include <thread>
/******************************************************************************/
class Latch
{
public:
 Latch(void) : expected(0) {}

 void count_up(void)
 {
     if(expected < std::numeric_limits<Count>::max())
         expected++;
     else
     {
         std::cerr<<"#Error! Latch::count_up: expected: "<<expected<<std::endl;
         exit(EXIT_FAILURE);
     }
 }
 /**************************************/

 void arrive_and_wait(void) // https://en.cppreference.com/w/cpp/thread/latch/arrive_and_wait
 {
     expected--;
     while(expected>0); // busy-loop waiting; how to implement it using std::condition_variable ?

     // cond.notify_all();
     // std::mutex mutex;
     // std::unique_lock lock(mutex);
     // cond.wait(lock, [&expected=expected]{return expected.load()==0;});
 }
 /**************************************/

private:
 using Count = uint_fast8_t;
 std::atomic<Count> expected;
 std::condition_variable cond;
};
/******************************************************************************/
void Run(const int delayInSeconds, Latch * const latch=nullptr)
{
 if(latch) latch->count_up();
 std::this_thread::sleep_for( std::chrono::seconds(delayInSeconds) );
 if(latch) latch->arrive_and_wait();
}
/******************************************************************************/
int main(void)
{
 std::cout<<"start...\n";

 Latch latch;
 auto f1 = std::async(Run, 2, &latch);
 auto f2 = std::async(Run, 4, &latch);
 auto f3 = std::async(Run, 8, &latch);

 f1.get(); std::cout<<"done 1\n";
 f2.get(); std::cout<<"done 2\n";
 f3.get(); std::cout<<"done 3\n";

 return EXIT_SUCCESS;
}
/******************************************************************************/

Thank you very much for your help!

S.V
  • 2,149
  • 2
  • 18
  • 41
  • 1
    You already made almost correct code, the only issue is that `mutex` should be class member variable. BTW, you check for `expected < std::numeric_limits::max()` doesn't have any sense. Even if you can reach limit you can't be sure that value during comparison will be same during increment. – sklott Feb 01 '23 at 18:32
  • @sklott You are right, this is exactly the fix that makes the code to work. Thank you! – S.V Feb 01 '23 at 18:37
  • 1
    Note that with a condition variable, you need to change `expected` under the locked mutex. Otherwise, you may end up with a race condition. `expected` then even doesn't need to be an atomic variable. – Daniel Langr Feb 01 '23 at 19:19

0 Answers0