0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-05-18 10:53:48 +02:00

ircd::fpe: Condition experimental asynchronous exception use.

This commit is contained in:
Jason Volk 2018-11-12 18:50:09 -08:00
parent 451dbe5def
commit 19ef3cfb67
3 changed files with 95 additions and 37 deletions

View file

@ -366,6 +366,33 @@ AM_COND_IF([GENERIC],
CXXFLAGS+=" -march=native"
])
dnl !!! Experimental !!!
dnl
dnl Allows a signal handler to throw a C++ exception if the signal is from
dnl a CPU trap on an instruction (ex. SIGFPE from a floating point divide
dnl by zero). This allows the signal handler to transfer control to the
dnl catch block wrapping the area which errored, thereby aborting any further
dnl execution after the signal was received which would have to manually check
dnl for such an error after each floating point operation and throw.
AC_ARG_ENABLE(asyncexcept, AC_HELP_STRING([--enable-asyncexcept], [Enable experimental asynchronous exceptions]),
[
asyncexcept="yes"
], [
asyncexcept="no"
])
dnl CHARYBDIS_C_GCC_TRY_FLAGS([-fnon-call-exceptions], charybdis_cv_c_gcc_f_non_call_exceptions)
if test "$asyncexcept" = "yes"; then
IRCD_DEFINE(USE_ASYNC_EXCEPTIONS, [1], [Experimental asynchronous exceptions are supported])
CXXFLAGS+=" -fasynchronous-unwind-tables"
CXXFLAGS+=" -fnon-call-exceptions"
fi
AM_CONDITIONAL([ASYNCEXCEPT], [[[[ $asyncexcept = "yes" ]]]])
dnl
dnl Optimization
dnl
@ -415,15 +442,6 @@ dnl CXXFLAGS+=" -fcheck-pointer-bounds"
CXXFLAGS+=" -fchkp-instrument-marked-only"
CXXFLAGS+=" -fstack-protector-explicit"
dnl !!! Experimental !!!
dnl Allows a signal handler to throw a C++ exception if the signal is from
dnl a CPU trap on an instruction (ex. SIGFPE from a floating point divide
dnl by zero). This allows the signal handler to transfer control to the
dnl catch block wrapping the area which errored, thereby aborting any further
dnl execution after the SIGFPE was received which would have to manually check
dnl for such an error after each floating point operation and throw.
CXXFLAGS+=" -fnon-call-exceptions"
dnl -ffriend-injection allows us to deduplicate declarations of friend
dnl functions in both the friend class and the enclosing namespace
CXXFLAGS+=" -ffriend-injection"
@ -636,6 +654,7 @@ RB_CHK_SYSHEADER(regex, [REGEX])
dnl unix platform
RB_CHK_SYSHEADER(unistd.h, [UNISTD_H])
RB_CHK_SYSHEADER(signal.h, [SIGNAL_H])
RB_CHK_SYSHEADER(sys/time.h, [SYS_TIME_H])
RB_CHK_SYSHEADER(sys/stat.h, [SYS_STAT_H])
RB_CHK_SYSHEADER(sys/statvfs.h, [SYS_STATVFS_H])

View file

