Producer-Consumer: Lost Wake-up issue - c++

I was trying to write code for Producer-Consumer problem. Below code works fine most of the time but stuck sometimes because of "Lost Wake-up" (i guess). I tried thread sleep() but it didn't work. What modification is needed to handle this case in my code? Is semaphore can be helpful here ? If yes, how will i implement them here ?
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <iostream>
using namespace std;
int product = 0;
boost::mutex mutex;
boost::condition_variable cv;
boost::condition_variable pv;
bool done = false;
void consumer(){
while(done==false){
//cout << "start c" << endl
boost::mutex::scoped_lock lock(mutex);
cv.wait(lock);
//cout << "wakeup c" << endl;
if (done==false)
{
cout << product << endl;
//cout << "notify c" << endl;
pv.notify_one();
}
//cout << "end c" << endl;
}
}
void producer(){
for(int i=0;i<10;i++){
//cout << "start p" << endl;
boost::mutex::scoped_lock lock(mutex);
boost::this_thread::sleep(boost::posix_time::microseconds(50000));
++product;
//cout << "notify p" << endl;
cv.notify_one();
pv.wait(lock);
//cout << "wakeup p" << endl;
}
//cout << "end p" << endl;
cv.notify_one();
done = true;
}
int main()
{
int t = 1000;
while(t--){
/*
This is not perfect, and is prone to a subtle issue called the lost wakeup (for example, producer calls notify()
on the condition, but client hasn't really called wait() yet, then both will wait() indefinitely.)
*/
boost::thread consumerThread(&consumer);
boost::thread producerThread(&producer);
producerThread.join();
consumerThread.join();
done =false;
//cout << "process end" << endl;
}
cout << "done" << endl;
getchar();
return 0;
}

Yes, you want a way to know (in the consumer) that you "missed" a signal. A semaphore can help. There's more than one way to skin a cat, so here's my simple take on it (using just c++11 standard library features):
class semaphore
{
private:
std::mutex mtx;
std::condition_variable cv;
int count;
public:
semaphore(int count_ = 0) : count(count_) { }
void notify()
{
std::unique_lock<std::mutex> lck(mtx);
++count;
cv.notify_one();
}
void wait() { return wait([]{}); } // no-op action
template <typename F>
auto wait(F&& func = []{}) -> decltype(std::declval<F>()())
{
std::unique_lock<std::mutex> lck(mtx);
while(count == 0){
cv.wait(lck);
}
count--;
return func();
}
};
For convenience, I added a convenience wait() overload that takes a function to be executed under the lock. This makes it possible for the consumer to operate the 'semaphore' without ever manually operating the lock (and still get the value of product without data-races):
semaphore sem;
void consumer() {
do {
bool stop = false;
int received_product = sem.wait([&stop] { stop = done; return product; });
if (stop)
break;
std::cout << received_product << std::endl;
std::unique_lock<std::mutex> lock(processed_mutex);
processed_signal.notify_one();
} while(true);
}
A fully working demo: Live on Coliru:
#include <condition_variable>
#include <iostream>
#include <mutex>
#include <thread>
#include <cassert>
class semaphore
{
private:
std::mutex mtx;
std::condition_variable cv;
int count;
public:
semaphore(int count_ = 0) : count(count_) { }
void notify()
{
std::unique_lock<std::mutex> lck(mtx);
++count;
cv.notify_one();
}
void wait() { return wait([]{}); } // no-op action
template <typename F>
auto wait(F&& func = []{}) -> decltype(std::declval<F>()())
{
std::unique_lock<std::mutex> lck(mtx);
while(count == 0){
cv.wait(lck);
}
count--;
return func();
}
};
semaphore sem;
int product = 0;
std::mutex processed_mutex;
std::condition_variable processed_signal;
bool done = false;
void consumer(int check) {
do {
bool stop = false;
int received_product = sem.wait([&stop] { stop = done; return product; });
if (stop)
break;
std::cout << received_product << std::endl;
assert(++check == received_product);
std::unique_lock<std::mutex> lock(processed_mutex);
processed_signal.notify_one();
} while(true);
}
void producer() {
std::unique_lock<std::mutex> lock(processed_mutex);
for(int i = 0; i < 10; ++i) {
++product;
sem.notify();
processed_signal.wait(lock);
}
done = true;
sem.notify();
}
int main() {
int t = 1000;
while(t--) {
std::thread consumerThread(&consumer, product);
std::thread producerThread(&producer);
producerThread.join();
consumerThread.join();
done = false;
std::cout << "process end" << std::endl;
}
std::cout << "done" << std::endl;
}

