Opened 8 years ago
Closed 8 years ago
#10093 closed Bugs (wontfix)
Exceptions "teleport" between coroutines when switching inside catch
Reported by: | Owned by: | olli | |
---|---|---|---|
Milestone: | To Be Determined | Component: | context |
Version: | Boost 1.54.0 | Severity: | Problem |
Keywords: | Cc: |
Description
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:
#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.
Change History (5)
comment:1 by , 8 years ago
Component: | coroutine → context |
---|
follow-up: 3 comment:2 by , 8 years ago
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, coroutine_context
, where coroutine_context::jump
would be easy to modify: declare and call __cxx_get_globals()
(similar to the way boost.log already does) and save/restore the exception information.
comment:3 by , 8 years ago
Replying to snaury@…:
for example boost.context doesn't know anything about exceptions
boost.context handles Windows' structured exception handling
comment:4 by , 8 years ago
- 32bit MSVC works
- 64bit MSVC crashes the app - no info regarding to this issue found in the INet
- compiler using C++ Itanium ABI are problematic because cxaget_globals() does call malloc()
store/restore of
__cxa_eh_globals::caughtExceptions
and__cxa_eh_globals::uncaughtExceptions
per context we might end up with memory leaks (== closing a context without cleaning up its list of the active exceptions).
=> maybe the best solution would be a hint in the docu. not to call context inside a catch-block
comment:5 by , 8 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
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.
I've added a note in the doc not to re-throw exceptions from a catch-clause inside a coroutine/context.
boost.context provides the 'jump' facility