progschj / ThreadPool

A simple C++11 Thread Pool implementation
zlib License
7.64k stars 2.21k forks source link

notify_one in ThreadPool::enqueue - valgrind error #34

Open mgerhardy opened 8 years ago

mgerhardy commented 8 years ago

valgrind drd error

==3727== Probably a race condition: condition variable 0x6127e30 has been signaled but the associated mutex 0x6127e08 is not locked by the signalling thread. ==3727== at 0x4C37B95: pthread_cond_signal@* (in /usr/lib/valgrind/vgpreload_drd-amd64-linux.so) ==3727== by 0x534DCF8: std::condition_variable::notify_one() (in /usr/lib/x86_64-linux-gnu/libstdc++.so.6.0.21)

yinchunxiang commented 8 years ago
// add new work item to the pool
template<class F, class... Args>
auto ThreadPool::enqueue(F&& f, Args&&... args) 
    -> std::future<typename std::result_of<F(Args...)>::type>
{
    using return_type = typename std::result_of<F(Args...)>::type;

    auto task = std::make_shared< std::packaged_task<return_type()> >(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );

    std::future<return_type> res = task->get_future();
    {
        std::unique_lock<std::mutex> lock(queue_mutex);

        // don't allow enqueueing after stopping the pool
        if(stop)
            throw std::runtime_error("enqueue on stopped ThreadPool");

        tasks.emplace([task](){ (*task)(); });
    }
**    condition.notify_one(); ///----> Should this line be in the lock scope ?**
    return res;
}

// the destructor joins all threads
inline ThreadPool::~ThreadPool()
{
    {
        std::unique_lock<std::mutex> lock(queue_mutex);
        stop = true;
    }
**    condition.notify_all(); ///----> Should this line be in the lock scope ?**
    for(std::thread &worker: workers)
        worker.join();
}
```cpp
foobar commented 8 years ago

condition.notify_one(); ///----> Should this line be in the lock scope ?

would it cause a deadlock ?

mnowotnik commented 8 years ago

Not deadlock, but the thread could never wake up.

lazylazypig commented 7 years ago

@mgerhardy excuse me, what's the final solution?

wilx commented 7 years ago

I think that in general it is a good idea to notify inside the locked region because you can miss wakeups otherwise. But this code is not edge triggered but level triggered, I think. In this particular case, I do not think it is possible to miss a wake up.

ilyapopov commented 7 years ago

This SO question is relevant: http://stackoverflow.com/questions/17101922/do-i-have-to-acquire-lock-before-calling-condition-variable-notify-one/

TL;DR: No, you don't need to hold a lock while doing notify, but there can be performance implications.