You seems to ignore that the variable done is also a shared state, to the same extend as product. Which can lead to several races conditions. In your case, I see at least one scenario where consumerThread make no progress:
The loop execute has intended
consumer executes, and is waiting at cv.wait(lock);
producer has finished the for loop, and notify consumer and is preempted
consumer wakes up, read "done==false", output product, read done == false again, wait on the condition
producer set done to true and exit
consumer is stuck forever
To avoid these kind of issues you should be holding a lock when reading or writing done. Btw your implementation is quite sequential, ie the producer and the consumer can only process a single piece of data at the time...

Related

c++: condition variable ownership

I am facing an issue while performing thread synchronisation.
I have a class very similar to the ThreadQueue implementation proposed in this answer, which I'll briefly report here for completeness:
#include <mutex>
#include <queue>
#include <condition_variable>
template <typename T>
class ThreadQueue {
std::queue<T> q_;
std::mutex mtx;
std::condition_variable cv;
public:
void enqueue (const T& t) {
{
std::lock_guard<std::mutex> lck(mtx);
q_.push(t);
}
cv.notify_one();
}
T dequeue () {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, [this] { return !q_.empty(); });
T t = q_.front();
q_.pop();
return t;
}
};
I have a consumer that continuously extracts the first available item of a shared instance of that class, say ThreadQueue<int> my_queue;, until it receives a signal to quit, for instance:
std::atomic_bool quit(false);
void worker(){
std::cout << "[worker] starting..." << std::endl;
while(!quit.load()) {
std::cout << "[worker] extract element from the queue" << std::endl;
auto el = my_queue.dequeue();
std::cout << "[worker] consume extracted element" << std::endl;
std::cout << el << std::endl;
}
std::cout << "[worker] exiting" << std::endl;
}
Suppose the program has to terminate (for any reason) before any producer can insert elements in the queue; in this case the worker would be stuck on the line auto el = my_queue.dequeue(); and cannot terminate.
An exemple of this case is the following:
int main() {
std::thread t(worker);
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "[main] terminating..." << std::endl;
quit.store(true);
t.join();
std::cout << "[main] terminated!" << std::endl;
return 0;
}
Clearly, the worker can be "unlocked" by pushing a dummy element in the queue, but it does not seem an elegant solution.
I am thus wondering whether the thread syncronisation on the empty queue should be taken out of the ThreadQueue class and done inside the worker instead, i.e. moving the "ownership" of the condition variable outside the ThreadQueue container.
In general, is a class such as ThreadQueue always a bad design?
In case it's not, is there any solution that allows to keep the condition variable encapsulated in ThreadQueue, hence removing the responsibility of thread syncronisation from the users of that class (bearing in mind I am limited to usage of C++11)?
Full MWE here
The object that contains the mutex should also own the condition variable. So the ThreadQueue code looks good. But it is unclear what dequeue() should return when an asynchronous stop is requested.
A common way to solve this is to introduce either a quit flag or a sentinel value to the queue itself, a stop() method and a way for dequeue() to signal a closed queue, for example, using std::optional<T> as return value.
template <typename T>
class ThreadQueue {
std::queue<T> q_;
std::mutex mtx;
std::condition_variable cv;
bool quit = false;
public:
void enqueue (const T& t) {
{
std::lock_guard<std::mutex> lck(mtx);
q_.push(t);
}
cv.notify_one();
}
std::optional<T> dequeue () {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, [this] { return quit || !q_.empty(); });
if (quit) {
return {};
}
T t = q_.front();
q_.pop();
return t;
}
void stop() {
std::unique_lock<std::mutex> lck(mtx);
quit = true;
cv.notify_all();
}
};
Then when dequeue() returns an empty optional, the worker can exit gracefully.
void worker() {
std::cout << "[worker] starting..." << std::endl;
while (true) {
std::cout << "[worker] extract element from the queue" << std::endl;
auto el = my_queue.dequeue();
if (!el) {
std::cout << "[worker] exiting" << std::endl;
break;
}
std::cout << "[worker] consume extracted element" << std::endl;
std::cout << *el << std::endl;
}
std::cout << "[worker] exiting" << std::endl;
}
int main() {
std::thread t(worker);
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "[main] terminating..." << std::endl;
my_queue.stop();
t.join();
std::cout << "[main] terminated!" << std::endl;
return 0;
}
This is a quick hacky mod to your class to add stop function:
template <typename T>
class ThreadQueue {
std::queue<T> q_;
std::mutex mtx;
std::condition_variable cv;
std::atomic<bool> running = true;
public:
void enqueue (const T& t) {
{
std::lock_guard<std::mutex> lck(mtx);
q_.push(t);
}
cv.notify_one();
}
T dequeue () {
std::unique_lock<std::mutex> lck(mtx);
cv.wait(lck, [this] { return !q_.empty() || !running; });
if (!running){return {};} // tidy-up part 1
T t = q_.front();
q_.pop();
return t;
}
bool is_running()
{
return running;
}
void stop()
{
running = false;
cv.notify_all(); // tidy-up part 2
}
};
see live example: https://godbolt.org/z/bje6Gj7o4
Obviously needs tidying up as you require

