diff --git a/src/libraries/JANA/CLI/JBenchmarker.cc b/src/libraries/JANA/CLI/JBenchmarker.cc index 754c2060a..6df57d73f 100644 --- a/src/libraries/JANA/CLI/JBenchmarker.cc +++ b/src/libraries/JANA/CLI/JBenchmarker.cc @@ -5,7 +5,7 @@ #include "JBenchmarker.h" #include -#include +#include #include #include diff --git a/src/libraries/JANA/CMakeLists.txt b/src/libraries/JANA/CMakeLists.txt index db790ff46..9766b5006 100644 --- a/src/libraries/JANA/CMakeLists.txt +++ b/src/libraries/JANA/CMakeLists.txt @@ -24,6 +24,7 @@ set(JANA2_SOURCES JLogger.h JMultifactory.cc JMultifactory.h + JService.cc Engine/JArrow.h Engine/JArrowMetrics.h diff --git a/src/libraries/JANA/JApplication.cc b/src/libraries/JANA/JApplication.cc index 63562a226..71638c33f 100644 --- a/src/libraries/JANA/JApplication.cc +++ b/src/libraries/JANA/JApplication.cc @@ -4,10 +4,7 @@ #include -#include #include -#include -#include #include #include @@ -15,24 +12,25 @@ #include #include #include -#include #include JApplication *japp = nullptr; JApplication::JApplication(JLogger::Level verbosity) { + m_service_locator = new JServiceLocator; m_params = std::make_shared(); m_params->SetParameter("log:global", verbosity); - m_service_locator.provide(m_params); - m_service_locator.provide(std::make_shared()); - m_service_locator.provide(std::make_shared(this)); - m_service_locator.provide(std::make_shared(this)); - m_service_locator.provide(std::make_shared()); - m_service_locator.provide(std::make_shared()); - - m_plugin_loader = m_service_locator.get(); - m_component_manager = m_service_locator.get(); - m_logger = m_service_locator.get()->get_logger("JApplication"); + m_service_locator->provide(m_params); + ProvideService(m_params); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + + m_plugin_loader = m_service_locator->get(); + m_component_manager = m_service_locator->get(); + m_logger = m_service_locator->get()->get_logger("JApplication"); m_logger.show_classname = false; } @@ -45,17 +43,18 @@ JApplication::JApplication(JParameterManager* params) { m_params = std::shared_ptr(params); } - m_service_locator.provide(m_params); - m_service_locator.provide(std::make_shared()); - m_service_locator.provide(std::make_shared(this)); - m_service_locator.provide(std::make_shared(this)); - m_service_locator.provide(std::make_shared()); - m_service_locator.provide(std::make_shared()); + m_service_locator = new JServiceLocator; + ProvideService(m_params); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); + ProvideService(std::make_shared()); - m_plugin_loader = m_service_locator.get(); - m_component_manager = m_service_locator.get(); + m_plugin_loader = m_service_locator->get(); + m_component_manager = m_service_locator->get(); - m_logger = m_service_locator.get()->get_logger("JApplication"); + m_logger = m_service_locator->get()->get_logger("JApplication"); m_logger.show_classname = false; } @@ -119,13 +118,17 @@ void JApplication::Initialize() { // Only run this once if (m_initialized) return; + // Obtain final values of parameters and loggers + m_plugin_loader->InitPhase2(); + m_component_manager->InitPhase2(); + m_logger = m_service_locator->get()->get_logger("JApplication"); + m_logger.show_classname = false; + // Attach all plugins m_plugin_loader->attach_plugins(m_component_manager.get()); - // Look for factories to auto-activate - if (JAutoActivator::IsRequested(m_params)) { - m_component_manager->add(new JAutoActivator); - } + // Resolve all event sources now that all plugins have been loaded + m_component_manager->resolve_event_sources(); // Set desired nthreads. We parse the 'nthreads' parameter two different ways for backwards compatibility. m_desired_nthreads = 1; @@ -137,7 +140,6 @@ void JApplication::Initialize() { m_params->SetDefaultParameter("jana:ticker_interval", m_ticker_interval_ms, "Controls the ticker interval (in ms)"); m_params->SetDefaultParameter("jana:extended_report", m_extended_report, "Controls whether the ticker shows simple vs detailed performance metrics"); - m_component_manager->initialize(); /* int engine_choice = 0; @@ -148,13 +150,13 @@ void JApplication::Initialize() { LOG_WARN(m_logger) << "Unrecognized engine choice! Falling back to jana:engine=0" << LOG_END; } */ - std::shared_ptr topology_builder = m_service_locator.get(); + std::shared_ptr topology_builder = m_service_locator->get(); auto topology = topology_builder->get_or_create(); auto japc = std::make_shared(topology); - m_service_locator.provide(japc); // Make concrete class available via SL - m_processing_controller = m_service_locator.get(); // Get deps from SL - m_service_locator.provide(m_processing_controller); // Make abstract class available via SL + m_service_locator->provide(japc); // Make concrete class available via SL + m_processing_controller = m_service_locator->get(); // Get deps from SL + m_service_locator->provide(m_processing_controller); // Make abstract class available via SL m_processing_controller->initialize(); m_initialized = true; diff --git a/src/libraries/JANA/JApplication.h b/src/libraries/JANA/JApplication.h index 16a76dcf2..59a09c85e 100644 --- a/src/libraries/JANA/JApplication.h +++ b/src/libraries/JANA/JApplication.h @@ -2,164 +2,11 @@ // Copyright 2020, Jefferson Science Associates, LLC. // Subject to the terms in the LICENSE file found in the top-level directory. -#ifndef _JApplication_h_ -#define _JApplication_h_ - -#include -#include -#include -#include - -class JApplication; -class JEventProcessor; -class JEventSource; -class JEventSourceGenerator; -class JFactoryGenerator; -class JFactorySet; -class JComponentManager; -class JPluginLoader; -class JProcessingController; -class JEventUnfolder; - -extern JApplication* japp; +#pragma once +#include #include #include -#include -#include -#include - - -////////////////////////////////////////////////////////////////////////////////////////////////// -/// JANA application class -/// -/// The JApplication class serves as a central access point for getting to most things -/// in the JANA application. It owns the JThreadManager, JParameterManager, etc. -/// It is also responsible for making sure all of the plugins are attached and other -/// user specified configurations for the run are implemented before starting the processing -/// of the data. User code (e.g. plugins) will generally register things like event sources -/// and processors with the JApplication so they can be called up later at the appropriate time. -////////////////////////////////////////////////////////////////////////////////////////////////// -class JApplication { - -public: - - /// These exit codes are what JANA uses internally. However they are fundamentally a suggestion -- - /// the user code is likely to use arbitrary exit codes. - enum class ExitCode {Success=0, UnhandledException, Timeout, Segfault=139}; - - explicit JApplication(JParameterManager* params = nullptr); - explicit JApplication(JLogger::Level verbosity); - ~JApplication(); - - - // Loading plugins - - void AddPlugin(std::string plugin_name); - void AddPluginPath(std::string path); - - - // Building a JProcessingTopology - - void Add(std::string event_source_name); - void Add(JEventSourceGenerator* source_generator); - void Add(JFactoryGenerator* factory_generator); - void Add(JEventSource* event_source); - void Add(JEventProcessor* processor); - void Add(JEventUnfolder* unfolder); - - - // Controlling processing - - void Initialize(void); - void Run(bool wait_until_finished = true); - void Scale(int nthreads); - void Stop(bool wait_until_idle = false); - void Resume() {}; // TODO: Do we need this? - void Quit(bool skip_join = false); - void SetExitCode(int exitCode); - int GetExitCode(); - - - // Performance/status monitoring - - bool IsInitialized(void){return m_initialized;} - bool IsQuitting(void) { return m_quitting; } - bool IsDrainingQueues(void) { return m_draining_queues; } - - void SetTicker(bool ticker_on = true); - bool IsTickerEnabled(); - void SetTimeoutEnabled(bool enabled = true); - bool IsTimeoutEnabled(); - void PrintStatus(); - void PrintFinalReport(); - uint64_t GetNThreads(); - uint64_t GetNEventsProcessed(); - float GetIntegratedRate(); - float GetInstantaneousRate(); - - JComponentSummary GetComponentSummary(); - - // Parameter config - - JParameterManager* GetJParameterManager() { return m_params.get(); } - - template - T GetParameterValue(std::string name); - - template - JParameter* GetParameter(std::string name, T& val); - - template - JParameter* SetParameterValue(std::string name, T val); - - template - JParameter* SetDefaultParameter(std::string name, T& val, std::string description=""); - - template - T RegisterParameter(std::string name, const T default_val, std::string description=""); - - // Locating services - - /// Use this in EventSources, Factories, or EventProcessors. Do not call this - /// from InitPlugin(), as not all JServices may have been loaded yet. - /// When initializing a Service, use acquire_services() instead. - template - std::shared_ptr GetService(); - - /// Call this from InitPlugin. - template - void ProvideService(std::shared_ptr service); - - -private: - - JLogger m_logger; - JServiceLocator m_service_locator; - - std::shared_ptr m_params; - std::shared_ptr m_plugin_loader; - std::shared_ptr m_component_manager; - std::shared_ptr m_processing_controller; - - bool m_quitting = false; - bool m_draining_queues = false; - bool m_skip_join = false; - std::atomic_bool m_initialized {false}; - bool m_ticker_on = true; - bool m_timeout_on = true; - bool m_extended_report = false; - int m_exit_code = (int) ExitCode::Success; - int m_desired_nthreads; - - std::mutex m_status_mutex; - int m_ticker_interval_ms = 1000; - std::chrono::time_point m_last_measurement; - std::unique_ptr m_perf_summary; - - void update_status(); -}; - /// A convenience method which delegates to JParameterManager @@ -192,25 +39,15 @@ JParameter* JApplication::GetParameter(std::string name, T& result) { /// A convenience method which delegates to JServiceLocator template std::shared_ptr JApplication::GetService() { - return m_service_locator.get(); + return m_service_locator->get(); } /// A convenience method which delegates to JServiceLocator template void JApplication::ProvideService(std::shared_ptr service) { - m_service_locator.provide(service); + service->SetApplication(this); + m_service_locator->provide(service); } -// This routine is used to bootstrap plugins. It is done outside -// of the JApplication class to ensure it sees the global variables -// that the rest of the plugin's InitPlugin routine sees. -inline void InitJANAPlugin(JApplication* app) { - // Make sure global pointers are pointing to the - // same ones being used by executable - japp = app; -} - -#endif // _JApplication_h_ - diff --git a/src/libraries/JANA/JApplicationFwd.h b/src/libraries/JANA/JApplicationFwd.h new file mode 100644 index 000000000..2d251264a --- /dev/null +++ b/src/libraries/JANA/JApplicationFwd.h @@ -0,0 +1,173 @@ + +// Copyright 2020, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#pragma once + +#include +#include +#include +#include +#include + +class JEventProcessor; +class JEventSource; +class JEventSourceGenerator; +class JFactoryGenerator; +class JFactorySet; +class JComponentManager; +class JPluginLoader; +class JProcessingController; +class JEventUnfolder; +class JServiceLocator; +class JParameter; +class JParameterManager; +class JApplication; +extern JApplication* japp; + +#include +#include +#include + + +////////////////////////////////////////////////////////////////////////////////////////////////// +/// JANA application class +/// +/// The JApplication class serves as a central access point for getting to most things +/// in the JANA application. It owns the JThreadManager, JParameterManager, etc. +/// It is also responsible for making sure all of the plugins are attached and other +/// user specified configurations for the run are implemented before starting the processing +/// of the data. User code (e.g. plugins) will generally register things like event sources +/// and processors with the JApplication so they can be called up later at the appropriate time. +////////////////////////////////////////////////////////////////////////////////////////////////// +class JApplication { + +public: + + /// These exit codes are what JANA uses internally. However they are fundamentally a suggestion -- + /// the user code is likely to use arbitrary exit codes. + enum class ExitCode {Success=0, UnhandledException, Timeout, Segfault=139}; + + explicit JApplication(JParameterManager* params = nullptr); + explicit JApplication(JLogger::Level verbosity); + ~JApplication(); + + + // Loading plugins + + void AddPlugin(std::string plugin_name); + void AddPluginPath(std::string path); + + + // Building a JProcessingTopology + + void Add(std::string event_source_name); + void Add(JEventSourceGenerator* source_generator); + void Add(JFactoryGenerator* factory_generator); + void Add(JEventSource* event_source); + void Add(JEventProcessor* processor); + void Add(JEventUnfolder* unfolder); + + + // Controlling processing + + void Initialize(void); + void Run(bool wait_until_finished = true); + void Scale(int nthreads); + void Stop(bool wait_until_idle = false); + void Resume() {}; // TODO: Do we need this? + void Quit(bool skip_join = false); + void SetExitCode(int exitCode); + int GetExitCode(); + + + // Performance/status monitoring + + bool IsInitialized(void){return m_initialized;} + bool IsQuitting(void) { return m_quitting; } + bool IsDrainingQueues(void) { return m_draining_queues; } + + void SetTicker(bool ticker_on = true); + bool IsTickerEnabled(); + void SetTimeoutEnabled(bool enabled = true); + bool IsTimeoutEnabled(); + void PrintStatus(); + void PrintFinalReport(); + uint64_t GetNThreads(); + uint64_t GetNEventsProcessed(); + float GetIntegratedRate(); + float GetInstantaneousRate(); + + JComponentSummary GetComponentSummary(); + + // Parameter config + + JParameterManager* GetJParameterManager() { return m_params.get(); } + + template + T GetParameterValue(std::string name); + + template + JParameter* GetParameter(std::string name, T& val); + + template + JParameter* SetParameterValue(std::string name, T val); + + template + JParameter* SetDefaultParameter(std::string name, T& val, std::string description=""); + + template + T RegisterParameter(std::string name, const T default_val, std::string description=""); + + // Locating services + + /// Use this in EventSources, Factories, or EventProcessors. Do not call this + /// from InitPlugin(), as not all JServices may have been loaded yet. + /// When initializing a Service, use acquire_services() instead. + template + std::shared_ptr GetService(); + + /// Call this from InitPlugin. + template + void ProvideService(std::shared_ptr service); + + +private: + + JLogger m_logger; + JServiceLocator* m_service_locator; + + std::shared_ptr m_params; + std::shared_ptr m_plugin_loader; + std::shared_ptr m_component_manager; + std::shared_ptr m_processing_controller; + + bool m_quitting = false; + bool m_draining_queues = false; + bool m_skip_join = false; + std::atomic_bool m_initialized {false}; + bool m_ticker_on = true; + bool m_timeout_on = true; + bool m_extended_report = false; + int m_exit_code = (int) ExitCode::Success; + int m_desired_nthreads; + + std::mutex m_status_mutex; + int m_ticker_interval_ms = 1000; + std::chrono::time_point m_last_measurement; + std::unique_ptr m_perf_summary; + + void update_status(); +}; + + +// This routine is used to bootstrap plugins. It is done outside +// of the JApplication class to ensure it sees the global variables +// that the rest of the plugin's InitPlugin routine sees. +inline void InitJANAPlugin(JApplication* app) { + // Make sure global pointers are pointing to the + // same ones being used by executable + japp = app; +} + + diff --git a/src/libraries/JANA/JService.cc b/src/libraries/JANA/JService.cc new file mode 100644 index 000000000..6ac7f2241 --- /dev/null +++ b/src/libraries/JANA/JService.cc @@ -0,0 +1,40 @@ +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. + +#include +#include + + +void JService::DoInit(JServiceLocator* sl) { + std::lock_guard lock(m_mutex); + try { + for (auto* parameter : m_parameters) { + parameter->Configure(*(m_app->GetJParameterManager()), m_prefix); + } + for (auto* service : m_services) { + service->Init(GetApplication()); // TODO: This badly needs to be renamed. Maybe Fetch? + } + this->acquire_services(sl); + this->Init(); + this->m_status = Status::Initialized; + } + catch (JException& ex) { + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + catch (std::exception& e){ + auto ex = JException("Exception in JService::DoInit(): %s", e.what()); + ex.nested_exception = std::current_exception(); + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } + catch (...) { + auto ex = JException("Unknown exception in JService::DoInit()"); + ex.nested_exception = std::current_exception(); + ex.plugin_name = m_plugin_name; + ex.component_name = m_type_name; + throw ex; + } +} diff --git a/src/libraries/JANA/JService.h b/src/libraries/JANA/JService.h new file mode 100644 index 000000000..70e29be16 --- /dev/null +++ b/src/libraries/JANA/JService.h @@ -0,0 +1,5 @@ + +#pragma once +#include +#include + diff --git a/src/libraries/JANA/JServiceFwd.h b/src/libraries/JANA/JServiceFwd.h new file mode 100644 index 000000000..211ef79fb --- /dev/null +++ b/src/libraries/JANA/JServiceFwd.h @@ -0,0 +1,28 @@ + +#pragma once +#include + + +class JServiceLocator; +struct JService : public jana::omni::JComponent { + + /// acquire_services is a callback which allows the user to configure a JService + /// which relies on other JServices. The idea is that the user uses a constructor + /// or initialize() method to configure things which don't rely on other JServices, and + /// then use acquire_services() to configure the things which do. We need this because + /// due to JANA's plugin architecture, we can't guarantee the order in which JServices + /// get provided. So instead, we collect all of the JServices first and wire them + /// together afterwards in a separate phase. + /// + /// Note: Don't call JApplication::GetService() or JServiceLocator::get() from InitPlugin()! + + virtual ~JService() = default; + + void DoInit(JServiceLocator*); + + virtual void Init() {}; + + // This will be deprecated eventually + virtual void acquire_services(JServiceLocator*) {}; +}; + diff --git a/src/libraries/JANA/Omni/JComponent.h b/src/libraries/JANA/Omni/JComponent.h index e5c735257..18cdcbce6 100644 --- a/src/libraries/JANA/Omni/JComponent.h +++ b/src/libraries/JANA/Omni/JComponent.h @@ -5,197 +5,100 @@ #pragma once #include +#include namespace jana { namespace omni { +template +class JComponent::ParameterRef : public JComponent::ParameterBase { -struct JComponent { - enum class Status { Uninitialized, Initialized, Finalized }; - enum class CallbackStyle { Compatibility, Classic, Declarative }; - -protected: - - struct ParameterBase; - struct ServiceBase; - - std::vector m_parameters; - std::vector m_services; - - JEventLevel m_level = JEventLevel::Event; - CallbackStyle m_callback_style = CallbackStyle::Compatibility; - std::string m_prefix; - std::string m_plugin_name; - std::string m_type_name; - Status m_status = Status::Uninitialized; - mutable std::mutex m_mutex; - JApplication* m_app = nullptr; - JLogger m_logger; + T* m_data; public: - // --------------------- - // Meant to be called by users, or alternatively from a Generator - // --------------------- - void SetLevel(JEventLevel level) { m_level = level; } - - void SetCallbackStyle(CallbackStyle style) { m_callback_style = style; } - - void SetPrefix(std::string prefix) { - m_prefix = prefix; - } - /// For convenience, we provide a NAME_OF_THIS macro so that the user doesn't have to store the type name as a string, - /// because that could get out of sync if automatic refactoring tools are used. - void SetTypeName(std::string type_name) { m_type_name = std::move(type_name); } - - JApplication* GetApplication() const { - if (m_app == nullptr) { - throw JException("JApplication pointer hasn't been provided yet! Hint: Component configuration should happen inside Init(), not in the constructor."); - } - return m_app; + ParameterRef(JComponent* owner, std::string name, T& slot, std::string description="") { + owner->RegisterParameter(this); + this->m_name = name; + this->m_description = description; + m_data = &slot; } - JLogger& GetLogger() { return m_logger; } - - - // --------------------- - // Meant to be called by JANA - // --------------------- - std::string GetPrefix() { return m_prefix; } - - JEventLevel GetLevel() { return m_level; } - - std::string GetLoggerName() const { return m_prefix.empty() ? m_type_name : m_prefix; } - - std::string GetPluginName() const { return m_plugin_name; } - - void SetPluginName(std::string plugin_name) { m_plugin_name = std::move(plugin_name); }; - - std::string GetTypeName() const { return m_type_name; } - - Status GetStatus() const { - std::lock_guard lock(m_mutex); - return m_status; - } - - void SetApplication(JApplication* app) { m_app = app; } - - void SetLogger(JLogger logger) { m_logger = logger; } - - -protected: - struct ParameterBase { - std::string m_name; - std::string m_description; - virtual void Configure(JParameterManager& parman, const std::string& prefix) = 0; - virtual void Configure(std::map fields) = 0; - }; - - struct ServiceBase { - virtual void Init(JApplication* app) = 0; - }; - - void RegisterParameter(ParameterBase* parameter) { - m_parameters.push_back(parameter); - } - - void RegisterService(ServiceBase* service) { - m_services.push_back(service); - } - -public: - - void ConfigureAllParameters(std::map fields) { - for (auto* parameter : this->m_parameters) { - parameter->Configure(fields); - } - } + const T& operator()() { return *m_data; } protected: - template - class ParameterRef : public ParameterBase { - - T* m_data; - public: - ParameterRef(JComponent* owner, std::string name, T& slot, std::string description="") { - owner->RegisterParameter(this); - this->m_name = name; - this->m_description = description; - m_data = &slot; + void Configure(JParameterManager& parman, const std::string& prefix) override { + if (prefix.empty()) { + parman.SetDefaultParameter(this->m_name, *m_data, this->m_description); } - - const T& operator()() { return *m_data; } - - protected: - - void Configure(JParameterManager& parman, const std::string& prefix) override { + else { parman.SetDefaultParameter(prefix + ":" + this->m_name, *m_data, this->m_description); } - void Configure(std::map fields) override { - auto it = fields.find(this->m_name); - if (it != fields.end()) { - const auto& value_str = it->second; - JParameterManager::Parse(value_str, *m_data); - } + } + void Configure(std::map fields) override { + auto it = fields.find(this->m_name); + if (it != fields.end()) { + const auto& value_str = it->second; + JParameterManager::Parse(value_str, *m_data); } - }; + } +}; - template - class Parameter : public ParameterBase { +template +class JComponent::Parameter : public JComponent::ParameterBase { - T m_data; + T m_data; - public: - Parameter(JComponent* owner, std::string name, T default_value, std::string description) { - owner->RegisterParameter(this); - this->m_name = name; - this->m_description = description; - m_data = default_value; - } +public: + Parameter(JComponent* owner, std::string name, T default_value, std::string description) { + owner->RegisterParameter(this); + this->m_name = name; + this->m_description = description; + m_data = default_value; + } - const T& operator()() { return m_data; } + const T& operator()() { return m_data; } - protected: +protected: - void Configure(JParameterManager& parman, const std::string& prefix) override { - parman.SetDefaultParameter(prefix + ":" + this->m_name, m_data, this->m_description); + void Configure(JParameterManager& parman, const std::string& prefix) override { + if (prefix.empty()) { + parman.SetDefaultParameter(this->m_name, m_data, this->m_description); } - void Configure(std::map fields) override { - auto it = fields.find(this->m_name); - if (it != fields.end()) { - const auto& value_str = it->second; - JParameterManager::Parse(value_str, m_data); - } + else { + parman.SetDefaultParameter(prefix + ":" + this->m_name, m_data, this->m_description); } - }; - - - template - class Service : public ServiceBase { - - std::shared_ptr m_data; - - public: - - Service(JComponent* owner) { - owner->RegisterService(this); + } + void Configure(std::map fields) override { + auto it = fields.find(this->m_name); + if (it != fields.end()) { + const auto& value_str = it->second; + JParameterManager::Parse(value_str, m_data); } + } +}; - ServiceT& operator()() { - return *m_data; - } - protected: +template +class JComponent::Service : public JComponent::ServiceBase { - void Init(JApplication* app) { - m_data = app->GetService(); - } + std::shared_ptr m_data; - }; +public: + Service(JComponent* owner) { + owner->RegisterService(this); + } + ServiceT& operator()() { + return *m_data; + } +protected: + void Init(JApplication* app) { + m_data = app->GetService(); + } }; diff --git a/src/libraries/JANA/Omni/JComponentFwd.h b/src/libraries/JANA/Omni/JComponentFwd.h new file mode 100644 index 000000000..a0b1c3f74 --- /dev/null +++ b/src/libraries/JANA/Omni/JComponentFwd.h @@ -0,0 +1,140 @@ + +// Copyright 2024, Jefferson Science Associates, LLC. +// Subject to the terms in the LICENSE file found in the top-level directory. +// Created by Nathan Brei + +#pragma once + +class JApplication; +class JParameterManager; + +#include +#include +#include + +#include +#include + +namespace jana { +namespace omni { + + +struct JComponent { + enum class Status { Uninitialized, Initialized, Finalized }; + enum class CallbackStyle { Compatibility, Classic, Declarative }; + + struct ParameterBase; + struct ServiceBase; + +protected: + std::vector m_parameters; + std::vector m_services; + + JEventLevel m_level = JEventLevel::Event; + CallbackStyle m_callback_style = CallbackStyle::Compatibility; + std::string m_prefix; + std::string m_plugin_name; + std::string m_type_name; + Status m_status = Status::Uninitialized; + mutable std::mutex m_mutex; + JApplication* m_app = nullptr; + JLogger m_logger; + +public: + // --------------------- + // Meant to be called by users, or alternatively from a Generator + // --------------------- + void SetLevel(JEventLevel level) { m_level = level; } + + void SetCallbackStyle(CallbackStyle style) { m_callback_style = style; } + + void SetPrefix(std::string prefix) { + m_prefix = prefix; + } + /// For convenience, we provide a NAME_OF_THIS macro so that the user doesn't have to store the type name as a string, + /// because that could get out of sync if automatic refactoring tools are used. + void SetTypeName(std::string type_name) { m_type_name = std::move(type_name); } + + JApplication* GetApplication() const { + if (m_app == nullptr) { + throw JException("JApplication pointer hasn't been provided yet! Hint: Component configuration should happen inside Init(), not in the constructor."); + } + return m_app; + } + + JLogger& GetLogger() { return m_logger; } + + + // --------------------- + // Meant to be called by JANA + // --------------------- + std::string GetPrefix() { return m_prefix; } + + JEventLevel GetLevel() { return m_level; } + + std::string GetLoggerName() const { return m_prefix.empty() ? m_type_name : m_prefix; } + + std::string GetPluginName() const { return m_plugin_name; } + + void SetPluginName(std::string plugin_name) { m_plugin_name = std::move(plugin_name); }; + + std::string GetTypeName() const { return m_type_name; } + + Status GetStatus() const { + std::lock_guard lock(m_mutex); + return m_status; + } + + void SetApplication(JApplication* app) { m_app = app; } + + void SetLogger(JLogger logger) { m_logger = logger; } + + + // --------------------- + // "Registered member" helpers + // --------------------- + + struct ParameterBase { + std::string m_name; + std::string m_description; + virtual void Configure(JParameterManager& parman, const std::string& prefix) = 0; + virtual void Configure(std::map fields) = 0; + }; + + template + class ParameterRef; + + template + class Parameter; + + struct ServiceBase { + virtual void Init(JApplication* app) = 0; + }; + + template + class Service; + + void RegisterParameter(ParameterBase* parameter) { + m_parameters.push_back(parameter); + } + + void RegisterService(ServiceBase* service) { + m_services.push_back(service); + } + + void ConfigureAllParameters(std::map fields) { + for (auto* parameter : this->m_parameters) { + parameter->Configure(fields); + } + } + + + // +}; + + + +} // namespace omni +} // namespace jana + + diff --git a/src/libraries/JANA/Services/JComponentManager.cc b/src/libraries/JANA/Services/JComponentManager.cc index 12f7068ad..548e03656 100644 --- a/src/libraries/JANA/Services/JComponentManager.cc +++ b/src/libraries/JANA/Services/JComponentManager.cc @@ -7,9 +7,9 @@ #include #include #include +#include -JComponentManager::JComponentManager(JApplication* app) : m_app(app) { -} +JComponentManager::JComponentManager() {} JComponentManager::~JComponentManager() { @@ -30,6 +30,23 @@ JComponentManager::~JComponentManager() { } } +void JComponentManager::InitPhase2() { + + // We don't set these in Init() because Init() gets called by the JApplication constructor and we want to give the user a chance to + // set them manually before they call JApplication::Init(). + m_params().SetDefaultParameter("event_source_type", m_user_evt_src_typename, "Manually specifies which JEventSource should open the input file"); + m_params().SetDefaultParameter("record_call_stack", m_enable_call_graph_recording, "Records a trace of who called each factory. Reduces performance but necessary for plugins such as janadot."); + m_params().SetDefaultParameter("jana:nevents", m_nevents, "Max number of events that sources can emit"); + m_params().SetDefaultParameter("jana:nskip", m_nskip, "Number of events that sources should skip before starting emitting"); + m_params().FilterParameters(m_default_tags, "DEFTAG:"); + + // Look for factories to auto-activate + // Right now AutoActivator parameter won't show up in parameters list. Reconsider this. + if (JAutoActivator::IsRequested(m_params())) { + add(new JAutoActivator); + } +} + void JComponentManager::next_plugin(std::string plugin_name) { // We defer resolving event sources until we have finished loading all plugins m_current_plugin_name = plugin_name; @@ -41,31 +58,36 @@ void JComponentManager::add(std::string event_source_name) { void JComponentManager::add(JEventSourceGenerator *source_generator) { source_generator->SetPluginName(m_current_plugin_name); - source_generator->SetJApplication(m_app); + source_generator->SetJApplication(GetApplication()); + // source_generator->SetLogger(m_logging().get_logger(source_generator->GetLoggerName())); m_src_gens.push_back(source_generator); } void JComponentManager::add(JFactoryGenerator *factory_generator) { factory_generator->SetPluginName(m_current_plugin_name); - factory_generator->SetApplication(m_app); + factory_generator->SetApplication(GetApplication()); + // factory_generator->SetLogger(m_logging().get_logger(factory_generator->GetLoggerName())); m_fac_gens.push_back(factory_generator); } void JComponentManager::add(JEventSource *event_source) { event_source->SetPluginName(m_current_plugin_name); - event_source->SetApplication(m_app); + event_source->SetApplication(GetApplication()); + event_source->SetLogger(m_logging().get_logger(event_source->GetLoggerName())); m_evt_srces.push_back(event_source); } void JComponentManager::add(JEventProcessor *processor) { processor->SetPluginName(m_current_plugin_name); - processor->SetApplication(m_app); + processor->SetApplication(GetApplication()); + processor->SetLogger(m_logging().get_logger(processor->GetLoggerName())); m_evt_procs.push_back(processor); } void JComponentManager::add(JEventUnfolder* unfolder) { unfolder->SetPluginName(m_current_plugin_name); - unfolder->SetApplication(m_app); + unfolder->SetApplication(GetApplication()); + unfolder->SetLogger(m_logging().get_logger(unfolder->GetLoggerName())); m_unfolders.push_back(unfolder); } @@ -76,47 +98,18 @@ void JComponentManager::configure_event(JEvent& event) { event.GetJCallGraphRecorder()->SetEnabled(m_enable_call_graph_recording); } -void JComponentManager::initialize() { - // We want to obtain parameters from here rather than in the constructor. - // If we set them here, plugins and test cases can set parameters right up until JApplication::Initialize() - // or Run() are called. Otherwise, the parameters have to be set before the - // JApplication is even constructed. - auto parms = m_app->GetJParameterManager(); - parms->SetDefaultParameter("record_call_stack", m_enable_call_graph_recording, "Records a trace of who called each factory. Reduces performance but necessary for plugins such as janadot."); - parms->FilterParameters(m_default_tags, "DEFTAG:"); - - resolve_event_sources(); - - auto logging_svc = m_app->GetService(); - - for (JEventSource* source : m_evt_srces) { - source->SetLogger(logging_svc->get_logger(source->GetLoggerName())); - } - for (JEventProcessor* proc : m_evt_procs) { - proc->SetLogger(logging_svc->get_logger(proc->GetLoggerName())); - } - for (JEventUnfolder* unfolder : m_unfolders) { - unfolder->SetLogger(logging_svc->get_logger(unfolder->GetLoggerName())); - } -} - void JComponentManager::resolve_event_sources() { - m_app->SetDefaultParameter("event_source_type", m_user_evt_src_typename, "Manually specifies which JEventSource should open the input file"); - m_user_evt_src_gen = resolve_user_event_source_generator(); for (auto& source_name : m_src_names) { auto* generator = resolve_event_source(source_name); auto source = generator->MakeJEventSource(source_name); source->SetPluginName(generator->GetPluginName()); - source->SetApplication(m_app); + source->SetApplication(GetApplication()); m_evt_srces.push_back(source); } - m_app->SetDefaultParameter("jana:nevents", m_nevents, "Max number of events that sources can emit"); - m_app->SetDefaultParameter("jana:nskip", m_nskip, "Number of events that sources should skip before starting emitting"); - for (auto source : m_evt_srces) { // If nskip/nevents are set individually on JEventSources, respect those. Otherwise use global values. // Note that this is not what we usually want when we have multiple event sources. It would make more sense to diff --git a/src/libraries/JANA/Services/JComponentManager.h b/src/libraries/JANA/Services/JComponentManager.h index fe7a0db55..ff5c4b6f5 100644 --- a/src/libraries/JANA/Services/JComponentManager.h +++ b/src/libraries/JANA/Services/JComponentManager.h @@ -18,8 +18,9 @@ class JEventUnfolder; class JComponentManager : public JService { public: - explicit JComponentManager(JApplication*); + explicit JComponentManager(); ~JComponentManager() override; + void InitPhase2(); void next_plugin(std::string plugin_name); @@ -30,7 +31,6 @@ class JComponentManager : public JService { void add(JEventProcessor* processor); void add(JEventUnfolder* unfolder); - void initialize(); void resolve_event_sources(); JEventSourceGenerator* resolve_user_event_source_generator() const; JEventSourceGenerator* resolve_event_source(std::string source_name) const; @@ -51,7 +51,8 @@ class JComponentManager : public JService { // Processors need: { typename, pluginname, mutexgroup, status, evtcnt } // Factories need: { typename, pluginname } - JApplication* m_app; + Service m_params {this}; + Service m_logging {this}; std::string m_current_plugin_name; std::vector m_src_names; diff --git a/src/libraries/JANA/Services/JPluginLoader.cc b/src/libraries/JANA/Services/JPluginLoader.cc index 16554c5c1..ed5fe08f3 100644 --- a/src/libraries/JANA/Services/JPluginLoader.cc +++ b/src/libraries/JANA/Services/JPluginLoader.cc @@ -5,7 +5,6 @@ #include "JPluginLoader.h" #include "JComponentManager.h" -#include "JLoggingService.h" #include "JParameterManager.h" #include @@ -15,6 +14,22 @@ class JApplication; +void JPluginLoader::InitPhase2() { + + m_params().SetDefaultParameter("plugins", m_plugins_to_include, "Comma-separated list of plugins to load."); + m_params().SetDefaultParameter("plugins_to_ignore", m_plugins_to_exclude, "Comma-separated list of plugins to NOT load, even if they are specified in 'plugins'."); + m_params().SetDefaultParameter("jana:plugin_path", m_plugin_paths_str, "Colon-separated list of paths to search for plugins"); + m_params().SetDefaultParameter("jana:debug_plugin_loading", m_verbose, "Trace the plugin search path and display any loading errors"); + + if (m_verbose) { + // The jana:debug_plugin_loading parameter is kept around for backwards compatibility + // at least for now + GetLogger().level = JLogger::Level::TRACE; + } +} + + + void JPluginLoader::add_plugin(std::string plugin_name) { /// Add the specified plugin to the list of plugins to be /// attached. This only records the name. The plugin is not @@ -211,7 +226,7 @@ void JPluginLoader::attach_plugin(std::string soname) { InitPlugin_t* initialize_proc = (InitPlugin_t*) dlsym(handle, "InitPlugin"); if (initialize_proc) { LOG_INFO(m_logger) << "Initializing plugin \"" << soname << "\"" << LOG_END; - (*initialize_proc)(m_app); + (*initialize_proc)(GetApplication()); m_sohandles[soname] = handle; } else { dlclose(handle); @@ -221,9 +236,9 @@ void JPluginLoader::attach_plugin(std::string soname) { } -JPluginLoader::JPluginLoader(JApplication* app) : m_app(app) {} +JPluginLoader::JPluginLoader() {} -JPluginLoader::~JPluginLoader(){ +JPluginLoader::~JPluginLoader() { // Loop over open plugin handles. // Call FinalizePlugin if it has one and close it in all cases. @@ -234,7 +249,7 @@ JPluginLoader::~JPluginLoader(){ FinalizePlugin_t* finalize_proc = (FinalizePlugin_t*) dlsym(handle, "FinalizePlugin"); if (finalize_proc) { LOG_INFO(m_logger) << "Finalizing plugin \"" << soname << "\"" << LOG_END; - (*finalize_proc)(m_app); + (*finalize_proc)(GetApplication()); } // Close plugin handle @@ -242,21 +257,3 @@ JPluginLoader::~JPluginLoader(){ } } -void JPluginLoader::acquire_services(JServiceLocator* sl) { - - auto params = sl->get(); - params->SetDefaultParameter("plugins", m_plugins_to_include, "Comma-separated list of plugins to load."); - params->SetDefaultParameter("plugins_to_ignore", m_plugins_to_exclude, "Comma-separated list of plugins to NOT load, even if they are specified in 'plugins'."); - m_app->SetDefaultParameter("jana:plugin_path", m_plugin_paths_str, "Colon-separated list of paths to search for plugins"); - params->SetDefaultParameter("jana:debug_plugin_loading", m_verbose, "Trace the plugin search path and display any loading errors"); - - m_logger = sl->get()->get_logger("JPluginLoader"); - if (m_verbose) { - // The jana:debug_plugin_loading parameter is kept around for backwards compatibility - // at least for now - m_logger.level = JLogger::Level::TRACE; - } -} - - - diff --git a/src/libraries/JANA/Services/JPluginLoader.h b/src/libraries/JANA/Services/JPluginLoader.h index 749da031e..ddd46a200 100644 --- a/src/libraries/JANA/Services/JPluginLoader.h +++ b/src/libraries/JANA/Services/JPluginLoader.h @@ -6,8 +6,8 @@ #ifndef JANA2_JPLUGINLOADER_H #define JANA2_JPLUGINLOADER_H -#include #include +#include #include #include @@ -20,9 +20,9 @@ class JPluginLoader : public JService { public: - JPluginLoader(JApplication* app); + JPluginLoader(); ~JPluginLoader() override; - void acquire_services(JServiceLocator*) override; + void InitPhase2(); void add_plugin(std::string plugin_name); void add_plugin_path(std::string path); @@ -30,6 +30,7 @@ class JPluginLoader : public JService { void attach_plugin(std::string plugin_name); private: + Service m_params {this}; std::vector m_plugins_to_include; std::vector m_plugins_to_exclude; @@ -38,9 +39,6 @@ class JPluginLoader : public JService { std::map m_sohandles; // key=plugin name val=dlopen handle bool m_verbose = false; - JLogger m_logger; - - JApplication* m_app = nullptr; }; diff --git a/src/libraries/JANA/Services/JServiceLocator.h b/src/libraries/JANA/Services/JServiceLocator.h index 22e9caa88..7d5414783 100644 --- a/src/libraries/JANA/Services/JServiceLocator.h +++ b/src/libraries/JANA/Services/JServiceLocator.h @@ -6,38 +6,23 @@ #ifndef _JSERVICELOCATOR_H_ #define _JSERVICELOCATOR_H_ -#include #include #include #include -#include #include #include #include + +#include #include "JANA/Utils/JTypeInfo.h" +#include -class JServiceLocator; /// JService is a trait indicating that an object can be shared among JANA components /// via a simple ServiceLocator. It provides a callback interface for configuring itself /// when it depends on other JServices. -struct JService { - - /// acquire_services is a callback which allows the user to configure a JService - /// which relies on other JServices. The idea is that the user uses a constructor - /// or initialize() method to configure things which don't rely on other JServices, and - /// then use acquire_services() to configure the things which do. We need this because - /// due to JANA's plugin architecture, we can't guarantee the order in which JServices - /// get provided. So instead, we collect all of the JServices first and wire them - /// together afterwards in a separate phase. - /// - /// Note: Don't call JApplication::GetService() or JServiceLocator::get() from InitPlugin()! - - virtual ~JService() = default; - virtual void acquire_services(JServiceLocator*) {}; -}; /// JServiceLocator is a nexus for collecting, initializing, and retrieving JServices. /// This may be exposed via the JApplication facade, or used on its own. JServiceLocator @@ -86,7 +71,7 @@ class JServiceLocator { auto& pair = iter->second; auto& wired = pair.second; auto ptr = pair.first; - std::call_once(*wired, [&](){ptr->acquire_services(this);}); + std::call_once(*wired, [&](){ptr->DoInit(this);}); auto svc = std::static_pointer_cast(ptr); return svc; } @@ -99,7 +84,7 @@ class JServiceLocator { for (auto& item : underlying) { auto sharedptr = item.second.first; auto& wired = item.second.second; - std::call_once(*wired, [&](){sharedptr->acquire_services(this);}); + std::call_once(*wired, [&](){sharedptr->DoInit(this);}); } } }; diff --git a/src/libraries/JANA/Utils/JAutoActivator.cc b/src/libraries/JANA/Utils/JAutoActivator.cc index 5669d5c07..c4497d2ac 100644 --- a/src/libraries/JANA/Utils/JAutoActivator.cc +++ b/src/libraries/JANA/Utils/JAutoActivator.cc @@ -9,8 +9,8 @@ JAutoActivator::JAutoActivator() { SetTypeName("JAutoActivator"); } -bool JAutoActivator::IsRequested(std::shared_ptr params) { - return params->Exists("autoactivate") && (!params->GetParameterValue("autoactivate").empty()); +bool JAutoActivator::IsRequested(JParameterManager& params) { + return params.Exists("autoactivate") && (!params.GetParameterValue("autoactivate").empty()); } void JAutoActivator::AddAutoActivatedFactory(string factory_name, string factory_tag) { diff --git a/src/libraries/JANA/Utils/JAutoActivator.h b/src/libraries/JANA/Utils/JAutoActivator.h index 2b2ed34f4..695a6d958 100644 --- a/src/libraries/JANA/Utils/JAutoActivator.h +++ b/src/libraries/JANA/Utils/JAutoActivator.h @@ -16,7 +16,7 @@ class JAutoActivator : public JEventProcessor { public: JAutoActivator(); - static bool IsRequested(std::shared_ptr params); + static bool IsRequested(JParameterManager& params); static std::pair Split(std::string factory_name); void AddAutoActivatedFactory(string factory_name, string factory_tag); void Init() override; diff --git a/src/libraries/JANA/Utils/JEventLevel.h b/src/libraries/JANA/Utils/JEventLevel.h index b10db525e..27f1b899b 100644 --- a/src/libraries/JANA/Utils/JEventLevel.h +++ b/src/libraries/JANA/Utils/JEventLevel.h @@ -3,6 +3,7 @@ #pragma once #include +#include enum class JEventLevel { Run, Subrun, Timeslice, Block, Event, Subevent, Task, None }; diff --git a/src/programs/unit_tests/JServiceLocatorTests.cc b/src/programs/unit_tests/JServiceLocatorTests.cc index 972e9aff1..329e9685b 100644 --- a/src/programs/unit_tests/JServiceLocatorTests.cc +++ b/src/programs/unit_tests/JServiceLocatorTests.cc @@ -5,8 +5,9 @@ #define CATCH_CONFIG_MAIN #include "catch.hpp" -#include #include "JServiceLocatorTests.h" +#include +#include TEST_CASE("JServiceLocatorMissingServiceTest") { @@ -73,6 +74,25 @@ TEST_CASE("JServiceLocator chicken-and-egg tests") { } +struct OmniService : public JService { + Service parman {this}; + Parameter bucket_count {this, "bucket_count", 5, "Some integer representing a bucket count"}; + void Init() override { + LOG_INFO(GetLogger()) << "Calling OmniService::Init" << LOG_END; + REQUIRE(parman().GetParameterValue("bucket_count") == 22); + REQUIRE(bucket_count() == 22); + } +}; + +TEST_CASE("JService Omni interface") { + JApplication app; + app.SetParameterValue("bucket_count", 22); + app.ProvideService(std::make_shared()); + app.Initialize(); + auto sut = app.GetService(); + REQUIRE(sut->GetStatus() == JService::Status::Initialized); + REQUIRE(sut->bucket_count() == 22); +}