From fdbaf88e5e30b95be85c08f8c4cc2857420a62d9 Mon Sep 17 00:00:00 2001 From: Eric Frias Date: Sat, 8 Mar 2014 18:48:19 -0500 Subject: [PATCH] Provide a way to intercept Win32 structured exceptions in async tasks Something about the stacks created by boost::context prevents global structured exception handlers from being called. This allows the user to register a handler which will be called when there is an unhandled structured exception in async task. --- CMakeLists.txt | 4 ++-- include/fc/thread/task.hpp | 2 ++ include/fc/thread/thread.hpp | 24 ++++++++++++++++++++++++ src/thread/task.cpp | 16 ++++++++++++++++ src/thread/thread.cpp | 18 ++++++++++++++++-- 5 files changed, 60 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index df294ed..bcef70b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,8 +174,8 @@ IF(WIN32) # Needed to disable MSVC autolinking feature (#pragma comment) BOOST_ALL_NO_LIB ) - # Activate C++ exception handling inc. SEH to catch GPFs - target_compile_options(fc PUBLIC /EHa) + # Activate C++ exception handling, assume extern C calls don't throw + target_compile_options(fc PUBLIC /EHsc) ELSE() SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall" ) diff --git a/include/fc/thread/task.hpp b/include/fc/thread/task.hpp index 664a5db..5c1b30e 100644 --- a/include/fc/thread/task.hpp +++ b/include/fc/thread/task.hpp @@ -33,6 +33,8 @@ namespace fc { void* _functor; void (*_destroy_functor)(void*); void (*_run_functor)(void*, void* ); + + void run_impl(); }; namespace detail { diff --git a/include/fc/thread/thread.hpp b/include/fc/thread/thread.hpp index eec4f72..ec0f029 100644 --- a/include/fc/thread/thread.hpp +++ b/include/fc/thread/thread.hpp @@ -175,5 +175,29 @@ namespace fc { auto async( Functor&& f, const char* desc ="", priority prio = priority()) -> fc::future { return fc::thread::current().async( fc::forward(f), desc, prio ); } + +} // end namespace fc + +#ifdef _MSC_VER +struct _EXCEPTION_POINTERS; + +namespace fc { + /* There's something about the setup of the stacks created for fc::async tasks + * that screws up the global structured exception filters installed by + * SetUnhandledExceptionFilter(). The only way I've found to catch an + * unhaldned structured exception thrown in an async task is to put a + * __try/__except block inside the async task. + * We do just that, and if a SEH escapes outside the function running + * in the async task, fc will call an exception filter privided by + * set_unhandled_structured_exception_filter(), passing as arguments + * the result of GetExceptionCode() and GetExceptionInformation(). + * + * Right now there is only one global exception filter, used for any + * async task. + */ + typedef int (*unhandled_exception_filter_type)(unsigned, _EXCEPTION_POINTERS*); + void set_unhandled_structured_exception_filter(unhandled_exception_filter_type new_filter); + unhandled_exception_filter_type get_unhandled_structured_exception_filter(); +#endif } diff --git a/src/thread/task.cpp b/src/thread/task.cpp index b963f2c..98edbd3 100644 --- a/src/thread/task.cpp +++ b/src/thread/task.cpp @@ -7,6 +7,11 @@ #include #include +#ifdef _MSC_VER +# include +# include +#endif + namespace fc { task_base::task_base(void* func) : @@ -18,6 +23,17 @@ namespace fc { } void task_base::run() { +#ifdef _MSC_VER + __try { +#endif + run_impl(); +#ifdef _MSC_VER + } __except (get_unhandled_structured_exception_filter() ? get_unhandled_structured_exception_filter()(GetExceptionCode(), GetExceptionInformation()) : EXCEPTION_CONTINUE_SEARCH) { + ExitProcess(1); + } +#endif + } + void task_base::run_impl() { try { _run_functor( _functor, _promise_impl ); } diff --git a/src/thread/thread.cpp b/src/thread/thread.cpp index 0a929eb..3ab54f6 100644 --- a/src/thread/thread.cpp +++ b/src/thread/thread.cpp @@ -381,5 +381,19 @@ namespace fc { return this == ¤t(); } - -} +#ifdef _MSC_VER + /* support for providing a structured exception handler for async tasks */ + namespace detail + { + unhandled_exception_filter_type unhandled_structured_exception_filter = nullptr; + } + void set_unhandled_structured_exception_filter(unhandled_exception_filter_type new_filter) + { + detail::unhandled_structured_exception_filter = new_filter; + } + unhandled_exception_filter_type get_unhandled_structured_exception_filter() + { + return detail::unhandled_structured_exception_filter; + } +#endif // _MSC_VER +} // end namespace fc