Boost C++ Libraries: Ticket #10093: Exceptions "teleport" between coroutines when switching inside catch https://svn.boost.org/trac10/ticket/10093 <p> Sorry for using coroutines v1 API, but the actual API doesn't matter as much as the result and the reason for it. Consider the following program: </p> <div class="wiki-code"><div class="code"><pre><span class="cp">#define BOOST_COROUTINES_V1</span> <span class="cp">#include</span> <span class="cpf">&lt;boost/coroutine/all.hpp&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;exception&gt;</span><span class="cp"></span> <span class="cp">#include</span> <span class="cpf">&lt;iostream&gt;</span><span class="cp"></span> <span class="k">typedef</span> <span class="n">boost</span><span class="o">::</span><span class="n">coroutines</span><span class="o">::</span><span class="n">coroutine</span><span class="o">&lt;</span><span class="kt">void</span><span class="p">()</span><span class="o">&gt;</span> <span class="n">coro_t</span><span class="p">;</span> <span class="kt">void</span> <span class="nf">mycoro</span><span class="p">(</span><span class="n">coro_t</span><span class="o">::</span><span class="n">caller_type</span><span class="o">&amp;</span> <span class="n">caller</span><span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;calling caller...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="n">caller</span><span class="p">();</span> <span class="k">try</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;mycoro exception&quot;</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;calling caller in the catch block...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="n">caller</span><span class="p">();</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;rethrowing mycoro exception...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="k">throw</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;mycoro caught: &quot;</span> <span class="o">&lt;&lt;</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="p">}</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;exiting mycoro...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="p">}</span> <span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">int</span> <span class="n">argc</span><span class="p">,</span> <span class="kt">char</span><span class="o">**</span> <span class="n">argv</span><span class="p">)</span> <span class="p">{</span> <span class="n">coro_t</span> <span class="n">callee</span><span class="p">(</span><span class="n">mycoro</span><span class="p">);</span> <span class="k">try</span> <span class="p">{</span> <span class="k">try</span> <span class="p">{</span> <span class="k">throw</span> <span class="n">std</span><span class="o">::</span><span class="n">runtime_error</span><span class="p">(</span><span class="s">&quot;main exception&quot;</span><span class="p">);</span> <span class="p">}</span> <span class="k">catch</span><span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;calling callee in the catch block...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="n">callee</span><span class="p">();</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;rethrowing main exception...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="k">throw</span><span class="p">;</span> <span class="p">}</span> <span class="p">}</span> <span class="k">catch</span> <span class="p">(</span><span class="k">const</span> <span class="n">std</span><span class="o">::</span><span class="n">exception</span><span class="o">&amp;</span> <span class="n">e</span><span class="p">)</span> <span class="p">{</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;main caught: &quot;</span> <span class="o">&lt;&lt;</span> <span class="n">e</span><span class="p">.</span><span class="n">what</span><span class="p">()</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="p">}</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;calling callee one last time...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="n">callee</span><span class="p">();</span> <span class="n">std</span><span class="o">::</span><span class="n">cout</span> <span class="o">&lt;&lt;</span> <span class="s">&quot;exiting main...&quot;</span> <span class="o">&lt;&lt;</span> <span class="n">std</span><span class="o">::</span><span class="n">endl</span><span class="p">;</span> <span class="k">return</span> <span class="mi">0</span><span class="p">;</span> <span class="p">}</span> </pre></div></div><p> When compiled with gcc it causes the following output: </p> <pre class="wiki">calling caller... calling callee in the catch block... calling caller in the catch block... rethrowing main exception... main caught: mycoro exception calling callee one last time... rethrowing mycoro exception... mycoro caught: main exception exiting mycoro... exiting main... </pre><p> Which shows that boost coroutines are unsafe in the presence of current exceptions, since in that case exceptions "teleport" between coroutines in weird ways. The problem here is that caught exceptions are saved in per-thread globals according to ABI, see: mentorembedded.github.io/cxx-abi/abi-eh.html (struct <span class="underline">cxa_eh_globals). </span></p> <p> It contains two very important fields, pointer to which can be obtained with <span class="underline">cxx_get_globals() or with </span>cxx_get_globals_fast() and check for null result (though the latter is not as reliable). Whenever coroutines switch they should save and restore those fields to properly support exceptions and avoid current exception bleeding between coroutines. </p> <p> This ABI is supported by both gcc and clang, though I'm not sure since which version. </p> en-us Boost C++ Libraries /htdocs/site/boost.png https://svn.boost.org/trac10/ticket/10093 Trac 1.4.3 olli Thu, 05 Jun 2014 06:21:13 GMT component changed https://svn.boost.org/trac10/ticket/10093#comment:1 https://svn.boost.org/trac10/ticket/10093#comment:1 <ul> <li><strong>component</strong> <span class="trac-field-old">coroutine</span> → <span class="trac-field-new">context</span> </li> </ul> <p> boost.context provides the 'jump' facility </p> Ticket snaury@… Thu, 05 Jun 2014 19:10:57 GMT <link>https://svn.boost.org/trac10/ticket/10093#comment:2 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10093#comment:2</guid> <description> <p> In my opinion boost.context is too low level for this, for example boost.context doesn't know anything about exceptions (and boost.coroutine does), besides calling C++ ABIs from assembler would be too tedious, and there's just nothing platform-dependant about it (more compiler or libc++ dependant). At the same time boost.coroutine has its own abstraction, <code>coroutine_context</code>, where <code>coroutine_context::jump</code> would be easy to modify: declare and call <code>__cxx_get_globals()</code> (similar to the way boost.log already does) and save/restore the exception information. </p> </description> <category>Ticket</category> </item> <item> <dc:creator>olli</dc:creator> <pubDate>Thu, 05 Jun 2014 20:44:06 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/10093#comment:3 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10093#comment:3</guid> <description> <p> Replying to <a class="ticket" href="https://svn.boost.org/trac10/ticket/10093#comment:2" title="Comment 2">snaury@…</a>: </p> <blockquote class="citation"> <p> for example boost.context doesn't know anything about exceptions </p> </blockquote> <p> boost.context handles Windows' structured exception handling </p> </description> <category>Ticket</category> </item> <item> <dc:creator>olli</dc:creator> <pubDate>Mon, 09 Jun 2014 08:51:48 GMT</pubDate> <title/> <link>https://svn.boost.org/trac10/ticket/10093#comment:4 </link> <guid isPermaLink="false">https://svn.boost.org/trac10/ticket/10093#comment:4</guid> <description> <ul><li>32bit MSVC works </li><li>64bit MSVC crashes the app - no info regarding to this issue found in the INet </li><li>compiler using C++ Itanium ABI are problematic because <span class="underline">cxa</span>get_globals() does call malloc() store/restore of <code>__cxa_eh_globals::caughtExceptions</code> and <code>__cxa_eh_globals::uncaughtExceptions</code> per context we might end up with memory leaks (== closing a context without cleaning up its list of the active exceptions). </li></ul><p> =&gt; maybe the best solution would be a hint in the docu. not to call context inside a catch-block </p> </description> <category>Ticket</category> </item> <item> <dc:creator>olli</dc:creator> <pubDate>Sat, 23 Aug 2014 12:29:56 GMT</pubDate> <title>status changed; resolution set https://svn.boost.org/trac10/ticket/10093#comment:5 https://svn.boost.org/trac10/ticket/10093#comment:5 <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">wontfix</span> </li> </ul> <p> It is not easy to solve because the different compilers handle it in a different way. even worse for some compilers (for instance MSVC) it is not know where currently catched exceptions are stored. </p> <p> I've added a note in the doc not to re-throw exceptions from a catch-clause inside a coroutine/context. </p> Ticket