diff --git a/include/AdePT/core/AsyncAdePTTransport.cuh b/include/AdePT/core/AsyncAdePTTransport.cuh index 386bc2a4..f832c57c 100644 --- a/include/AdePT/core/AsyncAdePTTransport.cuh +++ b/include/AdePT/core/AsyncAdePTTransport.cuh @@ -1016,6 +1016,13 @@ void TransportLoop(int trackCapacity, int leakCapacity, int scoringCapacity, int using namespace std::chrono_literals; std::this_thread::sleep_for(10ms); } + // Shutdown can wake the dedicated GPU steering thread out of the idle wait + // even when all event states are already LeakedTracksRetrieved. In that + // case, one Geant4 worker has entered FreeGPU() and flipped runTransport. + // The outer while-condition is only re-checked at the next iteration + // boundary, so we must exit explicitly before this current iteration + // touches the CUDA stream again during teardown. + if (!gpuState.runTransport) break; if (debugLevel > 2) { G4cout << "GPU transport starting" << std::endl; diff --git a/src/AdePTTrackingManager.cc b/src/AdePTTrackingManager.cc index 7ce76487..b0a17c1e 100644 --- a/src/AdePTTrackingManager.cc +++ b/src/AdePTTrackingManager.cc @@ -26,14 +26,42 @@ namespace { using AdePTTransport = AdePTTrackingManager::AdePTTransport; + +// Store only a weak reference here so the transport lifetime is still owned by +// the thread-local AdePTTrackingManager instances. A static owning shared_ptr +// would keep the transport alive until very late process teardown. +std::weak_ptr &SharedAdePTTransportStorage() +{ + static std::weak_ptr transport; + return transport; +} + +std::shared_ptr CreateSharedAdePTTransport(AdePTConfiguration &conf, + G4HepEmTrackingManagerSpecialized *hepEmTM) +{ + auto &transport = SharedAdePTTransportStorage(); + // weak_ptr::lock() promotes the stored weak reference to a shared_ptr if the + // shared transport is still alive. This is not a mutex lock. + if (auto existing = transport.lock()) { + return existing; + } + + auto created = std::make_shared(conf, hepEmTM->GetConfig()); + transport = created; + return created; } -std::shared_ptr GetSharedAdePTTransport(AdePTConfiguration &conf, - G4HepEmTrackingManagerSpecialized *hepEmTM) +std::shared_ptr GetSharedAdePTTransport() { - static std::shared_ptr AdePT{new AdePTTransport(conf, hepEmTM->GetConfig())}; - return AdePT; + // weak_ptr::lock() promotes the weak reference held in static storage. The + // actual ownership remains with the AdePTTrackingManager instances. + auto transport = SharedAdePTTransportStorage().lock(); + if (!transport) { + throw std::runtime_error("Shared AdePT transport is not available."); + } + return transport; } +} // namespace //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... @@ -65,7 +93,7 @@ void AdePTTrackingManager::InitializeSharedAdePTTransport() const auto uniformFieldValues = fGeant4Integration.GetUniformField(); // Create the shared AdePT transport engine on the first worker thread. - fAdeptTransport = GetSharedAdePTTransport(*fAdePTConfiguration, fHepEmTrackingManager.get()); + fAdeptTransport = CreateSharedAdePTTransport(*fAdePTConfiguration, fHepEmTrackingManager.get()); // Check VecGeom geometry matches Geant4 before deriving any geometry metadata for transport. fGeant4Integration.CheckGeometry(fAdeptTransport->GetHepEmState()); @@ -155,7 +183,7 @@ void AdePTTrackingManager::InitializeAdePT() // The shared AdePT transport was already created and initialized by the first worker. // The remaining workers only retrieve the shared pointer here. - fAdeptTransport = GetSharedAdePTTransport(*fAdePTConfiguration, fHepEmTrackingManager.get()); + fAdeptTransport = GetSharedAdePTTransport(); // Initialize the GPU region list if (!fAdePTConfiguration->GetTrackInAllRegions()) {