Thread: [asio-users] host_resolver and demuxer::work
Brought to you by:
chris_kohlhoff
From: Arvid N. <c9...@cs...> - 2006-01-17 04:18:11
|
Hi. I was a bit surprised that waiting for an async_get_host_by_name() on a host_resolver wouldn't be considered as "work" by the demuxer::run () function. i.e. it returned when the only operation I had pending was a name lookup. I find this to be a bit unexpected. Anyway, my attempt to work around this was to us a demuxer::work instance to pass along with my handler (by value) for the async_get_host_by_name() operation. That's when I encountered the second problem. From what I can understand (didn't investigate too thoroughly), it looks like asio deadlocks when the handler object is being copied, and the work instance is destructed. The destructor will lock the demuxer/reactor before it decrements the work counter, but the lock is already held by that thread, and it is not a recursive mutex. I found this a bit strange too, considering the text from the manual: "The work class is copy-constructible so that it may be used as a data member in a handler class. It is not assignable." So, am I doing something wrong? My use case is simply to: 1. look up name 2. connect 3. send data 4. receive data where each step is an async operation. I'm using MacOS X with the boost review version of asio. thanks. -- Arvid Norberg |
From: Christopher K. <ch...@ko...> - 2006-01-17 05:17:24
|
Hi Arvid, --- Arvid Norberg <c9...@cs...> wrote: > I was a bit surprised that waiting for an > async_get_host_by_name() on a host_resolver wouldn't be > considered as "work" by the demuxer::run () function. i.e. it > returned when the only operation I had pending was a name > lookup. > > I find this to be a bit unexpected. Hmm, sounds like a bug. There is a demuxer::work member variable inside the asio::ipv4::detail::host_resolver_service's nested class get_host_by_name_handler, so I'm not sure why it should've returned early. > Anyway, my attempt to work around this was to us a > demuxer::work instance to pass along with my handler (by > value) for the async_get_host_by_name() operation. That's > when I encountered the second problem. > > From what I can understand (didn't investigate too > thoroughly), it looks like asio deadlocks when the handler > object is being copied, and the work instance is destructed. > > The destructor will lock the demuxer/reactor before it > decrements the work counter, but the lock is already held by > that thread, and it is not a recursive mutex. Can you show me the callstack when the deadlock occurs? I have already fixed another couple of deadlock bugs since 0.3.6 that were a side effect of adding the demuxer::work class. Maybe this one is related. Cheers, Chris |
From: Arvid N. <c9...@cs...> - 2006-01-17 12:09:46
|
On Jan 17, 2006, at 06:17, Christopher Kohlhoff wrote: > Hi Arvid, > > --- Arvid Norberg <c9...@cs...> wrote: >> I was a bit surprised that waiting for an >> async_get_host_by_name() on a host_resolver wouldn't be >> considered as "work" by the demuxer::run () function. i.e. it >> returned when the only operation I had pending was a name >> lookup. >> >> I find this to be a bit unexpected. > > Hmm, sounds like a bug. There is a demuxer::work member variable > inside the asio::ipv4::detail::host_resolver_service's nested > class get_host_by_name_handler, so I'm not sure why it should've > returned early. > >> Anyway, my attempt to work around this was to us a >> demuxer::work instance to pass along with my handler (by >> value) for the async_get_host_by_name() operation. That's >> when I encountered the second problem. >> >> From what I can understand (didn't investigate too >> thoroughly), it looks like asio deadlocks when the handler >> object is being copied, and the work instance is destructed. >> >> The destructor will lock the demuxer/reactor before it >> decrements the work counter, but the lock is already held by >> that thread, and it is not a recursive mutex. > > Can you show me the callstack when the deadlock occurs? I have > already fixed another couple of deadlock bugs since 0.3.6 that > were a side effect of adding the demuxer::work class. Maybe this > one is related. Ok, I will try with the cvs version then. Here's the callstack at least, and when I think of it, I'm not sure my first analysis was correct, since the construction of the work object also locks the service, but it didn't deadlock. What I do here is that I create the name lookup operation and the work object from within a timer event. #0 0x9002b8a8 in semaphore_wait_signal_trap () #1 0x900019cc in pthread_mutex_lock () #2 0x00108b4c in boost::asio::detail::posix_mutex::lock (this=0xb02affb8) at ../../boost_1_33_0_asio/boost/asio/detail/ posix_mutex.hpp:65 #3 0x001148bc in boost::asio::detail::scoped_lock<boost::asio::detail::posix_mutex>::scop ed_lock (this=0xf007fff8, m=@0xb02affb8) at ../../boost_1_33_0_asio/ boost/asio/detail/scoped_lock.hpp:36 #4 0x0011ff40 in boost::asio::detail::task_demuxer_service<boost::asio::detail::kqueue_re actor<false> >::work_finished (this=0xb02affb8) at ../../ boost_1_33_0_asio/boost/asio/detail/task_demuxer_service.hpp:195 #5 0x0011fff0 in boost::asio::demuxer_service<std::allocator<void> >::work_finished (this=0xb02abff8) at ../../boost_1_33_0_asio/boost/ asio/demuxer_service.hpp:110 #6 0x00120040 in boost::asio::basic_demuxer<boost::asio::demuxer_service<std::allocator<v oid> > >::work::~work (this=0xf0080224) at ../../boost_1_33_0_asio/ boost/asio/basic_demuxer.hpp:291 #7 0x001968a4 in boost::_bi::value<boost::asio::basic_demuxer<boost::asio::demuxer_servic e<std::allocator<void> > >::work>::~value (this=0xf0080224) at /Users/ arvid/Documents/dev/libtorrent/client_test/../src/ http_tracker_connection.cpp:233 #8 0x00196a20 in boost::_bi::list3<boost::_bi::value<boost::intrusive_ptr<libtorrent::htt p_tracker_connection> >, boost::arg<1>, boost::_bi::value<boost::asio::basic_demuxer<boost::asio::demuxer_servic e<std::allocator<void> > >::work> >::~list3 (this=0xf008021c) at / Users/arvid/Documents/dev/libtorrent/client_test/../src/ http_tracker_connection.cpp:233 #9 0x00196d0c in boost::_bi::bind_t<void, boost::_mfi::mf2<void, libtorrent::http_tracker_connection, boost::asio::error const&, boost::asio::basic_demuxer<boost::asio::demuxer_service<std::allocator<v oid> > >::work>, boost::_bi::list3<boost::_bi::value<boost::intrusive_ptr<libtorrent::htt p_tracker_connection> >, boost::arg<1>, boost::_bi::value<boost::asio::basic_demuxer<boost::asio::demuxer_servic e<std::allocator<void> > >::work> > >::~bind_t (this=0xf0080214) at / Users/arvid/Documents/dev/libtorrent/client_test/../src/ http_tracker_connection.cpp:233 #10 0x0005fdd8 in libtorrent::http_tracker_connection::http_tracker_connection (this=0xb5b0bf1c, d=@0xbffff2ac, man=@0xbffff1dc, req=@0xf0080580, hostname=@0xf0080470, port=80, request=@0xf0080444, c=@0xf008043c, stn=@0xbffff304, auth=@0xf008057c) at /Users/arvid/Documents/dev/ libtorrent/client_test/../src/http_tracker_connection.cpp:233 #11 0x0002bdc8 in libtorrent::tracker_manager::queue_request (this=0xbffff1dc, d=@0xbffff2ac, req=@0xf0080580, auth=@0xf008057c, c=@0xf0080574) at /Users/arvid/Documents/dev/libtorrent/ client_test/../src/tracker_manager.cpp:480 #12 0x00035c74 in libtorrent::detail::session_impl::second_tick (this=0xbffff1ac, e=@0xb5af3ff4) at /Users/arvid/Documents/dev/ libtorrent/client_test/../src/session.cpp:803 #13 0x00159268 in boost::_mfi::mf1<void, libtorrent::detail::session_impl, boost::asio::error const&>::operator () (this=0xb5af3fe4, p=0xbffff1ac, a1=@0xb5af3ff4) at ../../ boost_1_33_0_asio/boost/bind/mem_fn_template.hpp:149 #14 0x00159300 in boost::_bi::list2<boost::_bi::value<libtorrent::detail::session_impl*>, boost::arg<1> >::operator()<boost::_mfi::mf1<void, libtorrent::detail::session_impl, boost::asio::error const&>, boost::_bi::list1<boost::asio::error&> > (this=0xb5af3fec, f=@0xb5af3fe4, a=@0xf008094c) at ../../boost_1_33_0_asio/boost/ bind.hpp:286 #15 0x0015937c in boost::_bi::bind_t<void, boost::_mfi::mf1<void, libtorrent::detail::session_impl, boost::asio::error const&>, boost::_bi::list2<boost::_bi::value<libtorrent::detail::session_impl*>, boost::arg<1> > >::operator()<boost::asio::error> (this=0xb5af3fe4, a1=@0xb5af3ff4) at ../../boost_1_33_0_asio/boost/bind/ bind_template.hpp:32 #16 0x001593d4 in boost::asio::detail::binder1<boost::_bi::bind_t<void, boost::_mfi::mf1<void, libtorrent::detail::session_impl, boost::asio::error const&>, boost::_bi::list2<boost::_bi::value<libtorrent::detail::session_impl*>, boost::arg<1> > >, boost::asio::error>::operator() (this=0xb5af3fe4) at ../../boost_1_33_0_asio/boost/asio/detail/bind_handler.hpp:43 #17 0x0015943c in boost::asio::detail::task_demuxer_service<boost::asio::detail::kqueue_re actor<false> >::handler_wrapper<boost::asio::detail::binder1<boost::_bi::bind_t<void , boost::_mfi::mf1<void, libtorrent::detail::session_impl, boost::asio::error const&>, boost::_bi::list2<boost::_bi::value<libtorrent::detail::session_impl*>, boost::arg<1> > >, boost::asio::error> >::do_call (base=0xb5af3fdc) at ../../boost_1_33_0_asio/boost/asio/detail/task_demuxer_service.hpp: 331 #18 0x00145b74 in boost::asio::detail::task_demuxer_service<boost::asio::detail::kqueue_re actor<false> >::handler_base::call (this=0xb5af3fdc) at ../../ boost_1_33_0_asio/boost/asio/detail/task_demuxer_service.hpp:300 #19 0x001568a8 in boost::asio::detail::task_demuxer_service<boost::asio::detail::kqueue_re actor<false> >::run (this=0xb02affb8) at ../../boost_1_33_0_asio/ boost/asio/detail/task_demuxer_service.hpp:99 #20 0x00156b84 in boost::asio::demuxer_service<std::allocator<void> >::run (this=0xb02abff8) at ../../boost_1_33_0_asio/boost/asio/ demuxer_service.hpp:86 #21 0x00156bd4 in boost::asio::basic_demuxer<boost::asio::demuxer_service<std::allocator<v oid> > >::run (this=0xbffff2ac) at ../../boost_1_33_0_asio/boost/asio/ basic_demuxer.hpp:106 #22 0x0003750c in libtorrent::detail::session_impl::operator() (this=0xbffff1ac) at /Users/arvid/Documents/dev/libtorrent/ client_test/../src/session.cpp:879 #23 0x0015cd04 in boost::detail::function::void_function_obj_invoker0<libtorrent::detail:: session_impl, void>::invoke (function_obj_ptr={obj_ptr = <incomplete type>, const_obj_ptr = 0xbffff1ac, func_ptr = 0xbffff1ac, data = "\277"}) at ../../boost_1_33_0_asio/boost/function/ function_template.hpp:136 #24 0x001b3200 in boost::function0<void, std::allocator<boost::function_base> >::operator() (this=0xf0080e2c) at ../../boost_1_33_0_asio/boost/function/function_template.hpp:576 #25 0x00065a48 in thread_proxy (param=<incomplete type>) at /Users/ arvid/Documents/dev/libtorrent/client_test/../../boost_1_33_0_asio/ libs/thread/src/thread.cpp:113 #26 0x9002b200 in _pthread_body () -- Arvid Norberg |
From: Christopher K. <ch...@ko...> - 2006-01-17 12:29:10
|
Hi Arvid, --- Arvid Norberg <c9...@cs...> wrote: > Ok, I will try with the cvs version then. You may notice quite a bit has changed when you do try it - I'm in the middle of implementing some of the changes suggested in the boost review. Here's a quick guide to some things you may need to do to your code: - Replace demuxer with io_service - Replace stream_socket with ipv4::tcp::socket - Replace socket_acceptor with ipv4::tcp::acceptor - Replace datagram_socket with ipv4::udp::socket Cheers, Chris |
From: Arvid N. <c9...@cs...> - 2006-01-17 20:33:25
|
On Jan 17, 2006, at 13:29, Christopher Kohlhoff wrote: > You may notice quite a bit has changed when you do try it - I'm > in the middle of implementing some of the changes suggested in > the boost review. Here's a quick guide to some things you may need > to do to your code: > > - Replace demuxer with io_service > > - Replace stream_socket with ipv4::tcp::socket > > - Replace socket_acceptor with ipv4::tcp::acceptor > > - Replace datagram_socket with ipv4::udp::socket Thanks! I'm using cvs head now. I didn't notice any difference until I found out that I was forgetting to call reset() on the demuxer (io_service), maybe there could be an assert for that? So, anyway, sorry for bothering the list. -- Arvid Norberg |
From: Christopher K. <ch...@ko...> - 2006-01-17 06:47:03
|
Hi Arvid, --- Arvid Norberg <c9...@cs...> wrote: > I was a bit surprised that waiting for an > async_get_host_by_name() on a host_resolver wouldn't be > considered as "work" by the demuxer::run () function. i.e. it > returned when the only operation I had pending was a name > lookup. > > I find this to be a bit unexpected. BTW, I haven't been able to reproduce this problem with the CVS version of asio using a simple test program, so there might be something else going on. Cheers, Chris |