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.
This commit is contained in:
Eric Frias 2014-03-08 18:48:19 -05:00
parent f5249dc2d6
commit fdbaf88e5e
5 changed files with 60 additions and 4 deletions

View file

@ -174,8 +174,8 @@ IF(WIN32)
# Needed to disable MSVC autolinking feature (#pragma comment) # Needed to disable MSVC autolinking feature (#pragma comment)
BOOST_ALL_NO_LIB BOOST_ALL_NO_LIB
) )
# Activate C++ exception handling inc. SEH to catch GPFs # Activate C++ exception handling, assume extern C calls don't throw
target_compile_options(fc PUBLIC /EHa) target_compile_options(fc PUBLIC /EHsc)
ELSE() ELSE()
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall" ) SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall" )

View file

@ -33,6 +33,8 @@ namespace fc {
void* _functor; void* _functor;
void (*_destroy_functor)(void*); void (*_destroy_functor)(void*);
void (*_run_functor)(void*, void* ); void (*_run_functor)(void*, void* );
void run_impl();
}; };
namespace detail { namespace detail {

View file

@ -175,5 +175,29 @@ namespace fc {
auto async( Functor&& f, const char* desc ="", priority prio = priority()) -> fc::future<decltype(f())> { auto async( Functor&& f, const char* desc ="", priority prio = priority()) -> fc::future<decltype(f())> {
return fc::thread::current().async( fc::forward<Functor>(f), desc, prio ); return fc::thread::current().async( fc::forward<Functor>(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
} }

View file

@ -7,6 +7,11 @@
#include <fc/log/logger.hpp> #include <fc/log/logger.hpp>
#include <boost/exception/all.hpp> #include <boost/exception/all.hpp>
#ifdef _MSC_VER
# include <fc/thread/thread.hpp>
# include <Windows.h>
#endif
namespace fc { namespace fc {
task_base::task_base(void* func) task_base::task_base(void* func)
: :
@ -18,6 +23,17 @@ namespace fc {
} }
void task_base::run() { 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 { try {
_run_functor( _functor, _promise_impl ); _run_functor( _functor, _promise_impl );
} }

View file

@ -381,5 +381,19 @@ namespace fc {
return this == &current(); return this == &current();
} }
#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