file src/exceptions.cpp

[No description available] More…

Namespaces

Name
Gambit
TODO: see if we can use this one:

Detailed Description

Author: Pat Scott (patscott@physics.mcgill.ca)

Date: 2014 Mar

Threadsafe exception class definitions.


Authors (add name and date if you modify):

Distantly inspired by SUFIT classes of the same name by Johan Lundberg, Aug 2011.


Source code

//   GAMBIT: Global and Modular BSM Inference Tool
//   *********************************************
///  \file
///
///  Threadsafe exception class definitions.
///
///  *********************************************
///
///  Authors (add name and date if you modify):
///
///  \author Pat Scott
///          (patscott@physics.mcgill.ca)
///  \date 2014 Mar
///
///  Distantly inspired by SUFIT classes of the
///  same name by Johan Lundberg, Aug 2011.
///
///  *********************************************

#include <string>
#include <iostream>
#include <algorithm>
#include <omp.h>

#include "gambit/Utils/mpiwrapper.hpp"
#include "gambit/Utils/util_macros.hpp"
#include "gambit/Utils/exceptions.hpp"
#include "gambit/Utils/standalone_error_handlers.hpp"
#include "gambit/Logs/logger.hpp"
#include "gambit/Printers/baseprinter.hpp"
#include "gambit/Printers/printermanager.hpp"

namespace Gambit
{
    using namespace std;
    using namespace LogTags;
    typedef std::string str;

    // Public members of GAMBIT exception base class.

