4

helgrindで公式のboosasioサンプルコードのhttpasync_clientサンプルを実行すると、警告が表示されます。ファイルからのこのコードboost/asio/detail/posix_event.hppが警告の原因であるようです:

// 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.
}

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's 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)

条件変数が通知される前にミューテックスのロックが解除されているのは間違っているようです。誤検知の場合は、上記のコードが正しい理由を教えてください。

4

1 に答える 1

5

のマッチング呼び出しで使用されたものとlockまったく同じオブジェクトのラッパーオブジェクトであると仮定すると、pthreadのほとんどの実装では、条件変数でブロックを解除して取得するように定義されているため、シグナルとロック解除をどちらの順序でも発行できます。アトミック操作としてのmutex-つまり、CVが通知され、呼び出し元のスレッドがmutexを取得するまで正常に返されません。pthread_mutex_tpthread_cond_(timed)waitpthread_cond_(timed)wait

Internet rumor suggests that nobody fails this operation no matter what order it's in, but some thread schedulers have been written for maximum efficiency when the unlock happens after the signal -- and that others have been written for maximum efficiency when the unlock happens before the signal, which means you can't win all the time regardless of how you write it. Personally, if I were maintaining the code in question, I would switch the order just on the grounds that it makes helgrind happy. You don't want to have to wade through junk complaints to find the real race conditions.

And I'm adding "pthread_cond_signal should take both a CV and a mutex, just like pthread_cond_wait" to my list of POSIX API botches to fix when I get a time machine.

于 2013-01-21T22:31:38.453 に答える