Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix G4VExceptionHandler lifetime and improve exception wrapping #773

Merged
merged 9 commits into from
May 30, 2023
2 changes: 1 addition & 1 deletion src/celeritas/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ if(CELERITAS_USE_Geant4)
ext/GeantImporter.cc
ext/GeantSetup.cc
ext/GeantVolumeMapper.cc
ext/ScopedGeantExceptionHandler.cc
ext/detail/GeantBremsstrahlungProcess.cc
ext/detail/GeantExceptionHandler.cc
ext/detail/GeantLoggerAdapter.cc
ext/detail/GeantMicroXsCalculator.cc
ext/detail/GeantModelImporter.cc
Expand Down
6 changes: 3 additions & 3 deletions src/celeritas/ext/GeantSetup.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#include "corecel/sys/ScopedMem.hh"

#include "GeantGeoUtils.hh"
#include "detail/GeantExceptionHandler.hh"
#include "ScopedGeantExceptionHandler.hh"
#include "detail/GeantLoggerAdapter.hh"
#include "detail/GeantPhysicsList.hh"

Expand Down Expand Up @@ -94,7 +94,7 @@ GeantSetup::GeantSetup(std::string const& gdml_filename, Options options)
{
// Run manager writes output that cannot be redirected...
ScopedTimeAndRedirect scoped_time("G4RunManager");
detail::GeantExceptionHandler scoped_exception_handler;
ScopedGeantExceptionHandler scoped_exceptions;
// Access the particle table before creating the run manager, so that
// missing environment variables like G4ENSDFSTATEDATA get caught
// cleanly rather than segfaulting
Expand Down Expand Up @@ -123,7 +123,7 @@ GeantSetup::GeantSetup(std::string const& gdml_filename, Options options)
}

detail::GeantLoggerAdapter scoped_logger;
detail::GeantExceptionHandler scoped_exception_handler;
ScopedGeantExceptionHandler scoped_exceptions;

