Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

condition_variable throwing system_error with Xcode - fine with VStudio

I'm getting an error every time I try waiting on my condition_variable when compiling in Xcode. Error is "terminating with uncaught exception of type std::__1::system_error: condition_variable wait failed: Invalid argument"

Everything works fine in Visual Studio 2013. Code works if I decide to not wait on the same condition_variable from multiple threads. Grrrr.

Ok, code.

main.cpp:

#include "ThreadPool.h"

int main(int argc, const char * argv[])
{
    ThreadPool pool;
    for (int i = 0; i < 10; ++i)
        pool.sendWork();

    std::this_thread::sleep_for(std::chrono::milliseconds(50000));
    return 0;
}

ThreadPool.h

#pragma once
#include <condition_variable>
#include <vector>
#include <thread>

class ThreadPool
{
protected:
    std::condition_variable     _condition;

private:
    std::vector<std::thread>    _threads;
    void threadLoop();

public:
    ThreadPool();
    void sendWork();
};

ThreadPool.cpp:

#include "ThreadPool.h"

ThreadPool::ThreadPool()
{
    for (unsigned int i {0}; i < 10; ++i)
        _threads.push_back(std::thread(&ThreadPool::threadLoop, this));
}
void ThreadPool::threadLoop()
{
    std::mutex mutex;
    std::unique_lock<std::mutex> lock {mutex};
    _condition.wait(lock);            // This is where the crash happens
}

void ThreadPool::sendWork()
{
    _condition.notify_one();
}

This is a major simplification of the real code, but it's enough to trigger the crash.

Is this supposed to work? Am I missing something here?

like image 580
Michael Gazonda Avatar asked Jul 20 '14 03:07

Michael Gazonda


2 Answers

To be more precise, the Requires section of the spec of condition_variable::wait() is (N3936 §30.5.1 [thread.condition.condvar]/p9):

void wait(unique_lock<mutex>& lock);

Requires: lock.owns_lock() is true and lock.mutex() is locked by the calling thread, and either

— no other thread is waiting on this condition_variable object or

lock.mutex() returns the same value for each of the lock arguments supplied by all concurrently waiting (via wait, wait_for, or wait_until) threads.

Thus, every thread concurrently waiting on a condition_variable must pass in the same mutex. It is technically allowed for two threads to wait on a condition_variable using one mutex, and then after both wait terminates (and no thread is waiting on the condition_variable), for them to both wait on the same object using a different mutex. I do not know whether there are any realistic use cases for this.

Note also that std::condition_variable_any::wait() does not have such a requirement. The only requirement on the type supplied is that it meets BasicLockable requirements, which basically means that it has lock() and unlock().

like image 133
T.C. Avatar answered Oct 27 '22 02:10

T.C.


Your mutex in ThreadPool::threadLoop is a local variable. That means that different threads are locking on different mutexes. This is invalid: condition variables are tied to a specific mutex and you must acquire a lock for that mutex before waiting on the condition.

You should declare a _mutex (or, even better, use the mutex_ naming convention: underscore-prefix identifiers are mostly reserved) member variable in your ThreadPool class, and always lock on that.

like image 27
Chris Jester-Young Avatar answered Oct 27 '22 01:10

Chris Jester-Young