Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:54:50

0001 //----------------------------------*-C++-*----------------------------------//
0002 // Copyright 2022-2024 UT-Battelle, LLC, and other Celeritas developers.
0003 // See the top-level COPYRIGHT file for details.
0004 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
0005 //---------------------------------------------------------------------------//
0006 //! \file corecel/sys/MultiExceptionHandler.hh
0007 //---------------------------------------------------------------------------//
0008 #pragma once
0009 
0010 #include <exception>
0011 #include <utility>
0012 #include <vector>
0013 
0014 #include "corecel/Macros.hh"
0015 
0016 namespace celeritas
0017 {
0018 //---------------------------------------------------------------------------//
0019 /*!
0020  * Temporarily store exception pointers.
0021  *
0022  * This is useful for storing multiple exceptions in unrelated loops (where one
0023  * exception shouldn't affect the program flow outside of the scope),
0024  * especially for OpenMP parallel execution, where exceptions cannot be
0025  * propagated.
0026  * \code
0027     MultiExceptionHandler capture_exception;
0028     #pragma omp parallel for
0029     for (size_type i = 0; i < data.states.size(); ++i)
0030     {
0031         CELER_TRY_HANDLE(step(TrackSlotId{i}), capture_exception);
0032     }
0033     log_and_rethrow(std::move(capture_exception));
0034  * \endcode
0035  *
0036  * \note This class implements an OpenMP \c critical mutex, not a \c std
0037  * mutex. If using this class in a \c std::thread context, wrap the call
0038  * operator in a lambda with a \c std::scoped_lock . We could refactor as a
0039  * CRTP class with a protected \c push_back function that lets us specialize
0040  * the mutex implementation.
0041  */
0042 class MultiExceptionHandler
0043 {
0044   public:
0045     //!@{
0046     //! \name Type aliases
0047     using VecExceptionPtr = std::vector<std::exception_ptr>;
0048     //!@}
0049 
0050   public:
0051     // Default all construct/copy/move
0052     MultiExceptionHandler() = default;
0053     CELER_DEFAULT_COPY_MOVE(MultiExceptionHandler);
0054 
0055     // Terminate if destroyed without handling exceptions
0056     inline ~MultiExceptionHandler();
0057 
0058     // Thread-safe capture of the given exception
0059     void operator()(std::exception_ptr p);
0060 
0061     //! Whether no exceptions have been stored (not thread safe)
0062     bool empty() const { return exceptions_.empty(); }
0063 
0064     //! Release exceptions for someone else to process (not thread safe)
0065     VecExceptionPtr release() { return std::move(exceptions_); }
0066 
0067   private:
0068     VecExceptionPtr exceptions_;
0069 
0070     [[noreturn]] void log_and_terminate() const;
0071 };
0072 
0073 //---------------------------------------------------------------------------//
0074 // FREE FUNCTIONS
0075 //---------------------------------------------------------------------------//
0076 namespace detail
0077 {
0078 // Private implementation function for throwing exceptions
0079 [[noreturn]] void log_and_rethrow_impl(MultiExceptionHandler&& exceptions);
0080 }  // namespace detail
0081 
0082 //---------------------------------------------------------------------------//
0083 /*!
0084  * Throw the first exception and log all the rest.
0085  *
0086  * All logged exceptions will be thrown using \c CELER_LOG_LOCAL(critical) .
0087  */
0088 inline void log_and_rethrow(MultiExceptionHandler&& exceptions)
0089 {
0090     if (CELER_UNLIKELY(!exceptions.empty()))
0091     {
0092         detail::log_and_rethrow_impl(std::move(exceptions));
0093     }
0094 }
0095 
0096 //---------------------------------------------------------------------------//
0097 // INLINE FUNCTIONS
0098 //---------------------------------------------------------------------------//
0099 /*!
0100  * Terminate if destroyed without handling exceptions.
0101  */
0102 MultiExceptionHandler::~MultiExceptionHandler()
0103 {
0104     if (CELER_UNLIKELY(!exceptions_.empty()))
0105     {
0106         this->log_and_terminate();
0107     }
0108 }
0109 
0110 //---------------------------------------------------------------------------//
0111 }  // namespace celeritas