From: Daniel Arndt Date: Sun, 13 Aug 2017 22:15:48 +0000 (+0200) Subject: Treat the nothrow exception handling special X-Git-Tag: v9.0.0-rc1~1207^2 X-Git-Url: https://gitweb.dealii.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d4a13ab15a20efbd59bcf07fd7bdfca443816806;p=dealii.git Treat the nothrow exception handling special --- diff --git a/include/deal.II/base/exceptions.h b/include/deal.II/base/exceptions.h index fb25f459cd..d70c4aa50d 100644 --- a/include/deal.II/base/exceptions.h +++ b/include/deal.II/base/exceptions.h @@ -234,16 +234,9 @@ namespace deal_II_exceptions * Depending on whether deal_II_exceptions::disable_abort_on_exception was * called, this function either aborts the program flow by printing the * error message provided by @p exc and calling std::abort(), or - * throws @p exc instead (if @p nothrow is set to false). - * - * If the boolean @p nothrow is set to true and - * deal_II_exceptions::disable_abort_on_exception was called, the - * exception type is just printed to deallog and program flow - * continues. This is useful if throwing an exception is prohibited - * (e.g. in a destructor with noexcept(true) or - * throw()). + * throws @p exc instead. */ - void abort (const ExceptionBase &exc, bool nothrow = false); + void abort (const ExceptionBase &exc); /** * An enum describing how to treat an exception in issue_error. @@ -298,13 +291,29 @@ namespace deal_II_exceptions dealii::deal_II_exceptions::internals::abort(e); break; case abort_nothrow_on_exception: - dealii::deal_II_exceptions::internals::abort(e, /*nothrow =*/ true); + // The proper way is to call the function below directly + // to preserve the noexcept specifier. + // For reasons of backward compatibility + // we treat this case here as well. + issue_error_nothrow(handling, file, line, function, cond, exc_name, e); break; case throw_on_exception: throw e; } } + /** + * Exception generation mechanism in case we must not throw. + * + * @ref ExceptionBase + */ + void issue_error_nothrow (ExceptionHandling, + const char *file, + int line, + const char *function, + const char *cond, + const char *exc_name, + ExceptionBase e) noexcept; } /*namespace internals*/ } /*namespace deal_II_exceptions*/ @@ -355,16 +364,16 @@ namespace deal_II_exceptions * @author Wolfgang Bangerth, 1997, 1998, Matthias Maier, 2013 */ #ifdef DEBUG -#define AssertNothrow(cond, exc) \ - { \ - if (!(cond)) \ - ::dealii::deal_II_exceptions::internals:: \ - issue_error( \ +#define AssertNothrow(cond, exc) \ + { \ + if (!(cond)) \ + ::dealii::deal_II_exceptions::internals:: \ + issue_error_nothrow( \ ::dealii::deal_II_exceptions::internals::abort_nothrow_on_exception, \ __FILE__, __LINE__, __PRETTY_FUNCTION__, #cond, #exc, exc); \ } #else -#define AssertNothrow(cond, exc) \ +#define AssertNothrow(cond, exc) \ {} #endif diff --git a/source/base/exceptions.cc b/source/base/exceptions.cc index 90c8962e8b..52c23a70c0 100644 --- a/source/base/exceptions.cc +++ b/source/base/exceptions.cc @@ -399,68 +399,87 @@ namespace StandardExceptions } #endif // DEAL_II_WITH_MPI - +namespace +{ + void internal_abort (const ExceptionBase &exc) noexcept + { + // first print the error + std::cerr << exc.what() << std::endl; + + // then bail out. if in MPI mode, bring down the entire + // house by asking the MPI system to do a best-effort + // operation at also terminating all of the other MPI + // processes. this is useful because if only one process + // runs into an assertion, then that may lead to deadlocks + // if the others don't recognize this, or at the very least + // delay their termination until they realize that their + // communication with the job that died times out. + // + // Unlike std::abort(), MPI_Abort() unfortunately doesn't break when + // running inside a debugger like GDB, so only use this strategy if + // absolutely necessary and inform the user how to use a debugger. +#ifdef DEAL_II_WITH_MPI + int is_initialized; + MPI_Initialized(&is_initialized); + if (is_initialized) + { + // do the same as in Utilities::MPI::n_mpi_processes() here, + // but without error checking to not throw again. + int n_proc=1; + MPI_Comm_size (MPI_COMM_WORLD, &n_proc); + if (n_proc>1) + { + std::cerr << "Calling MPI_Abort now.\n" + << "To break execution in a GDB session, execute 'break MPI_Abort' before " + << "running. You can also put the following into your ~/.gdbinit:\n" + << " set breakpoint pending on\n" + << " break MPI_Abort\n" + << " set breakpoint pending auto" << std::endl; + + MPI_Abort (MPI_COMM_WORLD, + /* return code = */ 255); + } + } +#endif + std::abort(); + } +} namespace deal_II_exceptions { namespace internals { - void abort (const ExceptionBase &exc, bool nothrow /*= false*/) + void issue_error_nothrow (ExceptionHandling, + const char *file, + int line, + const char *function, + const char *cond, + const char *exc_name, + ExceptionBase e) noexcept { + // Fill the fields of the exception object + e.set_fields (file, line, function, cond, exc_name); if (dealii::deal_II_exceptions::abort_on_exception) - { - // first print the error - std::cerr << exc.what() << std::endl; - - // then bail out. if in MPI mode, bring down the entire - // house by asking the MPI system to do a best-effort - // operation at also terminating all of the other MPI - // processes. this is useful because if only one process - // runs into an assertion, then that may lead to deadlocks - // if the others don't recognize this, or at the very least - // delay their termination until they realize that their - // communication with the job that died times out. - // - // Unlike std::abort(), MPI_Abort() unfortunately doesn't break when - // running inside a debugger like GDB, so only use this strategy if - // absolutely necessary and inform the user how to use a debugger. -#ifdef DEAL_II_WITH_MPI - int is_initialized; - MPI_Initialized(&is_initialized); - if (is_initialized) - { - const unsigned int n_proc = Utilities::MPI::n_mpi_processes(MPI_COMM_WORLD); - if (n_proc>1) - { - std::cerr << "Calling MPI_Abort now.\n" - << "To break execution in a GDB session, execute 'break MPI_Abort' before " - << "running. You can also put the following into your ~/.gdbinit:\n" - << " set breakpoint pending on\n" - << " break MPI_Abort\n" - << " set breakpoint pending auto" << std::endl; - - MPI_Abort (MPI_COMM_WORLD, - /* return code = */ 255); - } - } - std::abort (); -#else - std::abort(); -#endif - } - else if (nothrow) + internal_abort(e); + else { // We are not allowed to throw, and not allowed to abort. - // Just print the exception name to deallog and continue - // normally: - deallog << "Exception: " << exc.get_exc_name() << std::endl; + // Just print the exception name to deallog and continue normally: + deallog << "Exception: " << e.get_exc_name() << std::endl; + deallog << e.what() << std::endl; } + } + + + + void abort (const ExceptionBase &exc) + { + if (dealii::deal_II_exceptions::abort_on_exception) + internal_abort(exc); else { - // We are not allowed to abort, so just throw the error so just - // throw the error so just throw the error so just throw the - // error: + // We are not allowed to abort, so just throw the error: throw exc; } } diff --git a/tests/base/exception_nothrow.cc b/tests/base/exception_nothrow.cc new file mode 100644 index 0000000000..05f29cf8a4 --- /dev/null +++ b/tests/base/exception_nothrow.cc @@ -0,0 +1,26 @@ +// --------------------------------------------------------------------- +// +// Copyright (C) 2017 by the deal.II authors +// +// This file is part of the deal.II library. +// +// The deal.II library is free software; you can use it, 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 2.1 of the License, or (at your option) any later version. +// The full text of the license can be found in the file LICENSE at +// the top level of the deal.II distribution. +// +// --------------------------------------------------------------------- + + +// test that AssertNothrow is printing what we expect. + +#include "../tests.h" + +int main () +{ + initlog(); + deal_II_exceptions::disable_abort_on_exception(); + AssertNothrow(1==2, ExcInternalError()); +} diff --git a/tests/base/exception_nothrow.debug.output b/tests/base/exception_nothrow.debug.output new file mode 100644 index 0000000000..e1b46e4ebd --- /dev/null +++ b/tests/base/exception_nothrow.debug.output @@ -0,0 +1,12 @@ + +DEAL::Exception: ExcInternalError() +DEAL:: +-------------------------------------------------------- +An error occurred in file in function + int main() +The violated condition was: + 1==2 +Additional information: + (none) +-------------------------------------------------------- + diff --git a/tests/base/reference.debug.output b/tests/base/reference.debug.output index 26526ec33e..a10c6ffb28 100644 --- a/tests/base/reference.debug.output +++ b/tests/base/reference.debug.output @@ -11,6 +11,16 @@ DEAL::Construct C DEAL::Construct D DEAL::Destruct D DEAL::Exception: ExcInUse (counter, object_info->name(), infostring) +DEAL:: +-------------------------------------------------------- +An error occurred in file in function + void dealii::Subscriptor::check_no_subscribers() const +The violated condition was: + counter == 0 +Additional information: + (none) +-------------------------------------------------------- + DEAL::Destruct C DEAL::Destruct B DEAL::Destruct A diff --git a/tests/lac/vector_memory.debug.output b/tests/lac/vector_memory.debug.output index c3f1dd6d0b..9024914435 100644 --- a/tests/lac/vector_memory.debug.output +++ b/tests/lac/vector_memory.debug.output @@ -2,4 +2,24 @@ DEAL::GrowingVectorMemory:Overall allocated vectors: 10 DEAL::GrowingVectorMemory:Maximum allocated vectors: 6 DEAL::Exception: StandardExceptions::ExcMemoryLeak(current_alloc) +DEAL:: +-------------------------------------------------------- +An error occurred in file in function + dealii::GrowingVectorMemory::~GrowingVectorMemory() [with VectorType = dealii::Vector] +The violated condition was: + current_alloc == 0 +Additional information: + (none) +-------------------------------------------------------- + DEAL::Exception: StandardExceptions::ExcMemoryLeak(current_alloc) +DEAL:: +-------------------------------------------------------- +An error occurred in file in function + dealii::GrowingVectorMemory::~GrowingVectorMemory() [with VectorType = dealii::Vector] +The violated condition was: + current_alloc == 0 +Additional information: + (none) +-------------------------------------------------------- +