@ -11,11 +11,6 @@
#pragma once
#define HAVE_IRCD_FPE_H
extern "C"
{
struct sigaction;
};
namespace ircd::fpe
{
struct errors_handle;
@ -27,6 +22,9 @@ namespace ircd::fpe
void throw_errors(const ushort &flags);
}
/// Perform a single floating point operation at a time within the scope
/// of fpe::errors_handle. After each operation check the floating point
/// unit for an error status flag and throw a C++ exception.
struct ircd::fpe::errors_handle
{
fexcept_t theirs;
@ -40,6 +38,18 @@ struct ircd::fpe::errors_handle
~errors_handle() noexcept(false);
};
// We don't include <signal.h> in IRCd's standard include list. Instead that
// is conditionally included in the appropriate .cc file. All we require here
// is a forward declaration.
extern "C"
{
struct sigaction;
};
/// Experimental floating-point error handling strategy which throws
/// a C++ exception directly from the instruction which faulted the FPU.
/// The stack will unwind directly from that point in the execution and
/// control will be transferred to the appropriate catch block.
struct ircd::fpe::errors_throw
{
custom_ptr<struct sigaction> their_sa;

View file

@ -8,7 +8,7 @@
// copyright notice and this permission notice is present in all copies. The
// full license for this software is available in the LICENSE file.
#include <signal.h>
#include <RB_INC_SIGNAL_H
#ifndef __GNUC__
#pragma STDC FENV_ACCESS on
@ -18,34 +18,38 @@
// errors_throw
//
#if defined(IRCD_USE_ASYNC_EXCEPTIONS) \
&& defined(HAVE_SIGNAL_H) \
&& defined(__GNUC__) \
namespace ircd::fpe
{
constexpr const size_t &SA_HEAP_MAX {64};
/// The maximum number of sigactions we can maintain in our internal space.
constexpr const size_t &SA_HEAP_MAX {48};
/// This internal buffer is used for holding instances of struct sigaction
/// as our external interface only has a forward-declaration and our class
/// only maintains a pointer member for it.
thread_local allocator::fixed<struct sigaction, SA_HEAP_MAX> sa_heap;
extern const std::exception_ptr sigfpe_eptr;
extern "C" void ircd_fpe_handle_sigfpe(int signum, siginfo_t *const si, void *const uctx);
}
decltype(ircd::fpe::sigfpe_eptr)
ircd::fpe::sigfpe_eptr
{
std::make_exception_ptr(std::domain_error
{
"Floating Point Exception"
})
};
__attribute__((noreturn))
void
[[noreturn]] void
ircd::fpe::ircd_fpe_handle_sigfpe(int signum,
siginfo_t *const si,
void *const uctx)
{
assert(si);
assert(signum == SIGFPE);
std::rethrow_exception(sigfpe_eptr); //TODO: still malloc()'s :/
//TODO: due to __cxa_allocate_exception() this still malloc()'s :/
// We can't have a floating point error within malloc() and family
// or this signal will cleave it at an intermediate state and reenter.
throw std::domain_error
{
reflect_sicode(si->si_code)
};
}
//
@ -86,6 +90,29 @@ noexcept
syscall(std::fesetexceptflag, &their_fe, FE_ALL_EXCEPT);
}
#else // IRCD_USE_ASYNC_EXCEPTIONS
//
// errors_throw::errors_throw
//
__attribute__((noreturn))
ircd::fpe::errors_throw::errors_throw()
{
throw assertive
{
"Not implemented in this environment."
};
}
ircd::fpe::errors_throw::~errors_throw()
noexcept
{
assert(0);
}
#endif // IRCD_USE_ASYNC_EXCEPTIONS
//
// errors_handle
//
@ -182,14 +209,16 @@ ircd::fpe::reflect_sicode(const int &code)
{
switch(code)
{
case FPE_INTDIV: return "INTDIV";
case FPE_INTOVF: return "INTOVF";
case FPE_FLTDIV: return "FLTDIV";
case FPE_FLTOVF: return "FLTOVF";
case FPE_FLTUND: return "FLTUND";
case FPE_FLTRES: return "FLTRES";
case FPE_FLTINV: return "FLTINV";
case FPE_FLTSUB: return "FLTSUB";
#ifdef HAVE_SIGNAL_H
case FPE_INTDIV: return "INTDIV";
case FPE_INTOVF: return "INTOVF";
case FPE_FLTDIV: return "FLTDIV";
case FPE_FLTOVF: return "FLTOVF";
case FPE_FLTUND: return "FLTUND";
case FPE_FLTRES: return "FLTRES";
case FPE_FLTINV: return "FLTINV";
case FPE_FLTSUB: return "FLTSUB";
#endif // HAVE_SIGNAL_H
}
return "?????";