Boost asio http async_client warning example with helgrind: False positive?

Running the http async_client example of the official boos asio code example in helgrind shows a warning. This code from the file boost/asio/detail/posix_event.hpp

appears to be the cause of the warning:

// Signal the event and unlock the mutex.
template <typename Lock>
void signal_and_unlock(Lock& lock)
{
    BOOST_ASSERT(lock.locked());
    signalled_ = true;
    lock.unlock();
    ::pthread_cond_signal(&cond_); // Ignore EINVAL.
}

      

Here is the complete output of valgrind / helgrind:

jcm@Ubuntu:~/samples/async-client/build$ valgrind --tool=helgrind ./async-client stackoverflow.com error.html
==2894== Helgrind, a thread error detector
==2894== Copyright (C) 2007-2011, and GNU GPL'd, by OpenWorks LLP et al.
==2894== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==2894== Command: ./async-client stackoverflow.com error.html
==2894== 
==2894== ---Thread-Announcement------------------------------------------
==2894== 
==2894== Thread #1 is the program root thread
==2894== 
==2894== ----------------------------------------------------------------
==2894== 
==2894== Thread #1: pthread_cond_{signal,broadcast}: dubious: associated lock is not held by any thread
==2894==    at 0x4C2C978: ??? (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==2894==    by 0x4C2E5EA: pthread_cond_signal@* (in /usr/lib/valgrind/vgpreload_helgrind-amd64-linux.so)
==2894==    by 0x43FCB2: void boost::asio::detail::posix_event::signal_and_unlock<boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex> >(boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>&) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x43B659: boost::asio::detail::task_io_service::wake_one_idle_thread_and_unlock(boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>&) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x43B68A: boost::asio::detail::task_io_service::wake_one_thread_and_unlock(boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>&) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x43B0B8: boost::asio::detail::task_io_service::post_immediate_completion(boost::asio::detail::task_io_service_operation*) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x43DBFD: boost::asio::detail::resolver_service_base::start_resolve_op(boost::asio::detail::task_io_service_operation*) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x442F86: void boost::asio::detail::resolver_service<boost::asio::ip::tcp>::async_resolve<boost::_bi::bind_t<void, boost::_mfi::mf2<void, client, boost::system::error_code const&, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp> >, boost::_bi::list3<boost::_bi::value<client*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >(std::shared_ptr<void>&, boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::_bi::bind_t<void, boost::_mfi::mf2<void, client, boost::system::error_code const&, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp> >, boost::_bi::list3<boost::_bi::value<client*>, boost::arg<1> (*)(), boost::arg<2> (*)()> >) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x44187E: void boost::asio::ip::resolver_service<boost::asio::ip::tcp>::async_resolve<boost::_bi::bind_t<void, boost::_mfi::mf2<void, client, boost::system::error_code const&, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp> >, boost::_bi::list3<boost::_bi::value<client*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >(std::shared_ptr<void>&, boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::_bi::bind_t<void, boost::_mfi::mf2<void, client, boost::system::error_code const&, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp> >, boost::_bi::list3<boost::_bi::value<client*>, boost::arg<1> (*)(), boost::arg<2> (*)()> >&&) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x44097E: void boost::asio::ip::basic_resolver<boost::asio::ip::tcp, boost::asio::ip::resolver_service<boost::asio::ip::tcp> >::async_resolve<boost::_bi::bind_t<void, boost::_mfi::mf2<void, client, boost::system::error_code const&, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp> >, boost::_bi::list3<boost::_bi::value<client*>, boost::arg<1> (*)(), boost::arg<2> (*)()> > >(boost::asio::ip::basic_resolver_query<boost::asio::ip::tcp> const&, boost::_bi::bind_t<void, boost::_mfi::mf2<void, client, boost::system::error_code const&, boost::asio::ip::basic_resolver_iterator<boost::asio::ip::tcp> >, boost::_bi::list3<boost::_bi::value<client*>, boost::arg<1> (*)(), boost::arg<2> (*)()> >&&) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x43E05F: client::client(boost::asio::io_service&, std::string const&, std::string const&) (in /home/jcm/samples/async-client/build/async-client)
==2894==    by 0x437018: main (in /home/jcm/samples/async-client/build/async-client)
==2894== 
Response returned with status code 400
==2894== 
==2894== For counts of detected and suppressed errors, rerun with: -v
==2894== Use --history-level=approx or =none to gain increased speed, at
==2894== the cost of reduced accuracy of conflicting-access information
==2894== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 89 from 29)

      

It seems wrong to me that the mutex is unlocked before the condition is signaled. If this is not correct please let me know why the above code is correct.

+3


source to share


1 answer


Assuming that it lock

is a wrapper object for the same object pthread_mutex_t

that was used in the matching call pthread_cond_(timed)wait

, most pthreads implementations will allow you to signal and unlock in any order, because it pthread_cond_(timed)wait

is defined to unlock the condition variable and get the mutex as an atomic operation, that is, it will not return successfully until both CVs are signaled and the calling thread has received the mutex.

Rumors on the internet say that no one fails this operation no matter what order it is in, but some thread schedulers were written for maximum efficiency when unlocking occurs after a signal - and that others were written for maximum efficiency. when unlocked before the signal, which means you cannot win all the time no matter how you write it. Personally, if I were to maintain the code in question, I would switch the order only on the grounds that it makes the lucky one happy. You don't want to wade through unwanted complaints to find real race conditions.



And I add: " pthread_cond_signal

should take both CV and mutex as well pthread_cond_wait

" to my POSIX API error list to fix when I get the time machine.

+4


source







All Articles