void kr_fail(bool is_fatal, const char *expr, const char *func, const char *file, int line)
{
+ const int errno_orig = errno;
if (is_fatal)
kr_log_critical("requirement \"%s\" failed in %s@%s:%d\n", expr, func, file, line);
else
if (is_fatal || (kr_dbg_assumption_abort && !kr_dbg_assumption_fork))
abort();
else if (!kr_dbg_assumption_abort || !kr_dbg_assumption_fork)
- return;
+ goto recover;
// We want to fork and abort the child, unless rate-limited.
static uint64_t limited_until = 0;
const uint64_t now = kr_now();
if (now < limited_until)
- return;
+ goto recover;
if (kr_dbg_assumption_fork > 0) {
// Add jitter +- 25%; in other words: 75% + uniform(0,50%).
// Motivation: if a persistent problem starts happening, desynchronize
}
if (fork() == 0)
abort();
+recover:
+ errno = errno_orig;
}
/*
* If the check fails, optionally fork()+abort() to generate coredump
* and continue running in parent process. Return value must be handled to
* ensure safe recovery from error. Use kr_require() for unrecoverable checks.
+ * The errno variable is not mangled, e.g. you can: if (!kr_assume(...)) return errno;
*/
#define kr_assume(expression) kr_assume_func((expression), #expression, \
__func__, __FILE__, __LINE__)
return kr_error(EINVAL);
*val = strndup(node->string_, len);
if (!kr_assume(*val != NULL))
- return kr_error(EFAULT);
+ return kr_error(errno);
return kr_ok();
}