diff options
Diffstat (limited to 'libdimension/base/error.c')
-rw-r--r-- | libdimension/base/error.c | 144 |
1 files changed, 144 insertions, 0 deletions
diff --git a/libdimension/base/error.c b/libdimension/base/error.c new file mode 100644 index 0000000..6b8d18e --- /dev/null +++ b/libdimension/base/error.c @@ -0,0 +1,144 @@ +/************************************************************************* + * Copyright (C) 2009-2014 Tavian Barnes <tavianator@tavianator.com> * + * * + * This file is part of The Dimension Library. * + * * + * The Dimension Library is free software; you can redistribute it and/ * + * or modify it under the terms of the GNU Lesser General Public License * + * as published by the Free Software Foundation; either version 3 of the * + * License, or (at your option) any later version. * + * * + * The Dimension Library is distributed in the hope that it will be * + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty * + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this program. If not, see * + * <http://www.gnu.org/licenses/>. * + *************************************************************************/ + +/** + * @file + * Error handling. + */ + +#include "internal.h" +#include "internal/platform.h" +#include <errno.h> +#include <pthread.h> +#include <stdatomic.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/// Report internal errors in this file. +#define DMNSN_LOCAL_ERROR(str) \ + do { \ + fprintf(stderr, "Dimension ERROR: %s, %s:%u: %s\n", \ + DMNSN_FUNC, __FILE__, __LINE__, (str)); \ + abort(); \ + } while (0) + +/// The default fatal error handler. +static void dmnsn_default_fatal_error_fn(void); + +/// The current fatal error handler. +static atomic(dmnsn_fatal_error_fn *) dmnsn_fatal = ATOMIC_VAR_INIT(dmnsn_default_fatal_error_fn); + +/// The current resilience. +static atomic_bool dmnsn_always_die = ATOMIC_VAR_INIT(false); + +void +dmnsn_report_impl(bool die, const char *func, const char *file, unsigned int line, const char *str) +{ + // Save the value of errno + int err = errno; + + bool always_die = atomic_load(&dmnsn_always_die); + + // Print the diagnostic string + fprintf(stderr, "Dimension %s: %s, %s:%u: %s\n", + die ? "ERROR" : "WARNING", func, file, line, str); + + // Print the value of errno + if (err != 0) { + fprintf(stderr, "Last error: %d", err); +#if DMNSN_STRERROR_R + char errbuf[256]; + if (strerror_r(err, errbuf, 256) == 0) { + fprintf(stderr, " (%s)", errbuf); + } +#elif DMNSN_SYS_ERRLIST + if (err >= 0 && err < sys_nerr) { + fprintf(stderr, " (%s)", sys_errlist[err]); + } +#endif + fprintf(stderr, "\n"); + } + + // Print a stack trace to standard error + dmnsn_backtrace(stderr); + + // Call the fatal error handler if needed + if (die || always_die) { + static __thread bool thread_exiting = false; + + if (thread_exiting) { + if (die) { + // Prevent infinite recursion if the fatal error function itself calls + // dmnsn_error() (not dmnsn_warning()) + DMNSN_LOCAL_ERROR("Error raised while in error handler, aborting."); + } + } else { + thread_exiting = true; + + dmnsn_fatal_error_fn *fatal = dmnsn_get_fatal_error_fn(); + fatal(); + DMNSN_LOCAL_ERROR("Fatal error handler didn't exit."); + } + } +} + +void +dmnsn_report_warning(const char *func, const char *file, unsigned int line, const char *str) +{ + dmnsn_report_impl(false, func, file, line, str); +} + +DMNSN_NORETURN +dmnsn_report_error(const char *func, const char *file, unsigned int line, const char *str) +{ + dmnsn_report_impl(true, func, file, line, str); + DMNSN_UNREACHABLE(); +} + +void +dmnsn_die_on_warnings(bool always_die) +{ + atomic_store(&dmnsn_always_die, always_die); +} + +dmnsn_fatal_error_fn * +dmnsn_get_fatal_error_fn(void) +{ + dmnsn_fatal_error_fn *fatal = atomic_load(&dmnsn_fatal); + return fatal; +} + +void +dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal) +{ + atomic_store(&dmnsn_fatal, fatal); +} + +static void +dmnsn_default_fatal_error_fn(void) +{ + if (dmnsn_is_main_thread()) { + exit(EXIT_FAILURE); + } else { + pthread_exit(NULL); + } +} |