|
|
|||
File indexing completed on 2026-11-08 10:11:15
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 celeritas/ext/detail/LocalProcessorSlots.hh 0006 //! \sa test/celeritas/ext/GeantSd.test.cc 0007 //---------------------------------------------------------------------------// 0008 #pragma once 0009 0010 #include <memory> 0011 #include <utility> 0012 #include <vector> 0013 0014 #include "corecel/Assert.hh" 0015 #include "corecel/cont/Range.hh" 0016 #include "corecel/sys/ThreadId.hh" 0017 0018 namespace celeritas 0019 { 0020 namespace detail 0021 { 0022 //---------------------------------------------------------------------------// 0023 /*! 0024 * Per-stream cache of thread-local processors owned by local transporters. 0025 * 0026 * Classes that interface Celeritas with thread-local Geant4 objects (e.g., 0027 * sensitive detectors) are shared across threads but must dispatch to a 0028 * processor that is created, used, and destroyed on a single worker thread. 0029 * This class holds one weakly referenced processor per stream. 0030 * 0031 * The references between a slot and its processor are cyclic and 0032 * deliberately non-owning in both directions: 0033 * \verbatim 0034 Shared --(shared)--> Slot --(weak + raw cache)--> Processor 0035 ^ | 0036 +--(weak, via custom deleter)--+ 0037 0038 LocalTransporter --(shared, with custom deleter)--> Processor 0039 \endverbatim 0040 * 0041 * The thread-local transporter shares ownership of the processor, and the 0042 * shared object owns the slot. Since the cross references are weak, either 0043 * side may be destroyed first: 0044 * - when the last local reference to the processor is released (on the 0045 * worker thread that created it), the deleter resets the slot's cached 0046 * pointers so a later \c make call recreates the processor instead of 0047 * returning a dangling pointer; 0048 * - when the slots are destroyed first, locking the weak pointer fails and 0049 * only the processor is deleted. 0050 * 0051 * The weak pointer is used only when creating or recreating a local 0052 * processor. Step processing uses the cached raw pointer to avoid locking a 0053 * weak pointer on every step iteration. 0054 */ 0055 template<class T> 0056 class LocalProcessorSlots 0057 { 0058 public: 0059 //!@{ 0060 //! \name Type aliases 0061 using SPProcessor = std::shared_ptr<T>; 0062 //!@} 0063 0064 public: 0065 //! Construct without any streams 0066 LocalProcessorSlots() = default; 0067 0068 // Construct with the number of streams 0069 explicit inline LocalProcessorSlots(StreamId::size_type num_streams); 0070 0071 // Get or create the processor for a stream 0072 template<class F> 0073 inline SPProcessor make(StreamId sid, F&& construct); 0074 0075 // Access the processor for a stream 0076 inline T& get(StreamId sid) const; 0077 0078 //! Number of streams 0079 StreamId::size_type size() const { return slots_.size(); } 0080 0081 private: 0082 struct Slot 0083 { 0084 std::weak_ptr<T> weak_processor; 0085 T* processor{nullptr}; 0086 }; 0087 0088 struct SlotDeleter 0089 { 0090 std::weak_ptr<Slot> weak_slot; 0091 0092 void operator()(T* processor) const 0093 { 0094 if (auto slot = weak_slot.lock()) 0095 { 0096 if (slot->processor == processor) 0097 { 0098 slot->processor = nullptr; 0099 slot->weak_processor.reset(); 0100 } 0101 } 0102 delete processor; 0103 } 0104 }; 0105 0106 std::vector<std::shared_ptr<Slot>> slots_; 0107 }; 0108 0109 //---------------------------------------------------------------------------// 0110 // INLINE DEFINITIONS 0111 //---------------------------------------------------------------------------// 0112 /*! 0113 * Construct with the number of streams. 0114 */ 0115 template<class T> 0116 LocalProcessorSlots<T>::LocalProcessorSlots(StreamId::size_type num_streams) 0117 { 0118 CELER_EXPECT(num_streams > 0); 0119 slots_.reserve(num_streams); 0120 for ([[maybe_unused]] auto i : range(num_streams)) 0121 { 0122 slots_.push_back(std::make_shared<Slot>()); 0123 } 0124 } 0125 0126 //---------------------------------------------------------------------------// 0127 /*! 0128 * Get or create the processor for a stream. 0129 * 0130 * The \c construct function must return a newly allocated (raw) pointer to 0131 * the processor. Due to Geant4 multithread semantics, this \b must be called 0132 * on the same CPU thread on which the resulting processor is used. 0133 */ 0134 template<class T> 0135 template<class F> 0136 auto LocalProcessorSlots<T>::make(StreamId sid, F&& construct) -> SPProcessor 0137 { 0138 CELER_EXPECT(sid < slots_.size()); 0139 0140 auto const& slot = slots_[sid.get()]; 0141 CELER_ASSERT(slot); 0142 if (auto result = slot->weak_processor.lock()) 0143 { 0144 CELER_ASSERT(result.get() == slot->processor); 0145 return result; 0146 } 0147 0148 slot->processor = nullptr; 0149 0150 SPProcessor result{std::forward<F>(construct)(), SlotDeleter{slot}}; 0151 CELER_ASSERT(result); 0152 slot->weak_processor = result; 0153 slot->processor = result.get(); 0154 return result; 0155 } 0156 0157 //---------------------------------------------------------------------------// 0158 /*! 0159 * Access the processor for a stream. 0160 * 0161 * The processor must have been created with \c make and still be alive. 0162 */ 0163 template<class T> 0164 T& LocalProcessorSlots<T>::get(StreamId sid) const 0165 { 0166 CELER_EXPECT(sid < slots_.size()); 0167 auto* result = slots_[sid.get()]->processor; 0168 CELER_EXPECT(result); 0169 return *result; 0170 } 0171 0172 //---------------------------------------------------------------------------// 0173 } // namespace detail 0174 } // namespace celeritas
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|