Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-14 08:50:59

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