0

I've a problem with some code with the same logic of the following one:

#include <mutex>
#include <condition_variable>
#include <thread>
#include <iostream>

int main() {
  std::condition_variable myCV;
  std::mutex myMutex;
  std::unique_lock myLock(myMutex);
  bool isRunning{ true };

  std::jthread myThread([&isRunning, &myCV, &myLock]() {
    while (isRunning) {
      myCV.wait(myLock);
      // do some work here
    }
    std::cout << "Exited from myThread while loop." << std::endl;
    });

  for (int i = 0; i < 100; i++) {
    myCV.notify_one();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    std::cout << "Test loop " << i << std::endl;
  }

  isRunning = false;
  myCV.notify_one();

  return 0;
}

Basically, I've a loop where i do some work, and when my work is done at every iteration I call a method in another thread for handling the data without blocking the main thread; in order to avoid to create and destruct the thread every time, I'm using a loop inside that thread with a std::condition_variable for starting to manage the data when they are ready.

The logic works well but I've a problem when exiting the program; if I run the code I've an error when the program ends, because the mutex destructor calls abort():

...\stl\src\mutex.cpp(164): unlock of unowned mutex

If I comment the following row myCV.notify_one(); the program continues to run without stopping.

How can I exit the program gracefully without any error? What I'm doing wrong?

2
  • 1
    Your jthread routine is calling myCV.wait(myLock) where myLock was acquired on a different thread. Hence the error message you see when the condition variable attempts to unlock the mutex. I think. Commented Oct 10, 2023 at 14:39
  • 2
    It is undefined behavior when the thread unlocking the mutex is not the one that acquired it. Waiting on a condition variable unlocks the mutex Commented Oct 10, 2023 at 14:45

1 Answer 1

0

Thanks for your comments. The problem was in fact the mutex that was unlocked in a thread different from the one that acquired it. Moving the lock definition inside the thread code solved the problem:

#include <mutex>
#include <condition_variable>
#include <thread>
#include <iostream>

int main() {
  std::condition_variable myCV;
  std::mutex myMutex;
  bool isRunning{ true };

  std::jthread myThread([&isRunning, &myCV, &myMutex]() {
    std::unique_lock myLock(myMutex);
    while (isRunning) {
      myCV.wait(myLock);
      // do some work here
    }
    std::cout << "Exited from myThread while loop." << std::endl;
    });

  for (int i = 0; i < 100; i++) {
    myCV.notify_one();
    std::this_thread::sleep_for(std::chrono::milliseconds(20));
    std::cout << "Test loop " << i << std::endl;
  }

  isRunning = false;
  myCV.notify_one();

  return 0;
}
Sign up to request clarification or add additional context in comments.

4 Comments

I think there is a race condition there. The following order is possible: 1. The thread wakes up, checks isRunning, gets true. 2. Main thread sets isRunning = false, sends notify_one(). Nobody is waiting, so nothing happens. 3. Thread enters wait, goes to sleep indefinitely. Solution: Your main thread needs to acquire the mutex before changing isRunning or you need to switch to std::atomic<bool>::wait
Also, I hope you're aware that condition variables do not work as queues. Just because you notify 100 times does not mean the thread will be woken up 100 times. It could be more (spurious wakeups) or fewer times (if the thread is not currently waiting). The sleep time makes the second option very unlikely but not impossible, e.g. if the system is under heavy load of concurrent threads
Thanks, I'll try... What should I use instead of condition_variable, then?
condition_variable is fine for this but the details depend on the "do some work here" part that you omitted. Typically you combine it with a work queue or buffer and then have a condition such as while(queue.empty() && isRunning) myCV.wait().

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.