[Boost-bugs] [Boost C++ Libraries] #10093: Exceptions "teleport" between coroutines when switching inside catch

Subject: [Boost-bugs] [Boost C++ Libraries] #10093: Exceptions "teleport" between coroutines when switching inside catch
From: Boost C++ Libraries (noreply_at_[hidden])
Date: 2014-06-04 19:04:51


#10093: Exceptions "teleport" between coroutines when switching inside catch
------------------------------+-----------------------
 Reporter: snaury@… | Owner: olli
     Type: Bugs | Status: new
Milestone: To Be Determined | Component: coroutine
  Version: Boost 1.54.0 | Severity: Problem
 Keywords: |
------------------------------+-----------------------
 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:

 {{{#!cpp
 #define BOOST_COROUTINES_V1
 #include <boost/coroutine/all.hpp>
 #include <exception>
 #include <iostream>

 typedef boost::coroutines::coroutine<void()> coro_t;

 void mycoro(coro_t::caller_type& caller)
 {
     std::cout << "calling caller..." << std::endl;
     caller();
     try {
         try {
             throw std::runtime_error("mycoro exception");
         } catch(const std::exception& e) {
             std::cout << "calling caller in the catch block..." <<
 std::endl;
             caller();
             std::cout << "rethrowing mycoro exception..." << std::endl;
             throw;
         }
     } catch (const std::exception& e) {
         std::cout << "mycoro caught: " << e.what() << std::endl;
     }
     std::cout << "exiting mycoro..." << std::endl;
 }

 int main(int argc, char** argv)
 {
     coro_t callee(mycoro);
     try {
         try {
             throw std::runtime_error("main exception");
         } catch(const std::exception& e) {
             std::cout << "calling callee in the catch block..." <<
 std::endl;
             callee();
             std::cout << "rethrowing main exception..." << std::endl;
             throw;
         }
     } catch (const std::exception& e) {
         std::cout << "main caught: " << e.what() << std::endl;
     }
     std::cout << "calling callee one last time..." << std::endl;
     callee();
     std::cout << "exiting main..." << std::endl;
     return 0;
 }
 }}}

 When compiled with gcc it causes the following output:

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

 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 __cxa_eh_globals).

 It contains two very important fields, pointer to which can be obtained
 with __cxx_get_globals() or with __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.

 This ABI is supported by both gcc and clang, though I'm not sure since
 which version.

-- 
Ticket URL: <https://svn.boost.org/trac/boost/ticket/10093>
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:16 UTC