    /// Constructor without log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      exception_map()[inikey] = this;
    }

    /// Constructor with 1 log tag
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      exception_map()[inikey] = this;
    }

    /// Constructor with 2 log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1, LogTag t2) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      myLogTags.insert(t2);
      exception_map()[inikey] = this;
    }

    /// Constructor with 3 log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1, LogTag t2, LogTag t3) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      myLogTags.insert(t2);
      myLogTags.insert(t3);
      exception_map()[inikey] = this;
    }

    /// Constructor with 4 log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1, LogTag t2, LogTag t3, LogTag t4) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      myLogTags.insert(t2);
      myLogTags.insert(t3);
      myLogTags.insert(t4);
      exception_map()[inikey] = this;
    }

    /// Constructor with 5 log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      myLogTags.insert(t2);
      myLogTags.insert(t3);
      myLogTags.insert(t4);
      myLogTags.insert(t5);
      exception_map()[inikey] = this;
    }

    /// Constructor with 6 log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5, LogTag t6) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      myLogTags.insert(t2);
      myLogTags.insert(t3);
      myLogTags.insert(t4);
      myLogTags.insert(t5);
      myLogTags.insert(t6);
      exception_map()[inikey] = this;
    }

    /// Constructor with 7 log tags
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal,
     LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5, LogTag t6, LogTag t7) :
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      myLogTags.insert(t1);
      myLogTags.insert(t2);
      myLogTags.insert(t3);
      myLogTags.insert(t4);
      myLogTags.insert(t5);
      myLogTags.insert(t6);
      myLogTags.insert(t7);
      exception_map()[inikey] = this;
    }

    /// Constructor with log tags as a set
    exception::exception(const char* kind, const char* what, const char* message, const char* inikey, bool fatal, std::set<LogTag> tags) :
     myLogTags             (tags),
     myKind                (kind),
     myWhat                (what),
     myShortWhat           (what),
     myMessage             (message),
     isFatal               (fatal)
    {
      exception_map()[inikey] = this;
    }

    /// Setter for the fatal flag.
    void exception::set_fatal(bool fatal)
    {
      #pragma omp critical (GAMBIT_exception)
      {
        isFatal = fatal;
      }
    }

    /// Retrieve the identity of the exception.
    const char* exception::what() const throw()
    {
      return myWhat.c_str();
    }

    /// Raise the exception.
    /// Log the exception and, if it is considered fatal, actually throw it.
    /// This is the regular way to trigger a GAMBIT error or warning.
    void exception::raise(const std::string& origin, const std::string& specific_message)
    {
      str full_message = isFatal ? specific_message+parameters : specific_message;
      #pragma omp critical (GAMBIT_exception)
      {
        log_exception(origin, full_message);
      }
      if (isFatal) throw_iff_outside_parallel();
    }

    /// Log the exception and throw it regardless of whether is is fatal or not.
    void exception::forced_throw(const std::string& origin, const std::string& specific_message)
    {
      #pragma omp critical (GAMBIT_exception)
      {
        log_exception(origin, specific_message+parameters);
      }
      throw(*this);
    }

    /// As per forced_throw but without logging.
    void exception::silent_forced_throw()
    {
      throw(*this);
    }

    /// Get a read-only map of pointers to all instances of this class.
    const std::map<const char*,exception*>& exception::all_exceptions()
    {
      return exception_map();
    }

    /// Set the parameter point string to append if a fatal exception is thrown
    void exception::set_parameters(str params)
    {
      parameters = params;
    }

  // Private members of GAMBIT exception base class.

    /// Get a map of pointers to all instances of this class.
    std::map<const char*,exception*>& exception::exception_map()
    {
      static std::map<const char*,exception*> local_map;
      return local_map;
    }

    /// Log the details of the exception
    void exception::log_exception(const std::string& origin, const std::string& specific_message)
    {
      std::ostringstream msg;
      msg << myKind << ": " << myMessage << std::endl
          << specific_message << std::endl
          << "Raised at: " << origin << ".";
      if (isFatal)
      {
        logger() << fatal;
        std::ostringstream myLongWhat;
        myLongWhat << myShortWhat << std::endl << msg.str();
        myWhat = myLongWhat.str();
      }
      else
      {
        logger() << nonfatal;
        myWhat = myShortWhat;
      }
      for (std::set<LogTag>::iterator it = myLogTags.begin(); it != myLogTags.end(); ++it) { logger() << *it; }
      logger() << msg.str() << EOM;
    }

    /// Throw the exception onward if running serially, abort if not.
    void exception::throw_iff_outside_parallel()
    {
      if (omp_get_level()==0) // If not in an OpenMP parallel block, throw onwards
      {
        throw(*this);
      }
      else
      {
        abort_here_and_now(); // If in an OpenMP parallel block, just abort immediately.
      }
    }

    /// Cause the code to print the exception and abort.
    void exception::abort_here_and_now()
    {
      #pragma omp critical (GAMBIT_exception)
      {
        cerr << endl << " \033[00;31;1mFATAL ERROR\033[00m" << endl << endl
             << "GAMBIT has exited with fatal exception: " << what() << endl
             << "Please note: this error occurred inside an OpenMP parallel " << endl
             << "region and so caused a hard stop. If you are running in MPI " << endl
             << "mode, other processes were not informed of this error and " << endl
             << "may have to be killed manually (though your MPI implementation " << endl
             << "may automatically kill them).  For more 'gentle' handling of " << endl
             << "errors in OpenMP loops, please raise errors using the Piped_exceptions system." << endl;
        #ifdef WITH_MPI
          GMPI::Comm().Abort();
        #else
          abort();
        #endif
      }
    }


  /// GAMBIT error class constructors

    /// Constructor without log tags
    error::error(const char* message, const char* inikey) :
     exception("ERROR", "GAMBIT error", message, inikey, true, err) {}
    /// Constructor with 1 log tag
    error::error(const char* message, const char* inikey, LogTag t1)
     : exception("ERROR", "GAMBIT error", message, inikey, true, err, t1) {}
    /// Constructor with 2 log tags
    error::error(const char* message, const char* inikey, LogTag t1, LogTag t2)
     : exception("ERROR", "GAMBIT error", message, inikey, true, err, t1, t2) {}
    /// Constructor with 3 log tags
    error::error(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3)
     : exception("ERROR", "GAMBIT error", message, inikey, true, err, t1, t2, t3) {}
    /// Constructor with 4 log tags
    error::error(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3, LogTag t4)
     : exception("ERROR", "GAMBIT error", message, inikey, true, err, t1, t2, t3, t4) {}
    /// Constructor with 5 log tags
    error::error(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5)
     : exception("ERROR", "GAMBIT error", message, inikey, true, err, t1, t2, t3, t4, t5) {}
    /// Constructor with 6 log tags
    error::error(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5, LogTag t6)
     : exception("ERROR", "GAMBIT error", message, inikey, true, err, t1, t2, t3, t4, t5, t6) {}
    /// Constructor with log tags as a set
    error::error(const char* message, const char* inikey, std::set<LogTag> tags) :
     exception("ERROR", "GAMBIT error", message, inikey, true)
    {
      myLogTags = tags;
      myLogTags.insert(err);
    }

  /// GAMBIT warning class constructors

    /// Constructor without log tags
    warning::warning(const char* message, const char* inikey) :
     exception("WARNING", "GAMBIT warning", message, inikey, false, warn) {}
    /// Constructor with 1 log tag
    warning::warning(const char* message, const char* inikey, LogTag t1)
     : exception("WARNING", "GAMBIT warning", message, inikey, false, warn, t1) {}
    /// Constructor with 2 log tags
    warning::warning(const char* message, const char* inikey, LogTag t1, LogTag t2)
     : exception("WARNING", "GAMBIT warning", message, inikey, false, warn, t1, t2) {}
    /// Constructor with 3 log tags
    warning::warning(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3)
     : exception("WARNING", "GAMBIT warning", message, inikey, false, warn, t1, t2, t3) {}
    /// Constructor with 4 log tags
    warning::warning(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3, LogTag t4)
     : exception("WARNING", "GAMBIT warning", message, inikey, false, warn, t1, t2, t3, t4) {}
    /// Constructor with 5 log tags
    warning::warning(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5)
     : exception("WARNING", "GAMBIT warning", message, inikey, false, warn, t1, t2, t3, t4, t5) {}
    /// Constructor with 6 log tags
    warning::warning(const char* message, const char* inikey, LogTag t1, LogTag t2, LogTag t3, LogTag t4, LogTag t5, LogTag t6)
     : exception("WARNING", "GAMBIT warning", message, inikey, false, warn, t1, t2, t3, t4, t5, t6) {}
    /// Constructor with log tags as a set
    warning::warning(const char* message, const char* inikey, std::set<LogTag> tags) :
     exception("WARNING", "GAMBIT warning", message, inikey, false)
    {
      myLogTags = tags;
      myLogTags.insert(warn);
    }


  /// GAMBIT special exception class methods.

    /// Constructor
    special_exception::special_exception(const char* what) : myWhat(what), myMessage("") {}

    /// Retrieve the identity of the exception.
    const char* special_exception::what() const throw()
    {
      const char* temp;
      temp = myWhat;
      return temp;
    }

    /// Retrieve the message that this exception was raised with.
    std::string special_exception::message()
    {
      std::string temp;
      #pragma omp critical (GAMBIT_exception)
      {
        temp = myMessage;
      }
      return temp;
    }

    /// Raise the exception, i.e. throw it with a message.
    void special_exception::raise(const std::string& msg)
    {
      #pragma omp critical (GAMBIT_exception)
      {
        myMessage = msg;
      }
      throw(*this);
    }


  /// Gambit invalid point exception class methods.

    /// Constructor
    invalid_point_exception::invalid_point_exception() : special_exception("GAMBIT invalid point."), myThrower(NULL) {}

    /// Set the pointer to the functor that threw the invalid point exception.
    void invalid_point_exception::set_thrower(functor* thrown_from)
    {
      #pragma omp critical (myThrower)
      {
        myThrower = thrown_from;
      }
    }

    /// Retrieve pointer to the functor that threw the invalid point exception.
    functor* invalid_point_exception::thrower()
    {
      functor* temp;
      #pragma omp critical (myThrower)
      {
        temp = myThrower;
      }
      if (temp == NULL) utils_error().raise(LOCAL_INFO, "No throwing functor in invalid_point_exception.");
      return temp;
    }

    /// Raise the invalid point exception, i.e throw it with a message and a default code.
    void invalid_point_exception::raise(const std::string& msg)
    {
      // Default code is 1
      raise(msg, 1);
    }

    /// Raise the invalid point exception, i.e. throw it with a message and a code.
    void invalid_point_exception::raise(const std::string& msg, const int mycode)
    {
      if (omp_get_level()==0) // If not in an OpenMP parallel block, throw onwards
      {
        #pragma omp critical (GAMBIT_exception)
        {
          myMessage = msg;
        }
        invalidcode = mycode;
        throw(*this);
      }
      else
      {
        std::ostringstream full_msg;
        full_msg << "Sorry, you cannot raise an invalid point exception inside an OpenMP block." << endl
                 << "Please use piped_invalid_point.request() in your block, then check it with " << endl
                 << "piped_invalid_point.check() to raise the exception outside the block." << endl
                << "Message: " << msg;
        #pragma omp critical (GAMBIT_exception)
        {
          myMessage = full_msg.str();
        }
        abort_here_and_now(); // If in an OpenMP parallel block, just abort immediately.
      }
    }

    /// Cause the code to print the exception and abort.
    void invalid_point_exception::abort_here_and_now()
    {
      #pragma omp critical (GAMBIT_invalid_pt_exception)
      {
        cerr << endl << " \033[00;31;1mFATAL ERROR\033[00m" << endl << endl;
        cerr << "An invalid_point exception is fatal inside an OpenMP block. " << endl << what() << endl << message() << endl;
        #ifdef WITH_MPI
          GMPI::Comm().Abort();
        #else
          abort();
        #endif
      }
    }

    /// Request a piped invalid point exception.
    void Piped_invalid_point::request(std::string message)
    {
      #pragma omp critical (GAMBIT_piped_invalid_point)
      {
        this->message = message;
        this->flag = true;
      }
    }

    /// Check whether a piped invalid point exception was requested, and throw if necessary.
    void Piped_invalid_point::check()
    {
      if (omp_get_level()==0) // If not in an OpenMP parallel block, throw onwards
      {
        if (this->flag)
        {
          this->flag = false;  // Reset...
          invalid_point().raise(this->message);  // ...and throw.
        }
      }
      else
      {
        #pragma omp critical (GAMBIT_invalid_point)
        {
          cerr << endl << " \033[00;31;1mFATAL ERROR\033[00m" << endl << endl;
          cerr << "GAMBIT has exited with fatal exception: Piped_invalid_point::check() called inside an OpenMP block." << endl
              << "Piped exceptions may be requested inside OpenMP blocks, but should only be checked outside the block." << endl;
          if (this->flag)
            cerr << "Invalid point message requested: " << endl << this->message;
          else cerr << "No invalid point requested." << endl;
          #ifdef WITH_MPI
            GMPI::Comm().Abort();
          #else
            abort();
          #endif
        }
      }
    }

  /// Gambit halt loop exception class methods.

    /// Constructor
    halt_loop_exception::halt_loop_exception() : special_exception("Immediate halt of GAMBIT loop requested.") {}


  /// Gambit invalid loop iteration exception class methods.

    /// Constructor
    invalid_loop_iteration_exception::invalid_loop_iteration_exception() : special_exception("GAMBIT invalid loop iteration.") {}


    /// @{ SilentShutdownException member functions
    SilentShutdownException::SilentShutdownException() {}
    SilentShutdownException::SilentShutdownException(const std::string& message) : myWhat(message) {}
    const char* SilentShutdownException::what() const throw() { return myWhat.c_str(); }
    /// @}

    /// @{ SoftShutdownException member functions
    SoftShutdownException::SoftShutdownException(const std::string& message) : myWhat(message) {}
    const char* SoftShutdownException::what() const throw() { return myWhat.c_str(); }
    /// @}

    /// @{ HardShutdownException member functions
    HardShutdownException::HardShutdownException(const std::string& message) : myWhat(message) {}
    const char* HardShutdownException::what() const throw() { return myWhat.c_str(); }
    /// @}

    /// @{ MPIShutdownException member functions
    MPIShutdownException::MPIShutdownException(const std::string& message) : myWhat(message) {}
    const char* MPIShutdownException::what() const throw() { return myWhat.c_str(); }
    /// @}

    /// Global instance of piped invalid point class.
    Piped_invalid_point piped_invalid_point;

    /// Request a piped exception.
    void Piped_exceptions::request(std::string origin, std::string message)
    {
      #pragma omp critical (GAMBIT_piped_exception)
      {
        if(exceptions.size() < maxExceptions)
          this->exceptions.push_back(description(origin,message));
        this->flag = true;
      }
    }

    /// Request a piped exception.
    void Piped_exceptions::request(description desc)
    {
      this->request(desc.first, desc.second);
    }

    /// Check whether any exceptions were requested, and raise them.
    void Piped_exceptions::check(exception &excep)
    {
      if (omp_get_level()==0) // If not in an OpenMP parallel block, throw onwards
      {
        if (this->flag)
        {
          // Raise all exceptions (only the first if they are fatal)
          for(size_t i= 0; i < std::min(exceptions.size(),maxExceptions); i++)
          {
            excep.raise(exceptions.at(i).first, exceptions.at(i).second);
          }
          // Reset
          this->flag = false;
          exceptions.clear();
        }
      }
      else
      {
        #pragma omp critical (GAMBIT_exception)
        {
          cerr << endl << " \033[00;31;1mFATAL ERROR\033[00m" << endl << endl;
          cerr << "GAMBIT has exited with fatal exception: Piped_exceptions::check() called inside an OpenMP block." << endl
              << "Piped exceptions may be requested inside OpenMP blocks, but should only be checked outside the block." << endl;
          if (this->flag)
          {
            cerr << "Exceptions stored: " << endl << endl;
            for(size_t i= 0; i < std::min(exceptions.size(),maxExceptions); i++)
            {
              cerr << exceptions.at(i).second << endl
                   << "\nRaised at: " << exceptions.at(i).first << "." << endl << endl;
            }
          }
          else cerr << "No exceptions stored." << endl;
          #ifdef WITH_MPI
            GMPI::Comm().Abort();
          #else
            abort();
          #endif
        }
      }
    }

    /// Check whether any exceptions were requested without handling them.
    bool Piped_exceptions::inquire()
    {
      return this->flag;
    }

    /// Check whether any exceptions with a specific message were requested, without handling them.
    bool Piped_exceptions::inquire(std::string message)
    {
      if (not this->flag) return false;
      auto it = std::find_if(exceptions.cbegin(), exceptions.cend(),
       [&message](std::pair<std::string,std::string> const &e){ return e.second == message; });
      return it != exceptions.cend();
    }

    /// Global instance of Piped_exceptions class for errors.
    Piped_exceptions piped_errors(1000);

    /// Global instance of Piped_exceptions class for warnings.
    Piped_exceptions piped_warnings(1000);

    /// Raise the suspicious point exception. Print it with a message and a code. The default code is 1.
    void Suspicious_point_exception::raise(const std::string &msg, int code=1, bool debug=false)
    {
      // get the printer pointer
      Printers::BasePrinter& printer = *(get_global_printer_manager()->printerptr);
      printer.print(code, "Suspicious Point Code", Printers::get_main_param_id("Suspicious Point Code"), printer.getRank(), Printers::get_point_id());

      if (debug) std::cout << "Point Suspicious (" << code << "): " << msg << std::endl;
    }
}

Updated on 2024-07-18 at 13:53:32 +0000