mirror of
https://github.com/matrix-construct/construct
synced 2024-09-27 11:18:51 +02:00
ircd::fpe: Condition experimental asynchronous exception use.
This commit is contained in:
parent
451dbe5def
commit
19ef3cfb67
3 changed files with 95 additions and 37 deletions
37
configure.ac
37
configure.ac
|
@ -366,6 +366,33 @@ AM_COND_IF([GENERIC],
|
||||||
CXXFLAGS+=" -march=native"
|
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
|
||||||
dnl Optimization
|
dnl Optimization
|
||||||
dnl
|
dnl
|
||||||
|
@ -415,15 +442,6 @@ dnl CXXFLAGS+=" -fcheck-pointer-bounds"
|
||||||
CXXFLAGS+=" -fchkp-instrument-marked-only"
|
CXXFLAGS+=" -fchkp-instrument-marked-only"
|
||||||
CXXFLAGS+=" -fstack-protector-explicit"
|
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 -ffriend-injection allows us to deduplicate declarations of friend
|
||||||
dnl functions in both the friend class and the enclosing namespace
|
dnl functions in both the friend class and the enclosing namespace
|
||||||
CXXFLAGS+=" -ffriend-injection"
|
CXXFLAGS+=" -ffriend-injection"
|
||||||
|
@ -636,6 +654,7 @@ RB_CHK_SYSHEADER(regex, [REGEX])
|
||||||
|
|
||||||
dnl unix platform
|
dnl unix platform
|
||||||
RB_CHK_SYSHEADER(unistd.h, [UNISTD_H])
|
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/time.h, [SYS_TIME_H])
|
||||||
RB_CHK_SYSHEADER(sys/stat.h, [SYS_STAT_H])
|
RB_CHK_SYSHEADER(sys/stat.h, [SYS_STAT_H])
|
||||||
RB_CHK_SYSHEADER(sys/statvfs.h, [SYS_STATVFS_H])
|
RB_CHK_SYSHEADER(sys/statvfs.h, [SYS_STATVFS_H])
|
||||||
|
|
|
@ -11,11 +11,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#define HAVE_IRCD_FPE_H
|
#define HAVE_IRCD_FPE_H
|
||||||
|
|
||||||
extern "C"
|
|
||||||
{
|
|
||||||
struct sigaction;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace ircd::fpe
|
namespace ircd::fpe
|
||||||
{
|
{
|
||||||
struct errors_handle;
|
struct errors_handle;
|
||||||
|
@ -27,6 +22,9 @@ namespace ircd::fpe
|
||||||
void throw_errors(const ushort &flags);
|
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
|
struct ircd::fpe::errors_handle
|
||||||
{
|
{
|
||||||
fexcept_t theirs;
|
fexcept_t theirs;
|
||||||
|
@ -40,6 +38,18 @@ struct ircd::fpe::errors_handle
|
||||||
~errors_handle() noexcept(false);
|
~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
|
struct ircd::fpe::errors_throw
|
||||||
{
|
{
|
||||||
custom_ptr<struct sigaction> their_sa;
|
custom_ptr<struct sigaction> their_sa;
|
||||||
|
|
75
ircd/fpe.cc
75
ircd/fpe.cc
|
@ -8,7 +8,7 @@
|
||||||
// copyright notice and this permission notice is present in all copies. The
|
// copyright notice and this permission notice is present in all copies. The
|
||||||
// full license for this software is available in the LICENSE file.
|
// full license for this software is available in the LICENSE file.
|
||||||
|
|
||||||
#include <signal.h>
|
#include <RB_INC_SIGNAL_H
|
||||||
|
|
||||||
#ifndef __GNUC__
|
#ifndef __GNUC__
|
||||||
#pragma STDC FENV_ACCESS on
|
#pragma STDC FENV_ACCESS on
|
||||||
|
@ -18,34 +18,38 @@
|
||||||
// errors_throw
|
// errors_throw
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#if defined(IRCD_USE_ASYNC_EXCEPTIONS) \
|
||||||
|
&& defined(HAVE_SIGNAL_H) \
|
||||||
|
&& defined(__GNUC__) \
|
||||||
|
|
||||||
namespace ircd::fpe
|
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;
|
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);
|
extern "C" void ircd_fpe_handle_sigfpe(int signum, siginfo_t *const si, void *const uctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
decltype(ircd::fpe::sigfpe_eptr)
|
[[noreturn]] void
|
||||||
ircd::fpe::sigfpe_eptr
|
|
||||||
{
|
|
||||||
std::make_exception_ptr(std::domain_error
|
|
||||||
{
|
|
||||||
"Floating Point Exception"
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
__attribute__((noreturn))
|
|
||||||
void
|
|
||||||
ircd::fpe::ircd_fpe_handle_sigfpe(int signum,
|
ircd::fpe::ircd_fpe_handle_sigfpe(int signum,
|
||||||
siginfo_t *const si,
|
siginfo_t *const si,
|
||||||
void *const uctx)
|
void *const uctx)
|
||||||
{
|
{
|
||||||
assert(si);
|
assert(si);
|
||||||
assert(signum == SIGFPE);
|
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);
|
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
|
// errors_handle
|
||||||
//
|
//
|
||||||
|
@ -182,14 +209,16 @@ ircd::fpe::reflect_sicode(const int &code)
|
||||||
{
|
{
|
||||||
switch(code)
|
switch(code)
|
||||||
{
|
{
|
||||||
case FPE_INTDIV: return "INTDIV";
|
#ifdef HAVE_SIGNAL_H
|
||||||
case FPE_INTOVF: return "INTOVF";
|
case FPE_INTDIV: return "INTDIV";
|
||||||
case FPE_FLTDIV: return "FLTDIV";
|
case FPE_INTOVF: return "INTOVF";
|
||||||
case FPE_FLTOVF: return "FLTOVF";
|
case FPE_FLTDIV: return "FLTDIV";
|
||||||
case FPE_FLTUND: return "FLTUND";
|
case FPE_FLTOVF: return "FLTOVF";
|
||||||
case FPE_FLTRES: return "FLTRES";
|
case FPE_FLTUND: return "FLTUND";
|
||||||
case FPE_FLTINV: return "FLTINV";
|
case FPE_FLTRES: return "FLTRES";
|
||||||
case FPE_FLTSUB: return "FLTSUB";
|
case FPE_FLTINV: return "FLTINV";
|
||||||
|
case FPE_FLTSUB: return "FLTSUB";
|
||||||
|
#endif // HAVE_SIGNAL_H
|
||||||
}
|
}
|
||||||
|
|
||||||
return "?????";
|
return "?????";
|
||||||
|
|
Loading…
Reference in a new issue