/************************************************************************* * Copyright (C) 2009-2010 Tavian Barnes * * * * 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 * * . * *************************************************************************/ /** * @file * Error handling. */ #include "dimension-impl.h" #include #include /* For fprintf() */ #include /* For exit() */ /** The default fatal error handler. */ static void dmnsn_default_fatal_error_fn(void); /** The current fatal error handler. */ static dmnsn_fatal_error_fn *dmnsn_fatal = &dmnsn_default_fatal_error_fn; /** Mutex which protects \c dmnsn_fatal. */ static pthread_mutex_t dmnsn_fatal_mutex = PTHREAD_MUTEX_INITIALIZER; /** The current resilience. */ static dmnsn_severity dmnsn_resilience = DMNSN_SEVERITY_MEDIUM; /** Mutex which protexts \c dmnsn_resilience. */ static pthread_mutex_t dmnsn_resilience_mutex = PTHREAD_MUTEX_INITIALIZER; /* Called by dmnsn_error macro (don't call directly). */ void dmnsn_report_error(dmnsn_severity severity, const char *func, const char *file, unsigned int line, const char *str) { if (severity >= dmnsn_get_resilience()) { /* An error more severe than our resilience happened, bail out */ fprintf(stderr, "Dimension ERROR: %s, %s:%u: %s\n", func, file, line, str); dmnsn_fatal_error_fn *fatal = dmnsn_get_fatal_error_fn(); (*fatal)(); exit(EXIT_FAILURE); /* Failsafe in case *dmnsn_fatal doesn't exit */ } else { /* A trivial error happened, warn and continue */ fprintf(stderr, "Dimension WARNING: %s, %s:%u: %s\n", func, file, line, str); } } /* Return the current resilience, thread-safely. */ dmnsn_severity dmnsn_get_resilience(void) { dmnsn_severity resilience; if (pthread_mutex_lock(&dmnsn_resilience_mutex) != 0) { /* Couldn't lock the mutex, so warn and continue. */ fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't lock resilience mutex."); } resilience = dmnsn_resilience; /* Copy the static variable to a local */ if (pthread_mutex_unlock(&dmnsn_resilience_mutex) != 0) { /* Couldn't unlock the mutex, so warn and continue. If the mutex was locked earlier, the next dmnsn_get/set_resilience is likely to hang. */ fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't unlock resilience mutex."); } return resilience; } /* Set the resilience, thread-safely */ void dmnsn_set_resilience(dmnsn_severity resilience) { if (resilience < DMNSN_SEVERITY_LOW || resilience > DMNSN_SEVERITY_HIGH) { /* Tried to set an illegal resilience, bail out */ fprintf(stderr, "Dimension ERROR: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Resilience has wrong value."); dmnsn_fatal_error_fn *fatal = dmnsn_get_fatal_error_fn(); (*fatal)(); exit(EXIT_FAILURE); } if (pthread_mutex_lock(&dmnsn_resilience_mutex) != 0) { /* Couldn't lock the mutex, so warn and continue. */ fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't lock resilience mutex."); } dmnsn_resilience = resilience; if (pthread_mutex_unlock(&dmnsn_resilience_mutex) != 0) { /* Couldn't unlock the mutex, so warn and continue. If the mutex was locked earlier, the next dmnsn_get/set_resilience is likely to hang. */ fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't unlock resilience mutex."); } } dmnsn_fatal_error_fn * dmnsn_get_fatal_error_fn(void) { dmnsn_fatal_error_fn *fatal; if (pthread_mutex_lock(&dmnsn_fatal_mutex) != 0) { fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't lock fatal error handler mutex."); } fatal = dmnsn_fatal; if (pthread_mutex_unlock(&dmnsn_fatal_mutex) != 0) { fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't unlock fatal error handler mutex."); } return fatal; } void dmnsn_set_fatal_error_fn(dmnsn_fatal_error_fn *fatal) { if (pthread_mutex_lock(&dmnsn_fatal_mutex) != 0) { fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't lock fatal error handler mutex."); } dmnsn_fatal = fatal; if (pthread_mutex_unlock(&dmnsn_fatal_mutex) != 0) { fprintf(stderr, "Dimension WARNING: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Couldn't unlock fatal error handler mutex."); } } static void dmnsn_default_fatal_error_fn(void) { /* Prevent infinite recursion if the fatal error function itself calls dmnsn_error() */ static __thread bool thread_exiting = false; dmnsn_backtrace(stderr); if (thread_exiting) { fprintf(stderr, "Dimension ERROR: %s, line %u: %s\n", DMNSN_FUNC, __LINE__, "Error raised while in error handler, aborting."); abort(); } else if (dmnsn_is_main_thread()) { thread_exiting = true; exit(EXIT_FAILURE); } else { thread_exiting = true; pthread_exit(NULL); } }