diff --git a/include/ircd/ctx/exception_handler.h b/include/ircd/ctx/exception_handler.h index e07eeb608..bda135826 100644 --- a/include/ircd/ctx/exception_handler.h +++ b/include/ircd/ctx/exception_handler.h @@ -11,11 +11,10 @@ #pragma once #define HAVE_IRCD_CTX_EXCEPTION_HANDLER_H -namespace ircd::ctx { -inline namespace this_ctx +namespace ircd::ctx { struct exception_handler; -}} +} /// An instance of exception_handler must be present to allow a context /// switch inside a catch block. This is due to ABI limitations that stack @@ -28,9 +27,14 @@ inline namespace this_ctx /// /// The exception cannot then be rethrown. DO NOT RETHROW THE EXCEPTION. /// -struct ircd::ctx::this_ctx::exception_handler +struct ircd::ctx::exception_handler :std::exception_ptr { + static uint uncaught_exceptions(const uint &) noexcept; + static uint uncaught_exceptions() noexcept; + static void end_catch() noexcept; + + public: exception_handler() noexcept; exception_handler(exception_handler &&) = delete; exception_handler(const exception_handler &) = delete; diff --git a/ircd/Makefile.am b/ircd/Makefile.am index 046a1b9e3..502a7c465 100644 --- a/ircd/Makefile.am +++ b/ircd/Makefile.am @@ -149,6 +149,7 @@ libircd_la_SOURCES += prof.cc libircd_la_SOURCES += fs.cc libircd_la_SOURCES += ios.cc libircd_la_SOURCES += ctx.cc +libircd_la_SOURCES += ctx_eh.cc libircd_la_SOURCES += ctx_ole.cc if AIO libircd_la_SOURCES += fs_aio.cc diff --git a/ircd/ctx.cc b/ircd/ctx.cc index 2ba3bf39f..03ab9ca73 100644 --- a/ircd/ctx.cc +++ b/ircd/ctx.cc @@ -8,7 +8,6 @@ // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. -#include #include #include "ctx.h" @@ -756,30 +755,6 @@ noexcept interruptible(theirs, std::nothrow); } -// -// exception_handler -// - -ircd::ctx::this_ctx::exception_handler::exception_handler() -noexcept -:std::exception_ptr -{ - std::current_exception() -} -{ - assert(bool(*this)); - - #ifdef HAVE_CXXABI_H - if(current) - __cxxabiv1::__cxa_end_catch(); - #endif - - // We don't yet support more levels of exceptions; after ending this - // catch we can't still be in another one. This doesn't apply if we're - // not on any ctx currently. - assert(!current || !std::uncaught_exceptions()); -} - // // critical_assertion // @@ -996,14 +971,6 @@ ircd::ctx::continuation::noop_interruptor{[] return; }}; -#ifdef HAVE_CXXABI_H -struct __cxxabiv1::__cxa_eh_globals -{ - __cxa_exception *caughtExceptions; - unsigned int uncaughtExceptions; -}; -#endif - // // continuation // @@ -1025,7 +992,7 @@ ircd::ctx::continuation::continuation(const predicate &pred, } ,uncaught_exceptions { - uint(std::uncaught_exceptions()) + exception_handler::uncaught_exceptions(0) } { assert(self != nullptr); @@ -1036,6 +1003,10 @@ ircd::ctx::continuation::continuation(const predicate &pred, assert_critical(); assert(!critical_asserted); + // Confirming the uncaught exception count was saved and set to zero in the + // initializer list. + assert(!std::uncaught_exceptions()); + // Note: Construct an instance of ctx::exception_handler to enable yielding // in your catch block. // @@ -1057,15 +1028,6 @@ ircd::ctx::continuation::continuation(const predicate &pred, // its execution run and is now yielding. mark(prof::event::YIELD); - #ifdef HAVE_CXXABI_H - using __cxxabiv1::__cxa_get_globals_fast; - assert(__cxa_get_globals_fast()); - assert(__cxa_get_globals_fast()->uncaughtExceptions == uncaught_exceptions); - __cxa_get_globals_fast()->uncaughtExceptions = 0; - #else - assert(!uncaught_exceptions); - #endif - // Check that we saved a valid context reference to this object for later. assert(self->yc); @@ -1097,16 +1059,9 @@ noexcept // upon resuming execution. ircd::ctx::current = self; - #ifdef HAVE_CXXABI_H - using __cxxabiv1::__cxa_get_globals_fast; - assert(__cxa_get_globals_fast()); - assert(__cxa_get_globals_fast()->uncaughtExceptions == 0); - __cxa_get_globals_fast()->uncaughtExceptions = uncaught_exceptions; - assert(uint(std::uncaught_exceptions()) == uncaught_exceptions); - #else + // Restore the uncaught exception count for this context to the cxxabi assert(std::uncaught_exceptions() == 0); - assert(uncaught_exceptions == 0); - #endif + exception_handler::uncaught_exceptions(uncaught_exceptions); // Tell the profiler this is the point where the context is now resuming. // On some optimized builds this might lead nowhere. diff --git a/ircd/ctx_eh.cc b/ircd/ctx_eh.cc new file mode 100644 index 000000000..638202fc5 --- /dev/null +++ b/ircd/ctx_eh.cc @@ -0,0 +1,120 @@ +// Matrix Construct +// +// Copyright (C) Matrix Construct Developers, Authors & Contributors +// Copyright (C) 2016-2019 Jason Volk +// +// Permission to use, copy, modify, and/or distribute this software for any +// purpose with or without fee is hereby granted, provided that the above +// copyright notice and this permission notice is present in all copies. The +// full license for this software is available in the LICENSE file. + +#include +#include "ctx.h" + +#ifdef HAVE_CXXABI_H +struct __cxxabiv1::__cxa_eh_globals +{ + __cxa_exception *caughtExceptions; + unsigned int uncaughtExceptions; +}; +#endif + +// +// exception_handler +// + +ircd::ctx::exception_handler::exception_handler() +noexcept +:std::exception_ptr +{ + // capture a shared reference of the current exception before the catch + // block closes around it. + std::current_exception() +} +{ + assert(bool(*this)); + + // close the catch block. + end_catch(); + + // We don't yet support more levels of exceptions; after ending this + // catch we can't still be in another one. This doesn't apply if we're + // not on any ctx currently. + assert(!current || !std::uncaught_exceptions()); +} + +// +// util +// + +#ifdef HAVE_CXXABI_H +void +ircd::ctx::exception_handler::end_catch() +noexcept +{ + if(likely(current)) + __cxxabiv1::__cxa_end_catch(); +} +#else +#warning "CXXABI not available. Context switch inside catch block may be unsafe!" +void +ircd::ctx::exception_handler::end_catch() +noexcept +{ +} +#endif + +#ifdef HAVE_CXXABI_H +uint +ircd::ctx::exception_handler::uncaught_exceptions() +noexcept +{ + const auto &cxa_globals + { + __cxxabiv1::__cxa_get_globals_fast() + }; + + assert(cxa_globals); + return cxa_globals->uncaughtExceptions; +} +#else +#warning "CXXABI not available. Context switch with uncaught exceptions may be unsafe!" +uint +ircd::ctx::exception_handler::uncaught_exceptions() +noexcept +{ + return std::uncaught_exceptions(); +} +#endif + +#ifdef HAVE_CXXABI_H +uint +ircd::ctx::exception_handler::uncaught_exceptions(const uint &val) +noexcept +{ + const auto &cxa_globals + { + __cxxabiv1::__cxa_get_globals_fast() + }; + + assert(cxa_globals); + const auto ret + { + cxa_globals->uncaughtExceptions + }; + + assert(unsigned(ret) == unsigned(std::uncaught_exceptions())); + cxa_globals->uncaughtExceptions = val; + return ret; +} +#else +#warning "CXXABI not available. Context switch with uncaught exceptions may be unsafe!" +uint +ircd::ctx::exception_handler::uncaught_exceptions(const uint &val) +noexcept +{ + assert(std::uncaught_exceptions() == val); + return std::uncaught_exceptions(); +} +#endif