{
CELER_LOG(status) << "Initializing Geant4 geometry and physics";
Expand Down
112 changes: 112 additions & 0 deletions src/celeritas/ext/ScopedGeantExceptionHandler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file celeritas/ext/ScopedGeantExceptionHandler.cc
//---------------------------------------------------------------------------//
#include "ScopedGeantExceptionHandler.hh"

#include <G4ExceptionSeverity.hh>
#include <G4StateManager.hh>
#include <G4Types.hh>
#include <G4VExceptionHandler.hh>

#include "corecel/Assert.hh"
#include "corecel/io/Logger.hh"

namespace celeritas
{
namespace
{
//---------------------------------------------------------------------------//
/*!
* Process Geant4 exceptions with Celeritas.
*
* The Geant exception handler base class changes global state in its
* constructor (assigning "this") so this class instance must stay in scope
* once created. There is no way to save or restore the previous handler.
* Furthermore, creating a G4RunManagerKernel also resets the exception
* handler, so errors thrown during setup *CANNOT* be caught by celeritas, and
* this class can only be used after creating the G4RunManager.
*/
class GeantExceptionHandler final : public G4VExceptionHandler
{
public:
// Accept error codes from geant4
G4bool Notify(char const* originOfException,
char const* exceptionCode,
G4ExceptionSeverity severity,
char const* description) final;
};

//---------------------------------------------------------------------------//
/*!
* Propagate exceptions to Celeritas.
*/
G4bool GeantExceptionHandler::Notify(char const* origin_of_exception,
char const* exception_code,
G4ExceptionSeverity severity,
char const* description)
{
CELER_EXPECT(origin_of_exception);
CELER_EXPECT(exception_code);

// Construct message
auto err = RuntimeError::from_geant_exception(
origin_of_exception, exception_code, description);

switch (severity)
{
case FatalException:
case FatalErrorInArgument:
case RunMustBeAborted:
case EventMustBeAborted:
// Severe or initialization error
throw err;
case JustWarning:
// Display a message
CELER_LOG_LOCAL(error) << err.what();
break;
default:
CELER_ASSERT_UNREACHABLE();
}

// Return "true" to cause Geant4 to crash the program, or "false" to let it
// know that we've handled the exception.
return false;
}

//---------------------------------------------------------------------------//
} // namespace

//---------------------------------------------------------------------------//
/*!
* Install the Celeritas Geant4 exception handler.
*
* The base class of the exception handler calls SetExceptionHandler...
*/
ScopedGeantExceptionHandler::ScopedGeantExceptionHandler()
{
auto* state_mgr = G4StateManager::GetStateManager();
CELER_ASSERT(state_mgr);
previous_ = state_mgr->GetExceptionHandler();
current_ = std::make_unique<GeantExceptionHandler>();
CELER_ENSURE(state_mgr->GetExceptionHandler() == current_.get());
}

//---------------------------------------------------------------------------//
/*!
* Revert to the previous exception handler.
*/
ScopedGeantExceptionHandler::~ScopedGeantExceptionHandler()
{
auto* state_mgr = G4StateManager::GetStateManager();
if (state_mgr->GetExceptionHandler() == current_.get())
{
state_mgr->SetExceptionHandler(previous_);
}
}

//---------------------------------------------------------------------------//
} // namespace celeritas
51 changes: 51 additions & 0 deletions src/celeritas/ext/ScopedGeantExceptionHandler.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//----------------------------------*-C++-*----------------------------------//
// Copyright 2023 UT-Battelle, LLC, and other Celeritas developers.
// See the top-level COPYRIGHT file for details.
// SPDX-License-Identifier: (Apache-2.0 OR MIT)
//---------------------------------------------------------------------------//
//! \file celeritas/ext/ScopedGeantExceptionHandler.hh
//---------------------------------------------------------------------------//
#pragma once

#include <memory>

#include "celeritas_config.h"

class G4VExceptionHandler;

namespace celeritas
{
//---------------------------------------------------------------------------//
/*!
* Install and clear a Geant exception handler during this class lifetime.
*
* Note that creating a \c G4RunManagerKernel resets the exception
* handler, so errors thrown during setup *CANNOT* be caught by Celeritas, and
* this class can only be used after creating the \c G4RunManager.
*/
class ScopedGeantExceptionHandler
sethrj marked this conversation as resolved.
Show resolved Hide resolved
{
public:
// Construct exception handler
ScopedGeantExceptionHandler();

// Clear on destruction
~ScopedGeantExceptionHandler();

private:
#if CELERITAS_USE_GEANT4
G4VExceptionHandler* previous_{nullptr};
std::unique_ptr<G4VExceptionHandler> current_;
#endif
};

#if !CELERITAS_USE_GEANT4
//!@{
//! Do nothing if Geant4 is disabled (source file will not be compiled)
inline ScopedGeantExceptionHandler::ScopedGeantExceptionHandler() {}
inline ScopedGeantExceptionHandler::~ScopedGeantExceptionHandler() {}
//!@}
#endif

//---------------------------------------------------------------------------//
} // namespace celeritas
19 changes: 12 additions & 7 deletions src/celeritas/ext/ScopedRootErrorHandler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,22 @@ void RootErrorHandler(Int_t rootlevel,

//---------------------------------------------------------------------------//
/*!
* Install the Celeritas ROOT error handler
* Clear ROOT's signal handlers that get installed on startup/activation.
*/
void ScopedRootErrorHandler::disable_signal_handler()
{
gSystem->ResetSignals();
}

//---------------------------------------------------------------------------//
/*!
* Install the Celeritas ROOT error handler.
*/
ScopedRootErrorHandler::ScopedRootErrorHandler()
: previous_(SetErrorHandler(RootErrorHandler))
, prev_errored_{g_has_root_errored_}
{
// Disable ROOT interception of system signals the first time we run
[[maybe_unused]] static bool const disabled_root_backtrace = [] {
gSystem->ResetSignals();
return true;
}();
ScopedRootErrorHandler::disable_signal_handler();
}

//---------------------------------------------------------------------------//
Expand All @@ -101,7 +106,7 @@ void ScopedRootErrorHandler::throw_if_errors() const

//---------------------------------------------------------------------------//
/*!
* Revert to the previous ROOT error handler
* Revert to the previous ROOT error handler.
*/
ScopedRootErrorHandler::~ScopedRootErrorHandler()
{
Expand Down
4 changes: 4 additions & 0 deletions src/celeritas/ext/ScopedRootErrorHandler.hh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ namespace celeritas
class ScopedRootErrorHandler
{
public:
// Clear ROOT's signal handlers that get installed on startup/activation
static void disable_signal_handler();

// Install the error handler
ScopedRootErrorHandler();

Expand All @@ -38,6 +41,7 @@ class ScopedRootErrorHandler
#if !CELERITAS_USE_ROOT
//!@{
//! Do nothing if ROOT is disabled (source file will not be compiled)
inline void ScopedRootErrorHandler::disable_signal_handler() {}
inline ScopedRootErrorHandler::ScopedRootErrorHandler()
{
(void)sizeof(previous_);
Expand Down
58 changes: 0 additions & 58 deletions src/celeritas/ext/detail/GeantExceptionHandler.cc

This file was deleted.

41 changes: 0 additions & 41 deletions src/celeritas/ext/detail/GeantExceptionHandler.hh

This file was deleted.

1 change: 1 addition & 0 deletions test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ celeritas_add_library(testcel_celeritas
celeritas/user/ExampleMctruth.cc
celeritas/user/CaloTestBase.cc
celeritas/user/MctruthTestBase.cc
celeritas/user/StepCollectorTestBase.cc
)

celeritas_target_link_libraries(testcel_celeritas
Expand Down
Loading