Search code examples
c++multithreadinglambdacondition-variable

Is there a way to use std::condition_variable::wait(lock, pred) without a lambda?


I found this code online explaining how to use a std::condition_variable to solve the Producer-Consumer Problem: Producer-Consumer Problem Using Condition Variable in C++

#include <condition_variable> // std::condition_variale
#include <cstdlib>
#include <iostream>
#include <mutex>
#include <thread>
using namespace std;

std::mutex g_mutex;
std::condition_variable g_cv;

bool g_ready = false;
int g_data = 0;

int produceData() {
  int randomNumber = rand() % 1000;
  std::cout << "produce data: " << randomNumber << "\n";
  return randomNumber;
}

void consumeData(int data) { std::cout << "receive data: " << data << "\n"; }

void consumer() {
  int data = 0;
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_cv.wait(ul, []() { return g_ready; });
    consumeData(g_data);
    g_ready = false;
    ul.unlock();
    g_cv.notify_one();
  }
}

void producer() {
  while (true) {
    std::unique_lock<std::mutex> ul(g_mutex);
    g_data = produceData();
    g_ready = true;
    ul.unlock();
    g_cv.notify_one();
    ul.lock();
    g_cv.wait(ul, []() { return g_ready == false; });
  }
}

void consumerThread(int n) { consumer(); }

void producerThread(int n) { producer(); }

int main() {
  int times = 100;
  std::thread t1(consumerThread, times);
  std::thread t2(producerThread, times);
  t1.join();
  t2.join();
  return 0;
}

My code works, but my coding standard says I'm not allowed to use lambdas. How can I use this code without using lambdas in wait()?

I've tried the following, but it didn't work:

  1. use a normal bool
wait(g_ready)

Solution

  • As commenters have pointed out, disallowing lambdas is a dubious decision for a coding standard. Regardless, lambdas are just convenience syntax for creating types with a call operator, which you could do by hand.

    Solution with callable struct

    The lambda expression:

    []() { return g_ready; }
    

    roughly translates into

    struct g_ready_checker {
        bool operator()() const noexcept {
            return g_ready;
        }
    };
    

    You could then do the following:

    g_cv.wait(ul, g_ready_checker{});
    

    We can also turn g_ready_checker into a class template with a <bool DesiredValue> parameter, or we could store the value g_ready is meant to have as a class member.

    Solution with free function

    Or alternatively, since this lambda isn't capturing anything, you could also write a free function, possibly as a template:

    template <bool DesiredValue>
    bool is_g_ready() {
        return g_ready == DesiredValue;
    }
    
    /* ... */
    g_cv.wait(ul, &is_g_ready<true>);
    /* ... */
    g_cv.wait(ul, &is_g_ready<false>);
    

    Here, we provide wait with a predicate which is a function pointer. Note that the Predicate template parameter of std::condition_variable_wait is not restricted to lambdas, it accepts anything callable which returns something convertible bool.