Why 'break' in nested condition block creates 1 more iteration in while loop in C++?

I'm testing a multi-threading code piece. I'm trying to wrap C++ thread as a new class to bind with given function with additional parameters to run it under certain frequency and optionally repeat or not.
The weird thing I met is that 1 statement in while loop seems to be run after it exited the while loop.
// timer_main.cc
#include <functional>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <unordered_map>
#include <iostream>
using namespace std;
class TimerThread
{
public:
explicit TimerThread(const function<void()>& task): repeat_(false), running_(false), task_(task) {}
~TimerThread() { StopTimer(); }
public:
bool StartTimer(uint64_t interval_ms, bool repeat = true) {
if (thread_.get_id() == this_thread::get_id()) return false;
if (running_) return false;
repeat_ = repeat;
running_ = true;
thread_ = thread([this, interval_ms]() {
while (running_)
{
{
unique_lock<mutex> lock(mutex_);
cv_timer_.wait_for(lock, chrono::milliseconds(interval_ms), [this] {
return !running_;
});
}
if (!running_)
{
return;
}
task_();
if (!repeat_) {
return;
}
} // end while
}); // end thread_
return true;
}
bool StopTimer() {
cout << "StopTimer!" << endl;
if (thread_.get_id() == this_thread::get_id()) {
cout << "same thread id, return false"; return false;
}
running_ = false;
cv_timer_.notify_one();
cout << "waiting for timer thread to join..." << endl;
if (thread_.joinable()) {
thread_.join();
} else {
cout << "timer thread is not joinable" << endl;
}
cout << "timer stopped. this thread id: " << this_thread::get_id() << endl;
return true;
}
private:
atomic<bool> repeat_;
atomic<bool> running_;
function<void()> task_;
mutex mutex_;
condition_variable cv_timer_;
thread thread_;
};
int main(int argc, char* argv[])
{
shared_ptr<TimerThread> tt0 = make_shared<TimerThread>([&]{ cout << "hello" << endl; });
tt0->StartTimer(1000, true);
while (true) {
this_thread::sleep_for(chrono::milliseconds(2000));
tt0->StopTimer();
break;
}
cout << "exit while loop" << endl;
return 0;
}
My expected output is
❯ ./timer_main
hello
hello
StopTimer!
waiting for timer thread to join...
timer stopped. this thread id: 0x113d04600
exit while loop
However, I got following
❯ ./timer_main
hello
StopTimer!
waiting for timer thread to join...
hello
timer stopped. this thread id: 0x113d04600
exit while loop
StopTimer!
waiting for timer thread to join...
timer thread is not joinable
timer stopped. this thread id: 0x113d04600
According to the output, the StopTimer was called 2 times, and the 2nd time is outside the while loop..., I don't know why.
Any help would be highly appreciated, thanks!

c++ multithreading: condition variable

I am new to multithreading. Here is what I want
thread_function(){
// do job1;
//wait main thread to notify;
// do job2;
}
main(){
//create two threads
//wait both threads to finish job1
//finish job3, then let both threads start job2
//wait both threads to join
}
What is the best way to do this? Thanks.
Here is my code
void job1(){
}
void job2(){
}
void job3(){
}
int main(){
thread t11(job1);
thread t12(job1);
t11.join();
t12.join();
job3();
thread t21(job2);
thread t22(job2);
t21.join();
t22.join();
}
My question is whether I can combine job1 and job2 to one function, and use condition variable to control the order?
I will give you a sample (something similar to producer-consumer problem)
This is not the exact solution you are looking for, but below code will guide you,
Below "q" is protected by mutex, on which the condition variable waits for it to get notified or the !q.empty(needed for spurious wakeups) or time-out.
std::condition_variable cond;
std::deque<int> q;
std::mutex mu;
void function_1() {
int count = 50;
while (count > 0)
{
// Condition variables when used lock should be unique_lock
// lock the resource
std::unique_lock<mutex> locker(mu);
// defer the lock until further
//std::unique_lock<mutex> locker(mu, std::defer_lock);
q.push_front(count);
locker.unlock();
//cond.notify_one();
cond.notify_all();
//std::this_thread::sleep_for(chrono::seconds(1));
count--;
}
}
void function_2(int x,int y) {
int data = 0;
while (data != 1)
{
// mu is the common mutex this resource is protected for the q.
std::unique_lock<mutex> locker(mu);
// this will only be done when !q.empty()
// This will make sure it is handled by multiple threads
auto now = std::chrono::system_clock::now();
if (cond.wait_until(locker, now + y * 100ms, []() { return !q.empty(); }))
{
auto nowx = std::chrono::system_clock::now();
cout << "Thread " << x << "waited for " << (nowx-now).count() << endl;
}
else
{
cout << "Timed out " << endl;
break;
}
data = q.back();
q.pop_back();
locker.unlock();
cout << x << " got value from t1 " << data << endl;
}
}
int main()
{
std::thread t1(function_1);
std::thread t2(function_2,1,50);
std::thread t3(function_2,2,60);
std::thread t4(function_2,3,100);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}

