0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-25 00:02:34 +01:00

ircd: Rename and refactor ircd::assertion interface into ircd::panic.

This commit is contained in:
Jason Volk 2019-01-13 15:50:04 -08:00
parent 522656047e
commit cbf456a388
20 changed files with 115 additions and 148 deletions

View file

@ -23,7 +23,6 @@ namespace boost::system
namespace ircd
{
struct terminate;
struct assertion;
struct exception; // Root exception
// util
@ -52,8 +51,9 @@ namespace ircd
std::string string(const boost::system::error_code &);
std::string string(const boost::system::system_error &);
// Can be used to clobber the std::terminate_handler
void aborting() noexcept;
void panicking(const std::exception &) noexcept;
void panicking(const std::exception_ptr &) noexcept;
void _aborting_() noexcept;
}
/// The root exception type.
@ -106,18 +106,6 @@ struct ircd::exception
}
};
/// Terminates in debug mode; throws in release mode; always logs critical.
/// Not a replacement for a standard assert() macro. Used specifically when
/// the assert should also be hit in release-mode when NDEBUG is set. This
/// is basically the project's soft assert for now.
struct ircd::assertion
{
[[noreturn]] assertion(const std::exception &) noexcept(RB_DEBUG_LEVEL);
[[noreturn]] assertion(std::exception_ptr) noexcept(RB_DEBUG_LEVEL);
[[noreturn]] assertion(const string_view &) noexcept(RB_DEBUG_LEVEL);
[[noreturn]] assertion() noexcept(RB_DEBUG_LEVEL);
};
/// Always prefer ircd::terminate() to std::terminate() for all project code.
struct ircd::terminate
{
@ -192,26 +180,26 @@ struct name \
} \
};
/// Creates an assertion-type exception.
/// Creates a panic-type exception.
///
/// Throwable exception which will terminate on construction in debug mode
/// but throw normally in release mode. Ideally this should never be thrown
/// in release mode because the termination in debug means a test can never
/// pass and the triggering callsite should be eliminated. Nevertheless it
/// throws normally in release mode.
#define IRCD_ASSERTION(parent, name) \
/// Throwable which will terminate on construction in debug mode but throw
/// normally in release mode. Ideally this should never be thrown in release
/// mode because the termination in debug means a test can never pass and
/// the triggering callsite should be eliminated. Nevertheless it throws
/// normally in release mode for recovering at an exception handler.
#define IRCD_PANICKING(parent, name) \
struct name \
:parent \
{ \
template<class... args> \
name(const string_view &fmt = " ", args&&... ap) noexcept(RB_DEBUG_LEVEL) \
name(const string_view &fmt = " ", args&&... ap) noexcept \
:parent{generate_skip} \
{ \
generate(#name, fmt, ircd::va_rtti{std::forward<args>(ap)...}); \
ircd::assertion(*this); \
ircd::panicking(*this); \
} \
\
name(generate_skip_t) noexcept(RB_DEBUG_LEVEL) \
name(generate_skip_t) \
:parent{generate_skip} \
{ \
} \
@ -228,9 +216,9 @@ namespace ircd
IRCD_EXCEPTION(exception, error) // throw ircd::error("something bad")
IRCD_EXCEPTION(error, user_error) // throw ircd::user_error("something silly")
// Assertion errors; see IRCD_ASSERTION docs.
IRCD_ASSERTION(exception, assertive)
IRCD_ASSERTION(assertive, not_implemented)
// panic errors; see IRCD_PANICKING docs.
IRCD_PANICKING(exception, panic)
IRCD_PANICKING(panic, not_implemented)
}
template<class... args>

View file

@ -16,7 +16,7 @@
namespace ircd::json
{
IRCD_EXCEPTION(ircd::error, error);
IRCD_ASSERTION(error, print_error);
IRCD_PANICKING(error, print_error);
IRCD_EXCEPTION(error, parse_error);
IRCD_EXCEPTION(error, type_error);
IRCD_EXCEPTION(error, not_found);

View file

@ -98,9 +98,11 @@ struct ircd::log::log
public:
template<class... args> void operator()(const level &, const string_view &fmt, args&&...);
#if RB_LOG_LEVEL >= 0
#if RB_LOG_LEVEL >= 0 && !defined(NDEBUG)
template<class... args> [[noreturn]] void critical(const string_view &fmt, args&&...);
#elif RB_LOG_LEVEL >= 0
template<class... args> void critical(const string_view &fmt, args&&...);
#else // Required for DCE in gcc 6.3.0 20170519
#else
void critical(const string_view &fmt, ...);
#endif
@ -389,7 +391,24 @@ struct ircd::log::error
};
#endif
#if RB_LOG_LEVEL >= 0
#if RB_LOG_LEVEL >= 0 && !defined(NDEBUG)
struct ircd::log::critical
{
template<class... args> [[noreturn]]
critical(const log &log, const string_view &fmt, args&&... a)
{
vlog(log, level::CRITICAL, fmt, va_rtti{std::forward<args>(a)...});
ircd::terminate();
}
template<class... args> [[noreturn]]
critical(const string_view &fmt, args&&... a)
{
vlog(general, level::CRITICAL, fmt, va_rtti{std::forward<args>(a)...});
ircd::terminate();
}
};
#elif RB_LOG_LEVEL >= 0
struct ircd::log::critical
{
template<class... args>
@ -545,6 +564,10 @@ ircd::log::log::critical(const string_view &fmt,
args&&... a)
{
operator()(level::CRITICAL, fmt, va_rtti{std::forward<args>(a)...});
#ifndef NDEBUG
ircd::terminate();;
#endif
}
#else
inline void

View file

@ -125,7 +125,7 @@ try
}
catch(const std::bad_function_call &e)
{
throw assertive
throw panic
{
"Invalid parse (parsed:%p read:%p stop:%p unparsed:%zu remaining: %zu)",
parsed,

View file

@ -592,7 +592,7 @@ noexcept try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"AIO(%p) system::chase() qcount:%zu :%s", this, qcount, e.what()
};

View file

@ -798,7 +798,7 @@ void
ircd::ctx::assert_critical()
{
if(unlikely(critical_asserted))
throw ircd::assertive
throw panic
{
"%lu '%s' :Illegal context switch", id(), name()
};

View file

@ -2501,7 +2501,7 @@ rocksdb::Status
ircd::db::database::stats::passthru::Reset()
noexcept
{
throw assertive {"Unavailable for passthru"};
throw panic {"Unavailable for passthru"};
}
void
@ -2538,7 +2538,7 @@ uint64_t
ircd::db::database::stats::passthru::getTickerCount(const uint32_t tickerType)
const noexcept
{
throw assertive {"Unavailable for passthru"};
throw panic {"Unavailable for passthru"};
}
[[noreturn]]
@ -2547,7 +2547,7 @@ ircd::db::database::stats::passthru::setTickerCount(const uint32_t tickerType,
const uint64_t count)
noexcept
{
throw assertive {"Unavailable for passthru"};
throw panic {"Unavailable for passthru"};
}
[[noreturn]]
@ -2556,7 +2556,7 @@ ircd::db::database::stats::passthru::histogramData(const uint32_t type,
rocksdb::HistogramData *const data)
const noexcept
{
throw assertive {"Unavailable for passthru"};
throw panic {"Unavailable for passthru"};
}
[[noreturn]]
@ -2564,7 +2564,7 @@ uint64_t
ircd::db::database::stats::passthru::getAndResetTickerCount(const uint32_t tickerType)
noexcept
{
throw assertive {"Unavailable for passthru"};
throw panic {"Unavailable for passthru"};
}
///////////////////////////////////////////////////////////////////////////////
@ -4258,7 +4258,7 @@ noexcept try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"'%s': now micros :%s",
d.name,
@ -4285,7 +4285,7 @@ noexcept try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"'%s': time to string :%s",
d.name,
@ -4425,7 +4425,7 @@ noexcept
};
#endif
throw assertive
throw panic
{
"Independent (non-pool) context spawning not yet implemented"
};
@ -4485,7 +4485,7 @@ const noexcept try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"'%s': set background threads :%s",
d.name,
@ -4688,7 +4688,7 @@ const noexcept try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"'%s': get thread id :%s",
d.name,
@ -5682,7 +5682,7 @@ ircd::db::database::env::writable_file_direct::writable_file_direct(database *co
{
zero(buffer);
if(!aligned(logical_offset))
throw assertive
throw panic
{
"direct writable file requires read into buffer."
};
@ -6488,7 +6488,7 @@ noexcept try
// RocksDB sez that this call requires "External synchronization" i.e the
// caller, not this class is responsible for exclusion. We assert anyway.
if(unlikely(!bool(lock)))
throw assertive
throw panic
{
"'%s': Unexpected concurrent access to seqfile %p",
d.name,
@ -6575,7 +6575,7 @@ noexcept try
};
if(unlikely(!bool(lock)))
throw assertive
throw panic
{
"'%s': Unexpected concurrent access to seqfile %p",
d.name,
@ -6662,7 +6662,7 @@ noexcept
// RocksDB sez that this call requires "External synchronization" i.e the
// caller, not this class is responsible for exclusion. We assert anyway.
if(unlikely(!bool(lock)))
throw assertive
throw panic
{
"'%s': Unexpected concurrent access to seqfile %p",
d.name,
@ -8242,7 +8242,7 @@ rocksdb::Status
ircd::db::txn::handler::MarkBeginPrepare(bool b)
noexcept
{
ircd::assertion("not implemented");
ircd::not_implemented{};
return Status::OK();
}
@ -8250,7 +8250,7 @@ rocksdb::Status
ircd::db::txn::handler::MarkEndPrepare(const Slice &xid)
noexcept
{
ircd::assertion("not implemented");
ircd::not_implemented{};
return Status::OK();
}
@ -8258,7 +8258,7 @@ rocksdb::Status
ircd::db::txn::handler::MarkCommit(const Slice &xid)
noexcept
{
ircd::assertion("not implemented");
ircd::not_implemented{};
return Status::OK();
}
@ -8266,7 +8266,7 @@ rocksdb::Status
ircd::db::txn::handler::MarkRollback(const Slice &xid)
noexcept
{
ircd::assertion("not implemented");
ircd::not_implemented{};
return Status::OK();
}

View file

@ -18,12 +18,41 @@ noexcept
}
void
ircd::aborting()
ircd::_aborting_()
noexcept
{
std::set_terminate(&ircd_terminate_handler);
}
void
#ifndef NDEBUG
__attribute__((noreturn))
#endif
ircd::panicking(const std::exception_ptr &eptr)
noexcept
{
log::critical
{
"IRCd panic %s", what(eptr)
};
}
/// Called by the constructor of a panic exception when thown. We immediately
/// log a critical message, which is actually what triggers a termination when
/// assertions are enabled (!NDEBUG) otherwise a no-op.
void
#ifndef NDEBUG
__attribute__((noreturn))
#endif
ircd::panicking(const std::exception &e)
noexcept
{
log::critical
{
"IRCd panic %s", e.what()
};
}
std::string
ircd::string(const boost::system::system_error &e)
{
@ -223,76 +252,16 @@ noexcept
return size;
}
//
// assertion
//
ircd::assertion::assertion()
noexcept(RB_DEBUG_LEVEL)
:assertion
{
"without exception"_sv
}
{
}
ircd::assertion::assertion(const string_view &msg)
noexcept(RB_DEBUG_LEVEL)
{
log::critical
{
"IRCd Assertion :%s", msg
};
if(std::uncaught_exceptions())
assertion
{
std::current_exception()
};
assert(0);
throw assertive
{
"IRCd Assertion :%s", msg
};
}
ircd::assertion::assertion(std::exception_ptr eptr)
noexcept(RB_DEBUG_LEVEL) try
{
std::rethrow_exception(eptr);
}
catch(const std::exception &e)
{
assertion{e};
}
ircd::assertion::assertion(const std::exception &e)
noexcept(RB_DEBUG_LEVEL)
{
#ifdef RB_DEBUG
terminate{e};
#else
log::critical
{
"IRCd Assertion %s", e.what()
};
throw e;
#endif
}
//
// terminate
//
ircd::terminate::terminate()
noexcept
:terminate
{
std::current_exception()
}
{
fputs("\nIRCd Terminated.\n", stderr);
::fflush(stderr);
std::terminate();
}
ircd::terminate::terminate(std::exception_ptr eptr)
@ -307,14 +276,7 @@ noexcept
terminate{e};
}
log::critical
{
"IRCd Terminate without exception"
};
fputs("\nIRCd Terminate without exception\n", stderr);
::fflush(stdout);
::fflush(stderr);
std::terminate();
}
@ -322,13 +284,7 @@ noexcept
ircd::terminate::terminate(const std::exception &e)
noexcept
{
log::critical
{
"IRCd Terminated: %s", e.what()
};
fprintf(stderr, "\nIRCd Terminated: %s\n", e.what());
::fflush(stderr);
::fflush(stdout);
std::terminate();
}

View file

@ -98,7 +98,7 @@ noexcept
[[noreturn]]
ircd::fpe::errors_throw::errors_throw()
{
throw assertive
throw panic
{
"Not implemented in this environment."
};

View file

@ -1193,7 +1193,7 @@ ircd::fs::dev::sysfs(const mutable_buffer &out,
const ulong &id,
const string_view &relpath)
{
throw assertive
throw panic
{
"sysfs(5) is not available."
};

View file

@ -607,7 +607,7 @@ ircd::log::reflect(const level &f)
case level::_NUM_: break; // Allows -Wswitch to remind developer to add reflection here
};
throw assertive
throw panic
{
"'%d' is not a recognized log level", int(f)
};

View file

@ -3710,7 +3710,7 @@ const try
}
catch(const std::out_of_range &e)
{
throw assertive
throw panic
{
"Hook %p must name a '_site' to register with.", this
};
@ -3846,7 +3846,7 @@ const try
}
catch(const std::out_of_range &e)
{
throw assertive
throw panic
{
"Hook site %p requires a name", this
};

View file

@ -444,7 +444,7 @@ ircd::m::state::_insert(int8_t &height,
// Recursion metrics
const unwind down{[&height]{ --height; }};
if(unlikely(++height >= MAX_HEIGHT))
throw assertive{"recursion limit exceeded"};
throw panic{"recursion limit exceeded"};
// This function assumes that any node argument is a previously "existing"
// node which means it contains at least one key/value.
@ -734,7 +734,7 @@ ircd::m::state::_remove(int8_t &height,
{
const unwind down{[&height]{ --height; }};
if(unlikely(++height >= MAX_HEIGHT))
throw assertive{"recursion limit exceeded"};
throw panic{"recursion limit exceeded"};
node::rep rep{node};
const auto pos{node.find(key)};

View file

@ -1244,7 +1244,7 @@ try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"%s: %s", string(logheadbuf, *this), e.what()
};
@ -1948,7 +1948,7 @@ noexcept try
net::dock.notify_all();
if(unlikely(RB_DEBUG_LEVEL && opened(*this)))
throw assertive
throw panic
{
"Failed to ensure socket(%p) is disconnected from %s before dtor.",
this,
@ -2090,7 +2090,7 @@ catch(const boost::system::system_error &e)
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"socket:%lu disconnect: type: %d: %s",
this->id,
@ -2380,7 +2380,7 @@ noexcept try
}
// All other errors are unexpected, logged and ignored here.
default: throw assertive
default: throw panic
{
"socket(%p): unexpected: %s\n", (const void *)this, string(ec)
};

View file

@ -1090,7 +1090,7 @@ ircd::resource::response::response(client &client,
// Maximum size is realistically ok but ideally a small
// maximum; this exception should hit the developer in testing.
if(unlikely(!head.remaining()))
throw assertive
throw panic
{
"HTTP headers too large for buffer of %zu", sizeof(head_buf)
};

View file

@ -218,7 +218,7 @@ ircd::rfc3986::encode(const mutable_buffer &out,
{
assert(type(member.first) == json::STRING);
if(unlikely(!member.second.serial && type(member.second) != json::STRING))
throw assertive
throw panic
{
"Cannot encode non-serial json::member type '%s'",
reflect(type(member.second))

View file

@ -1798,7 +1798,7 @@ ircd::server::link::discard_read()
// just in case so this doesn't get loopy with discarding zero with
// an empty queue...
if(unlikely(!discard && !discarded))
throw assertive
throw panic
{
"peer(%p) link(%p) socket(%p) queue is empty and nothing to discard.",
peer,

View file

@ -306,7 +306,7 @@ try
}
catch(const std::exception &e)
{
ircd::assertion{e};
ircd::panicking(e);
}
bool

View file

@ -112,7 +112,7 @@ noexcept
};
if(!std::all_of(begin(tests), end(tests), [](const bool &b) { return b; }))
throw ircd::assertive
throw ircd::panic
{
"Seeded ed25519 test failed"
};

View file

@ -405,7 +405,7 @@ ircd::net::dns::resolver::set_tag(A&&... args)
return it->second;
}
throw assertive
throw panic
{
"Too many DNS queries"
};
@ -540,7 +540,7 @@ noexcept try
}
catch(const std::exception &e)
{
throw assertive
throw panic
{
"resolver::handle_reply(): %s", e.what()
};