Helgrind сообщает об ошибках синхронизации в программе simple boost::asio::thread_pool

#c #boost #threadpool #valgrind

Вопрос:

Я экспериментирую с boost::asio::thread_pool, и helgrind сообщает об ошибках в простой программе с пустой функцией задачи. В чем проблема и как я могу ее решить?

 
#include <boost/thread/mutex.hpp>
#include <boost/thread.hpp>
#include <boost/asio/thread_pool.hpp>
#include <boost/asio/post.hpp>


int main() {
    ushort thread_num = 4;
    boost::asio::thread_pool pool(thread_num);

    auto task = []() {};

    for (ushort i = 0; i < thread_num;   i)
        boost::asio::post(pool, task);

    pool.join();

    return 0;
}
 

Вот вывод helgrind:

 ==266706== Thread #1 is the program's root thread
==266706== 
==266706== ----------------------------------------------------------------
==266706== 
==266706== Thread #1: pthread_cond_{signal,broadcast}: dubious: associated lock is not held by any thread
==266706==    at 0x48405D6: ??? (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_helgrind-amd64-linux.so)
==266706==    by 0x11508D: bool boost::asio::detail::posix_event::maybe_unlock_and_signal_one<boost::asio::detail::conditionally_enabled_mutex::scoped_lock>(boost::asio::detail::conditionally_enabled_mutex::scoped_lockamp;) (in /home/arno/Programming/test/a.out)
==266706==    by 0x111AAD: boost::asio::detail::conditionally_enabled_event::maybe_unlock_and_signal_one(boost::asio::detail::conditionally_enabled_mutex::scoped_lockamp;) (in /home/arno/Programming/test/a.out)
==266706==    by 0x11361E: boost::asio::detail::scheduler::wake_one_thread_and_unlock(boost::asio::detail::conditionally_enabled_mutex::scoped_lockamp;) (in /home/arno/Programming/test/a.out)
==266706==    by 0x1132C4: boost::asio::detail::scheduler::post_immediate_completion(boost::asio::detail::scheduler_operation*, bool) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DD99: void boost::asio::thread_pool::executor_type::post<boost::asio::detail::work_dispatcher<main::{lambda()#1}>, std::allocator<void> >(boost::asio::detail::work_dispatcher<main::{lambda()#1}>amp;amp;, std::allocator<void> constamp;) const (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DBFB: void boost::asio::detail::initiate_post::operator()<main::{lambda()#1}amp;, boost::asio::thread_pool::executor_type constamp;>(main::{lambda()#1}amp;, boost::asio::thread_pool::executor_type constamp;) const (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DB82: void boost::asio::async_result<main::{lambda()#1}, void ()>::initiate<boost::asio::detail::initiate_post, {lambda()#1}amp;, boost::asio::thread_pool::executor_type constamp;>(boost::asio::detail::initiate_postamp;amp;, {lambda()#1}amp;, boost::asio::thread_pool::executor_type constamp;) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DB54: std::enable_if<void ()::async_result_has_initiate_memfn<main::{lambda()#1}amp;, void ()>::value, boost::asio::async_result<std::decay<void ()::async_result_has_initiate_memfn>::type, main::{lambda()#1}amp;>::return_type>::type boost::asio::async_initiate<main::{lambda()#1}amp;, void (), boost::asio::detail::initiate_post, boost::asio::thread_pool::executor_type constamp;>(boost::asio::detail::initiate_postamp;amp;, void (amp;)()::async_result_has_initiate_memfn, boost::asio::thread_pool::executor_type constamp;) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DB12: boost::asio::async_result<std::decay<main::{lambda()#1}amp;>::type, void ()>::return_type boost::asio::post<boost::asio::thread_pool::executor_type, main::{lambda()#1}amp;>(boost::asio::thread_pool::executor_type constamp;, std::decayamp;amp;, std::enable_if<boost::asio::is_executor<boost::asio::async_result<std::decay<main::{lambda()#1}amp;>::type, void ()>::return_type>::value, void>::type*) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DABD: boost::asio::async_result<std::decay<main::{lambda()#1}amp;>::type, void ()>::return_type boost::asio::post<boost::asio::thread_pool, main::{lambda()#1}amp;>(boost::asio::thread_poolamp;, std::decayamp;amp;, std::enable_if<std::is_convertible<boost::asio::thread_pool, boost::asio::execution_contextamp;>::value, void>::type*) (in /home/arno/Programming/test/a.out)
==266706==    by 0x10DA11: main (in /home/arno/Programming/test/a.out)
 

Комментарии:

1. Где он сообщает о гонке данных? Существует только одно «сомнительное» диагностическое сообщение, которое даже не указывает на какую-либо гонку данных.

2. @Ext3h Вы правы, отредактировано.

Ответ №1:

https://linux.die.net/man/3/pthread_cond_signal

Функции pthread_cond_broadcast() или pthread_cond_signal() могут вызываться потоком независимо от того, владеет ли он в настоящее время мьютексом, который потоки, вызывающие pthread_cond_wait() или pthread_cond_timedwait (), связали с переменной условия во время ожидания; однако, если требуется предсказуемое поведение планирования, то этот мьютекс должен быть заблокирован потоком, вызывающим pthread_cond_broadcast() или pthread_cond_signal().

Это просто диагностическое предупреждение, указывающее на распространенную причину неэффективного планирования вокруг переменных состояния. Вы получаете ложные пробуждения, если вы (как производитель) не держите мьютекс заблокированным во время сигнализации. Например, возможно, что ваше обновление переменной CV было обработано, как только вы разблокировали мьютекс, поэтому ваш сигнал вызывает еще одно ненужное пробуждение без какого-либо изменения состояния.

Сохранение мьютекса заблокированным для всего обновления переменной CV, а также сигнализация, напротив, обрабатываются эффективно, так как потребитель зарегистрировался попарно как на мьютексе, так и на CV, и сигнализация CV напрямую помещает потребителя в список ожидания для мьютекса без активации потока даже один раз.

Однако это просто неэффективно, а не логическая ошибка. И Хелгринд сообщил об этом только как о «сомнительном», а не как об ошибке.

Комментарии:

1. Вы имеете в виду, что это происходит внутри библиотеки boost и мой код правильный?

2. Да, предупреждение запускается библиотекой boost.