Condition Variable notify_one notify_all

I'm trying to learn the condition variables, and I'm stuck at the following example. I thought that notify_one on consumers should unlock only one waiting consumer. But after starting it repeatedly it seems to me that this isn't the case. I've changed notify_one into notify_all and haven't noticed a change in behavior. After the producer calls notify_one on consumers I can see Get… being written on screen by more then one consumer.
Why is this happening?
#include <iostream> // std::cout
#include <thread> // std::thread
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
#include <chrono>
std::mutex mtx;
std::condition_variable produce,consume;
int cargo = 0; // shared value by producers and consumers
void consumer () {
std::unique_lock<std::mutex> lck(mtx);
while (cargo==0) consume.wait(lck);
std::cout << "Get" << cargo << " "<< std::this_thread::get_id() << '\n';
cargo--;
produce.notify_one();
}
void producer (int id) {
std::unique_lock<std::mutex> lck(mtx);
while (cargo!=0) produce.wait(lck);
std::cout << "Push" << id << " "<< std::this_thread::get_id() << '\n';
cargo += id;
consume.notify_one();
}
void c () {
while(1) {
consumer();
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
void p(int n) {
while(1) {
producer(n);
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}
}
int main ()
{
std::thread consumers[5],producers[5];
for (int i=0; i<5; ++i) {
consumers[i] = std::thread(c);
producers[i] = std::thread(p,i+1);
}
for (int i=0; i<5; ++i) {
producers[i].join();
consumers[i].join();
}
return 0;
}

Understanding the example of using std::condition_variable

There is example of using condition_variable taken from cppreference.com:
#include <condition_variable>
#include <mutex>
#include <thread>
#include <iostream>
#include <queue>
#include <chrono>
int main()
{
std::queue<int> produced_nums;
std::mutex m;
std::condition_variable cond_var;
bool done = false;
bool notified = false;
std::thread producer([&]() {
for (int i = 0; i < 5; ++i) {
std::this_thread::sleep_for(std::chrono::seconds(1));
std::lock_guard<std::mutex> lock(m);
std::cout << "producing " << i << '\n';
produced_nums.push(i);
notified = true;
cond_var.notify_one();
}
std::lock_guard<std::mutex> lock(m);
notified = true;
done = true;
cond_var.notify_one();
});
std::thread consumer([&]() {
while (!done) {
std::unique_lock<std::mutex> lock(m);
while (!notified) { // loop to avoid spurious wakeups
cond_var.wait(lock);
}
while (!produced_nums.empty()) {
std::cout << "consuming " << produced_nums.front() << '\n';
produced_nums.pop();
}
notified = false;
}
});
producer.join();
consumer.join();
}
If variable done comes true before the consumer thread is started, the consumer thread will not get any message. Indeed, sleep_for(seconds(1)) almost avoids such situation, but could it be possible in theory (or if don't have sleep in code)?
In my opinion correct version should look like this to force running consumer loop at least once:
std::thread consumer([&]() {
std::unique_lock<std::mutex> lock(m);
do {
while (!notified || !done) { // loop to avoid spurious wakeups
cond_var.wait(lock);
}
while (!produced_nums.empty()) {
std::cout << "consuming " << produced_nums.front() << '\n';
produced_nums.pop();
}
notified = false;
} while (!done);
});
Yes, you are absolutely right: there is a (remote) possibility that the consumer thread will not start running until after done has been set. Further, the write to done in the producer thread and the read in the consumer thread produce a race condition, and the behavior is undefined. Same problem in your version. Wrap the mutex around the entire loop in each function. Sorry, don't have the energy to write the correct code.