Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 10:01:41

0001 
0002 #pragma once
0003 #include <mutex>
0004 
0005 #include <JANA/JEventProcessor.h>
0006 #include <JANA/Services/JGlobalRootLock.h>
0007 
0008 /// This class can be used to safely implement sequential code while ensuring
0009 /// the factory algorithms are run in parallel using the JANA ROOT lock.
0010 ///
0011 /// n.b. If you need to run sequentially, but for reasons other than ROOT,
0012 /// then please look at the JEventProcessorSequential class.
0013 ///
0014 /// This is a specialized version of JEventProcessor that allows data types
0015 /// that will be needed to be specified in the subclass definition. The
0016 /// types will be prefetched via event->Get() calls and then a mutex locked
0017 /// prior to calling the ProcessSequential method. The types are specified
0018 /// by adding PrefetchT data members to the class as illustrated in the
0019 /// example below.
0020 ///
0021 /// Note that this will automatically lock the
0022 ///
0023 /// class DaveTestProcessor: public JEventProcessorSequentialRoot {
0024 ///
0025 ///  public:
0026 ///
0027 ///     // These declare object types that should be automatically fetched
0028 ///     // from the event before ProcessSequential is called.
0029 ///     PrefetchT<Hit>     hits     = {this};
0030 ///     PrefetchT<Cluster> clusters = {this, "MyTag"};
0031 ///
0032 ///     // This will be run sequentially
0033 ///     void ProcessSequential(const std::shared_ptr<const JEvent>& event) override {
0034 ///
0035 ///         // Do NOT call event->Get() here!
0036 ///
0037 ///         // The hits and clusters objects will already be filled by calls
0038 ///         // to event->Get(). Just use them here via their operator().
0039 ///         for( auto h : hits() ){
0040 ///             // h is const Hit*
0041 ///         }
0042 ///     }
0043 ///
0044 ///     // Boilerplate stuff
0045 ///     DaveTestProcessor() { SetTypeName(NAME_OF_THIS); }
0046 ///     void Init() override {}
0047 ///     void Finish() override {}
0048 ///  };
0049 ///
0050 class JEventProcessorSequentialRoot : public JEventProcessor {
0051     // This works in the following way:
0052     //
0053     // Two utility classes are defined here: Prefetch and PrefetchT with the
0054     // latter being a template. Subclasses can add any number of data members
0055     // of type PrefetchT. Using initializers, a list of the Prefetch members
0056     // is stored in the JEventProcessorSequentialRoot object itself.
0057     //
0058     // The Process method here loops over the Prefetch members calling
0059     // event->Get() for each and storing the resulting vector<const *T>
0060     // in the Prefetch member object. It then locks its own mutex before
0061     // calling ProcessSequential().
0062     //
0063     // The Process method is defined here and marked as "final" so subclasses
0064     // may not overide it. Instead, a new virtual method, ProcessSequential,
0065     // is added that the user may override.
0066     //
0067     // The tricky business here is capturing a list of the Prefetch data
0068     // members so they can be looped over at run time. Thus, the strange syntax
0069     // of passing the "this" argument to the PrefetchT constructors.
0070     // Similarly, each Prefetch object passes its "this" pointer back to the
0071     // JEventProcessorSequentialRoot object that owns it.
0072 
0073 public:
0074 
0075     JEventProcessorSequentialRoot() {}
0076     virtual ~JEventProcessorSequentialRoot() = default;
0077 
0078     // non-templated base class
0079     class Prefetch {
0080     public:
0081         virtual void Get(const std::shared_ptr<const JEvent> &event) = 0;
0082         virtual void Fill(const std::shared_ptr<const JEvent> &event) = 0;
0083     };
0084 
0085     // typed class
0086     template<typename T>
0087     class PrefetchT : public Prefetch {
0088     public:
0089         // This constructor gets called by the {this} initializer for the data member.
0090         PrefetchT(JEventProcessorSequentialRoot *jeps, const std::string &tag = "") : mTag(
0091                 tag) { jeps->mPrefetch.push_back(this); }
0092 
0093         std::vector<const T*> &operator()() { return mObjs; }
0094         void Get(const std::shared_ptr<const JEvent> &event) { event->Get<T>(mTag); }
0095         void Fill(const std::shared_ptr<const JEvent> &event) { mObjs = event->Get<T>(mTag); }
0096 
0097     private:
0098         PrefetchT() = default; // user must pass "this" so member can be added to our mPrefetch
0099 
0100         std::string mTag;
0101         std::vector<const T *> mObjs;
0102     };
0103 
0104 
0105     // JEventProcessorSequentialRoot takes control of Init, Finish, and Process. The user overrides
0106     // ProcessSequential, InitWithGlobalRootLock, and FinishWithGlobalRootLock instead.
0107 
0108     void Init() final {
0109         mGlobalWriteLock = GetApplication()->GetService<JGlobalRootLock>();
0110         mGlobalWriteLock->acquire_write_lock();
0111         try {
0112             InitWithGlobalRootLock();
0113             mGlobalWriteLock->release_lock();
0114         }
0115         catch (...) {
0116             mGlobalWriteLock->release_lock();
0117             // Ideally we'd use the STL read-write lock (std::shared_mutex/lock) intead of the pthreads one.
0118             // However for now we are limited to C++14 (we would need C++17).
0119             throw;
0120             // It is important to re-throw exceptions so that the framework can handle them correctly
0121         }
0122     };
0123 
0124     void Finish() final {
0125         mGlobalWriteLock->acquire_write_lock();
0126         try {
0127             FinishWithGlobalRootLock();
0128             mGlobalWriteLock->release_lock();
0129         }
0130         catch (...) {
0131             mGlobalWriteLock->release_lock();
0132             // Ideally we'd use the STL read-write lock (std::shared_mutex/lock) intead of the pthreads one.
0133             // However for now we are limited to C++14 (we would need C++17).
0134             throw;
0135             // It is important to re-throw exceptions so that the framework can handle them correctly
0136         }
0137     };
0138 
0139     void Process(const std::shared_ptr<const JEvent> &event) override final {
0140         for (auto p: mPrefetch) p->Get(event);  // make sure all factories have been activated
0141 
0142         std::lock_guard<std::mutex> lock(mFillMutex);
0143         for (auto p: mPrefetch) p->Fill(event); // Copy object pointers into members
0144         ProcessSequential(event);
0145     }
0146 
0147     // These are what the user implements (in lieu of Process, Init, and Finish)
0148 
0149     virtual void ProcessSequential(const std::shared_ptr<const JEvent>&) {};
0150 
0151     virtual void InitWithGlobalRootLock() {};
0152 
0153     virtual void FinishWithGlobalRootLock() {};
0154 
0155 private:
0156 
0157     std::vector<Prefetch *> mPrefetch;
0158     std::shared_ptr<JGlobalRootLock> mGlobalWriteLock;
0159     std::mutex mFillMutex; // We don't need the global root lock to fill a histogram as long as all reads and writes happen inside ProcessSequential!
0160 };
0161 
0162 
0163