From cd39d44ea572a14f5c0d6d7fc4a48249b7d2054b Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Mon, 22 Jan 2024 23:52:51 -0500 Subject: [PATCH 01/34] Add 'level' tag to JEvent --- src/libraries/JANA/JEvent.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 16ddeb717..33bfbac87 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -40,6 +40,7 @@ class JEventSource; class JEvent : public JResettable, public std::enable_shared_from_this { public: + enum class Level { Timeslice, Block, SlowControls, PhysicsEvent, SubEvent}; explicit JEvent(JApplication* aApplication=nullptr) : mInspector(&(*this)) { mApplication = aApplication; @@ -127,6 +128,18 @@ class JEvent : public JResettable, public std::enable_shared_from_this friend class JEventPool; + // Hierarchical + Level GetLevel() const { return mLevel; } + + void AddParent(JEvent* parent) { + // TODO: Add parent + } + const JEvent& GetParent(Level level) const { + // TODO: Get parent + } + + + private: JApplication* mApplication = nullptr; @@ -140,6 +153,13 @@ class JEvent : public JResettable, public std::enable_shared_from_this JEventSource* mEventSource = nullptr; bool mIsBarrierEvent = false; + // Hierarchical stuff + Level mLevel = Level::PhysicsEvent; + std::map mParents; + size_t mReferenceCount = 0; + + + #ifdef JANA2_HAVE_PODIO std::map mPodioFactories; #endif From cfa75f8aae9e2fb9702eafbe5ecf1eb9e6252b21 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Wed, 24 Jan 2024 14:56:18 -0500 Subject: [PATCH 02/34] SubeventExample correctly installed to programs/ --- src/examples/SubeventExample/CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/examples/SubeventExample/CMakeLists.txt b/src/examples/SubeventExample/CMakeLists.txt index 8a7a74c6a..411944187 100644 --- a/src/examples/SubeventExample/CMakeLists.txt +++ b/src/examples/SubeventExample/CMakeLists.txt @@ -7,7 +7,7 @@ set (SubeventExample_SOURCES add_executable(SubeventExample ${SubeventExample_SOURCES}) target_link_libraries(SubeventExample jana2) -set_target_properties(SubeventExample PROPERTIES PREFIX "" OUTPUT_NAME "SubeventExample" SUFFIX ".so") -install(TARGETS SubeventExample DESTINATION plugins) +set_target_properties(SubeventExample PROPERTIES PREFIX "" OUTPUT_NAME "SubeventExample") +install(TARGETS SubeventExample DESTINATION programs) From 37585a0767a7f6d6af9b9c560ec1bdf9ad355243 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Tue, 6 Feb 2024 17:03:00 -0500 Subject: [PATCH 03/34] Topology building logic --- src/programs/unit_tests/CMakeLists.txt | 1 + .../unit_tests/MultiLevelTopologyTests.cc | 442 ++++++++++++++++++ 2 files changed, 443 insertions(+) create mode 100644 src/programs/unit_tests/MultiLevelTopologyTests.cc diff --git a/src/programs/unit_tests/CMakeLists.txt b/src/programs/unit_tests/CMakeLists.txt index 2d8b8114b..762dd0160 100644 --- a/src/programs/unit_tests/CMakeLists.txt +++ b/src/programs/unit_tests/CMakeLists.txt @@ -37,6 +37,7 @@ set(TEST_SOURCES JMultiFactoryTests.cc JPoolTests.cc ArrowTests.cc + MultiLevelTopologyTests.cc ) if (${USE_PODIO}) diff --git a/src/programs/unit_tests/MultiLevelTopologyTests.cc b/src/programs/unit_tests/MultiLevelTopologyTests.cc new file mode 100644 index 000000000..8eafd73d1 --- /dev/null +++ b/src/programs/unit_tests/MultiLevelTopologyTests.cc @@ -0,0 +1,442 @@ +#include +#include +#include + + +enum class Level { None, Timeslice, Event, Subevent }; +enum class ComponentType { None, Source, Filter, Map, Split, Merge, Reduce }; + +std::ostream& operator<<(std::ostream& os, Level l) { + switch (l) { + case Level::None: os << "None"; break; + case Level::Timeslice: os << "Timeslice"; break; + case Level::Event: os << "Event"; break; + case Level::Subevent: os << "Subevent"; break; + } + return os; +} + +std::ostream& operator<<(std::ostream& os, ComponentType ct) { + switch (ct) { + case ComponentType::None: os << "None"; break; + case ComponentType::Source: os << "Source"; break; + case ComponentType::Filter: os << "Filter"; break; + case ComponentType::Map: os << "Map"; break; + case ComponentType::Split: os << "Split"; break; + case ComponentType::Merge: os << "Merge"; break; + case ComponentType::Reduce: os << "Reduce"; break; + } + return os; +} + + +struct Arrow { + Level level = Level::None; + ComponentType componentType = ComponentType::None; + + Arrow* input = nullptr; + Arrow* output = nullptr; + +}; + +class Diagram { + std::vector grid; + size_t rows, cols, hspace, vspace; + + Diagram(size_t rows, size_t cols, size_t hspace=10, size_t vspace=4) + : rows(rows), cols(cols), hspace(hspace), vspace(vspace) { + + for (size_t i=0; i, Arrow*> arrows; + Arrow* source; + + void add(Level l, ComponentType ct) { + auto arrow = new Arrow; + arrow->level = l; + arrow->componentType = ct; + arrows[{l, ct}] = arrow; + } + + bool has_connection(Level l1, ComponentType c1, Level l2, ComponentType c2) { + + Arrow* a1 = arrows.at({l1, c1}); + Arrow* a2 = arrows.at({l2, c2}); + + bool success = true; + success &= (a1 != nullptr); + success &= (a2 != nullptr); + success &= (a1->output == a2); + success &= (a2->input == a1); + + return success; + } + + bool has_no_input(Level l, ComponentType c) { + return (arrows.at({l, c})->input == nullptr); + } + + bool has_no_output(Level l, ComponentType c) { + return (arrows.at({l, c})->output == nullptr); + } + + void print() { + Arrow* next = source; + while (next != nullptr) { + switch (next->level) { + case Level::Timeslice: std::cout << "Timeslice:"; break; + case Level::Event: std::cout << " Event:"; break; + case Level::Subevent: std::cout << " Subevent:"; break; + case Level::None: std::cout << "None:"; break; + } + std::cout << next->componentType << std::endl; + next = next->output; + } + } + + void print_diagram() { + std::cout << "====================================================================" << std::endl; + std::cout << "Level Source Filter Map Split Merge Reduce" << std::endl; + + Level levels[] = {Level::Timeslice, Level::Event, Level::Subevent}; + for (int i=0; i<3; ++i) { + // ----------- + // Print row + // ----------- + + Level l = levels[i]; + + switch (l) { + case Level::None: std::cout << "None "; break; + case Level::Timeslice: std::cout << "Timeslice "; break; + case Level::Event: std::cout << "Event "; break; + case Level::Subevent: std::cout << "Subevent "; break; + } + + bool pencil_active = false; + for (ComponentType ct : {ComponentType::Source, ComponentType::Filter, ComponentType::Map, ComponentType::Split, ComponentType::Merge, ComponentType::Reduce}) { + auto it = arrows.find({l, ct}); + + if (it == arrows.end()) { + if (pencil_active) { + std::cout << "----------"; + } + else { + std::cout << " "; + } + } + else { + std::cout << "[]"; + pencil_active = (it->second->output != nullptr && it->second->output->level == l); + if (pencil_active) { + std::cout << "--------"; + } + else { + std::cout << " "; + } + } + } + std::cout << std::endl; + + // -------- + // Print connectors to lower level + // -------- + // Skip lowest level + if (i < 2) { + // Vertical connector to split and merge + Level lower = levels[i+1]; + auto split_it = arrows.find({l, ComponentType::Split}); + if (split_it != arrows.end()) { + std::cout << " |"; + } + else { + std::cout << " "; + } + auto merge_it = arrows.find({l, ComponentType::Merge}); + if (merge_it != arrows.end()) { + std::cout << " |"; + } + else { + std::cout << " "; + } + std::cout << std::endl; + + // Horizontal connector + std::cout << " "; + bool pencil_active = false; + for (ComponentType ct : {ComponentType::Source, ComponentType::Filter, ComponentType::Map, ComponentType::Split, ComponentType::Merge, ComponentType::Reduce}) { + + bool found_split = (split_it != arrows.end() && ct == ComponentType::Split); + bool found_splitee = (split_it != arrows.end() && split_it->second->output->componentType == ct); + bool found_merge = (merge_it != arrows.end() && ct == ComponentType::Merge); + bool found_mergee = (merge_it != arrows.end() && merge_it->second->input->componentType == ct); + + if (found_splitee) { + pencil_active = !pencil_active; + } + if (found_split) { + pencil_active = !pencil_active; + } + if (found_merge) { + pencil_active = !pencil_active; + } + if (found_mergee) { + pencil_active = !pencil_active; + } + if (pencil_active) { + std::cout << "----------"; + } + else { + std::cout << " "; + } + } + std::cout << std::endl; + + + // Vertical connector to split and merge joinees + std::cout << " "; + for (ComponentType ct : {ComponentType::Source, ComponentType::Filter, ComponentType::Map, ComponentType::Split, ComponentType::Merge, ComponentType::Reduce}) { + if (split_it != arrows.end() && split_it->second->output->componentType == ct) { + std::cout << "| "; + } + else if (merge_it != arrows.end() && merge_it->second->input->componentType == ct) { + std::cout << "| "; + } + else { + std::cout << " "; + } + } + std::cout << std::endl; + } + } + std::cout << "====================================================================" << std::endl; + } + + + void wire_and_print() { + + source = nullptr; + for (Level l : {Level::Timeslice, Level::Event, Level::Subevent} ) { + Arrow* last = nullptr; + + for (ComponentType ct : {ComponentType::Source, ComponentType::Filter, ComponentType::Map, ComponentType::Split, ComponentType::Merge, ComponentType::Reduce}) { + auto it = arrows.find({l, ct}); + if (it != arrows.end()) { + + Arrow* next = it->second; + if (last != nullptr) { + next->input = last; + last->output = next; + } + last = next; + + // Handle sources: + if (ct == ComponentType::Source) { + if (source != nullptr) { + throw std::runtime_error("Too many sources!"); + } + source = next; + } + } + } + } + + Level levels[] = {Level::Timeslice, Level::Event, Level::Subevent}; + for (int i=0; i<2; ++i) { + Level upper = levels[i]; + Level lower = levels[i+1]; + + auto split_it = arrows.find({upper, ComponentType::Split}); + if (split_it != arrows.end()) { + // Have splitter, so we need to find the lower-level arrow it connects to + bool found_joinee = false; + for (ComponentType ct : {ComponentType::Filter, ComponentType::Map, ComponentType::Split, ComponentType::Reduce}) { + auto it = arrows.find({lower, ct}); + if (it != arrows.end()) { + Arrow* joinee = it->second; + split_it->second->output = joinee; + joinee->input = split_it->second; + found_joinee = true; + break; + } + } + if (!found_joinee) { + throw std::runtime_error("Invalid topology: Unable to find successor to split"); + } + } + + auto merge_it = arrows.find({upper, ComponentType::Merge}); + if (merge_it != arrows.end()) { + // Have merger, so we need to find the lower-level arrow it connects to + bool found_joinee = false; + for (ComponentType ct : {ComponentType::Reduce, ComponentType::Merge, ComponentType::Map, ComponentType::Filter}) { + auto it = arrows.find({lower, ct}); + if (it != arrows.end()) { + Arrow* joinee = it->second; + merge_it->second->input = joinee; + joinee->output = merge_it->second; + found_joinee = true; + break; + } + } + if (!found_joinee) { + throw std::runtime_error("Invalid topology: Unable to find predecessor to merge"); + } + } + } + + print(); + } +}; + + +// Source Filter Map Split Merge Reduce +// +// Timeslice [] -----> [] -----> [] -----> [] [] -----> [] +// / \ +// --- --- +// / \ +// Event [] -------------------------> [] + + + +TEST_CASE("MultiLevelTopologyBuilderTests") { + MultiLevelTopologyBuilder b; + + SECTION("Default topology") { + + b.add(Level::Event, ComponentType::Source); + b.add(Level::Event, ComponentType::Map); + + b.wire_and_print(); + + REQUIRE(b.has_no_input( Level::Event, ComponentType::Source )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Source, Level::Event, ComponentType::Map )); + REQUIRE(b.has_no_output( Level::Event, ComponentType::Map )); + } + + SECTION("Default with separated map/reduce phases") { + + b.add(Level::Event, ComponentType::Source); + b.add(Level::Event, ComponentType::Map); + b.add(Level::Event, ComponentType::Reduce); + + b.wire_and_print(); + + REQUIRE(b.has_no_input( Level::Event, ComponentType::Source )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Source, Level::Event, ComponentType::Map )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Map, Level::Event, ComponentType::Reduce )); + REQUIRE(b.has_no_output( Level::Event, ComponentType::Reduce )); + } + + SECTION("Timeslices topology") { + + b.add(Level::Timeslice, ComponentType::Source); + b.add(Level::Timeslice, ComponentType::Map); + b.add(Level::Timeslice, ComponentType::Split); + b.add(Level::Timeslice, ComponentType::Merge); + b.add(Level::Event, ComponentType::Map); + b.add(Level::Event, ComponentType::Reduce); + + b.wire_and_print(); + + REQUIRE(b.has_no_input( Level::Timeslice, ComponentType::Source )); + REQUIRE(b.has_connection( Level::Timeslice, ComponentType::Source, Level::Timeslice, ComponentType::Map )); + REQUIRE(b.has_connection( Level::Timeslice, ComponentType::Map, Level::Timeslice, ComponentType::Split )); + REQUIRE(b.has_no_output( Level::Timeslice, ComponentType::Merge )); + + REQUIRE(b.has_connection( Level::Event, ComponentType::Map, Level::Event, ComponentType::Reduce )); + REQUIRE(b.has_connection( Level::Timeslice, ComponentType::Split, Level::Event, ComponentType::Map )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Reduce, Level::Timeslice, ComponentType::Merge )); + + } + + SECTION("Three-level topology") { + + b.add(Level::Timeslice, ComponentType::Source); + b.add(Level::Timeslice, ComponentType::Map); + b.add(Level::Timeslice, ComponentType::Split); + b.add(Level::Timeslice, ComponentType::Merge); + b.add(Level::Event, ComponentType::Map); + b.add(Level::Event, ComponentType::Split); + b.add(Level::Event, ComponentType::Merge); + b.add(Level::Event, ComponentType::Reduce); + b.add(Level::Subevent, ComponentType::Map); + + b.wire_and_print(); + + REQUIRE(b.has_no_input( Level::Timeslice, ComponentType::Source )); + REQUIRE(b.has_connection( Level::Timeslice, ComponentType::Source, Level::Timeslice, ComponentType::Map )); + REQUIRE(b.has_connection( Level::Timeslice, ComponentType::Map, Level::Timeslice, ComponentType::Split )); + REQUIRE(b.has_connection( Level::Timeslice, ComponentType::Split, Level::Event, ComponentType::Map )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Map, Level::Event, ComponentType::Split )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Split, Level::Subevent, ComponentType::Map )); + REQUIRE(b.has_connection( Level::Subevent, ComponentType::Map, Level::Event, ComponentType::Merge )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Merge, Level::Event, ComponentType::Reduce )); + REQUIRE(b.has_connection( Level::Event, ComponentType::Reduce, Level::Timeslice, ComponentType::Merge )); + REQUIRE(b.has_no_output( Level::Timeslice, ComponentType::Merge )); + + } + + SECTION("Too many sources") { + b.add(Level::Timeslice, ComponentType::Source); + b.add(Level::Timeslice, ComponentType::Map); + b.add(Level::Timeslice, ComponentType::Split); + b.add(Level::Timeslice, ComponentType::Merge); + b.add(Level::Event, ComponentType::Map); + b.add(Level::Event, ComponentType::Reduce); + b.add(Level::Event, ComponentType::Source); + + REQUIRE_THROWS(b.wire_and_print()); + } + + SECTION("Inactive components") { + b.add(Level::Timeslice, ComponentType::Map); + b.add(Level::Timeslice, ComponentType::Split); + b.add(Level::Timeslice, ComponentType::Merge); + b.add(Level::Event, ComponentType::Map); + b.add(Level::Event, ComponentType::Reduce); + b.add(Level::Event, ComponentType::Source); + + b.wire_and_print(); + // Timeslice-level components are deactivated + } +} + + + + + + From 21e90335237b1925cd516afbc96fd1d4257189a0 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Wed, 31 Jan 2024 14:01:05 -0500 Subject: [PATCH 04/34] Add TimesliceExample TimesliceExample: Add timeslice source --- src/examples/CMakeLists.txt | 3 +- src/examples/TimesliceExample/CMakeLists.txt | 16 ++ .../TimesliceExample/TimesliceExample.cc | 143 ++++++++++++++++++ src/libraries/JANA/JEvent.h | 7 +- 4 files changed, 166 insertions(+), 3 deletions(-) create mode 100644 src/examples/TimesliceExample/CMakeLists.txt create mode 100644 src/examples/TimesliceExample/TimesliceExample.cc diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index 4e40f1867..a3c10326f 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -8,4 +8,5 @@ add_subdirectory(BlockExample) add_subdirectory(SubeventExample) add_subdirectory(SubeventCUDAExample) add_subdirectory(UnitTestingExample) -add_subdirectory(PodioExample) \ No newline at end of file +add_subdirectory(PodioExample) +add_subdirectory(TimesliceExample) diff --git a/src/examples/TimesliceExample/CMakeLists.txt b/src/examples/TimesliceExample/CMakeLists.txt new file mode 100644 index 000000000..cb2fc59c0 --- /dev/null +++ b/src/examples/TimesliceExample/CMakeLists.txt @@ -0,0 +1,16 @@ + + +if (USE_PODIO) + set (TimesliceExample_SOURCES + TimesliceExample.cc + ) + + add_executable(TimesliceExample ${TimesliceExample_SOURCES}) + target_link_libraries(TimesliceExample jana2 podio::podio PodioExampleDatamodel PodioExampleDatamodelDict podio::podioRootIO) + set_target_properties(TimesliceExample PROPERTIES PREFIX "" OUTPUT_NAME "TimesliceExample") + install(TARGETS TimesliceExample DESTINATION programs) + +else() + message(STATUS "Skipping examples/TimesliceExample because USE_PODIO=Off") +endif() + diff --git a/src/examples/TimesliceExample/TimesliceExample.cc b/src/examples/TimesliceExample/TimesliceExample.cc new file mode 100644 index 000000000..d9ce6d141 --- /dev/null +++ b/src/examples/TimesliceExample/TimesliceExample.cc @@ -0,0 +1,143 @@ + +// Copyright 2020, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#include +#include +#include +#include +#include +#include "JANA/Engine/JTopologyBuilder.h" + + +#include +#include +#include + + +struct MyInput : public JObject { + int x; + float y; + int evt = 0; + int sub = 0; + MyInput(int x, float y, int evt, int sub) : x(x), y(y), evt(evt), sub(sub) {} +}; + +struct MyOutput : public JObject { + float z; + int evt = 0; + int sub = 0; + explicit MyOutput(float z, int evt, int sub) : z(z), evt(evt), sub(sub) {} +}; + +struct MyProcessor : public JSubeventProcessor { + MyProcessor() { + inputTag = ""; + outputTag = "subeventted"; + } + MyOutput* ProcessSubevent(MyInput* input) override { + LOG << "Processing subevent " << input->evt << ":" << input->sub << LOG_END; + return new MyOutput(input->y + (float) input->x, input->evt, input->sub); + } +}; + + +struct ExampleTimesliceSource : public JEventSource { + SimpleSource(std::string name) : JEventSource(name) { + SetEventLevel(JEvent::Level::Timeslice); + }; + void GetEvent(std::shared_ptr event) override { + + auto evt = event->GetEventNumber(); + std::vector inputs; + inputs.push_back(new MyInput(22,3.6,evt,0)); + inputs.push_back(new MyInput(23,3.5,evt,1)); + inputs.push_back(new MyInput(24,3.4,evt,2)); + inputs.push_back(new MyInput(25,3.3,evt,3)); + inputs.push_back(new MyInput(26,3.2,evt,4)); + event->Insert(inputs); + + auto hits = std::make_unique(); + hits.push_back(ExampleHit(22)); + hits.push_back(ExampleHit(23)); + hits.push_back(ExampleHit(24)); + event->InsertCollection(hits); + + LOG << "Emitting event " << event->GetEventNumber() << LOG_END; + // throw JEventSource::RETURN_STATUS::kNO_MORE_EVENTS; + } +}; + +struct ExampleTimesliceProcessor : public JEventProcessor { + std::mutex m_mutex; + + ExampleTimesliceProcessor() { + SetEventLevel(JEvent::Level::Timeslice); + } + + void Process(const std::shared_ptr& event) { + + std::lock_guard guard(m_mutex); + + auto outputs = event->Get(); + // assert(outputs.size() == 4); + // assert(outputs[0]->z == 25.6f); + // assert(outputs[1]->z == 26.5f); + // assert(outputs[2]->z == 27.4f); + // assert(outputs[3]->z == 28.3f); + LOG << " Contents of event " << event->GetEventNumber() << LOG_END; + for (auto output : outputs) { + LOG << " " << output->evt << ":" << output->sub << " " << output->z << LOG_END; + } + LOG << " DONE with contents of event " << event->GetEventNumber() << LOG_END; + } +}; + + + +int main() { + + MyProcessor processor; + JMailbox*> events_in; + JMailbox*> events_out; + JMailbox> subevents_in; + JMailbox> subevents_out; + + auto split_arrow = new JSplitArrow("split", &processor, &events_in, &subevents_in); + auto subprocess_arrow = new JSubeventArrow("subprocess", &processor, &subevents_in, &subevents_out); + auto merge_arrow = new JMergeArrow("merge", &processor, &subevents_out, &events_out); + + auto parms = new JParameterManager; + // Some params need to be present BEFORE JApplication is constructed, e.g. log levels are lost + // parms->SetParameter("log:debug", "JWorker,JScheduler,JArrowProcessingController,JEventProcessorArrow"); + JApplication app(parms); + app.SetTimeoutEnabled(false); + app.SetTicker(false); + + auto source = new SimpleSource("simpleSource"); + source->SetNEvents(10); // limit ourselves to 10 events. Note that the 'jana:nevents' param won't work + // here because we aren't using JComponentManager to manage the EventSource + + auto topology = app.GetService()->create_empty(); + auto source_arrow = new JEventSourceArrow("simpleSource", + {source}, + &events_in, + topology->event_pool); + auto proc_arrow = new JEventProcessorArrow("simpleProcessor", &events_out, nullptr, topology->event_pool); + proc_arrow->add_processor(new SimpleProcessor); + + topology->arrows.push_back(source_arrow); + topology->arrows.push_back(split_arrow); + topology->arrows.push_back(subprocess_arrow); + topology->arrows.push_back(merge_arrow); + topology->arrows.push_back(proc_arrow); + + source_arrow->attach(split_arrow); + split_arrow->attach(subprocess_arrow); + subprocess_arrow->attach(merge_arrow); + merge_arrow->attach(proc_arrow); + + app.Run(true); + +} + diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 33bfbac87..9066822e4 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -132,10 +132,13 @@ class JEvent : public JResettable, public std::enable_shared_from_this Level GetLevel() const { return mLevel; } void AddParent(JEvent* parent) { - // TODO: Add parent + Level level = parent->GetLevel(); + mParents[level] = parent; + // TODO: Validate more } const JEvent& GetParent(Level level) const { - // TODO: Get parent + return *(mParents.at(level)); + // TODO: Validate more } From 8c2e869b6fd8d86255e413a7614ccae3c5a0d101 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Wed, 7 Feb 2024 12:43:22 -0500 Subject: [PATCH 05/34] All components know their own level --- src/libraries/JANA/JEvent.h | 12 ++++++------ src/libraries/JANA/JEventProcessor.h | 23 ++++++++++++++++------- src/libraries/JANA/JEventSource.h | 17 +++++++++++++---- src/libraries/JANA/JFactory.h | 17 ++++++++++++++--- src/libraries/JANA/Utils/JEventLevel.h | 8 ++++++++ 5 files changed, 57 insertions(+), 20 deletions(-) create mode 100644 src/libraries/JANA/Utils/JEventLevel.h diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 9066822e4..1ce4e42ff 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -40,7 +41,6 @@ class JEventSource; class JEvent : public JResettable, public std::enable_shared_from_this { public: - enum class Level { Timeslice, Block, SlowControls, PhysicsEvent, SubEvent}; explicit JEvent(JApplication* aApplication=nullptr) : mInspector(&(*this)) { mApplication = aApplication; @@ -129,14 +129,14 @@ class JEvent : public JResettable, public std::enable_shared_from_this // Hierarchical - Level GetLevel() const { return mLevel; } + JEventLevel GetLevel() const { return mLevel; } void AddParent(JEvent* parent) { - Level level = parent->GetLevel(); + JEventLevel level = parent->GetLevel(); mParents[level] = parent; // TODO: Validate more } - const JEvent& GetParent(Level level) const { + const JEvent& GetParent(JEventLevel level) const { return *(mParents.at(level)); // TODO: Validate more } @@ -157,8 +157,8 @@ class JEvent : public JResettable, public std::enable_shared_from_this bool mIsBarrierEvent = false; // Hierarchical stuff - Level mLevel = Level::PhysicsEvent; - std::map mParents; + JEventLevel mLevel = JEventLevel::Event; + std::map mParents; size_t mReferenceCount = 0; diff --git a/src/libraries/JANA/JEventProcessor.h b/src/libraries/JANA/JEventProcessor.h index 32038c635..b6477c8cb 100644 --- a/src/libraries/JANA/JEventProcessor.h +++ b/src/libraries/JANA/JEventProcessor.h @@ -7,6 +7,7 @@ #include #include +#include #include class JApplication; @@ -182,28 +183,36 @@ class JEventProcessor { void SetEventsOrdered(bool receive_events_in_order) { m_receive_events_in_order = receive_events_in_order; } - // TODO: Stop getting mApplication this way, use GetApplication() instead, or pass directly to Init() - JApplication* mApplication = nullptr; + // Meant to be called by user in constructor + void SetLevel(JEventLevel level) { m_level = level; } + + // Meant to be called by JANA + JEventLevel GetLevel() { return m_level; } + private: Status m_status; + std::string m_resource_name; + std::atomic_ullong m_event_count; + int32_t m_last_run_number = -1; + bool m_receive_events_in_order = false; + + // Common to components + JEventLevel m_level; + JApplication* mApplication = nullptr; std::string m_plugin_name; std::string m_type_name; - std::string m_resource_name; std::once_flag m_init_flag; std::once_flag m_finish_flag; - std::atomic_ullong m_event_count; - int32_t m_last_run_number = -1; std::mutex m_mutex; - bool m_receive_events_in_order = false; /// This is called by JApplication::Add(JEventProcessor*). There /// should be no need to call it from anywhere else. void SetJApplication(JApplication* app) { mApplication = app; } friend JComponentManager; + /// SetPluginName is called by ComponentManager and should not be exposed to the user. - // TODO: Maybe we want JApplication to track the current plugin instead void SetPluginName(std::string plugin_name) { m_plugin_name = std::move(plugin_name); }; }; diff --git a/src/libraries/JANA/JEventSource.h b/src/libraries/JANA/JEventSource.h index 17cead141..e244ecdcb 100644 --- a/src/libraries/JANA/JEventSource.h +++ b/src/libraries/JANA/JEventSource.h @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -290,8 +291,7 @@ class JEventSource { void SetTypeName(std::string type_name) { m_type_name = std::move(type_name); } // Meant to be called by user - /// SetFactoryGenerator allows us to override the set of factories. This is somewhat superfluous - /// The only time we _really_ need to call SetFactoryGenerator + /// SetFactoryGenerator allows us to override the set of factories. This is void SetFactoryGenerator(JFactoryGenerator* generator) { m_factory_generator = generator; } // Meant to be called by user @@ -302,6 +302,12 @@ class JEventSource { /// which will hurt performance. Conceptually, FinishEvent isn't great, and so should be avoided when possible. void EnableFinishEvent() { m_enable_free_event = true; } + // Meant to be called by user in constructor + void SetLevel(JEventLevel level) { m_level = level; } + + // Meant to be called by JANA + JEventLevel GetLevel() { return m_level; } + // Meant to be called by JANA void SetApplication(JApplication* app) { m_application = app; } @@ -317,18 +323,21 @@ class JEventSource { private: std::string m_resource_name; - JApplication* m_application = nullptr; JFactoryGenerator* m_factory_generator = nullptr; std::atomic m_status; std::atomic_ullong m_event_count {0}; uint64_t m_nskip = 0; uint64_t m_nevents = 0; + bool m_enable_free_event = false; + + // Common to all components? + JApplication* m_application = nullptr; std::string m_plugin_name; std::string m_type_name; std::once_flag m_init_flag; std::once_flag m_close_flag; std::mutex m_mutex; - bool m_enable_free_event = false; + JEventLevel m_level; }; #endif // _JEventSource_h_ diff --git a/src/libraries/JANA/JFactory.h b/src/libraries/JANA/JFactory.h index ce17fe8ba..90c8bf418 100644 --- a/src/libraries/JANA/JFactory.h +++ b/src/libraries/JANA/JFactory.h @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -146,10 +147,15 @@ class JFactory { virtual void Set(const std::vector &data) = 0; virtual void Insert(JObject *data) = 0; + // Meant to be called by user in constructor + void SetLevel(JEventLevel level) { mLevel = level; } + + // Meant to be called by JANA + JEventLevel GetLevel() { return mLevel; } + + protected: - std::string mPluginName; - std::string mFactoryName; std::string mObjectName; std::string mTag; uint32_t mFlags = 0; @@ -161,10 +167,15 @@ class JFactory { mutable JCallGraphRecorder::JDataOrigin m_insert_origin = JCallGraphRecorder::ORIGIN_NOT_AVAILABLE; // (see note at top of JCallGraphRecorder.h) CreationStatus mCreationStatus = CreationStatus::NotCreatedYet; - mutable std::mutex mMutex; // Used to make sure Init is called only once std::once_flag mInitFlag; + + // Common to components + JEventLevel mLevel; + std::string mPluginName; + std::string mFactoryName; + mutable std::mutex mMutex; }; // Because C++ doesn't support templated virtual functions, we implement our own dispatch table, mUpcastVTable. diff --git a/src/libraries/JANA/Utils/JEventLevel.h b/src/libraries/JANA/Utils/JEventLevel.h new file mode 100644 index 000000000..ec1282f49 --- /dev/null +++ b/src/libraries/JANA/Utils/JEventLevel.h @@ -0,0 +1,8 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +enum class JEventLevel { Run, SubRun, Timeslice, Block, Event, SubEvent, Task }; + + From 40f679b846800832e2d71ad87d68a0aebbf3eadf Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Fri, 9 Feb 2024 16:29:54 -0500 Subject: [PATCH 06/34] Add JEventUnfolder --- src/libraries/JANA/JEventUnfolder.h | 199 ++++++++++++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 src/libraries/JANA/JEventUnfolder.h diff --git a/src/libraries/JANA/JEventUnfolder.h b/src/libraries/JANA/JEventUnfolder.h new file mode 100644 index 000000000..ea11c695d --- /dev/null +++ b/src/libraries/JANA/JEventUnfolder.h @@ -0,0 +1,199 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +#include +#include + +class JEventUnfolder { + +private: + // Common to components... factor this out someday + + JEventLevel m_level; + JApplication* m_application = nullptr; + std::string m_plugin_name; + std::string m_type_name; + std::mutex m_mutex; + int32_t m_last_run_number = -1; + enum class Status { Uninitialized, Initialized, Finalized }; + Status m_status; + + // JEventUnfolder-specific + // + JEventLevel m_child_level; + int m_per_timeslice_event_count = 0; + bool m_call_preprocess_upstream = true; + + +public: + // JEventUnfolder interface + + enum class Result { KeepGoing, Finished }; + + virtual void Init() {}; + + virtual void ChangeRun(const JEvent&) {}; + + virtual void Preprocess(const JEvent& parent) const {}; + + virtual Result Unfold(const JEvent& parent, JEvent& child, int item) = 0; + + virtual void EndRun() {}; + + virtual void Finish() {}; + + + // Configuration + + void SetParentLevel(JEventLevel level) { m_level = level; } + + void SetChildLevel(JEventLevel level) { m_child_level = level; } + + void SetCallPreprocessUpstream(bool call_upstream) { m_call_preprocess_upstream = call_upstream; } + + + // Component setters (set by user) + + void SetTypeName(std::string type_name) { m_type_name = std::move(type_name); } + + void SetLevel(JEventLevel level) { m_level = level; } + + + // Component getters + + JEventLevel GetLevel() { return m_level; } + + JApplication* GetApplication() const { return m_application; } + + std::string GetPluginName() const { return m_plugin_name; } + + std::string GetTypeName() const { return m_type_name; } + + + private: + // Component setters (set by JANA) + + friend JComponentManager; + + friend JApplication; + + void SetJApplication(JApplication* app) { m_application = app; } + + void SetPluginName(std::string plugin_name) { m_plugin_name = std::move(plugin_name); }; + + + public: + // Backend + + void DoInit() { + try { + std::lock_guard lock(m_mutex); + if (m_status == Status::Uninitialized) { + Init(); + m_status = Status::Initialized; + } + } + catch (JException& ex) { + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + catch (...) { + auto ex = JException("Unknown exception in JEventUnfolder::Init()"); + ex.nested_exception = std::current_exception(); + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + } + + void DoPreprocess(const JEvent& parent, JEvent& child) { + try { + std::lock_guard lock(m_mutex); + if (m_status == Status::Initialized) { + Preprocess(parent); + } + else { + throw JException("Component needs to be initialized and not finalized before Unfold can be called"); + } + } + catch (JException& ex) { + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + catch (...) { + auto ex = JException("Unknown exception in JEventUnfolder::DoPreprocess()"); + ex.nested_exception = std::current_exception(); + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + } + + Result DoUnfold(const JEvent& parent, JEvent& child) { + try { + std::lock_guard lock(m_mutex); + if (m_status == Status::Initialized) { + if (!m_call_preprocess_upstream) { + Preprocess(parent); + } + if (m_last_run_number != parent.GetRunNumber()) { + ChangeRun(parent); + m_last_run_number = parent.GetRunNumber(); + } + Result result = Unfold(parent, child, m_per_timeslice_event_count); + m_per_timeslice_event_count += 1; + if (result == Result::Finished) { + m_per_timeslice_event_count = 0; + } + return result; + } + else { + throw JException("Component needs to be initialized and not finalized before Unfold can be called"); + } + } + catch (JException& ex) { + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + catch (...) { + auto ex = JException("Unknown exception in JEventUnfolder::DoUnfold()"); + ex.nested_exception = std::current_exception(); + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + } + + void DoFinish() { + try { + std::lock_guard lock(m_mutex); + if (m_status != Status::Finalized) { + if (m_last_run_number != -1) { + m_last_run_number = -1; + ChangeRun(); + } + Finish(); + m_status = Status::Finalized; + } + } + catch (JException& ex) { + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + catch (...) { + auto ex = JException("Unknown exception in JEventUnfolder::Finish()"); + ex.nested_exception = std::current_exception(); + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + } +} + + From be2d79460515dbc5cd756f0dc30ac681eede3112 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Sun, 11 Feb 2024 00:30:23 -0500 Subject: [PATCH 07/34] JFactorySet understands event level --- src/libraries/JANA/JFactorySet.cc | 15 +++++++++++++-- src/libraries/JANA/JFactorySet.h | 13 ++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/libraries/JANA/JFactorySet.cc b/src/libraries/JANA/JFactorySet.cc index f2f61abd4..188d49895 100644 --- a/src/libraries/JANA/JFactorySet.cc +++ b/src/libraries/JANA/JFactorySet.cc @@ -122,7 +122,13 @@ JFactory* JFactorySet::GetFactory(const std::string& object_name, const std::str { auto untyped_key = std::make_pair(object_name, tag); auto it = mFactoriesFromString.find(untyped_key); - return (it != std::end(mFactoriesFromString)) ? it->second : nullptr; + if (it != std::end(mFactoriesFromString)) { + if (it->second->GetLevel() != mLevel) { + throw JException("Factory belongs to a different level on the event hierarchy!"); + } + return it->second; + } + return nullptr; } //--------------------------------- @@ -131,7 +137,9 @@ JFactory* JFactorySet::GetFactory(const std::string& object_name, const std::str std::vector JFactorySet::GetAllFactories() const { std::vector results; for (auto p : mFactories) { - results.push_back(p.second); + if (p.second->GetLevel() == mLevel) { + results.push_back(p.second); + } } return results; } @@ -189,12 +197,14 @@ void JFactorySet::Print() const { size_t max_len = 0; for (auto p: mFactories) { + if (p.second->GetLevel() != mLevel) continue; auto len = p.second->GetObjectName().length(); if( len > max_len ) max_len = len; } max_len += 4; for (auto p: mFactories) { + if (p.second->GetLevel() != mLevel) continue; auto name = p.second->GetObjectName(); auto tag = p.second->GetTag(); @@ -219,6 +229,7 @@ std::vector JFactorySet::Summarize() const { std::vector results; for (auto& pair : mFactories) { + if (pair.second->GetLevel() != mLevel) continue; results.push_back({ .plugin_name = pair.second->GetPluginName(), .factory_name = pair.second->GetFactoryName(), diff --git a/src/libraries/JANA/JFactorySet.h b/src/libraries/JANA/JFactorySet.h index f589f770c..06bc38ca7 100644 --- a/src/libraries/JANA/JFactorySet.h +++ b/src/libraries/JANA/JFactorySet.h @@ -10,6 +10,7 @@ #include #include +#include #include #include @@ -44,6 +45,8 @@ class JFactorySet : public JResettable std::map, JFactory*> mFactoriesFromString; // {(objname, tag) : factory} std::vector mMultifactories; bool mIsFactoryOwner = true; + JEventLevel mLevel = JEventLevel::Event; + }; @@ -53,12 +56,18 @@ JFactoryT* JFactorySet::GetFactory(const std::string& tag) const { auto typed_key = std::make_pair(std::type_index(typeid(T)), tag); auto typed_iter = mFactories.find(typed_key); if (typed_iter != std::end(mFactories)) { + if (typed_iter->second->GetLevel() != mLevel) { + throw JException("Factory belongs to a different level on the event hierarchy!"); + } return static_cast*>(typed_iter->second); } auto untyped_key = std::make_pair(JTypeInfo::demangle(), tag); auto untyped_iter = mFactoriesFromString.find(untyped_key); if (untyped_iter != std::end(mFactoriesFromString)) { + if (untyped_iter->second->GetLevel() != mLevel) { + throw JException("Factory belongs to a different level on the event hierarchy!"); + } return static_cast*>(untyped_iter->second); } return nullptr; @@ -70,7 +79,9 @@ std::vector*> JFactorySet::GetAllFactories() const { std::vector*> data; for (auto it=std::begin(mFactories);it!=std::end(mFactories);it++){ if (it->first.first==sKey){ - data.push_back(static_cast*>(it->second)); + if (it->second->GetLevel() == mLevel) { + data.push_back(static_cast*>(it->second)); + } } } return data; From e21609488443a21b50f9847eca6297723975928e Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Mon, 12 Feb 2024 23:12:21 -0500 Subject: [PATCH 08/34] Add JUnfoldArrow + test case --- src/libraries/JANA/Engine/JUnfoldArrow.h | 129 +++++++++++++++++++++++ src/libraries/JANA/JEventUnfolder.h | 8 +- src/libraries/JANA/Utils/JEventLevel.h | 16 ++- src/programs/unit_tests/CMakeLists.txt | 1 + src/programs/unit_tests/UnfoldTests.cc | 63 +++++++++++ 5 files changed, 211 insertions(+), 6 deletions(-) create mode 100644 src/libraries/JANA/Engine/JUnfoldArrow.h create mode 100644 src/programs/unit_tests/UnfoldTests.cc diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h new file mode 100644 index 000000000..d8b2d8afc --- /dev/null +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -0,0 +1,129 @@ +// Copyright 2023, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +#include +#include +#include + +class JUnfoldArrow : public JArrow { +private: + using EventT = std::shared_ptr; + + JEventUnfolder* m_unfolder = nullptr; + EventT* m_parent_event = nullptr; + bool m_ready_to_fetch_parent = true; + + PlaceRef m_parent_in; + PlaceRef m_child_in; + PlaceRef m_child_out; + +public: + + JUnfoldArrow( + std::string name, + JEventUnfolder* unfolder, + JMailbox* parent_in, + JEventPool* child_in, + JMailbox* child_out) + + : JArrow(std::move(name), false, false, false), + m_unfolder(unfolder), + m_parent_in(this, parent_in, true, 1, 3), + m_child_in(this, child_in, true, 1, 3), + m_child_out(this, child_out, false, 1, 3) + { + } + + + void initialize() final { + m_unfolder->DoInit(); + LOG_INFO(m_logger) << "Initialized JEventUnfolder '" << m_unfolder->GetTypeName() << "'" << LOG_END; + } + + void finalize() final { + m_unfolder->DoFinish(); + LOG_INFO(m_logger) << "Finalized JEventUnfolder '" << m_unfolder->GetTypeName() << "'" << LOG_END; + } + + bool try_pull_all(Data& pi, Data& ci, Data& co) { + bool success; + success = m_parent_in.pull(pi); + if (! success) { + return false; + } + success = m_child_in.pull(ci); + if (! success) { + return false; + } + success = m_child_out.pull(co); + if (! success) { + return false; + } + return true; + } + + size_t push_all(Data& parent_in, Data& child_in, Data& child_out) { + size_t message_count = 0; + message_count += m_parent_in.push(parent_in); + message_count += m_child_in.push(child_in); + message_count += m_child_out.push(child_out); + return message_count; + } + + void execute(JArrowMetrics& metrics, size_t location_id) final { + + auto start_total_time = std::chrono::steady_clock::now(); + + Data parent_in_data {location_id}; + Data child_in_data {location_id}; + Data child_out_data {location_id}; + + bool success = try_pull_all(parent_in_data, child_in_data, child_out_data); + if (success) { + + auto start_processing_time = std::chrono::steady_clock::now(); + if (m_ready_to_fetch_parent) { + m_ready_to_fetch_parent = false; + m_parent_in.min_item_count = 0; + m_parent_in.max_item_count = 0; + m_parent_event = parent_in_data.items[0]; + parent_in_data.items[0] = nullptr; + parent_in_data.item_count = 0; + } + auto child = child_in_data.items[0]; + child_in_data.items[0] = nullptr; + child_in_data.item_count = 0; + + auto status = m_unfolder->DoUnfold(*(m_parent_event->get()), *(child->get())); + if (status == JEventUnfolder::Result::Finished) { + m_ready_to_fetch_parent = true; + m_parent_event = nullptr; + m_parent_in.min_item_count = 1; + m_parent_in.max_item_count = 1; + } + + child_out_data.items[0] = child; + child_out_data.item_count = 1; + + auto end_processing_time = std::chrono::steady_clock::now(); + size_t events_processed = push_all(parent_in_data, child_in_data, child_out_data); + + auto end_total_time = std::chrono::steady_clock::now(); + auto latency = (end_processing_time - start_processing_time); + auto overhead = (end_total_time - start_total_time) - latency; + + metrics.update(JArrowMetrics::Status::KeepGoing, events_processed, 1, latency, overhead); + return; + } + else { + auto end_total_time = std::chrono::steady_clock::now(); + metrics.update(JArrowMetrics::Status::ComeBackLater, 0, 1, std::chrono::milliseconds(0), end_total_time - start_total_time); + return; + } + } + +}; + + diff --git a/src/libraries/JANA/JEventUnfolder.h b/src/libraries/JANA/JEventUnfolder.h index ea11c695d..4197d8c23 100644 --- a/src/libraries/JANA/JEventUnfolder.h +++ b/src/libraries/JANA/JEventUnfolder.h @@ -4,8 +4,10 @@ #pragma once #include +#include #include +class JApplication; class JEventUnfolder { private: @@ -173,10 +175,6 @@ class JEventUnfolder { try { std::lock_guard lock(m_mutex); if (m_status != Status::Finalized) { - if (m_last_run_number != -1) { - m_last_run_number = -1; - ChangeRun(); - } Finish(); m_status = Status::Finalized; } @@ -194,6 +192,6 @@ class JEventUnfolder { throw ex; } } -} +}; diff --git a/src/libraries/JANA/Utils/JEventLevel.h b/src/libraries/JANA/Utils/JEventLevel.h index ec1282f49..b9881c5e0 100644 --- a/src/libraries/JANA/Utils/JEventLevel.h +++ b/src/libraries/JANA/Utils/JEventLevel.h @@ -2,7 +2,21 @@ // Subject to the terms in the LICENSE file found in the top-level directory. #pragma once +#include -enum class JEventLevel { Run, SubRun, Timeslice, Block, Event, SubEvent, Task }; +enum class JEventLevel { Run, Subrun, Timeslice, Block, Event, Subevent, Task }; + +inline std::ostream& operator<<(std::ostream& os, JEventLevel level) { + switch (level) { + case JEventLevel::Run: os << "Run"; break; + case JEventLevel::Subrun: os << "Subrun"; break; + case JEventLevel::Timeslice: os << "Timeslice"; break; + case JEventLevel::Block: os << "Block"; break; + case JEventLevel::Event: os << "Event"; break; + case JEventLevel::Subevent: os << "Subevent"; break; + case JEventLevel::Task: os << "Task"; break; + } + return os; +} diff --git a/src/programs/unit_tests/CMakeLists.txt b/src/programs/unit_tests/CMakeLists.txt index 762dd0160..4df2e65bd 100644 --- a/src/programs/unit_tests/CMakeLists.txt +++ b/src/programs/unit_tests/CMakeLists.txt @@ -38,6 +38,7 @@ set(TEST_SOURCES JPoolTests.cc ArrowTests.cc MultiLevelTopologyTests.cc + UnfoldTests.cc ) if (${USE_PODIO}) diff --git a/src/programs/unit_tests/UnfoldTests.cc b/src/programs/unit_tests/UnfoldTests.cc new file mode 100644 index 000000000..fb0797f55 --- /dev/null +++ b/src/programs/unit_tests/UnfoldTests.cc @@ -0,0 +1,63 @@ + +#include +#include + +namespace jana { +namespace unfoldtests { + +using EventT = std::shared_ptr; + + +struct TestUnfolder : public JEventUnfolder { + mutable std::vector preprocessed_event_nrs; + mutable std::vector preprocessed_event_levels; + std::vector unfolded_parent_nrs; + std::vector unfolded_parent_levels; + std::vector unfolded_child_nrs; + std::vector unfolded_child_levels; + + void Preprocess(const JEvent& parent) const override { + LOG << "Preprocessing " << parent.GetLevel() << " event " << parent.GetEventNumber() << LOG_END; + preprocessed_event_nrs.push_back(parent.GetEventNumber()); + preprocessed_event_levels.push_back(parent.GetLevel()); + } + + Result Unfold(const JEvent& parent, JEvent& child, int child_nr) override { + LOG << "Unfolding " << parent.GetLevel() << " event " << parent.GetEventNumber() << " into " << child.GetLevel() << " " << child_nr << LOG_END; + unfolded_parent_nrs.push_back(parent.GetEventNumber()); + unfolded_parent_levels.push_back(parent.GetLevel()); + unfolded_child_nrs.push_back(child_nr); + unfolded_child_levels.push_back(child.GetLevel()); + return (child_nr == 2 ? Result::Finished : Result::KeepGoing); + } +}; + +TEST_CASE("UnfoldTests_Basic") { + + JApplication app; + auto jcm = app.GetService(); + + JEventPool parent_pool {jcm, 5, 1, true}; // size=5, locations=1, limit_total_events_in_flight=true + JEventPool child_pool {jcm, 5, 1, true}; + JMailbox parent_queue {3}; // size=2 + JMailbox child_queue {3}; + + parent_pool.init(); + child_pool.init(); + + TestUnfolder unfolder; + JUnfoldArrow arrow("sut", &unfolder, &parent_queue, &child_pool, &child_queue); + + JArrowMetrics m; + arrow.execute(m, 0); + REQUIRE(m.get_last_status() == JArrowMetrics::Status::KeepGoing); + +} + + +} // namespace arrowtests +} // namespace jana + + + + From 0ad9a92cea8d6108d0f7d38611b81faad2a4c781 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Tue, 13 Feb 2024 01:44:35 -0500 Subject: [PATCH 09/34] Fix warnings --- src/libraries/JANA/JEvent.h | 2 +- src/libraries/JANA/JEventProcessor.h | 14 ++++++++------ src/libraries/JANA/JEventSource.h | 26 ++++++++++++++------------ src/libraries/JANA/JEventUnfolder.h | 4 ++-- 4 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 1ce4e42ff..4b33566ac 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -159,7 +159,7 @@ class JEvent : public JResettable, public std::enable_shared_from_this // Hierarchical stuff JEventLevel mLevel = JEventLevel::Event; std::map mParents; - size_t mReferenceCount = 0; + //size_t mReferenceCount = 0; diff --git a/src/libraries/JANA/JEventProcessor.h b/src/libraries/JANA/JEventProcessor.h index b6477c8cb..0984868bb 100644 --- a/src/libraries/JANA/JEventProcessor.h +++ b/src/libraries/JANA/JEventProcessor.h @@ -191,21 +191,23 @@ class JEventProcessor { private: - Status m_status; - std::string m_resource_name; - std::atomic_ullong m_event_count; - int32_t m_last_run_number = -1; - bool m_receive_events_in_order = false; - // Common to components JEventLevel m_level; JApplication* mApplication = nullptr; + Status m_status; std::string m_plugin_name; std::string m_type_name; std::once_flag m_init_flag; std::once_flag m_finish_flag; std::mutex m_mutex; + // JEventProcessor-specific + std::string m_resource_name; + std::atomic_ullong m_event_count; + int32_t m_last_run_number = -1; + bool m_receive_events_in_order = false; + + /// This is called by JApplication::Add(JEventProcessor*). There /// should be no need to call it from anywhere else. void SetJApplication(JApplication* app) { mApplication = app; } diff --git a/src/libraries/JANA/JEventSource.h b/src/libraries/JANA/JEventSource.h index e244ecdcb..764c71a4f 100644 --- a/src/libraries/JANA/JEventSource.h +++ b/src/libraries/JANA/JEventSource.h @@ -40,10 +40,10 @@ class JEventSource { // Constructor explicit JEventSource(std::string resource_name, JApplication* app = nullptr) - : m_resource_name(std::move(resource_name)) - , m_application(app) - , m_factory_generator(nullptr) + : m_application(app) , m_status(SourceStatus::Unopened) + , m_resource_name(std::move(resource_name)) + , m_factory_generator(nullptr) , m_event_count{0} {} @@ -322,22 +322,24 @@ class JEventSource { private: + // Common to all components + JEventLevel m_level; + JApplication* m_application = nullptr; + std::atomic m_status; + std::string m_plugin_name; + std::string m_type_name; + std::once_flag m_init_flag; + std::once_flag m_close_flag; + std::mutex m_mutex; + + // JEventSource-specific std::string m_resource_name; JFactoryGenerator* m_factory_generator = nullptr; - std::atomic m_status; std::atomic_ullong m_event_count {0}; uint64_t m_nskip = 0; uint64_t m_nevents = 0; bool m_enable_free_event = false; - // Common to all components? - JApplication* m_application = nullptr; - std::string m_plugin_name; - std::string m_type_name; - std::once_flag m_init_flag; - std::once_flag m_close_flag; - std::mutex m_mutex; - JEventLevel m_level; }; #endif // _JEventSource_h_ diff --git a/src/libraries/JANA/JEventUnfolder.h b/src/libraries/JANA/JEventUnfolder.h index 4197d8c23..038ed01a1 100644 --- a/src/libraries/JANA/JEventUnfolder.h +++ b/src/libraries/JANA/JEventUnfolder.h @@ -38,7 +38,7 @@ class JEventUnfolder { virtual void ChangeRun(const JEvent&) {}; - virtual void Preprocess(const JEvent& parent) const {}; + virtual void Preprocess(const JEvent& /*parent*/) const {}; virtual Result Unfold(const JEvent& parent, JEvent& child, int item) = 0; @@ -111,7 +111,7 @@ class JEventUnfolder { } } - void DoPreprocess(const JEvent& parent, JEvent& child) { + void DoPreprocess(const JEvent& parent) { try { std::lock_guard lock(m_mutex); if (m_status == Status::Initialized) { From e77f82a9b95ba86f10e26d63704bee8902604ae1 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Tue, 13 Feb 2024 14:17:45 -0500 Subject: [PATCH 10/34] JEventPool sets event level --- src/libraries/JANA/JEvent.h | 4 ++-- src/libraries/JANA/JFactorySet.h | 3 +++ src/libraries/JANA/Utils/JEventPool.h | 8 ++++++-- src/programs/unit_tests/UnfoldTests.cc | 15 +++++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 4b33566ac..cf87276a5 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -129,7 +129,8 @@ class JEvent : public JResettable, public std::enable_shared_from_this // Hierarchical - JEventLevel GetLevel() const { return mLevel; } + JEventLevel GetLevel() const { return mFactorySet->GetLevel(); } + void SetLevel(JEventLevel level) { mFactorySet->SetLevel(level); } void AddParent(JEvent* parent) { JEventLevel level = parent->GetLevel(); @@ -157,7 +158,6 @@ class JEvent : public JResettable, public std::enable_shared_from_this bool mIsBarrierEvent = false; // Hierarchical stuff - JEventLevel mLevel = JEventLevel::Event; std::map mParents; //size_t mReferenceCount = 0; diff --git a/src/libraries/JANA/JFactorySet.h b/src/libraries/JANA/JFactorySet.h index 06bc38ca7..e3ba8e620 100644 --- a/src/libraries/JANA/JFactorySet.h +++ b/src/libraries/JANA/JFactorySet.h @@ -40,6 +40,9 @@ class JFactorySet : public JResettable std::vector Summarize() const; + JEventLevel GetLevel() const { return mLevel; } + void SetLevel(JEventLevel level) { mLevel = level; } + protected: std::map, JFactory*> mFactories; // {(typeid, tag) : factory} std::map, JFactory*> mFactoriesFromString; // {(objname, tag) : factory} diff --git a/src/libraries/JANA/Utils/JEventPool.h b/src/libraries/JANA/Utils/JEventPool.h index c78cc5cc0..4fdc47abc 100644 --- a/src/libraries/JANA/Utils/JEventPool.h +++ b/src/libraries/JANA/Utils/JEventPool.h @@ -14,18 +14,22 @@ class JEventPool : public JPool> { std::shared_ptr m_component_manager; + JEventLevel m_level; public: inline JEventPool(std::shared_ptr component_manager, size_t pool_size, size_t location_count, - bool limit_total_events_in_flight) + bool limit_total_events_in_flight, + JEventLevel level = JEventLevel::Event) : JPool(pool_size, location_count, limit_total_events_in_flight) - , m_component_manager(component_manager) { + , m_component_manager(component_manager) + , m_level(level) { } void configure_item(std::shared_ptr* item) override { (*item) = std::make_shared(); + (*item)->SetLevel(m_level); m_component_manager->configure_event(**item); } diff --git a/src/programs/unit_tests/UnfoldTests.cc b/src/programs/unit_tests/UnfoldTests.cc index fb0797f55..b0710bb1b 100644 --- a/src/programs/unit_tests/UnfoldTests.cc +++ b/src/programs/unit_tests/UnfoldTests.cc @@ -37,14 +37,25 @@ TEST_CASE("UnfoldTests_Basic") { JApplication app; auto jcm = app.GetService(); - JEventPool parent_pool {jcm, 5, 1, true}; // size=5, locations=1, limit_total_events_in_flight=true - JEventPool child_pool {jcm, 5, 1, true}; + JEventPool parent_pool {jcm, 5, 1, true, JEventLevel::Timeslice}; // size=5, locations=1, limit_total_events_in_flight=true + JEventPool child_pool {jcm, 5, 1, true, JEventLevel::Event}; JMailbox parent_queue {3}; // size=2 JMailbox child_queue {3}; parent_pool.init(); child_pool.init(); + auto evt1 = parent_pool.get(); + (*evt1)->SetEventNumber(17); + (*evt1)->SetLevel(JEventLevel::Timeslice); + + auto evt2= parent_pool.get(); + (*evt1)->SetEventNumber(18); + (*evt1)->SetLevel(JEventLevel::Timeslice); + + parent_queue.try_push(&evt1, 1); + parent_queue.try_push(&evt2, 1); + TestUnfolder unfolder; JUnfoldArrow arrow("sut", &unfolder, &parent_queue, &child_pool, &child_queue); From b3798cb8c5ee0c806bc657508ce3c8515777729c Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 15 Feb 2024 01:18:03 -0500 Subject: [PATCH 11/34] Fixes to UnfoldArrow --- src/libraries/JANA/Engine/JUnfoldArrow.h | 6 +++--- src/programs/unit_tests/UnfoldTests.cc | 23 +++++++++++++++++------ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h index d8b2d8afc..d48e48c78 100644 --- a/src/libraries/JANA/Engine/JUnfoldArrow.h +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -30,9 +30,9 @@ class JUnfoldArrow : public JArrow { : JArrow(std::move(name), false, false, false), m_unfolder(unfolder), - m_parent_in(this, parent_in, true, 1, 3), - m_child_in(this, child_in, true, 1, 3), - m_child_out(this, child_out, false, 1, 3) + m_parent_in(this, parent_in, true, 1, 1), + m_child_in(this, child_in, true, 1, 1), + m_child_out(this, child_out, false, 1, 1) { } diff --git a/src/programs/unit_tests/UnfoldTests.cc b/src/programs/unit_tests/UnfoldTests.cc index b0710bb1b..32fc07061 100644 --- a/src/programs/unit_tests/UnfoldTests.cc +++ b/src/programs/unit_tests/UnfoldTests.cc @@ -22,13 +22,15 @@ struct TestUnfolder : public JEventUnfolder { preprocessed_event_levels.push_back(parent.GetLevel()); } - Result Unfold(const JEvent& parent, JEvent& child, int child_nr) override { - LOG << "Unfolding " << parent.GetLevel() << " event " << parent.GetEventNumber() << " into " << child.GetLevel() << " " << child_nr << LOG_END; + Result Unfold(const JEvent& parent, JEvent& child, int iter) override { + auto child_nr = iter + 100 + parent.GetEventNumber(); unfolded_parent_nrs.push_back(parent.GetEventNumber()); unfolded_parent_levels.push_back(parent.GetLevel()); unfolded_child_nrs.push_back(child_nr); unfolded_child_levels.push_back(child.GetLevel()); - return (child_nr == 2 ? Result::Finished : Result::KeepGoing); + child.SetEventNumber(child_nr); + LOG << "Unfolding " << parent.GetLevel() << " event " << parent.GetEventNumber() << " into " << child.GetLevel() << " " << child_nr << "; iter=" << iter << LOG_END; + return (iter == 2 ? Result::Finished : Result::KeepGoing); } }; @@ -49,9 +51,9 @@ TEST_CASE("UnfoldTests_Basic") { (*evt1)->SetEventNumber(17); (*evt1)->SetLevel(JEventLevel::Timeslice); - auto evt2= parent_pool.get(); - (*evt1)->SetEventNumber(18); - (*evt1)->SetLevel(JEventLevel::Timeslice); + auto evt2 = parent_pool.get(); + (*evt2)->SetEventNumber(28); + (*evt2)->SetLevel(JEventLevel::Timeslice); parent_queue.try_push(&evt1, 1); parent_queue.try_push(&evt2, 1); @@ -60,8 +62,17 @@ TEST_CASE("UnfoldTests_Basic") { JUnfoldArrow arrow("sut", &unfolder, &parent_queue, &child_pool, &child_queue); JArrowMetrics m; + arrow.initialize(); arrow.execute(m, 0); REQUIRE(m.get_last_status() == JArrowMetrics::Status::KeepGoing); + REQUIRE(child_queue.size() == 1); + REQUIRE(unfolder.preprocessed_event_nrs.size() == 0); + REQUIRE(unfolder.unfolded_parent_nrs.size() == 1); + REQUIRE(unfolder.unfolded_parent_nrs[0] == 17); + REQUIRE(unfolder.unfolded_parent_levels[0] == JEventLevel::Timeslice); + REQUIRE(unfolder.unfolded_child_nrs.size() == 1); + REQUIRE(unfolder.unfolded_child_nrs[0] == 117); + REQUIRE(unfolder.unfolded_child_levels[0] == JEventLevel::Event); } From 2fd3876cd4139e977735a631e2cb1a89715ae190 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 15 Feb 2024 01:18:09 -0500 Subject: [PATCH 12/34] Add FoldArrow --- src/libraries/JANA/Engine/JFoldArrow.h | 133 +++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 src/libraries/JANA/Engine/JFoldArrow.h diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h new file mode 100644 index 000000000..088b9e370 --- /dev/null +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -0,0 +1,133 @@ +// Copyright 2023, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +#include +#include +#include + +class JFoldArrow : public JArrow { +private: + using EventT = std::shared_ptr; + + JEventFolder* m_folder = nullptr; + + PlaceRef m_child_in; + PlaceRef m_child_out; + PlaceRef m_parent_out; + +public: + + JFoldArrow( + std::string name, + JEventFolder* folder, + JMailbox* child_in, + JEventPool* child_out, + JMailbox* parent_out) + + : JArrow(std::move(name), false, false, false), + m_folder(folder), + m_child_in(this, child_in, true, 1, 1), + m_child_out(this, child_out, false, 1, 1) + m_parent_in(this, parent_out, false, 1, 1), + { + } + + + void initialize() final { + /* + if (m_folder != nullptr) { + m_folder->DoInit(); + LOG_INFO(m_logger) << "Initialized JEventFolder '" << m_unfolder->GetTypeName() << "'" << LOG_END; + } + else { + } + */ + LOG_INFO(m_logger) << "Initialized JEventFolder (trivial)" << LOG_END; + } + + void finalize() final { + /* + if (m_folder != nullptr) { + m_folder->DoFinish(); + LOG_INFO(m_logger) << "Finalized JEventFolder '" << m_unfolder->GetTypeName() << "'" << LOG_END; + } + */ + LOG_INFO(m_logger) << "Finalized JEventFolder (trivial)" << LOG_END; + } + + bool try_pull_all(Data& ci, Data& co, Data& po) { + bool success; + success = m_parent_in.pull(ci); + if (! success) { + return false; + } + success = m_child_in.pull(co); + if (! success) { + return false; + } + success = m_child_out.pull(po); + if (! success) { + return false; + } + return true; + } + + size_t push_all(Data& ci, Data& co, Data& po) { + size_t message_count = 0; + message_count += m_child_in.push(ci); + message_count += m_child_out.push(co); + message_count += m_parent_out.push(po); + return message_count; + } + + void execute(JArrowMetrics& metrics, size_t location_id) final { + + auto start_total_time = std::chrono::steady_clock::now(); + + Data child_in_data {location_id}; + Data child_out_data {location_id}; + Data parent_out_data {location_id}; + + bool success = try_pull_all(child_in_data, child_out_data, parent_out_data); + if (success) { + + auto start_processing_time = std::chrono::steady_clock::now(); + auto child = child_in_data.items[0]; + child_in_data.items[0] = nullptr; + child_in_data.item_count = 0; + // TODO: Decrement parent's ref count + // TODO: Maybe fold should provide an identity for parent? What if no items make it past the filter? + + //auto status = m_folder->DoFold(*(child->get()), *(parent->get())); + + EventT* parent = child->GetParent(); + // TODO: How does fold know parent level? + + child_out_data.items[0] = child; + child_out_data.item_count = 1; + + parent_out_data.items[0] = child; + parent_out_data.item_count = 1; + + auto end_processing_time = std::chrono::steady_clock::now(); + size_t events_processed = push_all(parent_in_data, child_in_data, child_out_data); + + auto end_total_time = std::chrono::steady_clock::now(); + auto latency = (end_processing_time - start_processing_time); + auto overhead = (end_total_time - start_total_time) - latency; + + metrics.update(JArrowMetrics::Status::KeepGoing, events_processed, 1, latency, overhead); + return; + } + else { + auto end_total_time = std::chrono::steady_clock::now(); + metrics.update(JArrowMetrics::Status::ComeBackLater, 0, 1, std::chrono::milliseconds(0), end_total_time - start_total_time); + return; + } + } + +}; + + From 3a19826778428516ea7d0e695f2e25aa32387730 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Tue, 20 Feb 2024 10:42:44 -0500 Subject: [PATCH 13/34] JApp, JCM understand JEventUnfolder --- src/libraries/JANA/JApplication.cc | 17 +++++++++----- src/libraries/JANA/JApplication.h | 2 ++ src/libraries/JANA/JEventUnfolder.h | 4 +++- src/libraries/JANA/JFactorySet.cc | 1 + .../JANA/Services/JComponentManager.cc | 22 +++++++++++++++++-- .../JANA/Services/JComponentManager.h | 4 ++++ .../JANA/Status/JComponentSummary.cc | 21 +++++++++++++++--- src/libraries/JANA/Status/JComponentSummary.h | 11 ++++++++++ 8 files changed, 70 insertions(+), 12 deletions(-) diff --git a/src/libraries/JANA/JApplication.cc b/src/libraries/JANA/JApplication.cc index f1bb784c6..bd325beee 100644 --- a/src/libraries/JANA/JApplication.cc +++ b/src/libraries/JANA/JApplication.cc @@ -63,31 +63,36 @@ void JApplication::AddPluginPath(std::string path) { // Building a ProcessingTopology void JApplication::Add(JEventSource* event_source) { + /// Adds the given JEventSource to the JANA context. Ownership is passed to JComponentManager. m_component_manager->add(event_source); } void JApplication::Add(JEventSourceGenerator *source_generator) { - /// Add the given JFactoryGenerator to the list of queues - /// - /// @param source_generator pointer to source generator to add. Ownership is passed to JApplication + /// Adds the given JEventSourceGenerator to the JANA context. Ownership is passed to JComponentManager. m_component_manager->add(source_generator); } void JApplication::Add(JFactoryGenerator *factory_generator) { - /// Add the given JFactoryGenerator to the list of queues - /// - /// @param factory_generator pointer to factory generator to add. Ownership is passed to JApplication + /// Adds the given JFactoryGenerator to the JANA context. Ownership is passed to JComponentManager. m_component_manager->add(factory_generator); } void JApplication::Add(JEventProcessor* processor) { + /// Adds the given JEventProcessor to the JANA context. Ownership is passed to JComponentManager. m_component_manager->add(processor); } void JApplication::Add(std::string event_source_name) { + /// Adds the event source name (e.g. a file or socket name) to the JANA context. JANA will instantiate + /// the corresponding JEventSource using a user-provided JEventSourceGenerator. m_component_manager->add(event_source_name); } +void JApplication::Add(JEventUnfolder* unfolder) { + /// Adds the given JEventUnfolder to the JANA context. Ownership is passed to JComponentManager. + m_component_manager->add(unfolder); +} + // Controlling processing diff --git a/src/libraries/JANA/JApplication.h b/src/libraries/JANA/JApplication.h index ccdc3e016..b2f6e7f47 100644 --- a/src/libraries/JANA/JApplication.h +++ b/src/libraries/JANA/JApplication.h @@ -19,6 +19,7 @@ class JFactorySet; class JComponentManager; class JPluginLoader; class JProcessingController; +class JEventUnfolder; extern JApplication* japp; @@ -64,6 +65,7 @@ class JApplication { void Add(JFactoryGenerator* factory_generator); void Add(JEventSource* event_source); void Add(JEventProcessor* processor); + void Add(JEventUnfolder* unfolder); // Controlling processing diff --git a/src/libraries/JANA/JEventUnfolder.h b/src/libraries/JANA/JEventUnfolder.h index 038ed01a1..cb1bee220 100644 --- a/src/libraries/JANA/JEventUnfolder.h +++ b/src/libraries/JANA/JEventUnfolder.h @@ -31,6 +31,8 @@ class JEventUnfolder { public: // JEventUnfolder interface + + virtual ~JEventUnfolder() {}; enum class Result { KeepGoing, Finished }; @@ -81,7 +83,7 @@ class JEventUnfolder { friend JApplication; - void SetJApplication(JApplication* app) { m_application = app; } + void SetApplication(JApplication* app) { m_application = app; } void SetPluginName(std::string plugin_name) { m_plugin_name = std::move(plugin_name); }; diff --git a/src/libraries/JANA/JFactorySet.cc b/src/libraries/JANA/JFactorySet.cc index 188d49895..04dbe3ff1 100644 --- a/src/libraries/JANA/JFactorySet.cc +++ b/src/libraries/JANA/JFactorySet.cc @@ -231,6 +231,7 @@ std::vector JFactorySet::Summarize() const { for (auto& pair : mFactories) { if (pair.second->GetLevel() != mLevel) continue; results.push_back({ + .level = pair.second->GetLevel(), .plugin_name = pair.second->GetPluginName(), .factory_name = pair.second->GetFactoryName(), .factory_tag = pair.second->GetTag(), diff --git a/src/libraries/JANA/Services/JComponentManager.cc b/src/libraries/JANA/Services/JComponentManager.cc index e57fdacb3..0ec70b022 100644 --- a/src/libraries/JANA/Services/JComponentManager.cc +++ b/src/libraries/JANA/Services/JComponentManager.cc @@ -6,6 +6,7 @@ #include "JComponentManager.h" #include #include +#include JComponentManager::JComponentManager(JApplication* app) : m_app(app) { } @@ -24,6 +25,9 @@ JComponentManager::~JComponentManager() { for (auto* src_gen : m_src_gens) { delete src_gen; } + for (auto* unfolder : m_unfolders) { + delete unfolder; + } } void JComponentManager::next_plugin(std::string plugin_name) { @@ -67,6 +71,12 @@ void JComponentManager::add(JEventProcessor *processor) { m_evt_procs.push_back(processor); } +void JComponentManager::add(JEventUnfolder* unfolder) { + unfolder->SetPluginName(m_current_plugin_name); + unfolder->SetApplication(m_app); + m_unfolders.push_back(unfolder); +} + void JComponentManager::configure_event(JEvent& event) { auto factory_set = new JFactorySet(m_fac_gens); event.SetFactorySet(factory_set); @@ -190,17 +200,25 @@ std::vector& JComponentManager::get_fac_gens() { return m_fac_gens; } +std::vector& JComponentManager::get_unfolders() { + return m_unfolders; +} + JComponentSummary JComponentManager::get_component_summary() { JComponentSummary result; // Event sources for (auto * src : m_evt_srces) { - result.event_sources.push_back({.plugin_name=src->GetPluginName(), .type_name=src->GetType(), .source_name=src->GetName()}); + result.event_sources.push_back({.level=src->GetLevel(), .plugin_name=src->GetPluginName(), .type_name=src->GetType(), .source_name=src->GetName()}); } // Event processors for (auto * evt_proc : m_evt_procs) { - result.event_processors.push_back({.plugin_name = evt_proc->GetPluginName(), .type_name=evt_proc->GetTypeName()}); + result.event_processors.push_back({.level=evt_proc->GetLevel(), .plugin_name = evt_proc->GetPluginName(), .type_name=evt_proc->GetTypeName()}); + } + + for (auto * unfolder : m_unfolders) { + result.event_unfolders.push_back({.level=unfolder->GetLevel(), .plugin_name = unfolder->GetPluginName(), .type_name=unfolder->GetTypeName()}); } // Factories diff --git a/src/libraries/JANA/Services/JComponentManager.h b/src/libraries/JANA/Services/JComponentManager.h index cfe080f32..fe7a0db55 100644 --- a/src/libraries/JANA/Services/JComponentManager.h +++ b/src/libraries/JANA/Services/JComponentManager.h @@ -13,6 +13,7 @@ #include class JEventProcessor; +class JEventUnfolder; class JComponentManager : public JService { public: @@ -27,6 +28,7 @@ class JComponentManager : public JService { void add(JFactoryGenerator* factory_generator); void add(JEventSource* event_source); void add(JEventProcessor* processor); + void add(JEventUnfolder* unfolder); void initialize(); void resolve_event_sources(); @@ -40,6 +42,7 @@ class JComponentManager : public JService { std::vector& get_evt_srces(); std::vector& get_evt_procs(); std::vector& get_fac_gens(); + std::vector& get_unfolders(); void configure_event(JEvent& event); @@ -56,6 +59,7 @@ class JComponentManager : public JService { std::vector m_fac_gens; std::vector m_evt_srces; std::vector m_evt_procs; + std::vector m_unfolders; std::map m_default_tags; bool m_enable_call_graph_recording = false; diff --git a/src/libraries/JANA/Status/JComponentSummary.cc b/src/libraries/JANA/Status/JComponentSummary.cc index 3afa75aab..4ede844ea 100644 --- a/src/libraries/JANA/Status/JComponentSummary.cc +++ b/src/libraries/JANA/Status/JComponentSummary.cc @@ -16,27 +16,42 @@ std::ostream& operator<<(std::ostream& os, JComponentSummary const& cs) { sourcesTable.AddColumn("Plugin"); sourcesTable.AddColumn("Name"); sourcesTable.AddColumn("Source"); + sourcesTable.AddColumn("Level"); for (const auto& source : cs.event_sources) { - sourcesTable | source.plugin_name | source.type_name | source.source_name; + sourcesTable | source.plugin_name | source.type_name | source.source_name | source.level; } sourcesTable.Render(os); os << " PROCESSORS" << std::endl; JTablePrinter procsTable; procsTable.AddColumn("Plugin"); procsTable.AddColumn("Name"); + procsTable.AddColumn("Level"); for (const auto& proc : cs.event_processors) { - procsTable | proc.plugin_name | proc.type_name; + procsTable | proc.plugin_name | proc.type_name | proc.level; } procsTable.Render(os); + if (cs.event_unfolders.size() != 0) { + os << " UNFOLDERS" << std::endl; + JTablePrinter unfoldersTable; + unfoldersTable.AddColumn("Plugin"); + unfoldersTable.AddColumn("Name"); + unfoldersTable.AddColumn("Level"); + for (const auto& unfolder : cs.event_unfolders) { + unfoldersTable | unfolder.plugin_name | unfolder.type_name | unfolder.level; + } + sourcesTable.Render(os); + } + os << " FACTORIES" << std::endl; JTablePrinter factoryTable; factoryTable.AddColumn("Plugin"); factoryTable.AddColumn("Object name"); factoryTable.AddColumn("Tag"); + factoryTable.AddColumn("Level"); for (const auto& factory : cs.factories) { - factoryTable | factory.plugin_name | factory.object_name | factory.factory_tag; + factoryTable | factory.plugin_name | factory.object_name | factory.factory_tag | factory.level; } factoryTable.Render(os); return os; diff --git a/src/libraries/JANA/Status/JComponentSummary.h b/src/libraries/JANA/Status/JComponentSummary.h index 398e420e9..a4f4054d5 100644 --- a/src/libraries/JANA/Status/JComponentSummary.h +++ b/src/libraries/JANA/Status/JComponentSummary.h @@ -8,8 +8,10 @@ #include #include +#include struct JFactorySummary { + JEventLevel level; std::string plugin_name; std::string factory_name; std::string factory_tag; @@ -17,12 +19,20 @@ struct JFactorySummary { }; struct JEventSourceSummary { + JEventLevel level; std::string plugin_name; std::string type_name; std::string source_name; }; struct JEventProcessorSummary { + JEventLevel level; + std::string plugin_name; + std::string type_name; +}; + +struct JEventUnfolderSummary { + JEventLevel level; std::string plugin_name; std::string type_name; }; @@ -31,6 +41,7 @@ struct JComponentSummary { std::vector factories; std::vector event_sources; std::vector event_processors; + std::vector event_unfolders; }; std::ostream& operator<<(std::ostream& os, JComponentSummary const & cs); From 30952e682872bc7bec9c061d3b76139cbdb74541 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 22 Feb 2024 16:06:40 -0500 Subject: [PATCH 14/34] Skeleton of recursive topology building --- src/libraries/JANA/Engine/JTopologyBuilder.h | 92 ++++++++++++++++++-- src/libraries/JANA/Utils/JEventLevel.h | 15 +++- 2 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index cd1a0783a..378578d89 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -6,6 +6,7 @@ #define JANA2_JTOPOLOGYBUILDER_H #include +#include #include "JEventSourceArrow.h" #include "JEventProcessorArrow.h" #include @@ -128,24 +129,94 @@ class JTopologyBuilder : public JService { return m_topology; } -/* - inline void add_eventsource_arrows() { - } + void attach_lower_level(JEventLevel current_level, JArrow* parent_unfolder, JArrow* parent_folder) { + + // Attach unfolders, folders, processors + + JEventPool* pool = new JEventPool(m_components, + m_event_pool_size, + m_location_count, + m_limit_total_events_in_flight, + current_level); + pool->init(); + m_topology->pools.push_back(pool); // Establishes ownership + + std::vector unfolders_at_level; + for (JEventUnfolder* unfolder : m_components->get_unfolders()) { + if (unfolder->GetLevel() == current_level) { + unfolders_at_level.push_back(unfolder); + } + } + if (unfolders_at_level.size() == 0) { + // No unfolders, so this is the last level + // skip_lower_level(next_level(current_level)); + } + else { + // TODO:: Attach preprocess, unfolder arrow, folder arrow + // attach_lower_level(next_level(current_level)); + } - inline void add_eventproc_arrow() { - } - template - void add_subevent_arrows() { } - inline void add_blockeventsource_arrow() { + void attach_top_level(JEventLevel current_level) { + + JEventPool* pool = new JEventPool(m_components, + m_event_pool_size, + m_location_count, + m_limit_total_events_in_flight, + current_level); + pool->init(); + m_topology->pools.push_back(pool); // Establishes ownership + + std::vector sources_at_level; + for (JEventSource* source : m_components->get_evt_srces()) { + if (source->GetLevel() == current_level) { + sources_at_level.push_back(source); + } + } + if (sources_at_level.size() == 0) { + // Skip level entirely for now. Consider eventually supporting + // folding low levels into higher levels without corresponding unfold + JEventLevel next = next_level(current_level); + if (next == JEventLevel::None) { + throw JException("Unable to construct topology: No sources found!"); + } + return attach_top_level(next); + } + + // We have a source, so we've found our top level. Now we have two options: + // a. This is the only level, in which case we are done + // b. We have an unfolder/folder pair, in which case we recursively attach_lower_level(). + std::vector unfolders_at_level; + for (JEventUnfolder* unfolder : m_components->get_unfolders()) { + if (unfolder->GetLevel() == current_level) { + unfolders_at_level.push_back(unfolder); + } + } + if (unfolders_at_level.size() == 0) { + // No unfolders, so this is the only level + // Attach the source to the tap just like before + // + // We might want to print a friendly warning message communicating why any lower-level + // components are being ignored, like so: + // skip_lower_level(next_level(current_level)); + } + else { + // Have unfolders, so we need to connect our source arrow + // to our unfolder (and maybe preprocessor) arrows + // Here we attach the source to the map to the unfolder + // Also the folder to the tap + // Then we pass the unfolder and folder to the attach_lower_level so it can hook those up as well + // attach_lower_level(next_level(current_level)); + } } -*/ + + inline std::shared_ptr create_default_topology() { @@ -179,6 +250,9 @@ class JTopologyBuilder : public JService { return m_topology; } + + + }; diff --git a/src/libraries/JANA/Utils/JEventLevel.h b/src/libraries/JANA/Utils/JEventLevel.h index b9881c5e0..0867978a7 100644 --- a/src/libraries/JANA/Utils/JEventLevel.h +++ b/src/libraries/JANA/Utils/JEventLevel.h @@ -4,7 +4,7 @@ #pragma once #include -enum class JEventLevel { Run, Subrun, Timeslice, Block, Event, Subevent, Task }; +enum class JEventLevel { Run, Subrun, Timeslice, Block, Event, Subevent, Task, None }; inline std::ostream& operator<<(std::ostream& os, JEventLevel level) { switch (level) { @@ -15,8 +15,21 @@ inline std::ostream& operator<<(std::ostream& os, JEventLevel level) { case JEventLevel::Event: os << "Event"; break; case JEventLevel::Subevent: os << "Subevent"; break; case JEventLevel::Task: os << "Task"; break; + case JEventLevel::None: os << "None"; break; } return os; } +inline JEventLevel next_level(JEventLevel current_level) { + switch (current_level) { + case JEventLevel::Run: return JEventLevel::Subrun; + case JEventLevel::Subrun: return JEventLevel::Timeslice; + case JEventLevel::Timeslice: return JEventLevel::Block; + case JEventLevel::Block: return JEventLevel::Event; + case JEventLevel::Event: return JEventLevel::Subevent; + case JEventLevel::Subevent: return JEventLevel::Task; + case JEventLevel::Task: return JEventLevel::None; + case JEventLevel::None: return JEventLevel::None; + } +} From 9b6cc03118931b0bd3624b2da704e56f57d77c29 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 22 Feb 2024 23:33:54 -0500 Subject: [PATCH 15/34] JArrowTopology::event_pool is no longer a shared_ptr --- src/libraries/JANA/Engine/JArrowTopology.h | 2 +- src/libraries/JANA/Engine/JBlockDisentanglerArrow.h | 6 +++--- src/libraries/JANA/Engine/JEventProcessorArrow.cc | 4 ++-- src/libraries/JANA/Engine/JEventProcessorArrow.h | 2 +- src/libraries/JANA/Engine/JEventSourceArrow.cc | 4 ++-- src/libraries/JANA/Engine/JEventSourceArrow.h | 2 +- src/libraries/JANA/Engine/JTopologyBuilder.h | 10 ++++------ 7 files changed, 14 insertions(+), 16 deletions(-) diff --git a/src/libraries/JANA/Engine/JArrowTopology.h b/src/libraries/JANA/Engine/JArrowTopology.h index 5893eda8e..622d7f4ff 100644 --- a/src/libraries/JANA/Engine/JArrowTopology.h +++ b/src/libraries/JANA/Engine/JArrowTopology.h @@ -28,7 +28,7 @@ struct JArrowTopology { // Ensure that ComponentManager stays alive at least as long as JArrowTopology does // Otherwise there is a potential use-after-free when JArrowTopology or JArrowProcessingController access components - std::shared_ptr event_pool; // TODO: Move into pools eventually + JEventPool* event_pool; // TODO: Move into pools eventually JPerfMetrics metrics; std::vector arrows; diff --git a/src/libraries/JANA/Engine/JBlockDisentanglerArrow.h b/src/libraries/JANA/Engine/JBlockDisentanglerArrow.h index bbc80268f..530a90bd6 100644 --- a/src/libraries/JANA/Engine/JBlockDisentanglerArrow.h +++ b/src/libraries/JANA/Engine/JBlockDisentanglerArrow.h @@ -14,7 +14,7 @@ class JBlockDisentanglerArrow : public JArrow { JBlockedEventSource* m_source; // non-owning JMailbox* m_block_queue; // non-owning JMailbox*>* m_event_queue; // non-owning - std::shared_ptr m_pool; + JEventPool* m_pool; size_t m_max_events_per_block = 40; @@ -23,13 +23,13 @@ class JBlockDisentanglerArrow : public JArrow { JBlockedEventSource* source, JMailbox* block_queue, JMailbox*>* event_queue, - std::shared_ptr pool + JEventPool* pool ) : JArrow(std::move(name), true, false, false, 1) , m_source(source) , m_block_queue(block_queue) , m_event_queue(event_queue) - , m_pool(std::move(pool)) + , m_pool(pool) {} ~JBlockDisentanglerArrow() { diff --git a/src/libraries/JANA/Engine/JEventProcessorArrow.cc b/src/libraries/JANA/Engine/JEventProcessorArrow.cc index ae4c5faea..c4590c0f8 100644 --- a/src/libraries/JANA/Engine/JEventProcessorArrow.cc +++ b/src/libraries/JANA/Engine/JEventProcessorArrow.cc @@ -12,14 +12,14 @@ JEventProcessorArrow::JEventProcessorArrow(std::string name, EventQueue *input_queue, EventQueue *output_queue, - std::shared_ptr pool) + JEventPool *pool) : JPipelineArrow(std::move(name), true, false, true, input_queue, output_queue, - pool.get()) {} + pool) {} void JEventProcessorArrow::add_processor(JEventProcessor* processor) { m_processors.push_back(processor); diff --git a/src/libraries/JANA/Engine/JEventProcessorArrow.h b/src/libraries/JANA/Engine/JEventProcessorArrow.h index e710fbcd2..c29555131 100644 --- a/src/libraries/JANA/Engine/JEventProcessorArrow.h +++ b/src/libraries/JANA/Engine/JEventProcessorArrow.h @@ -23,7 +23,7 @@ class JEventProcessorArrow : public JPipelineArrow JEventProcessorArrow(std::string name, EventQueue *input_queue, EventQueue *output_queue, - std::shared_ptr pool); + JEventPool *pool); void add_processor(JEventProcessor* processor); diff --git a/src/libraries/JANA/Engine/JEventSourceArrow.cc b/src/libraries/JANA/Engine/JEventSourceArrow.cc index defaccf38..a78e8f620 100644 --- a/src/libraries/JANA/Engine/JEventSourceArrow.cc +++ b/src/libraries/JANA/Engine/JEventSourceArrow.cc @@ -14,9 +14,9 @@ using SourceStatus = JEventSource::RETURN_STATUS; JEventSourceArrow::JEventSourceArrow(std::string name, std::vector sources, EventQueue* output_queue, - std::shared_ptr pool + JEventPool* pool ) - : JPipelineArrow(name, false, true, false, nullptr, output_queue, pool.get()), m_sources(sources) { + : JPipelineArrow(name, false, true, false, nullptr, output_queue, pool), m_sources(sources) { } diff --git a/src/libraries/JANA/Engine/JEventSourceArrow.h b/src/libraries/JANA/Engine/JEventSourceArrow.h index 1d6d6bb96..2c55dee2a 100644 --- a/src/libraries/JANA/Engine/JEventSourceArrow.h +++ b/src/libraries/JANA/Engine/JEventSourceArrow.h @@ -19,7 +19,7 @@ class JEventSourceArrow : public JPipelineArrow { size_t m_current_source = 0; public: - JEventSourceArrow(std::string name, std::vector sources, EventQueue* output_queue, std::shared_ptr pool); + JEventSourceArrow(std::string name, std::vector sources, EventQueue* output_queue, JEventPool* pool); void initialize() final; void finalize() final; diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index 378578d89..dd148cb3a 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -121,10 +121,10 @@ class JTopologyBuilder : public JService { m_topology->mapping.initialize(static_cast(m_affinity), static_cast(m_locality)); - m_topology->event_pool = std::make_shared(m_components, - m_event_pool_size, - m_location_count, - m_limit_total_events_in_flight); + m_topology->event_pool = new JEventPool(m_components, + m_event_pool_size, + m_location_count, + m_limit_total_events_in_flight); m_topology->event_pool->init(); return m_topology; @@ -158,8 +158,6 @@ class JTopologyBuilder : public JService { } - - } void attach_top_level(JEventLevel current_level) { From 0fbad31c7d79fc2cb368ffd1221248b03bca87e6 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Fri, 23 Feb 2024 23:09:09 -0500 Subject: [PATCH 16/34] Add JEventMapArrow --- src/libraries/JANA/CMakeLists.txt | 2 + src/libraries/JANA/Engine/JEventMapArrow.cc | 55 +++++++++++++++++++++ src/libraries/JANA/Engine/JEventMapArrow.h | 33 +++++++++++++ src/libraries/JANA/JEventProcessor.h | 5 +- src/libraries/JANA/JEventSource.h | 2 + 5 files changed, 95 insertions(+), 2 deletions(-) create mode 100644 src/libraries/JANA/Engine/JEventMapArrow.cc create mode 100644 src/libraries/JANA/Engine/JEventMapArrow.h diff --git a/src/libraries/JANA/CMakeLists.txt b/src/libraries/JANA/CMakeLists.txt index 303521a0a..668c2673a 100644 --- a/src/libraries/JANA/CMakeLists.txt +++ b/src/libraries/JANA/CMakeLists.txt @@ -41,6 +41,8 @@ set(JANA2_SOURCES Engine/JEventSourceArrow.h Engine/JBlockSourceArrow.h Engine/JBlockDisentanglerArrow.h + Engine/JEventMapArrow.h + Engine/JEventMapArrow.cc Engine/JPool.h Engine/JMailbox.h diff --git a/src/libraries/JANA/Engine/JEventMapArrow.cc b/src/libraries/JANA/Engine/JEventMapArrow.cc new file mode 100644 index 000000000..eff60a4f6 --- /dev/null +++ b/src/libraries/JANA/Engine/JEventMapArrow.cc @@ -0,0 +1,55 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + + +#include + +#include +#include +#include + + +JEventMapArrow::JEventMapArrow(std::string name, + EventQueue *input_queue, + EventQueue *output_queue) + : JPipelineArrow(std::move(name), + true, + false, + true, + input_queue, + output_queue, + nullptr) {} + +void JEventMapArrow::add_source(JEventSource* source) { + m_sources.push_back(source); +} + +void JEventMapArrow::add_unfolder(JEventUnfolder* unfolder) { + m_unfolders.push_back(unfolder); +} + +void JEventMapArrow::process(Event* event, bool& success, JArrowMetrics::Status& status) { + + + LOG_DEBUG(m_logger) << "JEventMapArrow '" << get_name() << "': Starting event# " << (*event)->GetEventNumber() << LOG_END; + for (JEventSource* source : m_sources) { + JCallGraphEntryMaker cg_entry(*(*event)->GetJCallGraphRecorder(), source->GetTypeName()); // times execution until this goes out of scope + source->Preprocess(**event); + } + for (JEventUnfolder* unfolder : m_unfolders) { + JCallGraphEntryMaker cg_entry(*(*event)->GetJCallGraphRecorder(), unfolder->GetTypeName()); // times execution until this goes out of scope + unfolder->Preprocess(**event); + } + LOG_DEBUG(m_logger) << "JEventMapArrow '" << get_name() << "': Finished event# " << (*event)->GetEventNumber() << LOG_END; + success = true; + status = JArrowMetrics::Status::KeepGoing; +} + +void JEventMapArrow::initialize() { + LOG_DEBUG(m_logger) << "Initializing arrow '" << get_name() << "'" << LOG_END; +} + +void JEventMapArrow::finalize() { + LOG_DEBUG(m_logger) << "Finalizing arrow '" << get_name() << "'" << LOG_END; +} + diff --git a/src/libraries/JANA/Engine/JEventMapArrow.h b/src/libraries/JANA/Engine/JEventMapArrow.h new file mode 100644 index 000000000..7f110b4e4 --- /dev/null +++ b/src/libraries/JANA/Engine/JEventMapArrow.h @@ -0,0 +1,33 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +#include + +class JEventPool; +class JEventSource; +class JEventUnfolder; +class JEvent; + +using Event = std::shared_ptr; +using EventQueue = JMailbox; + +class JEventMapArrow : public JPipelineArrow { + +private: + std::vector m_sources; + std::vector m_unfolders; + +public: + JEventMapArrow(std::string name, EventQueue *input_queue, EventQueue *output_queue); + + void add_source(JEventSource* source); + void add_unfolder(JEventUnfolder* unfolder); + + void process(Event* event, bool& success, JArrowMetrics::Status& status); + + void initialize() final; + void finalize() final; +}; + diff --git a/src/libraries/JANA/JEventProcessor.h b/src/libraries/JANA/JEventProcessor.h index 0984868bb..9dd357cda 100644 --- a/src/libraries/JANA/JEventProcessor.h +++ b/src/libraries/JANA/JEventProcessor.h @@ -152,6 +152,9 @@ class JEventProcessor { return m_type_name; } + // Meant to be called by JANA + JEventLevel GetLevel() { return m_level; } + protected: @@ -186,8 +189,6 @@ class JEventProcessor { // Meant to be called by user in constructor void SetLevel(JEventLevel level) { m_level = level; } - // Meant to be called by JANA - JEventLevel GetLevel() { return m_level; } private: diff --git a/src/libraries/JANA/JEventSource.h b/src/libraries/JANA/JEventSource.h index 764c71a4f..704b4a67f 100644 --- a/src/libraries/JANA/JEventSource.h +++ b/src/libraries/JANA/JEventSource.h @@ -88,6 +88,8 @@ class JEventSource { virtual void GetEvent(std::shared_ptr) = 0; + virtual void Preprocess(const JEvent&) {}; + /// `FinishEvent` is used to notify the `JEventSource` that an event has been completely processed. This is the final /// chance to interact with the `JEvent` before it is either cleared and recycled, or deleted. Although it is From bcb450113ce33e4243e7a5581619b7712d65aad2 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Sun, 25 Feb 2024 00:32:01 -0500 Subject: [PATCH 17/34] Rough cut of topology building with unfold/fold --- src/libraries/JANA/Engine/JFoldArrow.h | 33 +++-- src/libraries/JANA/Engine/JTopologyBuilder.h | 120 +++++++++++++++++-- 2 files changed, 127 insertions(+), 26 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 088b9e370..879a419d1 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -4,33 +4,40 @@ #pragma once #include -#include #include +#include class JFoldArrow : public JArrow { private: using EventT = std::shared_ptr; - JEventFolder* m_folder = nullptr; + // TODO: Support user-provided folders + // JEventFolder* m_folder = nullptr; PlaceRef m_child_in; PlaceRef m_child_out; PlaceRef m_parent_out; + // TODO: I don't particularly like this. Maybe we should make JEvent::m_parents be a stack instead. + // Or maybe go back to the original idea of having the pool also recycle the parent to the parent pool + JEventLevel m_parent_level; + + public: JFoldArrow( std::string name, - JEventFolder* folder, + //JEventFolder* folder, + JEventLevel m_parent_level, JMailbox* child_in, JEventPool* child_out, JMailbox* parent_out) : JArrow(std::move(name), false, false, false), - m_folder(folder), + // m_folder(folder), m_child_in(this, child_in, true, 1, 1), - m_child_out(this, child_out, false, 1, 1) - m_parent_in(this, parent_out, false, 1, 1), + m_child_out(this, child_out, false, 1, 1), + m_parent_out(this, parent_out, false, 1, 1) { } @@ -59,15 +66,15 @@ class JFoldArrow : public JArrow { bool try_pull_all(Data& ci, Data& co, Data& po) { bool success; - success = m_parent_in.pull(ci); + success = m_child_in.pull(co); if (! success) { return false; } - success = m_child_in.pull(co); + success = m_child_out.pull(po); if (! success) { return false; } - success = m_child_out.pull(po); + success = m_parent_out.pull(ci); if (! success) { return false; } @@ -102,17 +109,17 @@ class JFoldArrow : public JArrow { //auto status = m_folder->DoFold(*(child->get()), *(parent->get())); - EventT* parent = child->GetParent(); - // TODO: How does fold know parent level? + std::shared_ptr* parent = nullptr; //std::const_pointer_cast>(*child)->GetParent(m_parent_level).shared_from_this(); + // TODO: Need to obtain pointer to parent child_out_data.items[0] = child; child_out_data.item_count = 1; - parent_out_data.items[0] = child; + parent_out_data.items[0] = parent; // FIXME: parent_out_data.item_count = 1; auto end_processing_time = std::chrono::steady_clock::now(); - size_t events_processed = push_all(parent_in_data, child_in_data, child_out_data); + size_t events_processed = push_all(child_in_data, child_out_data, parent_out_data); auto end_total_time = std::chrono::steady_clock::now(); auto latency = (end_processing_time - start_processing_time); diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index dd148cb3a..26f5c14e7 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -6,9 +6,13 @@ #define JANA2_JTOPOLOGYBUILDER_H #include -#include + #include "JEventSourceArrow.h" #include "JEventProcessorArrow.h" +#include "JEventMapArrow.h" +#include "JUnfoldArrow.h" +#include "JFoldArrow.h" + #include class JTopologyBuilder : public JService { @@ -162,13 +166,6 @@ class JTopologyBuilder : public JService { void attach_top_level(JEventLevel current_level) { - JEventPool* pool = new JEventPool(m_components, - m_event_pool_size, - m_location_count, - m_limit_total_events_in_flight, - current_level); - pool->init(); - m_topology->pools.push_back(pool); // Establishes ownership std::vector sources_at_level; for (JEventSource* source : m_components->get_evt_srces()) { @@ -186,9 +183,30 @@ class JTopologyBuilder : public JService { return attach_top_level(next); } - // We have a source, so we've found our top level. Now we have two options: - // a. This is the only level, in which case we are done - // b. We have an unfolder/folder pair, in which case we recursively attach_lower_level(). + // We've found the top level + + // Create the topology (once). (We are putting this here for now, but may move it to the caller) + m_topology = std::make_shared(); + m_topology->component_manager = m_components; // Ensure the lifespan of the component manager exceeds that of the topology + m_topology->mapping.initialize(static_cast(m_affinity), + static_cast(m_locality)); + + // We've now found our top level. No matter what, we need an event pool for this level + JEventPool* pool_at_level = new JEventPool(m_components, + m_event_pool_size, + m_location_count, + m_limit_total_events_in_flight, + current_level); + pool_at_level->init(); + m_topology->pools.push_back(pool_at_level); // Hand over ownership of the pool to the topology + + // There are two possibilities at this point: + // a. This is the only level, in which case we wire up the arrows and exit + // b. We have an unfolder/folder pair, in which case we wire everything up, and then recursively attach_lower_level(). + // We use the presence of an unfolder as our test for whether or not a lower level should be included. This is because + // the folder might be trivial and hence omitted by the user. (Note that some folder is always needed in order to return + // the higher-level event to the pool). + // The user always needs to provide an unfolder because I can't think of a trivial unfolder that would be useful. std::vector unfolders_at_level; for (JEventUnfolder* unfolder : m_components->get_unfolders()) { @@ -196,21 +214,97 @@ class JTopologyBuilder : public JService { unfolders_at_level.push_back(unfolder); } } + + std::vector procs_at_level; + for (JEventProcessor* proc : m_components->get_evt_procs()) { + if (proc->GetLevel() == current_level) { + procs_at_level.push_back(proc); + } + } + if (unfolders_at_level.size() == 0) { // No unfolders, so this is the only level - // Attach the source to the tap just like before + // Attach the source to the map/tap just like before // // We might want to print a friendly warning message communicating why any lower-level // components are being ignored, like so: // skip_lower_level(next_level(current_level)); + + auto queue = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); + m_topology->queues.push_back(queue); + + auto* src_arrow = new JEventSourceArrow("sources", sources_at_level, queue, pool_at_level); + m_topology->arrows.push_back(src_arrow); + src_arrow->set_chunksize(m_event_source_chunksize); + src_arrow->set_logger(m_arrow_logger); + + auto* proc_arrow = new JEventProcessorArrow("processors", queue, nullptr, pool_at_level); + m_topology->arrows.push_back(proc_arrow); + proc_arrow->set_chunksize(m_event_processor_chunksize); + proc_arrow->set_logger(m_arrow_logger); + + for (auto proc: procs_at_level) { + proc_arrow->add_processor(proc); + } + src_arrow->attach(proc_arrow); + } + else if (unfolders_at_level.size() != 1) { + throw JException("At most one unfolder must be provided for each level in the event hierarchy!"); } else { - // Have unfolders, so we need to connect our source arrow + // Have our unfolder, so we need to connect our source arrow // to our unfolder (and maybe preprocessor) arrows // Here we attach the source to the map to the unfolder // Also the folder to the tap // Then we pass the unfolder and folder to the attach_lower_level so it can hook those up as well // attach_lower_level(next_level(current_level)); + + auto q1 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); + auto q2 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); + + m_topology->queues.push_back(q1); + m_topology->queues.push_back(q2); + + auto *src_arrow = new JEventSourceArrow("sources", sources_at_level, q1, pool_at_level); + m_topology->arrows.push_back(src_arrow); + src_arrow->set_chunksize(m_event_source_chunksize); + src_arrow->set_logger(m_arrow_logger); + + auto *map_arrow = new JEventMapArrow("maps", q1, q2);; + m_topology->arrows.push_back(map_arrow); + map_arrow->set_chunksize(m_event_source_chunksize); + map_arrow->set_logger(m_arrow_logger); + src_arrow->attach(map_arrow); + + auto *unfold_arrow = new JUnfoldArrow("unfold", unfolders_at_level[0], q2, nullptr, nullptr); + m_topology->arrows.push_back(unfold_arrow); + unfold_arrow->set_chunksize(m_event_source_chunksize); + unfold_arrow->set_logger(m_arrow_logger); + map_arrow->attach(unfold_arrow); + + auto *fold_arrow = new JFoldArrow("fold", current_level, nullptr, pool_at_level, nullptr); + // TODO: Support user-provided folders + m_topology->arrows.push_back(fold_arrow); + fold_arrow->set_chunksize(m_event_source_chunksize); + fold_arrow->set_logger(m_arrow_logger); + + if (procs_at_level.size() != 0) { + + auto q3 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); + m_topology->queues.push_back(q3); + + auto* proc_arrow = new JEventProcessorArrow("processors", q3, nullptr, pool_at_level); + m_topology->arrows.push_back(proc_arrow); + proc_arrow->set_chunksize(m_event_processor_chunksize); + proc_arrow->set_logger(m_arrow_logger); + + for (auto proc: procs_at_level) { + proc_arrow->add_processor(proc); + } + + // TODO: Redirect fold_arrow to output to q3 instead of pool + fold_arrow->attach(proc_arrow); + } } } From 44b8c646e5275653f52e43473ef50701f70dd5e5 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Mon, 26 Feb 2024 14:48:02 -0500 Subject: [PATCH 18/34] USE_ASAN no longer pulls in -O2 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 250b5e846..90d75dde6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -108,12 +108,12 @@ if (${USE_XERCES}) endif() if (${USE_ASAN}) - add_compile_options(-fsanitize=address -g -O2) + add_compile_options(-fsanitize=address) add_link_options(-fsanitize=address) endif() if (${USE_TSAN}) - add_compile_options(-fsanitize=thread -g -O2) + add_compile_options(-fsanitize=thread) add_link_options(-fsanitize=thread) endif() From b903205b1d1c6aea61ab7eb89202cad693a12020 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Mon, 26 Feb 2024 15:05:09 -0500 Subject: [PATCH 19/34] Excise last references to removed 'jana:legacy_mode' parameter --- src/examples/StreamingExample/ZmqMain.cc | 2 -- src/programs/unit_tests/ExactlyOnceTests.cc | 1 - 2 files changed, 3 deletions(-) diff --git a/src/examples/StreamingExample/ZmqMain.cc b/src/examples/StreamingExample/ZmqMain.cc index 8f611f35b..d363e129a 100644 --- a/src/examples/StreamingExample/ZmqMain.cc +++ b/src/examples/StreamingExample/ZmqMain.cc @@ -58,8 +58,6 @@ void InitPlugin(JApplication *app) { app->Add(new AHitAnomalyDetector(app, 5000)); app->Add(new JFactoryGeneratorT()); - // So we don't have to put this on the cmd line every time - app->SetParameterValue("jana:legacy_mode", 0); app->SetParameterValue("jana:extended_report", 0); new std::thread(dummy_publisher_loop); diff --git a/src/programs/unit_tests/ExactlyOnceTests.cc b/src/programs/unit_tests/ExactlyOnceTests.cc index 8bb4a0f99..4a60c1b52 100644 --- a/src/programs/unit_tests/ExactlyOnceTests.cc +++ b/src/programs/unit_tests/ExactlyOnceTests.cc @@ -25,7 +25,6 @@ TEST_CASE("ExactlyOnceTests") { REQUIRE(processor->init_count == 0); REQUIRE(processor->finish_count == 0); - app.SetParameterValue("jana:legacy_mode", 1); app.Run(true); REQUIRE(source->open_count == 1); From 6087d8d631d1893c4b0fc83ff0e2f1f29ff8d07f Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Mon, 26 Feb 2024 15:43:44 -0500 Subject: [PATCH 20/34] JTopologyBuilder: Test cases pass --- src/libraries/JANA/Engine/JTopologyBuilder.h | 22 +++++++++++--------- src/libraries/JANA/JEventProcessor.h | 2 +- src/libraries/JANA/JEventSource.h | 2 +- src/libraries/JANA/JEventUnfolder.h | 11 ++++++---- src/libraries/JANA/JFactory.h | 2 +- 5 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index 26f5c14e7..efb31511a 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -68,7 +68,12 @@ class JTopologyBuilder : public JService { return m_topology; } else { - m_topology = create_default_topology(); + m_topology = std::make_shared(); + m_topology->component_manager = m_components; // Ensure the lifespan of the component manager exceeds that of the topology + m_topology->mapping.initialize(static_cast(m_affinity), + static_cast(m_locality)); + + attach_top_level(JEventLevel::Run); return m_topology; } } @@ -176,20 +181,15 @@ class JTopologyBuilder : public JService { if (sources_at_level.size() == 0) { // Skip level entirely for now. Consider eventually supporting // folding low levels into higher levels without corresponding unfold + LOG_TRACE(m_arrow_logger) << "JTopologyBuilder: No sources found at level " << current_level << ", skipping" << LOG_END; JEventLevel next = next_level(current_level); if (next == JEventLevel::None) { - throw JException("Unable to construct topology: No sources found!"); + LOG_WARN(m_arrow_logger) << "No sources found: Processing topology will be empty." << LOG_END; + return; } return attach_top_level(next); } - - // We've found the top level - - // Create the topology (once). (We are putting this here for now, but may move it to the caller) - m_topology = std::make_shared(); - m_topology->component_manager = m_components; // Ensure the lifespan of the component manager exceeds that of the topology - m_topology->mapping.initialize(static_cast(m_affinity), - static_cast(m_locality)); + LOG_DEBUG(m_arrow_logger) << "JTopologyBuilder: Attaching components at level " << current_level << LOG_END; // We've now found our top level. No matter what, we need an event pool for this level JEventPool* pool_at_level = new JEventPool(m_components, @@ -230,6 +230,8 @@ class JTopologyBuilder : public JService { // components are being ignored, like so: // skip_lower_level(next_level(current_level)); + LOG_DEBUG(m_arrow_logger) << "JTopologyBuilder: No unfolders found at level " << current_level << ", finishing here." << LOG_END; + auto queue = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); m_topology->queues.push_back(queue); diff --git a/src/libraries/JANA/JEventProcessor.h b/src/libraries/JANA/JEventProcessor.h index 9dd357cda..e31e97c7e 100644 --- a/src/libraries/JANA/JEventProcessor.h +++ b/src/libraries/JANA/JEventProcessor.h @@ -193,7 +193,7 @@ class JEventProcessor { private: // Common to components - JEventLevel m_level; + JEventLevel m_level = JEventLevel::Event; JApplication* mApplication = nullptr; Status m_status; std::string m_plugin_name; diff --git a/src/libraries/JANA/JEventSource.h b/src/libraries/JANA/JEventSource.h index 704b4a67f..08881be00 100644 --- a/src/libraries/JANA/JEventSource.h +++ b/src/libraries/JANA/JEventSource.h @@ -325,7 +325,7 @@ class JEventSource { private: // Common to all components - JEventLevel m_level; + JEventLevel m_level = JEventLevel::Event; JApplication* m_application = nullptr; std::atomic m_status; std::string m_plugin_name; diff --git a/src/libraries/JANA/JEventUnfolder.h b/src/libraries/JANA/JEventUnfolder.h index cb1bee220..f4436c545 100644 --- a/src/libraries/JANA/JEventUnfolder.h +++ b/src/libraries/JANA/JEventUnfolder.h @@ -13,14 +13,14 @@ class JEventUnfolder { private: // Common to components... factor this out someday - JEventLevel m_level; + JEventLevel m_level = JEventLevel::Event; JApplication* m_application = nullptr; std::string m_plugin_name; std::string m_type_name; std::mutex m_mutex; int32_t m_last_run_number = -1; enum class Status { Uninitialized, Initialized, Finalized }; - Status m_status; + Status m_status = Status::Uninitialized; // JEventUnfolder-specific // @@ -98,6 +98,9 @@ class JEventUnfolder { Init(); m_status = Status::Initialized; } + else { + throw JException("JEventUnfolder: Attempting to initialize twice or from an invalid state"); + } } catch (JException& ex) { ex.plugin_name = m_plugin_name; @@ -105,7 +108,7 @@ class JEventUnfolder { throw ex; } catch (...) { - auto ex = JException("Unknown exception in JEventUnfolder::Init()"); + auto ex = JException("JEventUnfolder: Unknown exception in JEventUnfolder::Init()"); ex.nested_exception = std::current_exception(); ex.plugin_name = m_plugin_name; ex.component_name = m_type_name; @@ -120,7 +123,7 @@ class JEventUnfolder { Preprocess(parent); } else { - throw JException("Component needs to be initialized and not finalized before Unfold can be called"); + throw JException("JEventUnfolder: Component needs to be initialized and not finalized before Unfold can be called"); } } catch (JException& ex) { diff --git a/src/libraries/JANA/JFactory.h b/src/libraries/JANA/JFactory.h index 90c8bf418..abc10f89c 100644 --- a/src/libraries/JANA/JFactory.h +++ b/src/libraries/JANA/JFactory.h @@ -172,7 +172,7 @@ class JFactory { std::once_flag mInitFlag; // Common to components - JEventLevel mLevel; + JEventLevel mLevel = JEventLevel::Event; std::string mPluginName; std::string mFactoryName; mutable std::mutex mMutex; From 19bbb23adf411333b7c0541b899f2b621a343688 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Mon, 26 Feb 2024 23:16:14 -0500 Subject: [PATCH 21/34] Rough cut of JTopologyBuilder::attach_lower_level --- src/libraries/JANA/Engine/JTopologyBuilder.h | 107 ++++++++++--------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index efb31511a..e72767f0c 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -141,7 +141,10 @@ class JTopologyBuilder : public JService { void attach_lower_level(JEventLevel current_level, JArrow* parent_unfolder, JArrow* parent_folder) { - // Attach unfolders, folders, processors + std::stringstream ss; + ss << current_level; + + LOG_DEBUG(m_arrow_logger) << "JTopologyBuilder: Attaching components at lower level = " << current_level << LOG_END; JEventPool* pool = new JEventPool(m_components, m_event_pool_size, @@ -149,28 +152,65 @@ class JTopologyBuilder : public JService { m_limit_total_events_in_flight, current_level); pool->init(); - m_topology->pools.push_back(pool); // Establishes ownership - + m_topology->pools.push_back(pool); // Transfers ownership + + + std::vector sources_at_level; + for (JEventSource* source : m_components->get_evt_srces()) { + if (source->GetLevel() == current_level) { + sources_at_level.push_back(source); + } + } + std::vector procs_at_level; + for (JEventProcessor* proc : m_components->get_evt_procs()) { + if (proc->GetLevel() == current_level) { + procs_at_level.push_back(proc); + } + } std::vector unfolders_at_level; for (JEventUnfolder* unfolder : m_components->get_unfolders()) { if (unfolder->GetLevel() == current_level) { unfolders_at_level.push_back(unfolder); } } - if (unfolders_at_level.size() == 0) { - // No unfolders, so this is the last level - // skip_lower_level(next_level(current_level)); + + + if (sources_at_level.size() != 0) { + throw JException("Support for lower-level event sources coming soon!"); } - else { - // TODO:: Attach preprocess, unfolder arrow, folder arrow - // attach_lower_level(next_level(current_level)); + if (unfolders_at_level.size() != 0) { + throw JException("Support for lower-level event unfolders coming soon!"); + } + if (procs_at_level.size() == 0) { + throw JException("For now we require you to provide at least one JEventProcessor"); } + auto q1 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); + m_topology->queues.push_back(q1); + + auto q2 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); + m_topology->queues.push_back(q2); + + auto* proc_arrow = new JEventProcessorArrow("processors"+ss.str(), q1, q2, nullptr); + m_topology->arrows.push_back(proc_arrow); + proc_arrow->set_chunksize(m_event_processor_chunksize); + proc_arrow->set_logger(m_arrow_logger); + + for (auto proc: procs_at_level) { + proc_arrow->add_processor(proc); + } + // TODO: Redirect unfold_arrow to output to q1 + // TODO: Redirect fold_arrow to input from q2 + parent_unfolder->attach(proc_arrow); + proc_arrow->attach(parent_folder); } + void attach_top_level(JEventLevel current_level) { + std::stringstream ss; + ss << current_level; std::vector sources_at_level; for (JEventSource* source : m_components->get_evt_srces()) { @@ -189,7 +229,7 @@ class JTopologyBuilder : public JService { } return attach_top_level(next); } - LOG_DEBUG(m_arrow_logger) << "JTopologyBuilder: Attaching components at level " << current_level << LOG_END; + LOG_DEBUG(m_arrow_logger) << "JTopologyBuilder: Attaching components at top level = " << current_level << LOG_END; // We've now found our top level. No matter what, we need an event pool for this level JEventPool* pool_at_level = new JEventPool(m_components, @@ -267,24 +307,24 @@ class JTopologyBuilder : public JService { m_topology->queues.push_back(q1); m_topology->queues.push_back(q2); - auto *src_arrow = new JEventSourceArrow("sources", sources_at_level, q1, pool_at_level); + auto *src_arrow = new JEventSourceArrow("sources"+ss.str(), sources_at_level, q1, pool_at_level); m_topology->arrows.push_back(src_arrow); src_arrow->set_chunksize(m_event_source_chunksize); src_arrow->set_logger(m_arrow_logger); - auto *map_arrow = new JEventMapArrow("maps", q1, q2);; + auto *map_arrow = new JEventMapArrow("maps"+ss.str(), q1, q2);; m_topology->arrows.push_back(map_arrow); map_arrow->set_chunksize(m_event_source_chunksize); map_arrow->set_logger(m_arrow_logger); src_arrow->attach(map_arrow); - auto *unfold_arrow = new JUnfoldArrow("unfold", unfolders_at_level[0], q2, nullptr, nullptr); + auto *unfold_arrow = new JUnfoldArrow("unfold"+ss.str(), unfolders_at_level[0], q2, nullptr, nullptr); m_topology->arrows.push_back(unfold_arrow); unfold_arrow->set_chunksize(m_event_source_chunksize); unfold_arrow->set_logger(m_arrow_logger); map_arrow->attach(unfold_arrow); - auto *fold_arrow = new JFoldArrow("fold", current_level, nullptr, pool_at_level, nullptr); + auto *fold_arrow = new JFoldArrow("fold"+ss.str(), current_level, nullptr, pool_at_level, nullptr); // TODO: Support user-provided folders m_topology->arrows.push_back(fold_arrow); fold_arrow->set_chunksize(m_event_source_chunksize); @@ -295,7 +335,7 @@ class JTopologyBuilder : public JService { auto q3 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); m_topology->queues.push_back(q3); - auto* proc_arrow = new JEventProcessorArrow("processors", q3, nullptr, pool_at_level); + auto* proc_arrow = new JEventProcessorArrow("processors"+ss.str(), q3, nullptr, pool_at_level); m_topology->arrows.push_back(proc_arrow); proc_arrow->set_chunksize(m_event_processor_chunksize); proc_arrow->set_logger(m_arrow_logger); @@ -310,43 +350,6 @@ class JTopologyBuilder : public JService { } } - - - inline std::shared_ptr create_default_topology() { - - create_empty(); - - auto queue = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); - m_topology->queues.push_back(queue); - - // We generally want to assert that there is at least one event source (or block source, or any arrow in topology->sources, really), - // as otherwise the topology fundamentally cannot do anything. We are not doing that here for two reasons: - // 1. If a user provides a custom topology that is missing an event source, this won't catch it - // 2. Oftentimes we want to call JApplication::Initialize() just to set up plugins and services, i.e. for testing. - // We don't want to force the user to create a dummy event source if they know they are never going to call JApplication::Run(). - - // Create arrow for sources. - JArrow *arrow = new JEventSourceArrow("sources", m_components->get_evt_srces(), queue, m_topology->event_pool); - m_topology->arrows.push_back(arrow); - arrow->set_chunksize(m_event_source_chunksize); - arrow->set_logger(m_arrow_logger); - - - auto proc_arrow = new JEventProcessorArrow("processors", queue, nullptr, m_topology->event_pool); - proc_arrow->set_chunksize(m_event_processor_chunksize); - proc_arrow->set_logger(m_arrow_logger); - m_topology->arrows.push_back(proc_arrow); - - for (auto proc: m_components->get_evt_procs()) { - proc_arrow->add_processor(proc); - } - arrow->attach(proc_arrow); - return m_topology; - } - - - - }; From bb2757af1a4b68a19fa9565db89a18305d78982a Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Tue, 27 Feb 2024 17:11:34 -0500 Subject: [PATCH 22/34] Change representation of JEvent::mParents --- src/libraries/JANA/Engine/JFoldArrow.h | 20 ++++++++++++-------- src/libraries/JANA/Engine/JUnfoldArrow.h | 9 ++++++++- src/libraries/JANA/JEvent.h | 18 ++++++++---------- 3 files changed, 28 insertions(+), 19 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 879a419d1..07432083e 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -104,19 +104,23 @@ class JFoldArrow : public JArrow { auto child = child_in_data.items[0]; child_in_data.items[0] = nullptr; child_in_data.item_count = 0; - // TODO: Decrement parent's ref count - // TODO: Maybe fold should provide an identity for parent? What if no items make it past the filter? - //auto status = m_folder->DoFold(*(child->get()), *(parent->get())); - - std::shared_ptr* parent = nullptr; //std::const_pointer_cast>(*child)->GetParent(m_parent_level).shared_from_this(); - // TODO: Need to obtain pointer to parent + std::shared_ptr* parent = child->get()->mParents.back().second; + child->get()->mParents.pop_back(); + size_t remaining_siblings = parent->get()->mReferenceCount.fetch_sub(1); child_out_data.items[0] = child; child_out_data.item_count = 1; - parent_out_data.items[0] = parent; // FIXME: - parent_out_data.item_count = 1; + // Only recycle the parent once the reference count hits zero + if (remaining_siblings == 1) { + parent_out_data.items[0] = parent; + parent_out_data.item_count = 1; + } + else { + parent_out_data.items[0] = nullptr; + parent_out_data.item_count = 0; + } auto end_processing_time = std::chrono::steady_clock::now(); size_t events_processed = push_all(child_in_data, child_out_data, parent_out_data); diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h index d48e48c78..5a5562c48 100644 --- a/src/libraries/JANA/Engine/JUnfoldArrow.h +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -95,8 +95,15 @@ class JUnfoldArrow : public JArrow { auto child = child_in_data.items[0]; child_in_data.items[0] = nullptr; child_in_data.item_count = 0; - + // TODO: Assert that the input levels match what the unfolder expects + auto status = m_unfolder->DoUnfold(*(m_parent_event->get()), *(child->get())); + + // Join always succeeds (for now) + (*child)->mParents.push_back({m_parent_event->get()->GetLevel(), m_parent_event}); + m_parent_event->get()->mReferenceCount.fetch_add(1); + // TODO: We'll need something more complicated for the streaming join case + if (status == JEventUnfolder::Result::Finished) { m_ready_to_fetch_parent = true; m_parent_event = nullptr; diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index cf87276a5..86b190ddf 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -132,19 +132,15 @@ class JEvent : public JResettable, public std::enable_shared_from_this JEventLevel GetLevel() const { return mFactorySet->GetLevel(); } void SetLevel(JEventLevel level) { mFactorySet->SetLevel(level); } - void AddParent(JEvent* parent) { - JEventLevel level = parent->GetLevel(); - mParents[level] = parent; - // TODO: Validate more - } const JEvent& GetParent(JEventLevel level) const { - return *(mParents.at(level)); - // TODO: Validate more + for (const auto& pair : mParents) { + if (pair.first == level) return *(*(pair.second)); + } + throw JException("Unable to find parent at level %s", level); } - private: JApplication* mApplication = nullptr; int32_t mRunNumber = 0; @@ -158,8 +154,10 @@ class JEvent : public JResettable, public std::enable_shared_from_this bool mIsBarrierEvent = false; // Hierarchical stuff - std::map mParents; - //size_t mReferenceCount = 0; + friend class JUnfoldArrow; + friend class JFoldArrow; + std::vector*>> mParents; + std::atomic_size_t mReferenceCount = 0; From 5ad990b4be63b4bd37b97f7c82188b6189ed014d Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Tue, 27 Feb 2024 21:37:01 -0500 Subject: [PATCH 23/34] Finish attaching arrows between levels --- src/libraries/JANA/Engine/JFoldArrow.h | 15 +++++++++++++++ src/libraries/JANA/Engine/JTopologyBuilder.h | 17 ++++++----------- src/libraries/JANA/Engine/JUnfoldArrow.h | 15 +++++++++++++++ src/libraries/JANA/JEventUnfolder.h | 2 ++ 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 07432083e..68681102a 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -41,6 +41,21 @@ class JFoldArrow : public JArrow { { } + void attach_child_in(JMailbox* child_in) { + m_child_in.place_ref = child_in; + m_child_in.is_queue = true; + } + + void attach_child_out(JMailbox* child_out) { + m_child_out.place_ref = child_out; + m_child_out.is_queue = true; + } + + void attach_parent_out(JMailbox* parent_out) { + m_parent_out.place_ref = parent_out; + m_parent_out.is_queue = true; + } + void initialize() final { /* diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index e72767f0c..2242f04a7 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -139,7 +139,7 @@ class JTopologyBuilder : public JService { } - void attach_lower_level(JEventLevel current_level, JArrow* parent_unfolder, JArrow* parent_folder) { + void attach_lower_level(JEventLevel current_level, JUnfoldArrow* parent_unfolder, JFoldArrow* parent_folder) { std::stringstream ss; ss << current_level; @@ -200,8 +200,8 @@ class JTopologyBuilder : public JService { proc_arrow->add_processor(proc); } - // TODO: Redirect unfold_arrow to output to q1 - // TODO: Redirect fold_arrow to input from q2 + parent_unfolder->attach_child_out(q1); + parent_folder->attach_child_in(q2); parent_unfolder->attach(proc_arrow); proc_arrow->attach(parent_folder); } @@ -294,12 +294,6 @@ class JTopologyBuilder : public JService { throw JException("At most one unfolder must be provided for each level in the event hierarchy!"); } else { - // Have our unfolder, so we need to connect our source arrow - // to our unfolder (and maybe preprocessor) arrows - // Here we attach the source to the map to the unfolder - // Also the folder to the tap - // Then we pass the unfolder and folder to the attach_lower_level so it can hook those up as well - // attach_lower_level(next_level(current_level)); auto q1 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); auto q2 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); @@ -318,7 +312,7 @@ class JTopologyBuilder : public JService { map_arrow->set_logger(m_arrow_logger); src_arrow->attach(map_arrow); - auto *unfold_arrow = new JUnfoldArrow("unfold"+ss.str(), unfolders_at_level[0], q2, nullptr, nullptr); + auto *unfold_arrow = new JUnfoldArrow("unfold"+ss.str(), unfolders_at_level[0], q2, pool_at_level, nullptr); m_topology->arrows.push_back(unfold_arrow); unfold_arrow->set_chunksize(m_event_source_chunksize); unfold_arrow->set_logger(m_arrow_logger); @@ -344,9 +338,10 @@ class JTopologyBuilder : public JService { proc_arrow->add_processor(proc); } - // TODO: Redirect fold_arrow to output to q3 instead of pool + fold_arrow->attach_parent_out(q3); fold_arrow->attach(proc_arrow); } + attach_lower_level(unfolders_at_level[0]->GetChildLevel(), unfold_arrow, fold_arrow); } } diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h index 5a5562c48..dde02d40d 100644 --- a/src/libraries/JANA/Engine/JUnfoldArrow.h +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -36,6 +36,21 @@ class JUnfoldArrow : public JArrow { { } + void attach_parent_in(JMailbox* parent_in) { + m_parent_in.place_ref = parent_in; + m_parent_in.is_queue = true; + } + + void attach_child_in(JMailbox* child_in) { + m_child_in.place_ref = child_in; + m_child_in.is_queue = true; + } + + void attach_child_out(JMailbox* child_out) { + m_child_out.place_ref = child_out; + m_child_out.is_queue = true; + } + void initialize() final { m_unfolder->DoInit(); diff --git a/src/libraries/JANA/JEventUnfolder.h b/src/libraries/JANA/JEventUnfolder.h index f4436c545..7224f73ef 100644 --- a/src/libraries/JANA/JEventUnfolder.h +++ b/src/libraries/JANA/JEventUnfolder.h @@ -56,6 +56,8 @@ class JEventUnfolder { void SetChildLevel(JEventLevel level) { m_child_level = level; } void SetCallPreprocessUpstream(bool call_upstream) { m_call_preprocess_upstream = call_upstream; } + + JEventLevel GetChildLevel() { return m_child_level; } // Component setters (set by user) From 2a297bcf11a7f9fc2931d5408e80a5e6cdeb68f8 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Wed, 28 Feb 2024 13:01:25 -0500 Subject: [PATCH 24/34] Fix unused variable warning --- src/libraries/JANA/Engine/JFoldArrow.h | 7 ------- src/libraries/JANA/Engine/JTopologyBuilder.h | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 68681102a..2d28bc2f2 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -18,17 +18,10 @@ class JFoldArrow : public JArrow { PlaceRef m_child_out; PlaceRef m_parent_out; - // TODO: I don't particularly like this. Maybe we should make JEvent::m_parents be a stack instead. - // Or maybe go back to the original idea of having the pool also recycle the parent to the parent pool - JEventLevel m_parent_level; - - public: - JFoldArrow( std::string name, //JEventFolder* folder, - JEventLevel m_parent_level, JMailbox* child_in, JEventPool* child_out, JMailbox* parent_out) diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index 2242f04a7..5459c070a 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -318,7 +318,7 @@ class JTopologyBuilder : public JService { unfold_arrow->set_logger(m_arrow_logger); map_arrow->attach(unfold_arrow); - auto *fold_arrow = new JFoldArrow("fold"+ss.str(), current_level, nullptr, pool_at_level, nullptr); + auto *fold_arrow = new JFoldArrow("fold"+ss.str(), nullptr, pool_at_level, nullptr); // TODO: Support user-provided folders m_topology->arrows.push_back(fold_arrow); fold_arrow->set_chunksize(m_event_source_chunksize); From 86cc2a8f2c594a95c3b4e857c6117154482ae8af Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 29 Feb 2024 14:30:53 -0500 Subject: [PATCH 25/34] Rough cut of Timeslice test case --- src/libraries/JANA/CMakeLists.txt | 2 + src/libraries/JANA/Engine/JTopologyBuilder.h | 7 +- .../unit_tests/MultiLevelTopologyTests.cc | 24 +++ .../unit_tests/MultiLevelTopologyTests.h | 156 ++++++++++++++++++ 4 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 src/programs/unit_tests/MultiLevelTopologyTests.h diff --git a/src/libraries/JANA/CMakeLists.txt b/src/libraries/JANA/CMakeLists.txt index 668c2673a..2189cccd3 100644 --- a/src/libraries/JANA/CMakeLists.txt +++ b/src/libraries/JANA/CMakeLists.txt @@ -246,6 +246,7 @@ file(GLOB jana_calibs_headers "Calibrations/*.h*") file(GLOB jana_cli_headers "CLI/*.h*") file(GLOB jana_compat_headers "Compatibility/*.h*") file(GLOB jana_podio_headers "Podio/*.h*") +file(GLOB jana_omni_headers "Omni/*.h*") install(FILES ${jana_headers} DESTINATION include/JANA) install(FILES ${jana_engine_headers} DESTINATION include/JANA/Engine) @@ -256,6 +257,7 @@ install(FILES ${jana_utils_headers} DESTINATION include/JANA/Utils) install(FILES ${jana_calibs_headers} DESTINATION include/JANA/Calibrations) install(FILES ${jana_cli_headers} DESTINATION include/JANA/CLI) install(FILES ${jana_compat_headers} DESTINATION include/JANA/Compatibility) +install(FILES ${jana_omni_headers} DESTINATION include/JANA/Omni) if (${USE_PODIO}) install(FILES ${jana_podio_headers} DESTINATION include/JANA/Podio) diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index 5459c070a..c3c5253f7 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -312,13 +312,16 @@ class JTopologyBuilder : public JService { map_arrow->set_logger(m_arrow_logger); src_arrow->attach(map_arrow); - auto *unfold_arrow = new JUnfoldArrow("unfold"+ss.str(), unfolders_at_level[0], q2, pool_at_level, nullptr); + // TODO: We are using q2 temporarily knowing that it will be overwritten in attach_lower_level. + // It would be better to rejigger how we validate PlaceRefs and accept empty placerefs/fewer ctor args + auto *unfold_arrow = new JUnfoldArrow("unfold"+ss.str(), unfolders_at_level[0], q2, pool_at_level, q2); m_topology->arrows.push_back(unfold_arrow); unfold_arrow->set_chunksize(m_event_source_chunksize); unfold_arrow->set_logger(m_arrow_logger); map_arrow->attach(unfold_arrow); - auto *fold_arrow = new JFoldArrow("fold"+ss.str(), nullptr, pool_at_level, nullptr); + // child_in, child_out, parent_out + auto *fold_arrow = new JFoldArrow("fold"+ss.str(), q2, pool_at_level, q2); // TODO: Support user-provided folders m_topology->arrows.push_back(fold_arrow); fold_arrow->set_chunksize(m_event_source_chunksize); diff --git a/src/programs/unit_tests/MultiLevelTopologyTests.cc b/src/programs/unit_tests/MultiLevelTopologyTests.cc index 8eafd73d1..3e1878b33 100644 --- a/src/programs/unit_tests/MultiLevelTopologyTests.cc +++ b/src/programs/unit_tests/MultiLevelTopologyTests.cc @@ -1,6 +1,7 @@ #include #include #include +#include "MultiLevelTopologyTests.h" enum class Level { None, Timeslice, Event, Subevent }; @@ -438,5 +439,28 @@ TEST_CASE("MultiLevelTopologyBuilderTests") { +TEST_CASE("TimeslicesTests") { + + auto parms = new JParameterManager; + parms->SetParameter("log:trace", "JScheduler,JArrow,JArrowProcessingController"); + parms->SetParameter("jana:nevents", "5"); + JApplication app(parms); + + app.Add(new MyTimesliceSource("Dummy", &app)); + app.Add(new MyTimesliceUnfolder); + app.Add(new MyEventProcessor); + app.Add(new JFactoryGeneratorT); + app.Add(new JFactoryGeneratorT); + app.SetTicker(true); + app.Run(); +} + + +} // namespace timeslice_tests +} // namespce jana + + + + diff --git a/src/programs/unit_tests/MultiLevelTopologyTests.h b/src/programs/unit_tests/MultiLevelTopologyTests.h new file mode 100644 index 000000000..64cfa6c07 --- /dev/null +++ b/src/programs/unit_tests/MultiLevelTopologyTests.h @@ -0,0 +1,156 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace jana { +namespace timeslice_tests { + + +struct MyHit : public JObject { + int hit_id; + int energy, x, y; +}; + +struct MyCluster : public JObject { + int cluster_id; + int energy, x, y; + std::vector hits; +}; + + +struct MyTimesliceSource : public JEventSource { + + MyTimesliceSource(std::string source_name, JApplication *app) : JEventSource(source_name, app) { + SetLevel(JEventLevel::Timeslice); + } + + static std::string GetDescription() { return "MyTimesliceSource"; } + std::string GetType(void) const override { return JTypeInfo::demangle(); } + void Open() override { } + + void GetEvent(std::shared_ptr event) override { + // TODO: Insert something + jout << "MyTimesliceSource: Emitting " << event->GetEventNumber() << jendl; + } +}; + + +struct MyTimesliceUnfolder : public JEventUnfolder { + + std::atomic_int init_called_count {0}; + mutable std::atomic_int preprocess_called_count {0}; + std::atomic_int unfold_called_count {0}; + std::atomic_int finish_called_count {0}; + + MyTimesliceUnfolder() { + SetParentLevel(JEventLevel::Timeslice); + SetChildLevel(JEventLevel::Event); + } + + virtual void Init() { + init_called_count++; + }; + + virtual void Preprocess(const JEvent& parent) const { + preprocess_called_count++; + // TODO: Are we going to need an omni unfolder? + // TODO: Call protocluster factory + }; + + virtual Result Unfold(const JEvent& parent, JEvent& child, int item) { + unfold_called_count++; + // TODO: + if (child.GetEventNumber() % 3 == 0) { + // TODO: Insert protocluster into child + return Result::Finished; + } + return Result::KeepGoing; + } + + virtual void Finish() { + finish_called_count++; + }; + +}; + +struct MyEventProcessor : public JEventProcessor { + + std::atomic_int init_called_count {0}; + std::atomic_int process_called_count {0}; + std::atomic_int finish_called_count {0}; + + void Init() override { + init_called_count++; + } + + void Process(const std::shared_ptr& event) override { + process_called_count++; + // TODO: Trigger cluster factory + // TODO: Validate that the clusters make sense + jout << "MyEventProcessor: Processing " << event->GetEventNumber() << jendl; + REQUIRE(init_called_count == 1); + REQUIRE(finish_called_count == 0); + } + + void Finish() override { + finish_called_count += 1; + } + +}; + + +struct MyProtoClusterFactory : public JFactoryT { + + int init_call_count = 0; + int change_run_call_count = 0; + int process_call_count = 0; + + MyProtoClusterFactory() { + SetLevel(JEventLevel::Timeslice); + } + + void Init() override { + ++init_call_count; + } + + void ChangeRun(const std::shared_ptr&) override { + ++change_run_call_count; + } + + void Process(const std::shared_ptr&) override { + ++process_call_count; + Insert(new MyCluster); + // TODO: Obtain timeslice-level hits + } +}; + + +struct MyClusterFactory : public JFactoryT { + + int init_call_count = 0; + int change_run_call_count = 0; + int process_call_count = 0; + + MyClusterFactory() { + SetLevel(JEventLevel::Event); + } + + void Init() override { + ++init_call_count; + } + + void ChangeRun(const std::shared_ptr&) override { + ++change_run_call_count; + } + + void Process(const std::shared_ptr&) override { + ++process_call_count; + Insert(new MyCluster); + // TODO: Obtain timeslice-level protoclusters + } +}; + + From 7b5801926ba5278f188e3964efdcc54c2a703d2b Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 29 Feb 2024 17:19:00 -0500 Subject: [PATCH 26/34] JTopologyBuilder: Build everything before calling configurer --- src/libraries/JANA/Engine/JTopologyBuilder.h | 24 +++++++++----------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index c3c5253f7..df1063d2a 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -55,27 +55,25 @@ class JTopologyBuilder : public JService { m_configure_topology = std::move(configure_fn); } - inline std::shared_ptr get() { - return m_topology; - } - inline std::shared_ptr get_or_create() { - if (m_topology != nullptr) { - return m_topology; - } - else if (m_configure_topology) { - m_topology = m_configure_topology(create_empty()); - return m_topology; - } - else { + if (m_topology == nullptr) { m_topology = std::make_shared(); m_topology->component_manager = m_components; // Ensure the lifespan of the component manager exceeds that of the topology m_topology->mapping.initialize(static_cast(m_affinity), static_cast(m_locality)); + m_topology->event_pool = new JEventPool(m_components, + m_event_pool_size, + m_location_count, + m_limit_total_events_in_flight); + m_topology->event_pool->init(); attach_top_level(JEventLevel::Run); - return m_topology; + + if (m_configure_topology) { + m_topology = m_configure_topology(m_topology); + } } + return m_topology; } From 1d381b00842d50d7e72ec952e572285827fd4e2a Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Thu, 29 Feb 2024 22:46:39 -0500 Subject: [PATCH 27/34] TopologyBuilder fix --- src/libraries/JANA/Engine/JArrow.h | 1 + src/libraries/JANA/Engine/JFoldArrow.h | 26 ++++++ src/libraries/JANA/Engine/JTopologyBuilder.h | 87 ++++++++++++++++++-- src/libraries/JANA/Engine/JUnfoldArrow.h | 5 ++ src/libraries/JANA/Utils/JTablePrinter.cc | 5 ++ src/libraries/JANA/Utils/JTablePrinter.h | 1 + 6 files changed, 117 insertions(+), 8 deletions(-) diff --git a/src/libraries/JANA/Engine/JArrow.h b/src/libraries/JANA/Engine/JArrow.h index d0d96a51a..7c8fee8e9 100644 --- a/src/libraries/JANA/Engine/JArrow.h +++ b/src/libraries/JANA/Engine/JArrow.h @@ -39,6 +39,7 @@ class JArrow { friend class JScheduler; std::vector m_listeners; // Downstream Arrows + friend class JTopologyBuilder; std::vector m_places; // Will eventually supplant m_listeners, m_chunksize protected: diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 2d28bc2f2..1dd8111ca 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -34,6 +34,21 @@ class JFoldArrow : public JArrow { { } + JFoldArrow( + std::string name, + //JEventFolder* folder, + JMailbox* child_in, + JEventPool* child_out, + JEventPool* parent_out) + + : JArrow(std::move(name), false, false, false), + // m_folder(folder), + m_child_in(this, child_in, true, 1, 1), + m_child_out(this, child_out, false, 1, 1), + m_parent_out(this, parent_out, false, 1, 1) + { + } + void attach_child_in(JMailbox* child_in) { m_child_in.place_ref = child_in; m_child_in.is_queue = true; @@ -44,6 +59,17 @@ class JFoldArrow : public JArrow { m_child_out.is_queue = true; } + void attach_child_out(JEventPool* child_out) { + m_child_out.place_ref = child_out; + m_child_out.is_queue = false; + } + + void attach_parent_out(JEventPool* parent_out) { + m_parent_out.place_ref = parent_out; + m_parent_out.is_queue = false; + } + + void attach_parent_out(JMailbox* parent_out) { m_parent_out.place_ref = parent_out; m_parent_out.is_queue = true; diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index df1063d2a..b0e42015b 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -12,6 +12,7 @@ #include "JEventMapArrow.h" #include "JUnfoldArrow.h" #include "JFoldArrow.h" +#include #include @@ -40,6 +41,69 @@ class JTopologyBuilder : public JService { ~JTopologyBuilder() override = default; + std::string print_topology() { + JTablePrinter t; + t.AddColumn("Arrow name", JTablePrinter::Justify::Left, 0); + t.AddColumn("Arrow type", JTablePrinter::Justify::Left, 0); + t.AddColumn("Direction", JTablePrinter::Justify::Left, 0); + t.AddColumn("Place type", JTablePrinter::Justify::Left, 0); + t.AddColumn("ID", JTablePrinter::Justify::Left, 0); + + // Build index lookup for queues + int i = 0; + std::map lookup; + for (JQueue* queue : m_topology->queues) { + lookup[queue] = i; + i += 1; + } + // Build index lookup for pools + for (JPoolBase* pool : m_topology->pools) { + lookup[pool] = i; + i += 1; + } + // Build table + + bool show_row = true; + + for (JArrow* arrow : m_topology->arrows) { + + show_row = true; + for (PlaceRefBase* place : arrow->m_places) { + if (show_row) { + t | arrow->get_name(); + if (dynamic_cast(arrow) != nullptr) { + t | "JEventSourceArrow"; + } + else if (dynamic_cast(arrow) != nullptr) { + t | "JEventProcessorArrow"; + + } + else if (dynamic_cast(arrow) != nullptr) { + t | "JEventMapArrow"; + } + else if (dynamic_cast(arrow) != nullptr) { + t | "JUnfoldArrow"; + } + else if (dynamic_cast(arrow) != nullptr) { + t | "JFoldArrow"; + } + else { + t | "Unknown"; + } + show_row = false; + } + else { + t | "" | ""; + } + + t | ((place->is_input) ? "Input ": "Output"); + t | ((place->is_queue) ? "Queue ": "Pool"); + t | lookup[place->place_ref]; + } + } + return t.Render(); + } + /// set allows the user to specify a topology directly. Note that this needs to be set before JApplication::Initialize /// gets called, which means that you won't be able to include components loaded from plugins. You probably want to use /// JTopologyBuilder::set_configure_fn instead, which does give you that access. @@ -68,9 +132,11 @@ class JTopologyBuilder : public JService { m_limit_total_events_in_flight); m_topology->event_pool->init(); attach_top_level(JEventLevel::Run); + LOG_DEBUG(m_arrow_logger) << "Arrow topology is:\n" << print_topology() << LOG_END; if (m_configure_topology) { m_topology = m_configure_topology(m_topology); + LOG_DEBUG(m_arrow_logger) << "Found custom topology configurator! Modified arrow topology is: \n" << print_topology() << LOG_END; } } return m_topology; @@ -189,7 +255,7 @@ class JTopologyBuilder : public JService { auto q2 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); m_topology->queues.push_back(q2); - auto* proc_arrow = new JEventProcessorArrow("processors"+ss.str(), q1, q2, nullptr); + auto* proc_arrow = new JEventProcessorArrow(ss.str()+"Proc", q1, q2, nullptr); m_topology->arrows.push_back(proc_arrow); proc_arrow->set_chunksize(m_event_processor_chunksize); proc_arrow->set_logger(m_arrow_logger); @@ -198,8 +264,10 @@ class JTopologyBuilder : public JService { proc_arrow->add_processor(proc); } + parent_unfolder->attach_child_in(pool); parent_unfolder->attach_child_out(q1); parent_folder->attach_child_in(q2); + parent_folder->attach_child_out(pool); parent_unfolder->attach(proc_arrow); proc_arrow->attach(parent_folder); } @@ -299,12 +367,12 @@ class JTopologyBuilder : public JService { m_topology->queues.push_back(q1); m_topology->queues.push_back(q2); - auto *src_arrow = new JEventSourceArrow("sources"+ss.str(), sources_at_level, q1, pool_at_level); + auto *src_arrow = new JEventSourceArrow(ss.str()+"Src", sources_at_level, q1, pool_at_level); m_topology->arrows.push_back(src_arrow); src_arrow->set_chunksize(m_event_source_chunksize); src_arrow->set_logger(m_arrow_logger); - auto *map_arrow = new JEventMapArrow("maps"+ss.str(), q1, q2);; + auto *map_arrow = new JEventMapArrow(ss.str()+"Map", q1, q2);; m_topology->arrows.push_back(map_arrow); map_arrow->set_chunksize(m_event_source_chunksize); map_arrow->set_logger(m_arrow_logger); @@ -312,25 +380,29 @@ class JTopologyBuilder : public JService { // TODO: We are using q2 temporarily knowing that it will be overwritten in attach_lower_level. // It would be better to rejigger how we validate PlaceRefs and accept empty placerefs/fewer ctor args - auto *unfold_arrow = new JUnfoldArrow("unfold"+ss.str(), unfolders_at_level[0], q2, pool_at_level, q2); + auto *unfold_arrow = new JUnfoldArrow(ss.str()+"Unfold", unfolders_at_level[0], q2, pool_at_level, q2); m_topology->arrows.push_back(unfold_arrow); unfold_arrow->set_chunksize(m_event_source_chunksize); unfold_arrow->set_logger(m_arrow_logger); map_arrow->attach(unfold_arrow); // child_in, child_out, parent_out - auto *fold_arrow = new JFoldArrow("fold"+ss.str(), q2, pool_at_level, q2); + auto *fold_arrow = new JFoldArrow(ss.str()+"Fold", q2, pool_at_level, pool_at_level); // TODO: Support user-provided folders - m_topology->arrows.push_back(fold_arrow); fold_arrow->set_chunksize(m_event_source_chunksize); fold_arrow->set_logger(m_arrow_logger); + attach_lower_level(unfolders_at_level[0]->GetChildLevel(), unfold_arrow, fold_arrow); + + // Push fold arrow back _after_ attach_lower_level so that arrows can be iterated over in order + m_topology->arrows.push_back(fold_arrow); + if (procs_at_level.size() != 0) { auto q3 = new EventQueue(m_event_queue_threshold, m_topology->mapping.get_loc_count(), m_enable_stealing); m_topology->queues.push_back(q3); - auto* proc_arrow = new JEventProcessorArrow("processors"+ss.str(), q3, nullptr, pool_at_level); + auto* proc_arrow = new JEventProcessorArrow(ss.str()+"Proc", q3, nullptr, pool_at_level); m_topology->arrows.push_back(proc_arrow); proc_arrow->set_chunksize(m_event_processor_chunksize); proc_arrow->set_logger(m_arrow_logger); @@ -342,7 +414,6 @@ class JTopologyBuilder : public JService { fold_arrow->attach_parent_out(q3); fold_arrow->attach(proc_arrow); } - attach_lower_level(unfolders_at_level[0]->GetChildLevel(), unfold_arrow, fold_arrow); } } diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h index dde02d40d..059418f15 100644 --- a/src/libraries/JANA/Engine/JUnfoldArrow.h +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -41,6 +41,11 @@ class JUnfoldArrow : public JArrow { m_parent_in.is_queue = true; } + void attach_child_in(JEventPool* child_in) { + m_child_in.place_ref = child_in; + m_child_in.is_queue = false; + } + void attach_child_in(JMailbox* child_in) { m_child_in.place_ref = child_in; m_child_in.is_queue = true; diff --git a/src/libraries/JANA/Utils/JTablePrinter.cc b/src/libraries/JANA/Utils/JTablePrinter.cc index 34e25c4ad..f6f590d7d 100644 --- a/src/libraries/JANA/Utils/JTablePrinter.cc +++ b/src/libraries/JANA/Utils/JTablePrinter.cc @@ -109,6 +109,11 @@ void JTablePrinter::FormatLine(std::ostream& os, std::string contents, int max_w for (int i=0; i SplitContents(std::string contents, size_t max_width); static std::vector SplitContentsByNewlines(std::string contents); static std::vector SplitContentsBySpaces(std::string contents, size_t max_width); From 0ecdb5ab5015c348e840a256cd244813820c0477 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Fri, 1 Mar 2024 15:51:31 -0500 Subject: [PATCH 28/34] Move JEvent parent release logic into JEvent --- src/libraries/JANA/Engine/JFoldArrow.h | 41 ++++++++++++++++++-- src/libraries/JANA/Engine/JTopologyBuilder.h | 2 +- src/libraries/JANA/Engine/JUnfoldArrow.h | 18 +++++++-- src/libraries/JANA/JEvent.h | 41 ++++++++++++++++++-- 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 1dd8111ca..5f3864b51 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -13,6 +13,9 @@ class JFoldArrow : public JArrow { // TODO: Support user-provided folders // JEventFolder* m_folder = nullptr; + + JEventLevel m_parent_level; + JEventLevel m_child_level; PlaceRef m_child_in; PlaceRef m_child_out; @@ -22,12 +25,16 @@ class JFoldArrow : public JArrow { JFoldArrow( std::string name, //JEventFolder* folder, + JEventLevel parent_level, + JEventLevel child_level, JMailbox* child_in, JEventPool* child_out, JMailbox* parent_out) : JArrow(std::move(name), false, false, false), // m_folder(folder), + m_parent_level(parent_level), + m_child_level(child_level), m_child_in(this, child_in, true, 1, 1), m_child_out(this, child_out, false, 1, 1), m_parent_out(this, parent_out, false, 1, 1) @@ -37,12 +44,35 @@ class JFoldArrow : public JArrow { JFoldArrow( std::string name, //JEventFolder* folder, + JEventLevel parent_level, + JEventLevel child_level, + JMailbox* child_in, + JMailbox* child_out, + JMailbox* parent_out) + + : JArrow(std::move(name), false, false, false), + // m_folder(folder), + m_parent_level(parent_level), + m_child_level(child_level), + m_child_in(this, child_in, true, 1, 1), + m_child_out(this, child_out, false, 1, 1), + m_parent_out(this, parent_out, false, 1, 1) + { + } + + JFoldArrow( + std::string name, + //JEventFolder* folder, + JEventLevel parent_level, + JEventLevel child_level, JMailbox* child_in, JEventPool* child_out, JEventPool* parent_out) : JArrow(std::move(name), false, false, false), // m_folder(folder), + m_parent_level(parent_level), + m_child_level(child_level), m_child_in(this, child_in, true, 1, 1), m_child_out(this, child_out, false, 1, 1), m_parent_out(this, parent_out, false, 1, 1) @@ -138,16 +168,19 @@ class JFoldArrow : public JArrow { auto child = child_in_data.items[0]; child_in_data.items[0] = nullptr; child_in_data.item_count = 0; + if (child->get()->GetLevel() != m_child_level) { + throw JException("JFoldArrow received a child with the wrong event level"); + } - std::shared_ptr* parent = child->get()->mParents.back().second; - child->get()->mParents.pop_back(); - size_t remaining_siblings = parent->get()->mReferenceCount.fetch_sub(1); + // TODO: Call folders here + auto* parent = child->get()->ReleaseParent(m_parent_level); + // Put child on the output queue child_out_data.items[0] = child; child_out_data.item_count = 1; // Only recycle the parent once the reference count hits zero - if (remaining_siblings == 1) { + if (parent != nullptr) { parent_out_data.items[0] = parent; parent_out_data.item_count = 1; } diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index b0e42015b..4c103f90f 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -387,7 +387,7 @@ class JTopologyBuilder : public JService { map_arrow->attach(unfold_arrow); // child_in, child_out, parent_out - auto *fold_arrow = new JFoldArrow(ss.str()+"Fold", q2, pool_at_level, pool_at_level); + auto *fold_arrow = new JFoldArrow(ss.str()+"Fold", current_level, unfolders_at_level[0]->GetChildLevel(), q2, pool_at_level, pool_at_level); // TODO: Support user-provided folders fold_arrow->set_chunksize(m_event_source_chunksize); fold_arrow->set_logger(m_arrow_logger); diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h index 059418f15..3c1102fb4 100644 --- a/src/libraries/JANA/Engine/JUnfoldArrow.h +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -115,17 +115,29 @@ class JUnfoldArrow : public JArrow { auto child = child_in_data.items[0]; child_in_data.items[0] = nullptr; child_in_data.item_count = 0; - // TODO: Assert that the input levels match what the unfolder expects + if (m_parent_event == nullptr) { + throw JException("Attempting to unfold without a valid parent event"); + } + if (m_parent_event->get()->GetLevel() != m_unfolder->GetLevel()) { + throw JException("JUnfolder: Expected parent with level %s, got %s", m_unfolder->GetLevel(), m_parent_event->get()->GetLevel()); + } + if (child->get()->GetLevel() != m_unfolder->GetChildLevel()) { + throw JException("JUnfolder: Expected child with level %s, got %s", m_unfolder->GetChildLevel(), child->get()->GetLevel()); + } auto status = m_unfolder->DoUnfold(*(m_parent_event->get()), *(child->get())); + // Join always succeeds (for now) - (*child)->mParents.push_back({m_parent_event->get()->GetLevel(), m_parent_event}); - m_parent_event->get()->mReferenceCount.fetch_add(1); + child->get()->SetParent(m_parent_event); + + LOG_DEBUG(m_logger) << "Unfold succeeded: Parent event = " << m_parent_event->get()->GetEventNumber() << ", child event = " << child->get()->GetEventNumber() << LOG_END; // TODO: We'll need something more complicated for the streaming join case if (status == JEventUnfolder::Result::Finished) { + LOG_DEBUG(m_logger) << "Unfold finished with parent event = " << m_parent_event->get()->GetEventNumber() << LOG_END; m_ready_to_fetch_parent = true; + m_parent_event->get()->Release(); m_parent_event = nullptr; m_parent_in.min_item_count = 1; m_parent_in.max_item_count = 1; diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 86b190ddf..39b8bdb87 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -139,6 +139,43 @@ class JEvent : public JResettable, public std::enable_shared_from_this throw JException("Unable to find parent at level %s", level); } + void SetParent(std::shared_ptr* parent) { + JEventLevel level = parent->get()->GetLevel(); + for (const auto& pair : mParents) { + if (pair.first == level) throw JException("Event already has a parent at level %s", parent->get()->GetLevel()); + } + mParents.push_back({level, parent}); + parent->get()->mReferenceCount.fetch_add(1); + } + + std::shared_ptr* ReleaseParent(JEventLevel level) { + if (mParents.size() == 0) { + throw JException("ReleaseParent failed: child has no parents!"); + } + auto pair = mParents.back(); + if (pair.first != level) { + throw JException("JEvent::ReleaseParent called out of level order"); + } + mParents.pop_back(); + auto remaining_refs = pair.second->get()->mReferenceCount.fetch_sub(1); + if (remaining_refs < 0) { + throw JException("Parent refcount has gone negative!"); + } + if (remaining_refs == 0) { + return pair.second; + // Parent is no longer shared. Transfer back to arrow + } + else { + return nullptr; // Parent is still shared by other children + } + } + + void Release() { + auto remaining_refs = mReferenceCount.fetch_sub(1); + if (remaining_refs < 0) { + throw JException("JEvent's own refcount has gone negative!"); + } + } private: @@ -154,10 +191,8 @@ class JEvent : public JResettable, public std::enable_shared_from_this bool mIsBarrierEvent = false; // Hierarchical stuff - friend class JUnfoldArrow; - friend class JFoldArrow; std::vector*>> mParents; - std::atomic_size_t mReferenceCount = 0; + std::atomic_size_t mReferenceCount {1}; From 111703dacca94859048c64db35574d852cd20c50 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Fri, 1 Mar 2024 17:56:19 -0500 Subject: [PATCH 29/34] Test JFoldArrow --- src/libraries/JANA/Engine/JFoldArrow.h | 6 +- src/libraries/JANA/JEvent.h | 6 +- src/libraries/JANA/Utils/JEventPool.h | 3 +- src/programs/unit_tests/UnfoldTests.cc | 120 ++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 9 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index 5f3864b51..fe877357b 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -130,15 +130,15 @@ class JFoldArrow : public JArrow { bool try_pull_all(Data& ci, Data& co, Data& po) { bool success; - success = m_child_in.pull(co); + success = m_child_in.pull(ci); if (! success) { return false; } - success = m_child_out.pull(po); + success = m_child_out.pull(co); if (! success) { return false; } - success = m_parent_out.pull(ci); + success = m_parent_out.pull(po); if (! success) { return false; } diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 39b8bdb87..456ba090c 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -154,14 +154,14 @@ class JEvent : public JResettable, public std::enable_shared_from_this } auto pair = mParents.back(); if (pair.first != level) { - throw JException("JEvent::ReleaseParent called out of level order"); + throw JException("JEvent::ReleaseParent called out of level order: Caller expected %d, but parent was actually %d", level, pair.first); } mParents.pop_back(); auto remaining_refs = pair.second->get()->mReferenceCount.fetch_sub(1); - if (remaining_refs < 0) { + if (remaining_refs < 1) { // Remember, this was fetched _before_ the last subtraction throw JException("Parent refcount has gone negative!"); } - if (remaining_refs == 0) { + if (remaining_refs == 1) { return pair.second; // Parent is no longer shared. Transfer back to arrow } diff --git a/src/libraries/JANA/Utils/JEventPool.h b/src/libraries/JANA/Utils/JEventPool.h index 4fdc47abc..4ea8fcf4a 100644 --- a/src/libraries/JANA/Utils/JEventPool.h +++ b/src/libraries/JANA/Utils/JEventPool.h @@ -29,9 +29,8 @@ class JEventPool : public JPool> { void configure_item(std::shared_ptr* item) override { (*item) = std::make_shared(); - (*item)->SetLevel(m_level); m_component_manager->configure_event(**item); - + item->get()->SetLevel(m_level); // This needs to happen _after_ configure_event } void release_item(std::shared_ptr* item) override { diff --git a/src/programs/unit_tests/UnfoldTests.cc b/src/programs/unit_tests/UnfoldTests.cc index 32fc07061..7a6006a4a 100644 --- a/src/programs/unit_tests/UnfoldTests.cc +++ b/src/programs/unit_tests/UnfoldTests.cc @@ -1,6 +1,7 @@ #include #include +#include namespace jana { namespace unfoldtests { @@ -41,7 +42,7 @@ TEST_CASE("UnfoldTests_Basic") { JEventPool parent_pool {jcm, 5, 1, true, JEventLevel::Timeslice}; // size=5, locations=1, limit_total_events_in_flight=true JEventPool child_pool {jcm, 5, 1, true, JEventLevel::Event}; - JMailbox parent_queue {3}; // size=2 + JMailbox parent_queue {3}; // size JMailbox child_queue {3}; parent_pool.init(); @@ -76,6 +77,123 @@ TEST_CASE("UnfoldTests_Basic") { } +TEST_CASE("FoldArrowTests") { + + JApplication app; + auto jcm = app.GetService(); + + // We only use these to obtain preconfigured JEvents + JEventPool parent_pool {jcm, 5, 1, true, JEventLevel::Timeslice}; // size=5, locations=1, limit_total_events_in_flight=true + JEventPool child_pool {jcm, 5, 1, true, JEventLevel::Event}; + parent_pool.init(); + child_pool.init(); + + + // We set up our test cases by putting events on these queues + JMailbox*> child_in; + JMailbox*> child_out; + JMailbox*> parent_out; + + JFoldArrow arrow("sut", JEventLevel::Timeslice, JEventLevel::Event, &child_in, &child_out, &parent_out); + JArrowMetrics metrics; + arrow.initialize(); + + SECTION("One-to-one relationship between timeslices and events") { + + auto ts1 = parent_pool.get(); + (*ts1)->SetEventNumber(17); + REQUIRE(ts1->get()->GetLevel() == JEventLevel::Timeslice); + + auto ts2 = parent_pool.get(); + (*ts2)->SetEventNumber(28); + + auto evt1 = child_pool.get(); + (*evt1)->SetEventNumber(111); + + auto evt2 = child_pool.get(); + (*evt2)->SetEventNumber(112); + + + evt1->get()->SetParent(ts1); + ts1->get()->Release(); // One-to-one + child_in.try_push(&evt1, 1, 0); + + evt2->get()->SetParent(ts2); + ts2->get()->Release(); // One-to-one + child_in.try_push(&evt2, 1, 0); + + arrow.execute(metrics, 0); + + REQUIRE(child_in.size() == 1); + REQUIRE(child_out.size() == 1); + REQUIRE(parent_out.size() == 1); + + } + + + SECTION("One-to-two relationship between timeslices and events") { + + auto ts1 = parent_pool.get(); + (*ts1)->SetEventNumber(17); + REQUIRE(ts1->get()->GetLevel() == JEventLevel::Timeslice); + + auto ts2 = parent_pool.get(); + (*ts2)->SetEventNumber(28); + + auto evt1 = child_pool.get(); + (*evt1)->SetEventNumber(111); + + auto evt2 = child_pool.get(); + (*evt2)->SetEventNumber(112); + + auto evt3 = child_pool.get(); + (*evt3)->SetEventNumber(113); + + auto evt4 = child_pool.get(); + (*evt4)->SetEventNumber(114); + + + evt1->get()->SetParent(ts1); + evt2->get()->SetParent(ts1); + ts1->get()->Release(); // One-to-two + + evt3->get()->SetParent(ts2); + evt4->get()->SetParent(ts2); + ts2->get()->Release(); // One-to-two + + child_in.try_push(&evt1, 1, 0); + child_in.try_push(&evt2, 1, 0); + child_in.try_push(&evt3, 1, 0); + child_in.try_push(&evt4, 1, 0); + + arrow.execute(metrics, 0); + + REQUIRE(child_in.size() == 3); + REQUIRE(child_out.size() == 1); + REQUIRE(parent_out.size() == 0); + + arrow.execute(metrics, 0); + + REQUIRE(child_in.size() == 2); + REQUIRE(child_out.size() == 2); + REQUIRE(parent_out.size() == 1); + + arrow.execute(metrics, 0); + + REQUIRE(child_in.size() == 1); + REQUIRE(child_out.size() == 3); + REQUIRE(parent_out.size() == 1); + + arrow.execute(metrics, 0); + + REQUIRE(child_in.size() == 0); + REQUIRE(child_out.size() == 4); + REQUIRE(parent_out.size() == 2); + } + + +} + } // namespace arrowtests } // namespace jana From 482a22fc3826a1b41d680a157597ce5015aae24c Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Sun, 3 Mar 2024 01:37:48 -0500 Subject: [PATCH 30/34] Small UnfoldTests fix --- src/programs/unit_tests/UnfoldTests.cc | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/programs/unit_tests/UnfoldTests.cc b/src/programs/unit_tests/UnfoldTests.cc index 7a6006a4a..e8f9a4587 100644 --- a/src/programs/unit_tests/UnfoldTests.cc +++ b/src/programs/unit_tests/UnfoldTests.cc @@ -17,6 +17,11 @@ struct TestUnfolder : public JEventUnfolder { std::vector unfolded_child_nrs; std::vector unfolded_child_levels; + TestUnfolder() { + SetParentLevel(JEventLevel::Timeslice); + SetChildLevel(JEventLevel::Event); + } + void Preprocess(const JEvent& parent) const override { LOG << "Preprocessing " << parent.GetLevel() << " event " << parent.GetEventNumber() << LOG_END; preprocessed_event_nrs.push_back(parent.GetEventNumber()); @@ -48,16 +53,14 @@ TEST_CASE("UnfoldTests_Basic") { parent_pool.init(); child_pool.init(); - auto evt1 = parent_pool.get(); - (*evt1)->SetEventNumber(17); - (*evt1)->SetLevel(JEventLevel::Timeslice); + auto ts1 = parent_pool.get(); + (*ts1)->SetEventNumber(17); - auto evt2 = parent_pool.get(); - (*evt2)->SetEventNumber(28); - (*evt2)->SetLevel(JEventLevel::Timeslice); + auto ts2 = parent_pool.get(); + (*ts2)->SetEventNumber(28); - parent_queue.try_push(&evt1, 1); - parent_queue.try_push(&evt2, 1); + parent_queue.try_push(&ts1, 1); + parent_queue.try_push(&ts2, 1); TestUnfolder unfolder; JUnfoldArrow arrow("sut", &unfolder, &parent_queue, &child_pool, &child_queue); From ea6f6367db32003b15ffc666b7dea679c1682c2d Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Sun, 3 Mar 2024 00:37:00 -0500 Subject: [PATCH 31/34] Fix refcount<0 bug in TimeslicesTest --- src/libraries/JANA/Engine/JUnfoldArrow.h | 4 ++-- src/libraries/JANA/JEvent.h | 4 ++++ src/libraries/JANA/Utils/JEventPool.h | 1 + src/programs/unit_tests/MultiLevelTopologyTests.h | 6 +++++- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/libraries/JANA/Engine/JUnfoldArrow.h b/src/libraries/JANA/Engine/JUnfoldArrow.h index 3c1102fb4..212f6e01d 100644 --- a/src/libraries/JANA/Engine/JUnfoldArrow.h +++ b/src/libraries/JANA/Engine/JUnfoldArrow.h @@ -119,10 +119,10 @@ class JUnfoldArrow : public JArrow { throw JException("Attempting to unfold without a valid parent event"); } if (m_parent_event->get()->GetLevel() != m_unfolder->GetLevel()) { - throw JException("JUnfolder: Expected parent with level %s, got %s", m_unfolder->GetLevel(), m_parent_event->get()->GetLevel()); + throw JException("JUnfolder: Expected parent with level %d, got %d", m_unfolder->GetLevel(), m_parent_event->get()->GetLevel()); } if (child->get()->GetLevel() != m_unfolder->GetChildLevel()) { - throw JException("JUnfolder: Expected child with level %s, got %s", m_unfolder->GetChildLevel(), child->get()->GetLevel()); + throw JException("JUnfolder: Expected child with level %d, got %d", m_unfolder->GetChildLevel(), child->get()->GetLevel()); } auto status = m_unfolder->DoUnfold(*(m_parent_event->get()), *(child->get())); diff --git a/src/libraries/JANA/JEvent.h b/src/libraries/JANA/JEvent.h index 456ba090c..148dedb96 100644 --- a/src/libraries/JANA/JEvent.h +++ b/src/libraries/JANA/JEvent.h @@ -177,6 +177,10 @@ class JEvent : public JResettable, public std::enable_shared_from_this } } + void Reset() { + mReferenceCount = 1; + } + private: JApplication* mApplication = nullptr; diff --git a/src/libraries/JANA/Utils/JEventPool.h b/src/libraries/JANA/Utils/JEventPool.h index 4ea8fcf4a..15873619a 100644 --- a/src/libraries/JANA/Utils/JEventPool.h +++ b/src/libraries/JANA/Utils/JEventPool.h @@ -38,6 +38,7 @@ class JEventPool : public JPool> { (*item)->mFactorySet->Release(); (*item)->mInspector.Reset(); (*item)->GetJCallGraphRecorder()->Reset(); + (*item)->Reset(); } }; diff --git a/src/programs/unit_tests/MultiLevelTopologyTests.h b/src/programs/unit_tests/MultiLevelTopologyTests.h index 64cfa6c07..8fb4c8065 100644 --- a/src/programs/unit_tests/MultiLevelTopologyTests.h +++ b/src/programs/unit_tests/MultiLevelTopologyTests.h @@ -61,9 +61,13 @@ struct MyTimesliceUnfolder : public JEventUnfolder { }; virtual Result Unfold(const JEvent& parent, JEvent& child, int item) { + child.SetEventNumber(parent.GetEventNumber()*10 + item); + LOG << "Unfolding parent=" << parent.GetEventNumber() << ", child=" << child.GetEventNumber() << ", item=" << item << LOG_END; unfold_called_count++; // TODO: - if (child.GetEventNumber() % 3 == 0) { + + if (item == 3) { + jout << "Unfold found item 3, finishing join" << jendl; // TODO: Insert protocluster into child return Result::Finished; } From adf865e3f10ce7cef1380c0d1f54e18a0a3ce3b5 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Sun, 3 Mar 2024 01:08:16 -0500 Subject: [PATCH 32/34] Identify sinks correctly --- src/libraries/JANA/Engine/JArrow.h | 6 +++++- src/libraries/JANA/Engine/JEventMapArrow.cc | 2 +- src/libraries/JANA/Engine/JTopologyBuilder.h | 8 ++++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/libraries/JANA/Engine/JArrow.h b/src/libraries/JANA/Engine/JArrow.h index 7c8fee8e9..26cd1d0cc 100644 --- a/src/libraries/JANA/Engine/JArrow.h +++ b/src/libraries/JANA/Engine/JArrow.h @@ -29,7 +29,7 @@ class JArrow { const std::string m_name; // Used for human understanding const bool m_is_parallel; // Whether or not it is safe to parallelize const bool m_is_source; // Whether or not this arrow should activate/drain the topology - const bool m_is_sink; // Whether or not tnis arrow contributes to the final event count + bool m_is_sink; // Whether or not tnis arrow contributes to the final event count JArrowMetrics m_metrics; // Performance information accumulated over all workers mutable std::mutex m_arrow_mutex; // Protects access to arrow properties @@ -58,6 +58,10 @@ class JArrow { m_logger = logger; } + void set_is_sink(bool is_sink) { + m_is_sink = is_sink; + } + // TODO: Get rid of me void set_chunksize(size_t chunksize) { std::lock_guard lock(m_arrow_mutex); diff --git a/src/libraries/JANA/Engine/JEventMapArrow.cc b/src/libraries/JANA/Engine/JEventMapArrow.cc index eff60a4f6..ac6f869db 100644 --- a/src/libraries/JANA/Engine/JEventMapArrow.cc +++ b/src/libraries/JANA/Engine/JEventMapArrow.cc @@ -15,7 +15,7 @@ JEventMapArrow::JEventMapArrow(std::string name, : JPipelineArrow(std::move(name), true, false, - true, + false, input_queue, output_queue, nullptr) {} diff --git a/src/libraries/JANA/Engine/JTopologyBuilder.h b/src/libraries/JANA/Engine/JTopologyBuilder.h index 4c103f90f..d2cb0989d 100644 --- a/src/libraries/JANA/Engine/JTopologyBuilder.h +++ b/src/libraries/JANA/Engine/JTopologyBuilder.h @@ -203,7 +203,7 @@ class JTopologyBuilder : public JService { } - void attach_lower_level(JEventLevel current_level, JUnfoldArrow* parent_unfolder, JFoldArrow* parent_folder) { + void attach_lower_level(JEventLevel current_level, JUnfoldArrow* parent_unfolder, JFoldArrow* parent_folder, bool found_sink) { std::stringstream ss; ss << current_level; @@ -259,6 +259,9 @@ class JTopologyBuilder : public JService { m_topology->arrows.push_back(proc_arrow); proc_arrow->set_chunksize(m_event_processor_chunksize); proc_arrow->set_logger(m_arrow_logger); + if (found_sink) { + proc_arrow->set_is_sink(false); + } for (auto proc: procs_at_level) { proc_arrow->add_processor(proc); @@ -392,7 +395,8 @@ class JTopologyBuilder : public JService { fold_arrow->set_chunksize(m_event_source_chunksize); fold_arrow->set_logger(m_arrow_logger); - attach_lower_level(unfolders_at_level[0]->GetChildLevel(), unfold_arrow, fold_arrow); + bool found_sink = (procs_at_level.size() > 0); + attach_lower_level(unfolders_at_level[0]->GetChildLevel(), unfold_arrow, fold_arrow, found_sink); // Push fold arrow back _after_ attach_lower_level so that arrows can be iterated over in order m_topology->arrows.push_back(fold_arrow); From 085183e5189f9870b6cf66ebf64bc0bfb953d61f Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Sun, 3 Mar 2024 01:35:39 -0500 Subject: [PATCH 33/34] Fix JFoldArrow processed count --- src/libraries/JANA/Engine/JFoldArrow.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libraries/JANA/Engine/JFoldArrow.h b/src/libraries/JANA/Engine/JFoldArrow.h index fe877357b..43c360d17 100644 --- a/src/libraries/JANA/Engine/JFoldArrow.h +++ b/src/libraries/JANA/Engine/JFoldArrow.h @@ -146,10 +146,10 @@ class JFoldArrow : public JArrow { } size_t push_all(Data& ci, Data& co, Data& po) { - size_t message_count = 0; - message_count += m_child_in.push(ci); - message_count += m_child_out.push(co); - message_count += m_parent_out.push(po); + size_t message_count = co.item_count; + m_child_in.push(ci); + m_child_out.push(co); + m_parent_out.push(po); return message_count; } From 6787236c1c861a4bf08976277d07845989c52ad4 Mon Sep 17 00:00:00 2001 From: Nathan Brei Date: Wed, 13 Mar 2024 00:32:44 -0400 Subject: [PATCH 34/34] Flesh out TimesliceExample --- src/examples/TimesliceExample/CMakeLists.txt | 4 +- src/examples/TimesliceExample/MyDataModel.h | 18 +++ .../TimesliceExample/MyEventFactory.h | 39 +++++ .../TimesliceExample/MyEventProcessor.h | 35 ++++ .../TimesliceExample/MyTimesliceFactory.h | 37 +++++ .../TimesliceExample/MyTimesliceSource.h | 40 +++++ .../TimesliceExample/MyTimesliceUnfolder.h | 43 +++++ .../TimesliceExample/TimesliceExample.cc | 149 +++--------------- 8 files changed, 232 insertions(+), 133 deletions(-) create mode 100644 src/examples/TimesliceExample/MyDataModel.h create mode 100644 src/examples/TimesliceExample/MyEventFactory.h create mode 100644 src/examples/TimesliceExample/MyEventProcessor.h create mode 100644 src/examples/TimesliceExample/MyTimesliceFactory.h create mode 100644 src/examples/TimesliceExample/MyTimesliceSource.h create mode 100644 src/examples/TimesliceExample/MyTimesliceUnfolder.h diff --git a/src/examples/TimesliceExample/CMakeLists.txt b/src/examples/TimesliceExample/CMakeLists.txt index cb2fc59c0..54acd64ae 100644 --- a/src/examples/TimesliceExample/CMakeLists.txt +++ b/src/examples/TimesliceExample/CMakeLists.txt @@ -5,9 +5,9 @@ if (USE_PODIO) TimesliceExample.cc ) - add_executable(TimesliceExample ${TimesliceExample_SOURCES}) + add_library(TimesliceExample SHARED ${TimesliceExample_SOURCES}) target_link_libraries(TimesliceExample jana2 podio::podio PodioExampleDatamodel PodioExampleDatamodelDict podio::podioRootIO) - set_target_properties(TimesliceExample PROPERTIES PREFIX "" OUTPUT_NAME "TimesliceExample") + set_target_properties(TimesliceExample PROPERTIES PREFIX "" SUFFIX ".so" OUTPUT_NAME "TimesliceExample") install(TARGETS TimesliceExample DESTINATION programs) else() diff --git a/src/examples/TimesliceExample/MyDataModel.h b/src/examples/TimesliceExample/MyDataModel.h new file mode 100644 index 000000000..e2d710750 --- /dev/null +++ b/src/examples/TimesliceExample/MyDataModel.h @@ -0,0 +1,18 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +#include + +struct MyHit : public JObject { + int hit_id; + int energy, x, y; +}; + +struct MyCluster : public JObject { + int cluster_id; + int energy, x, y; + std::vector hits; +}; + diff --git a/src/examples/TimesliceExample/MyEventFactory.h b/src/examples/TimesliceExample/MyEventFactory.h new file mode 100644 index 000000000..37e883f4c --- /dev/null +++ b/src/examples/TimesliceExample/MyEventFactory.h @@ -0,0 +1,39 @@ + + + +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once +#include "MyDataModel.h" +#include +#include + + +struct MyClusterFactory : public JFactoryT { + + int init_call_count = 0; + int change_run_call_count = 0; + int process_call_count = 0; + + MyClusterFactory() { + SetLevel(JEventLevel::Event); + } + + void Init() override { + ++init_call_count; + } + + void ChangeRun(const std::shared_ptr&) override { + ++change_run_call_count; + } + + void Process(const std::shared_ptr& event) override { + ++process_call_count; + + auto protos = event->Get("protos"); + // TODO: Output something sensible + } +}; + + diff --git a/src/examples/TimesliceExample/MyEventProcessor.h b/src/examples/TimesliceExample/MyEventProcessor.h new file mode 100644 index 000000000..b49e99541 --- /dev/null +++ b/src/examples/TimesliceExample/MyEventProcessor.h @@ -0,0 +1,35 @@ + +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once +#include "MyDataModel.h" +#include + +struct ExampleEventProcessor : public JEventProcessor { + + std::mutex m_mutex; + + ExampleTimesliceProcessor() { + SetEventLevel(JEvent::Level::Event); + } + + void Process(const std::shared_ptr& event) { + + std::lock_guard guard(m_mutex); + + auto outputs = event->Get(); + // assert(outputs.size() == 4); + // assert(outputs[0]->z == 25.6f); + // assert(outputs[1]->z == 26.5f); + // assert(outputs[2]->z == 27.4f); + // assert(outputs[3]->z == 28.3f); + LOG << " Contents of event " << event->GetEventNumber() << LOG_END; + for (auto output : outputs) { + LOG << " " << output->evt << ":" << output->sub << " " << output->z << LOG_END; + } + LOG << " DONE with contents of event " << event->GetEventNumber() << LOG_END; + } +}; + + diff --git a/src/examples/TimesliceExample/MyTimesliceFactory.h b/src/examples/TimesliceExample/MyTimesliceFactory.h new file mode 100644 index 000000000..64dc4a026 --- /dev/null +++ b/src/examples/TimesliceExample/MyTimesliceFactory.h @@ -0,0 +1,37 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once +#include "MyDataModel.h" + +#include +#include + + +struct MyProtoClusterFactory : public JFactoryT { + + int init_call_count = 0; + int change_run_call_count = 0; + int process_call_count = 0; + + MyProtoClusterFactory() { + SetLevel(JEventLevel::Timeslice); + } + + void Init() override { + ++init_call_count; + } + + void ChangeRun(const std::shared_ptr&) override { + ++change_run_call_count; + } + + void Process(const std::shared_ptr& event) override { + ++process_call_count; + + auto protos = event->Get("protos"); + // TODO: Output something sensible + } +}; + + diff --git a/src/examples/TimesliceExample/MyTimesliceSource.h b/src/examples/TimesliceExample/MyTimesliceSource.h new file mode 100644 index 000000000..b0aa1b69d --- /dev/null +++ b/src/examples/TimesliceExample/MyTimesliceSource.h @@ -0,0 +1,40 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once +#include "MyDataModel.h" +#include + + +struct MyTimesliceSource : public JEventSource { + + MyTimesliceSource(std::string source_name, JApplication *app) : JEventSource(source_name, app) { + SetLevel(JEventLevel::Timeslice); + } + + static std::string GetDescription() { return "MyTimesliceSource"; } + + std::string GetType(void) const override { return JTypeInfo::demangle(); } + + void Open() override { } + + void GetEvent(std::shared_ptr event) override { + + auto evt = event->GetEventNumber(); + std::vector inputs; + inputs.push_back(new MyInput(22,3.6,evt,0)); + inputs.push_back(new MyInput(23,3.5,evt,1)); + inputs.push_back(new MyInput(24,3.4,evt,2)); + inputs.push_back(new MyInput(25,3.3,evt,3)); + inputs.push_back(new MyInput(26,3.2,evt,4)); + event->Insert(inputs); + + auto hits = std::make_unique(); + hits.push_back(ExampleHit(22)); + hits.push_back(ExampleHit(23)); + hits.push_back(ExampleHit(24)); + event->InsertCollection(hits); + + jout << "MyTimesliceSource: Emitting " << event->GetEventNumber() << jendl; + } +}; diff --git a/src/examples/TimesliceExample/MyTimesliceUnfolder.h b/src/examples/TimesliceExample/MyTimesliceUnfolder.h new file mode 100644 index 000000000..c238db7f5 --- /dev/null +++ b/src/examples/TimesliceExample/MyTimesliceUnfolder.h @@ -0,0 +1,43 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once +#include "MyDataModel.h" +#include + +struct ExampleTimesliceUnfolder : public JEventUnfolder { + + MyTimesliceUnfolder() { + SetParentLevel(JEventLevel::Timeslice); + SetChildLevel(JEventLevel::Event); + } + + void Preprocess(const JEvent& parent) const override { + parent->Get("protos"); + } + + Result Unfold(const JEvent& parent, JEvent& child, int item) override { + auto protos = parent->Get("protos"); + + child.SetEventNumber(parent.GetEventNumber()*10 + item); + LOG << "Unfolding parent=" << parent.GetEventNumber() << ", child=" << child.GetEventNumber() << ", item=" << item << LOG_END; + + std::vector child_protos; + for (auto proto: protos) { + if (true) { + // TODO: condition + child_protos.push_back(proto); + } + } + child->Insert(child_protos, "event_protos")->SetFactoryFlag(JFactoryFlag::NOT_OBJECT_OWNER); + + if (item == 3) { + jout << "Unfold found item 3, finishing join" << jendl; + return Result::Finished; + } + return Result::KeepGoing; + } +} + + + diff --git a/src/examples/TimesliceExample/TimesliceExample.cc b/src/examples/TimesliceExample/TimesliceExample.cc index d9ce6d141..b1acad9ac 100644 --- a/src/examples/TimesliceExample/TimesliceExample.cc +++ b/src/examples/TimesliceExample/TimesliceExample.cc @@ -1,143 +1,30 @@ - -// Copyright 2020, Jefferson Science Associates, LLC. +// Copyright 2024, Jefferson Science Associates, LLC. // Subject to the terms in the LICENSE file found in the top-level directory. -#include -#include -#include -#include -#include -#include "JANA/Engine/JTopologyBuilder.h" - - -#include -#include -#include - - -struct MyInput : public JObject { - int x; - float y; - int evt = 0; - int sub = 0; - MyInput(int x, float y, int evt, int sub) : x(x), y(y), evt(evt), sub(sub) {} -}; - -struct MyOutput : public JObject { - float z; - int evt = 0; - int sub = 0; - explicit MyOutput(float z, int evt, int sub) : z(z), evt(evt), sub(sub) {} -}; - -struct MyProcessor : public JSubeventProcessor { - MyProcessor() { - inputTag = ""; - outputTag = "subeventted"; - } - MyOutput* ProcessSubevent(MyInput* input) override { - LOG << "Processing subevent " << input->evt << ":" << input->sub << LOG_END; - return new MyOutput(input->y + (float) input->x, input->evt, input->sub); - } -}; - - -struct ExampleTimesliceSource : public JEventSource { - SimpleSource(std::string name) : JEventSource(name) { - SetEventLevel(JEvent::Level::Timeslice); - }; - void GetEvent(std::shared_ptr event) override { - - auto evt = event->GetEventNumber(); - std::vector inputs; - inputs.push_back(new MyInput(22,3.6,evt,0)); - inputs.push_back(new MyInput(23,3.5,evt,1)); - inputs.push_back(new MyInput(24,3.4,evt,2)); - inputs.push_back(new MyInput(25,3.3,evt,3)); - inputs.push_back(new MyInput(26,3.2,evt,4)); - event->Insert(inputs); - - auto hits = std::make_unique(); - hits.push_back(ExampleHit(22)); - hits.push_back(ExampleHit(23)); - hits.push_back(ExampleHit(24)); - event->InsertCollection(hits); - LOG << "Emitting event " << event->GetEventNumber() << LOG_END; - // throw JEventSource::RETURN_STATUS::kNO_MORE_EVENTS; - } -}; +#include "MyTimesliceSource.h" +#include "MyTimesliceUnfolder.h" +#include "MyEventProcessor.h" +#include "MyTimesliceFactory.h" +#include "MyEventFactory.h" -struct ExampleTimesliceProcessor : public JEventProcessor { - std::mutex m_mutex; - - ExampleTimesliceProcessor() { - SetEventLevel(JEvent::Level::Timeslice); - } - - void Process(const std::shared_ptr& event) { - - std::lock_guard guard(m_mutex); - - auto outputs = event->Get(); - // assert(outputs.size() == 4); - // assert(outputs[0]->z == 25.6f); - // assert(outputs[1]->z == 26.5f); - // assert(outputs[2]->z == 27.4f); - // assert(outputs[3]->z == 28.3f); - LOG << " Contents of event " << event->GetEventNumber() << LOG_END; - for (auto output : outputs) { - LOG << " " << output->evt << ":" << output->sub << " " << output->z << LOG_END; - } - LOG << " DONE with contents of event " << event->GetEventNumber() << LOG_END; - } -}; - - - -int main() { - - MyProcessor processor; - JMailbox*> events_in; - JMailbox*> events_out; - JMailbox> subevents_in; - JMailbox> subevents_out; - - auto split_arrow = new JSplitArrow("split", &processor, &events_in, &subevents_in); - auto subprocess_arrow = new JSubeventArrow("subprocess", &processor, &subevents_in, &subevents_out); - auto merge_arrow = new JMergeArrow("merge", &processor, &subevents_out, &events_out); - - auto parms = new JParameterManager; - // Some params need to be present BEFORE JApplication is constructed, e.g. log levels are lost - // parms->SetParameter("log:debug", "JWorker,JScheduler,JArrowProcessingController,JEventProcessorArrow"); - JApplication app(parms); - app.SetTimeoutEnabled(false); - app.SetTicker(false); +#include - auto source = new SimpleSource("simpleSource"); - source->SetNEvents(10); // limit ourselves to 10 events. Note that the 'jana:nevents' param won't work - // here because we aren't using JComponentManager to manage the EventSource - auto topology = app.GetService()->create_empty(); - auto source_arrow = new JEventSourceArrow("simpleSource", - {source}, - &events_in, - topology->event_pool); - auto proc_arrow = new JEventProcessorArrow("simpleProcessor", &events_out, nullptr, topology->event_pool); - proc_arrow->add_processor(new SimpleProcessor); +extern "C"{ +void InitPlugin(JApplication *app) { - topology->arrows.push_back(source_arrow); - topology->arrows.push_back(split_arrow); - topology->arrows.push_back(subprocess_arrow); - topology->arrows.push_back(merge_arrow); - topology->arrows.push_back(proc_arrow); + InitJANAPlugin(app); - source_arrow->attach(split_arrow); - split_arrow->attach(subprocess_arrow); - subprocess_arrow->attach(merge_arrow); - merge_arrow->attach(proc_arrow); + app->Add(new MyTimesliceSource("Dummy")); + app->Add(new MyTimesliceUnfolder); + app->Add(new MyEventProcessor); - app.Run(true); + app->Add(new JFactoryGeneratorT()); + app->Add(new JFactoryGeneratorT()); + app->SetParameterValue("jana:extended_report", 0); } +} // "C" +