Boost C++ Libraries: Ticket #3848: Boost.Exception and transporting exceptions between threads https://svn.boost.org/trac10/ticket/3848 <p> The attached example program exc.cc crashes from time to time. I suspect that the problem is that detail::error_info_container_impl is not thread safe. </p> <p> Here is gdb stack trace: Core was generated by `./exc'. Program terminated with signal 11, Segmentation fault. </p> <p> <a class="missing ticket">#0</a> 0x0000000000000000 in ?? () <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/1" title="#1: Bugs: boost.build causes ftjam to segfault (closed: Wont Fix)">#1</a> 0x000000000040532a in boost::exception_detail::refcount_ptr&lt;boost::exception_detail::error_info_container&gt;::release (this=0x2aaab4001e28) at include/boost/exception/exception.hpp:73 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/2" title="#2: Bugs: list::size should be const (closed: fixed)">#2</a> 0x0000000000405341 in ~refcount_ptr (this=0x2aaab4001e28) </p> <blockquote> <p> at include/boost/exception/exception.hpp:28 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/3" title="#3: Bugs: automatic conversion and overload proble (closed: fixed)">#3</a> 0x000000000040da36 in ~exception (this=0x2aaab4001e20) </p> <blockquote> <p> at include/boost/exception/exception.hpp:255 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/4" title="#4: Bugs: any_ptr in any library documentation? (closed: Fixed)">#4</a> 0x000000000040e2f7 in ~err (this=0x2aaab4001e20) at exc.cc:10 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/5" title="#5: Bugs: shared_ptr and self-owning objects (closed: Fixed)">#5</a> 0x000000000040e437 in ~clone_impl (this=0x2aaab4001e20) </p> <blockquote> <p> at include/boost/exception/exception.hpp:368 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/6" title="#6: Bugs: tie in utility.hpp and tuple.hpp clash. (closed: Duplicate)">#6</a> 0x000000000040612f in boost::checked_delete&lt;boost::exception_detail::clone_base const&gt; (x=0x2aaab4001e50) </p> <blockquote> <p> at include/boost/checked_delete.hpp:34 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/7" title="#7: Bugs: g++ 2.96 requires NO_STRINGSTREAM (closed: Fixed)">#7</a> 0x00000000004074ff in boost::detail::sp_counted_impl_p&lt;boost::exception_detail::clone_base const&gt;::dispose (this=0x2aaab4000910) at include/boost/smart_ptr/detail/sp_counted_impl.hpp:78 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/8" title="#8: Bugs: prop in undirected graph + out_edges (closed: Works For Me)">#8</a> 0x0000000000404706 in boost::detail::sp_counted_base::release (this=0x2aaab4000910) </p> <blockquote> <p> at include/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp:145 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/9" title="#9: Bugs: config_info ambiguity error (closed: Invalid)">#9</a> 0x0000000000404762 in ~shared_count (this=0x42002fe8) </p> <blockquote> <p> at include/boost/smart_ptr/detail/shared_count.hpp:217 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/10" title="#10: Bugs: allyourbase.jam file is bad. (closed: Out of Date)">#10</a> 0x0000000000404ae9 in ~shared_ptr (this=0x42002fe0) </p> <blockquote> <p> at include/boost/smart_ptr/shared_ptr.hpp:169 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/11" title="#11: Bugs: why not using mt19937? (closed: Fixed)">#11</a> 0x0000000000404c5e in ~exception_ptr (this=0x42002fd8) </p> <blockquote> <p> at include/boost/exception_ptr.hpp:43 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/12" title="#12: Bugs: Can't specify VertexPredicate (closed: Fixed)">#12</a> 0x000000000040ca4b in ~future (this=0x42002fd0) at exc.cc:14 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/13" title="#13: Bugs: iterator_adapter and abstract classes (closed: Out of Date)">#13</a> 0x00000000004041f3 in consumer () at exc.cc:54 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/14" title="#14: Feature Requests: Support for static data members (closed: Out of Date)">#14</a> 0x0000000000404253 in consume () at exc.cc:61 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/15" title="#15: Feature Requests: thread class needs join() with timeout (closed: None)">#15</a> 0x00000000004071b3 in boost::detail::thread_data&lt;void (*)()&gt;::run (this=0x6238c0) </p> <blockquote> <p> at include/boost/thread/detail/thread.hpp:56 </p> </blockquote> <p> <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/16" title="#16: Feature Requests: way to defer thread creation needed (closed: None)">#16</a> 0x00002b033252d33f in thread_proxy () from lib/libboost_thread.so.1.40.0 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/17" title="#17: Feature Requests: Socket wrapper class (closed: fixed)">#17</a> 0x00002b0333651fc7 in start_thread () from lib/libpthread.so.0 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/18" title="#18: Bugs: lexical_cast fails in some cases (closed: Fixed)">#18</a> 0x00002b03331bd5ad in clone () from lib/libc.so.6 <a class="closed ticket" href="https://svn.boost.org/trac10/ticket/19" title="#19: Bugs: Problems building on NT &amp; Linux (closed: Out of Date)">#19</a> 0x0000000000000000 in ?? () </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/3848 Trac 1.4.3 Nikki Chumakov <nikkikom@…> Tue, 19 Jan 2010 17:55:22 GMT attachment set https://svn.boost.org/trac10/ticket/3848 https://svn.boost.org/trac10/ticket/3848 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">exc.cc</span> </li> </ul> Ticket Nikki Chumakov <nikkikom@…> Tue, 19 Jan 2010 18:09:43 GMT cc set https://svn.boost.org/trac10/ticket/3848#comment:1 https://svn.boost.org/trac10/ticket/3848#comment:1 <ul> <li><strong>cc</strong> <span class="trac-author">nikkikom@…</span> added </li> </ul> <p> Sorry for backtrace misformatting. Here is the right one: </p> <pre class="wiki">#0 0x0000000000000000 in ?? () #1 0x000000000040532a in boost::exception_detail::refcount_ptr&lt;boost::exception_detail::error_info_container&gt;::release (this=0x2aaab4001e28) at include/boost/exception/exception.hpp:73 #2 0x0000000000405341 in ~refcount_ptr (this=0x2aaab4001e28) at include/boost/exception/exception.hpp:28 #3 0x000000000040da36 in ~exception (this=0x2aaab4001e20) at include/boost/exception/exception.hpp:255 #4 0x000000000040e2f7 in ~err (this=0x2aaab4001e20) at exc.cc:10 #5 0x000000000040e437 in ~clone_impl (this=0x2aaab4001e20) at include/boost/exception/exception.hpp:368 #6 0x000000000040612f in boost::checked_delete&lt;boost::exception_detail::clone_base const&gt; (x=0x2aaab4001e50) at include/boost/checked_delete.hpp:34 #7 0x00000000004074ff in boost::detail::sp_counted_impl_p&lt;boost::exception_detail::clone_base const&gt;::dispose (this=0x2aaab4000910) at include/boost/smart_ptr/detail/sp_counted_impl.hpp:78 #8 0x0000000000404706 in boost::detail::sp_counted_base::release (this=0x2aaab4000910) at include/boost/smart_ptr/detail/sp_counted_base_gcc_x86.hpp:145 #9 0x0000000000404762 in ~shared_count (this=0x42002fe8) at include/boost/smart_ptr/detail/shared_count.hpp:217 #10 0x0000000000404ae9 in ~shared_ptr (this=0x42002fe0) at include/boost/smart_ptr/shared_ptr.hpp:169 #11 0x0000000000404c5e in ~exception_ptr (this=0x42002fd8) at include/boost/exception_ptr.hpp:43 #12 0x000000000040ca4b in ~future (this=0x42002fd0) at exc.cc:14 #13 0x00000000004041f3 in consumer () at exc.cc:54 #14 0x0000000000404253 in consume () at exc.cc:61 #15 0x00000000004071b3 in boost::detail::thread_data&lt;void (*)()&gt;::run (this=0x6238c0) at include/boost/thread/detail/thread.hpp:56 #16 0x00002b033252d33f in thread_proxy () from lib/libboost_thread.so.1.40.0 #17 0x00002b0333651fc7 in start_thread () from /lib/libpthread.so.0 #18 0x00002b03331bd5ad in clone () from /lib/libc.so.6 #19 0x0000000000000000 in ?? ()}}} </pre> Ticket Emil Dotchevski Wed, 20 Jan 2010 03:36:58 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/3848#comment:2 https://svn.boost.org/trac10/ticket/3848#comment:2 <ul> <li><strong>status</strong> <span class="trac-field-old">new</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> The exception object itself is NOT thread safe. </p> <p> Try this instead: </p> <pre class="wiki">.... boost::exception_ptr get_exception () const { boost::unique_lock&lt;boost::mutex&gt; lck (mux_); while (! ready_) cond_.wait (lck); return exc_; } .... future f; boost::thread thr (boost::bind (&amp;producer, boost::ref (f))); if( boost::exception_ptr e=f.get_exception() ) rethrow_exception(e); </pre> Ticket Nikki Chumakov <nikkikom@…> Wed, 20 Jan 2010 12:00:06 GMT attachment set https://svn.boost.org/trac10/ticket/3848 https://svn.boost.org/trac10/ticket/3848 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">exc1.cc</span> </li> </ul> Ticket Nikki Chumakov <nikkikom@…> Wed, 20 Jan 2010 12:00:21 GMT attachment set https://svn.boost.org/trac10/ticket/3848 https://svn.boost.org/trac10/ticket/3848 <ul> <li><strong>attachment</strong> → <span class="trac-field-new">exc1_log.txt</span> </li> </ul> Ticket Nikki Chumakov <nikkikom@…> Wed, 20 Jan 2010 12:15:27 GMT status changed; resolution deleted https://svn.boost.org/trac10/ticket/3848#comment:3 https://svn.boost.org/trac10/ticket/3848#comment:3 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">invalid</span> </li> </ul> <p> Please take a look at example a little bit closer. </p> <p> I'm not transferring boost::exception object between threads, I'm transferring exception_ptr. </p> <p> Boost.Exception docs says about thread safety: </p> <ul><li>It is legal for multiple threads to hold exception_ptr references to the same exception object. </li><li>It is illegal for multiple threads to modify the same exception_ptr object concurrently. </li><li>While calling current_exception makes a copy of the current exception object, it is still possible for the two copies to share internal state. Therefore, in general it is not safe to call rethrow_exception concurrently to throw the same exception object into multiple threads. </li></ul><p> Well, I do not modify same exception_ptr object concurrently. And I do not call rethrow_exception concurrently to throw the same exception object. </p> <p> I guess the race condition happen when temporal boost::exception object destructor called in producer thread in the very same time when rethrow_exception called in consumer thread. </p> <p> I applied the changes you suggested, the example still crashes. Please take a look at attached exc1.cc and exc1_log.txt. </p> <p> BTW, it is possible to reproduce the very same bug with boost::promise/boost::future in 1.41+ </p> <pre class="wiki">void producer (boost::promise&lt;void&gt;&amp; promise, boost::barrier&amp; barrier) { barrier.wait (); promise.set_exception (boost::copy_exception (err () &lt;&lt; err_info ("stub"))); } void consumer () { boost::barrier barrier (2); boost::promise&lt;void&gt; promise; boost::unique_future&lt;void&gt; future = promise.get_future (); boost::thread thr ( boost::bind (&amp;producer, boost::ref (promise), boost::ref (barrier)) ); barrier.wait (); try { future.get (); } catch (err const&amp; e) {} thr.join (); } </pre><p> It should fail as well. </p> Ticket Nikki Chumakov <nikkikom@…> Mon, 25 Jan 2010 13:57:15 GMT <link>https://svn.boost.org/trac10/ticket/3848#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3848#comment:4</guid> <description> <p> This simple patch would help: </p> <pre class="wiki">--- boost.1.40/exception/info.hpp 2009-10-09 23:11:15.000000000 +0400 +++boost/exception/info.hpp 2010-01-25 14:50:03.000000000 +0300 @@ -10,6 +10,7 @@ #include &lt;boost/exception/to_string_stub.hpp&gt; #include &lt;boost/exception/detail/error_info_impl.hpp&gt; #include &lt;boost/shared_ptr.hpp&gt; +#include &lt;boost/thread.hpp&gt; #include &lt;map&gt; namespace @@ -121,19 +122,25 @@ error_info_map info_; mutable std::string diagnostic_info_str_; mutable int count_; + mutable mutex mux_; void add_ref() const { + unique_lock&lt;mutex&gt; lock (mux_); ++count_; } void release() const { + unique_lock&lt;mutex&gt; lock (mux_); if( !--count_ ) + { + lock.unlock (); delete this; } + } }; } </pre><p> However the atomic count_ implementation would be much more efficient. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>Emil Dotchevski</dc:creator> <pubDate>Fri, 29 Jan 2010 17:11:36 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/3848#comment:5 https://svn.boost.org/trac10/ticket/3848#comment:5 <ul> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">invalid</span> </li> </ul> <p> Forget about boost::exception for a moment, think about using exception_ptr to transfer an exception of some user-defined type between threads. It is incorrect to assume that copies of the exception object don't have shared state, or that if they do they do it in a thread-safe manner, because in principle exceptions are thread-local objects (note that using shared state is the only way non-trivial exception types can provide no-throw copy constructor which the C++ standard requires.) </p> <p> It is not possible for exception_ptr to help you solve this problem. </p> <p> The solution is to join the thread before rethrowing the exception. See <a class="ext-link" href="http://svn.boost.org/svn/boost/trunk/libs/exception/test/exception_ptr_test.cpp"><span class="icon">​</span>http://svn.boost.org/svn/boost/trunk/libs/exception/test/exception_ptr_test.cpp</a>. </p> Ticket anonymous Fri, 29 Jan 2010 22:40:30 GMT status changed; resolution deleted https://svn.boost.org/trac10/ticket/3848#comment:6 https://svn.boost.org/trac10/ticket/3848#comment:6 <ul> <li><strong>status</strong> <span class="trac-field-old">closed</span> → <span class="trac-field-new">reopened</span> </li> <li><strong>resolution</strong> <span class="trac-field-deleted">invalid</span> </li> </ul> Ticket Emil Dotchevski Sat, 30 Jan 2010 05:31:44 GMT status changed; resolution set https://svn.boost.org/trac10/ticket/3848#comment:7 https://svn.boost.org/trac10/ticket/3848#comment:7 <ul> <li><strong>status</strong> <span class="trac-field-old">reopened</span> → <span class="trac-field-new">closed</span> </li> <li><strong>resolution</strong> → <span class="trac-field-new">fixed</span> </li> </ul> <p> Nikki, thanks for reporting this tricky bug. Should be fixed in trunk revision 59364. Your code is now incorporated in copy_exception_test.cpp. </p> Ticket Nikki Chumakov <nikkikom@…> Thu, 04 Feb 2010 13:51:30 GMT <link>https://svn.boost.org/trac10/ticket/3848#comment:8 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/3848#comment:8</guid> <description> <p> Thank you. </p> <p> Also I'm getting a lot of errors when translating latest svn boost with g++ 4.4.2 20091222 (Red Hat 4.4.2-20) (GCC) in с++0x mode. </p> <pre class="wiki">g++ -std=c++0x -O6 -s -I. -I../boost/trunk -o t test.cc -lltdl In file included from ../boost/trunk/boost/exception_ptr.hpp:9, from ../boost/trunk/boost/exception/all.hpp:30, from ./service/error.h:6, from ./service/library.h:7, from test.cc:4: ../boost/trunk/boost/exception/detail/exception_ptr.hpp: In function ‘boost::exception_ptr boost::exception_detail::get_bad_alloc()’: ../boost/trunk/boost/exception/detail/exception_ptr.hpp:80: error: call of overloaded ‘copy_exception(const boost::exception_detail::bad_alloc_&amp;)’ is ambiguous ../boost/trunk/boost/exception/detail/exception_ptr.hpp:38: note: candidates are: boost::exception_ptr boost::copy_exception(const T&amp;) [with T = boost::exception_detail::bad_alloc_] /usr/lib/gcc/x86_64-redhat-linux/4.4.2/../../../../include/c++/4.4.2/exception_ptr.h:152: note: std::__exception_ptr::exception_ptr std::copy_exception(_Ex) [with _Ex = boost::exception_detail::bad_alloc_] ../boost/trunk/boost/exception/detail/exception_ptr.hpp: In function ‘boost::exception_ptr boost::exception_detail::current_exception_unknown_exception()’: ../boost/trunk/boost/exception/detail/exception_ptr.hpp:221: error: call of overloaded ‘copy_exception(boost::unknown_exception)’ is ambiguous ../boost/trunk/boost/exception/detail/exception_ptr.hpp:38: note: candidates are: boost::exception_ptr boost::copy_exception(const T&amp;) [with T = boost::unknown_exception] /usr/lib/gcc/x86_64-redhat-linux/4.4.2/../../../../include/c++/4.4.2/exception_ptr.h:152: note: std::__exception_ptr::exception_ptr std::copy_exception(_Ex) [with _Ex = boost::unknown_exception] ../boost/trunk/boost/exception/detail/exception_ptr.hpp: In function ‘boost::exception_ptr boost::exception_detail::current_exception_unknown_boost_exception(const boost::exception&amp;)’: ../boost/trunk/boost/exception/detail/exception_ptr.hpp:228: error: call of overloaded ‘copy_exception(boost::unknown_exception)’ is ambiguous etc... etc... </pre><p> I would suggest the following patch, it works great: </p> <pre class="wiki">--- boost/exception/detail/exception_ptr.hpp 2010-02-03 20:08:23.114427338 +0300 +++ /usr/local/include/boost/exception/detail/exception_ptr.hpp 2010-02-04 05:57:37.490551407 +0300 @@ -73,7 +73,7 @@ exception_ptr get_bad_alloc() { - static exception_ptr e = copy_exception( + static exception_ptr e = boost::copy_exception( bad_alloc_() &lt;&lt; throw_function("boost::current_exception()") &lt;&lt; throw_file(__FILE__) &lt;&lt; @@ -209,23 +209,23 @@ current_exception_std_exception( T const &amp; e1 ) { if( boost::exception const * e2 = get_boost_exception(&amp;e1) ) - return copy_exception(current_exception_std_exception_wrapper&lt;T&gt;(e1,*e2)); + return boost::copy_exception(current_exception_std_exception_wrapper&lt;T&gt;(e1,*e2)); else - return copy_exception(current_exception_std_exception_wrapper&lt;T&gt;(e1)); + return boost::copy_exception(current_exception_std_exception_wrapper&lt;T&gt;(e1)); } inline exception_ptr current_exception_unknown_exception() { - return copy_exception(unknown_exception()); + return boost::copy_exception(unknown_exception()); } inline exception_ptr current_exception_unknown_boost_exception( boost::exception const &amp; e ) { - return copy_exception(unknown_exception(e)); + return boost::copy_exception(unknown_exception(e)); } inline @@ -235,7 +235,7 @@ if( boost::exception const * be = get_boost_exception(&amp;e) ) return current_exception_unknown_boost_exception(*be); else - return copy_exception(unknown_exception(e)); + return boost::copy_exception(unknown_exception(e)); } inline </pre> </description> <category>Ticket</category> </item> </channel> </rss>