[Boost-bugs] [Boost C++ Libraries] #9319: Using future continuations (.then) leads to deadlock waiting to acquire an already held mutex

Subject: [Boost-bugs] [Boost C++ Libraries] #9319: Using future continuations (.then) leads to deadlock waiting to acquire an already held mutex
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2013-10-29 20:11:54


#9319: Using future continuations (.then) leads to deadlock waiting to acquire an
already held mutex
---------------------------------+----------------------
 Reporter: arichardatwork@… | Owner: anthonyw
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: thread
  Version: Boost 1.54.0 | Severity: Problem
 Keywords: future continuation |
---------------------------------+----------------------
 I encountered a problem with a continuation attempting to relock a mutex
 which is already held by the code which launched the continuation.
 (Leading to a deadlock of sorts).

 Here is an example test program. I realize that in this trivial example
 some of my choices (like using a promise instead of a packaged task)
 probably don't make sense, but this is distilled out of a more complex
 block of code.

 gcc version 4.6.3
 Boost version 1.54.0


 g++ -g -I <path to boost include> -L <path to boost lib> -lboost_thread
 -lboost_system futtest.cpp
 {{{
 // futtest.cpp
 #include <iostream>
 #define BOOST_THREAD_VERSION 4

 #include <boost/thread/future.hpp>
 #include <boost/thread/thread.hpp>
 #include <boost/bind.hpp>
 #include <boost/chrono.hpp>
 #include <boost/shared_ptr.hpp>
 #include <boost/scoped_ptr.hpp>

 using namespace boost;

 typedef shared_ptr< promise<int> > IntPromise;

 void foo(IntPromise p)
 {
     std::cout << "foo" << std::endl;
     p->set_value(123); // This line locks the future's mutex, then calls
 the continuation with the mutex already locked.
 }

 void bar(future<int> & fooResult)
 {
     std::cout << "bar" << std::endl;
     int i = fooResult.get(); // Code hangs on this line (Due to future
 already being locked by the set_value call)
     std::cout << "i: " << i << std::endl;
 }

 int main()
 {
     IntPromise p(new promise<int>());
     thread t(boost::bind(foo, p));
     future<int> f1 = p->get_future();
     f1.then(launch::deferred, boost::bind(bar, _1));
     t.join();
 }
 }}}

 When this is run, this starts the 'foo' method on another thread. Foo sets
 the value of the promise (which locks the mutex on the future), which then
 launches the continuation 'bar' (on the same thread). The continuation
 calls 'get' on the future which attempts to relock the future (which is
 still locked from the promise being set, and the program hangs
 indefinitely.

 Alternately, if the continuation is added as shown below:
 {{{
 future<void> f2 = f1.then(launch::deferred, boost::bind(bar, _1));
 f2.get();
 }}}

 Then the code will generally work, but theris a race condition, because if
 the f2.get() call doesn't happen before set_value is called, then the
 deadlock still occurs. (You can see this by injecting a sleep before the
 f2.get() call.


 Here is a stack trace of the original program.

 {{{
 THREAD 1
 #0 0x000000362a20bae5 in pthread_cond_wait@@GLIBC_2.3.2 () from
 /lib64/libpthread.so.0
 #1 0x00007fe699c356bb in boost::condition_variable::wait ()
    from
 build/common/externals/boost/boost_build/install/lib/libboost_thread.so.1.54.0
 #2 0x00007fe699c35a0e in boost::thread::join_noexcept() ()
    from
 build/common/externals/boost/boost_build/install/lib/libboost_thread.so.1.54.0
 #3 0x000000000040d173 in boost::thread::join (this=0x7fff1ed084f0)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/detail/thread.hpp:756
 #4 0x0000000000409b4b in main () at futtest.cpp:34

 THREAD 2
 [Switching to thread 2 (Thread 0x7fe6999f4700 (LWP 11875))]
 #0 0x000000362a20e34d in __lll_lock_wait () from /lib64/libpthread.so.0
 (gdb) bt
 #0 0x000000362a20e34d in __lll_lock_wait () from /lib64/libpthread.so.0
 #1 0x000000362a209f97 in _L_lock_863 () from /lib64/libpthread.so.0
 #2 0x000000362a209deb in pthread_mutex_lock () from
 /lib64/libpthread.so.0
 #3 0x000000000040adf1 in pthread_mutex_lock (m=0x19e3128)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/pthread/mutex.hpp:61
 #4 boost::mutex::lock (this=0x19e3128) at
 build/common/externals/boost/boost_build/install/include/boost/thread/pthread/mutex.hpp:113
 #5 0x0000000000410da4 in boost::unique_lock<boost::mutex>::lock
 (this=0x7fe6999f3a80)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/lock_types.hpp:346
 #6 0x0000000000410f41 in boost::unique_lock<boost::mutex>::unique_lock
 (this=0x7fe6999f3a80, m_=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/lock_types.hpp:124
 #7 0x000000000040dc95 in boost::detail::future_object_base::wait
 (this=0x19e30f0, rethrow=true)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:366
 #8 0x0000000000417d17 in boost::detail::future_object<int>::get
 (this=0x19e30f0)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:647
 #9 0x0000000000411d4f in boost::future<int>::get (this=0x19e3670)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:1577
 #10 0x00000000004099cb in bar (fooResult=...) at futtest.cpp:24
 #11 0x0000000000427eee in boost::_bi::list1<boost::arg<1>
>::operator()<void (*)(boost::future<int>&),
 boost::_bi::list1<boost::future<int>&> > (this=0x19e3688, f=@0x19e3680,
 a=...) at
 build/common/externals/boost/boost_build/install/include/boost/bind/bind.hpp:253
 #12 0x0000000000426aea in boost::_bi::bind_t<void, void
 (*)(boost::future<int>&), boost::_bi::list1<boost::arg<1> >
>::operator()<boost::future<int> > (this=0x19e3680, a1=...) at
 build/common/externals/boost/boost_build/install/include/boost/bind/bind_template.hpp:32
 #13 0x0000000000425349 in
 boost::detail::future_deferred_continuation<boost::future<int>, void,
 boost::_bi::bind_t<void, void (*)(boost::future<int>&),
 boost::_bi::list1<boost::arg<1> > > >::execute (this=0x19e3570, lck=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:3649
 #14 0x0000000000425316 in
 boost::detail::future_deferred_continuation<boost::future<int>, void,
 boost::_bi::bind_t<void, void (*)(boost::future<int>&),
 boost::_bi::list1<boost::arg<1> > > >::launch_continuation
 (this=0x19e3570, lk=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:3644
 #15 0x000000000040d915 in
 boost::detail::future_object_base::do_continuation (this=0x19e30f0,
 lock=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:286
 #16 0x000000000040da61 in
 boost::detail::future_object_base::mark_finished_internal (this=0x19e30f0,
 lock=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:315
 #17 0x0000000000417ab4 in
 boost::detail::future_object<int>::mark_finished_with_result_internal
 (this=0x19e30f0, result_=123, lock=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:614
 #18 0x0000000000411be4 in boost::promise<int>::set_value (this=0x19e30d0,
 r=123)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/future.hpp:1803
 #19 0x0000000000409995 in foo (p=...) at futtest.cpp:18
 #20 0x0000000000427e58 in
 boost::_bi::list1<boost::_bi::value<boost::shared_ptr<boost::promise<int>
> > >::operator()<void (*)(boost::shared_ptr<boost::promise<int> >),
 boost::_bi::list0> (this=0x19e3400, f=@0x19e33f8, a=...)
     at
 build/common/externals/boost/boost_build/install/include/boost/bind/bind.hpp:253
 #21 0x0000000000426a9f in boost::_bi::bind_t<void, void
 (*)(boost::shared_ptr<boost::promise<int> >),
 boost::_bi::list1<boost::_bi::value---Type <return> to continue, or q
 <return> to quit---
 <boost::shared_ptr<boost::promise<int> > > > >::operator()
 (this=0x19e33f8)
     at
 build/common/externals/boost/boost_build/install/include/boost/bind/bind_template.hpp:20
 #22 0x0000000000425262 in
 boost::detail::thread_data<boost::_bi::bind_t<void, void
 (*)(boost::shared_ptr<boost::promise<int> >),
 boost::_bi::list1<boost::_bi::value<boost::shared_ptr<boost::promise<int>
> > > > >::run (this=0x19e3240)
     at
 build/common/externals/boost/boost_build/install/include/boost/thread/detail/thread.hpp:117
 #23 0x00007fe699c342fa in thread_proxy () from
 build/common/externals/boost/boost_build/install/lib/libboost_thread.so.1.54.0
 #24 0x000000362a207d90 in start_thread () from /lib64/libpthread.so.0
 #25 0x0000003629af119d in clone () from /lib64/libc.so.6
 }}}

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/9319>
Boost C++ Libraries <http://www.boost.org/>
Boost provides free peer-reviewed portable C++ source libraries.

This archive was generated by hypermail 2.1.7 : 2017-02-16 18:50:14 UTC