Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-07-19 08:32:50

0001 
0002 // Copyright 2023, Jefferson Science Associates, LLC.
0003 // Subject to the terms in the LICENSE file found in the top-level directory.
0004 
0005 #include <JANA/JException.h>
0006 #include <JANA/JLogger.h>
0007 #include <JANA/JFactory.h>
0008 #include <JANA/JEvent.h>
0009 #include <JANA/JEventSource.h>
0010 #include <JANA/Utils/JTypeInfo.h>
0011 #include <JANA/JVersion.h>
0012 
0013 #if JANA2_HAVE_PERFETTO
0014 #include <JANA/Services/JPerfettoService.h>
0015 
0016 // Thread-local pointer to the factory whose span is currently open on this thread.
0017 // Used to build inter-factory dependency flow arrows: when factory B is triggered from
0018 // within factory A's Process(), g_current_executing_factory == A, so we can draw
0019 // an arrow A → B in the Perfetto timeline.
0020 static thread_local JFactory* g_current_executing_factory = nullptr;
0021 #endif
0022 
0023 
0024 class FlagGuard {
0025     bool* m_flag;
0026 public:
0027     FlagGuard(bool* flag) : m_flag(flag) {
0028         *m_flag = true;
0029     }
0030     ~FlagGuard() {
0031         *m_flag = false;
0032     }
0033 };
0034 
0035 void JFactory::Create(const std::shared_ptr<const JEvent>& event) {
0036     Create(*event.get());
0037 }
0038 
0039 void JFactory::Create(const JEvent& event) {
0040 
0041     if (mInsideCreate && (mStatus != Status::Inserted)) {
0042         // Ideally, we disallow any calls to Create() that end up calling it right back. However, we do allow
0043         // calls that go down to GetObjects, who inserts the data, but then RETRIEVES the same data it just inserted,
0044         // so that it can subsequently calculate and insert OTHER data. Once we refactor JEventSourceEVIOpp, we can consider
0045         // removing this weird edge case.
0046         throw JException("Encountered a cycle in the factory dependency graph! Hint: Maybe this data was supposed to be inserted in the JEventSource");
0047     }
0048     FlagGuard insideCreateFlagGuard (&mInsideCreate); // No matter how we exit from Create() (particularly with exceptions) mInsideCreate will be set back to false
0049 
0050     if (m_app == nullptr && event.GetJApplication() != nullptr) {
0051         // These are usually set by JFactoryGeneratorT, but some user code has custom JFactoryGenerators which don't!
0052         // The design of JFactoryGenerator doesn't give us a better place to inject things
0053         m_app = event.GetJApplication();
0054         m_logger = m_app->GetJParameterManager()->GetLogger(GetLoggerName());
0055     }
0056 
0057     // How do we obtain our data? The priority is as follows:
0058     // 1. JFactory::Process() if REGENERATE flag is set
0059     // 2. JEvent::Insert()
0060     // 3. JEventSource::GetObjects() if source has GetObjects() enabled
0061     // 4. JFactory::Process()
0062 
0063     // ---------------------------------------------------------------------
0064     // 1. JFactory::Process() if REGENERATE flag is set
0065     // ---------------------------------------------------------------------
0066 
0067     if (mRegenerate) {
0068         if (mStatus == Status::Inserted) {
0069             // Status::Inserted indicates that the data came from either src->GetObjects() or evt->Insert()
0070             ClearData(); 
0071             // ClearData() resets mStatus to Unprocessed so that the data will be regenerated exactly once.
0072         }
0073         // After this point, control flow falls through to "4. JFactory::Process"
0074     }
0075     else {
0076 
0077         // ---------------------------------------------------------------------
0078         // 2. JEvent::Insert()
0079         // ---------------------------------------------------------------------
0080 
0081         if (mStatus == Status::Inserted) {
0082             // This may include data cached from eventsource->GetObjects().
0083             // Either way, short-circuit here, because the data is present.
0084             return;
0085         }
0086 
0087         // ---------------------------------------------------------------------
0088         // 3. JEventSource::GetObjects() if source has GetObjects() enabled
0089         // ---------------------------------------------------------------------
0090 
0091         auto src = event.GetJEventSource();
0092         if (src != nullptr && src->IsGetObjectsEnabled()) {
0093             bool found_data = false;
0094 
0095             CallWithJExceptionWrapper("JEventSource::GetObjects", [&](){ 
0096                 found_data = src->GetObjects(event.shared_from_this(), this); });
0097 
0098             if (found_data) {
0099                 mStatus = Status::Inserted;
0100                 return;
0101             }
0102         }
0103         // If neither "2. JEvent::Insert()" nor "3. JEventSource::GetObjects()" succeeded, fall through to "4. JFactory::Process()"
0104     }
0105 
0106     // ---------------------------------------------------------------------
0107     // 4. JFactory::Process()
0108     // ---------------------------------------------------------------------
0109 
0110     // Check if init had _previously_ excepted but the cache was since cleared
0111     if (mInitStatus == InitStatus::InitExcepted && mStatus == Status::Empty) {
0112         for (auto* output : GetOutputs()) {
0113             output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0114         }
0115         for (auto* output : GetVariadicOutputs()) {
0116             output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0117         }
0118         mStatus = Status::Excepted;
0119         std::rethrow_exception(mException);
0120     }
0121 
0122     // The factory span wraps the entire activation: Init + run callbacks + input population + Process.
0123     // factory_init spans (Init, BeginRun, ChangeRun, EndRun) appear as children of this factory span,
0124     // making the phase structure visible when inspecting any factory span in the trace.
0125     // Flow events connect each factory span to the parent factory that triggered it,
0126     // drawn as arrows showing the data-dependency chain in the Perfetto UI.
0127     {
0128 #if JANA2_HAVE_PERFETTO
0129         // If a parent factory is currently executing on this thread it triggered us.
0130         // Emit a flow-start instant inside the parent's still-open span, then open
0131         // our span with a TerminatingFlow — Perfetto draws an arrow parent → us.
0132         JFactory* const caller = g_current_executing_factory;
0133         // Guard flow_id computation behind the category check: the FNV hash and
0134         // GetEventNumber() are non-trivial work that should not run when tracing
0135         // is disabled, even though the TRACE_EVENT_* args are already lazy.
0136         if (TRACE_EVENT_CATEGORY_ENABLED("factory") && caller != nullptr) {
0137             // FNV-1a-inspired hash: unique 64-bit flow ID per (caller, callee, event).
0138             uint64_t flow_id = 14695981039346656037ULL;
0139             flow_id = (flow_id ^ static_cast<uint64_t>(reinterpret_cast<uintptr_t>(caller))) * 1099511628211ULL;
0140             flow_id = (flow_id ^ static_cast<uint64_t>(reinterpret_cast<uintptr_t>(this)))   * 1099511628211ULL;
0141             flow_id = (flow_id ^ static_cast<uint64_t>(event.GetEventNumber())) * 1099511628211ULL;
0142             // Flow starts here — we are still executing inside the parent's open span.
0143             TRACE_EVENT_INSTANT("factory", perfetto::DynamicString{GetFactoryName()},
0144                 perfetto::ThreadTrack::Current(), perfetto::Flow::Global(flow_id));
0145             // Our span starts, consuming the arrow from the parent's instant above.
0146             TRACE_EVENT_BEGIN("factory", perfetto::DynamicString{GetFactoryName()},
0147                 perfetto::TerminatingFlow::Global(flow_id),
0148                 "tag", GetTag(), "event_nr", event.GetEventNumber());
0149         } else {
0150             TRACE_EVENT_BEGIN("factory", perfetto::DynamicString{GetFactoryName()},
0151                 "tag", GetTag(), "event_nr", event.GetEventNumber());
0152         }
0153         // RAII: close the Perfetto span on scope exit (exception-safe).
0154         struct SpanGuard { ~SpanGuard() { TRACE_EVENT_END("factory"); } } span_guard;
0155         // RAII: track the currently-executing factory so sub-factories can link back to us.
0156         // Declared after span_guard — its destructor runs first, restoring the caller
0157         // before the span closes.
0158         struct CallerGuard {
0159             JFactory** slot; JFactory* saved;
0160             ~CallerGuard() { *slot = saved; }
0161         } caller_guard{&g_current_executing_factory, caller};
0162         g_current_executing_factory = this;
0163 #endif
0164 
0165         // Make sure Init() ran, which might except...
0166         try {
0167             DoInit(); // This checks mInitStatus internally before calling Init()
0168         }
0169         catch(...) {
0170             // If Init() excepts, we still need to store an empty collection
0171             mStatus = Status::Excepted;
0172             for (auto* output : GetOutputs()) {
0173                 output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0174             }
0175             for (auto* output : GetVariadicOutputs()) {
0176                 output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0177             }
0178             std::rethrow_exception(mException);
0179         }
0180 
0181         // At this point, Init() has run and has _not_ excepted
0182 
0183         if (mStatus == Status::Excepted) {
0184             // But Process() might have already excepted!
0185             std::rethrow_exception(mException);
0186         }
0187         else if (mStatus == Status::Empty) {
0188             // Now we know that we need to run Process() to create the data in the first place
0189             try {
0190                 auto run_number = event.GetRunNumber();
0191                 if (mPreviousRunNumber != run_number) {
0192                     if (m_callback_style == CallbackStyle::LegacyMode) {
0193                         if (mPreviousRunNumber != -1) {
0194                             {
0195 #if JANA2_HAVE_PERFETTO
0196                                 TRACE_EVENT("factory_end_run", perfetto::DynamicString{GetFactoryName()},
0197                                     "tag", GetTag(), "run_nr", mPreviousRunNumber);
0198 #endif
0199                                 CallWithJExceptionWrapper("JFactory::EndRun", [&](){ EndRun(); });
0200                             }
0201                         }
0202                         {
0203 #if JANA2_HAVE_PERFETTO
0204                             TRACE_EVENT("factory_change_run", perfetto::DynamicString{GetFactoryName()},
0205                                 "tag", GetTag(), "run_nr", run_number);
0206 #endif
0207                             CallWithJExceptionWrapper("JFactory::ChangeRun", [&](){ ChangeRun(event.shared_from_this()); });
0208                         }
0209                         {
0210 #if JANA2_HAVE_PERFETTO
0211                             TRACE_EVENT("factory_begin_run", perfetto::DynamicString{GetFactoryName()},
0212                                 "tag", GetTag(), "run_nr", run_number);
0213 #endif
0214                             CallWithJExceptionWrapper("JFactory::BeginRun", [&](){ BeginRun(event.shared_from_this()); });
0215                         }
0216                     }
0217                     else if (m_callback_style == CallbackStyle::ExpertMode) {
0218                         {
0219 #if JANA2_HAVE_PERFETTO
0220                             TRACE_EVENT("factory_change_run", perfetto::DynamicString{GetFactoryName()},
0221                                 "tag", GetTag(), "run_nr", run_number);
0222 #endif
0223                             CallWithJExceptionWrapper("JFactory::ChangeRun", [&](){ ChangeRun(event); });
0224                         }
0225                     }
0226                     mPreviousRunNumber = run_number;
0227                 }
0228                 for (auto* input : GetInputs()) {
0229                     input->Populate(event);
0230                 }
0231                 for (auto* input : GetVariadicInputs()) {
0232                     input->Populate(event);
0233                 }
0234                 // Process() runs inside the factory span; dependent factory spans appear as children
0235                 if (m_callback_style == CallbackStyle::LegacyMode) {
0236                     CallWithJExceptionWrapper("JFactory::Process", [&](){ Process(event.shared_from_this()); });
0237                 }
0238                 else if (m_callback_style == CallbackStyle::ExpertMode) {
0239                     CallWithJExceptionWrapper("JFactory::Process", [&](){ Process(event); });
0240                 }
0241                 else {
0242                     throw JException("Invalid callback style");
0243                 }
0244             }
0245             catch (JException& ex) {
0246                 // Save everything already created even if we throw an exception
0247                 // This is so that we leave everything in a valid state just in case someone tries to catch the exception recover,
0248                 // such as EICrecon. (Remember that a missing collection in the podio frame will segfault if anyone tries to write that frame)
0249                 // Note that the collections themselves won't know that they exited early
0250 
0251                 LOG_DEBUG(GetLogger()) << "Exception in JFactory::Create, prefix=" << GetPrefix()
0252                                        << ", message=" << ex.GetMessage();
0253                 mStatus = Status::Excepted;
0254                 mException = std::current_exception();
0255                 for (auto* output : GetOutputs()) {
0256                     output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0257                 }
0258                 for (auto* output : GetVariadicOutputs()) {
0259                     output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0260                 }
0261                 throw;
0262             }
0263             catch (...) {
0264                 // Save everything already created even if we throw an exception
0265                 // This is so that we leave everything in a valid state just in case someone tries to catch the exception recover,
0266                 // such as EICrecon. (Remember that a missing collection in the podio frame will segfault if anyone tries to write that frame)
0267                 // Note that the collections themselves won't know that they exited early
0268 
0269                 LOG_DEBUG(GetLogger()) << "Exception in JFactory::Create, prefix=" << GetPrefix();
0270                 mStatus = Status::Excepted;
0271                 mException = std::current_exception();
0272                 for (auto* output : GetOutputs()) {
0273                     output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0274                 }
0275                 for (auto* output : GetVariadicOutputs()) {
0276                     output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Excepted);
0277                 }
0278                 throw;
0279             }
0280 
0281             // Save the (successfully processed) data
0282             mStatus = Status::Processed;
0283             for (auto* output : GetOutputs()) {
0284                 output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Created);
0285             }
0286             for (auto* output : GetVariadicOutputs()) {
0287                 output->LagrangianStore(*event.GetFactorySet(), JDatabundle::Status::Created);
0288             }
0289         }
0290     } // end factory span
0291 }
0292 
0293 void JFactory::DoInit() {
0294     if (mInitStatus != InitStatus::InitNotRun) {
0295         return;
0296     }
0297     for (auto* parameter : m_parameters) {
0298         parameter->Init(*(m_app->GetJParameterManager()), m_prefix);
0299     }
0300     for (auto* service : m_services) {
0301         service->Fetch(m_app);
0302     }
0303     try {
0304         {
0305 #if JANA2_HAVE_PERFETTO
0306             TRACE_EVENT("factory_init", perfetto::DynamicString{GetFactoryName()},
0307                 "tag", GetTag());
0308 #endif
0309             CallWithJExceptionWrapper("JFactory::Init", [&](){ Init(); });
0310         }
0311         mInitStatus = InitStatus::InitRun;
0312     }
0313     catch (...) {
0314         mInitStatus = InitStatus::InitExcepted;
0315         mException = std::current_exception();
0316         throw;
0317     }
0318 }
0319 
0320 void JFactory::DoFinish() {
0321     if (mInitStatus == InitStatus::InitRun) {
0322         if (mPreviousRunNumber != -1) {
0323             CallWithJExceptionWrapper("JFactory::EndRun", [&](){ EndRun(); });
0324         }
0325         CallWithJExceptionWrapper("JFactory::Finish", [&](){ Finish(); });
0326     }
0327 }
0328 
0329 void JFactory::Summarize(JComponentSummary& summary) const {
0330 
0331     auto fs = new JComponentSummary::Component(
0332             "Factory",
0333             GetPrefix(),
0334             GetTypeName(),
0335             GetLevel(),
0336             GetPluginName());
0337 
0338     SummarizeInputs(*fs);
0339     SummarizeOutputs(*fs);
0340     summary.Add(fs);
0341 }
0342 
0343