diff --git a/GaudiExamples/CMakeLists.txt b/GaudiExamples/CMakeLists.txt index 55efd285c33d17a7881e9b591572f2c3b28ac03c..af1a9948551683b812ab490766a58bca8ddecb5f 100644 --- a/GaudiExamples/CMakeLists.txt +++ b/GaudiExamples/CMakeLists.txt @@ -62,6 +62,7 @@ set(GaudiExamples_srcs src/PluginService/*.cpp src/ToolHandles/*.cpp src/ReEntAlg/*.cpp + src/ConditionAccessor/*.cpp ) # Add the sources requiring non-standard externals: @@ -113,7 +114,7 @@ endif() # Now set up the libraries: gaudi_add_library( GaudiExamplesLib src/Lib/*.cpp LINK_LIBRARIES GaudiKernel - PUBLIC_HEADERS GaudiExamples ) + PUBLIC_HEADERS GaudiExamples Gaudi) gaudi_add_module( GaudiExamples ${GaudiExamples_srcs} INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${extra_includes} diff --git a/GaudiExamples/Gaudi/Examples/Conditions/ConditionAccessorHolder.h b/GaudiExamples/Gaudi/Examples/Conditions/ConditionAccessorHolder.h new file mode 100644 index 0000000000000000000000000000000000000000..c3adb9e9af846fb98883fbad8c9a5b136a3c3d0a --- /dev/null +++ b/GaudiExamples/Gaudi/Examples/Conditions/ConditionAccessorHolder.h @@ -0,0 +1,155 @@ +#pragma once + +#include <GaudiKernel/EventContext.h> +#include <GaudiKernel/Property.h> +#include <GaudiKernel/StateMachine.h> +#include <GaudiKernel/StatusCode.h> +#include <any> +#include <mutex> +#include <string> + +namespace Gaudi::Examples::Conditions { + namespace details { + /// Example class to implement an actual backend for Gaudi::Examples::Conditions::ConditionAccessorHolder + class ConditionsStore { + public: + using ConditionContext = int; + using ConditionKey = std::string; + + static ConditionsStore& instance(); + + void declareKey( const ConditionKey& key ); + + const ConditionContext& getConditionContext( const EventContext& ctx ) const; + + template <typename T> + const T& getCondition( const ConditionKey& key, const ConditionContext& ctx ) const { + return *std::any_cast<T>( getConditionAny( key, ctx ) ); + } + + private: + ConditionsStore() = default; + const std::any* getConditionAny( const ConditionKey& key, const ConditionContext& ctx ) const; + + using IOVSlot = std::map<ConditionKey, std::any>; + using IOVSlots = std::map<ConditionContext, IOVSlot>; + + mutable std::mutex m_storageMtx; + mutable IOVSlots m_storage; + std::set<ConditionKey> m_requestedKeys; + }; + } // namespace details + + /// Example of a ConditionAccessorHolder implementation. + /// + /// Requirements are: + /// - inherit from the template argument (forwarding the constructor) + /// - provide the following types (could be type aliases): + /// - `base_class` (this type) + /// - `ConditionAccessor<T>` + /// - `ConditionContext` + /// - `ConditionKey` + /// - provide the method + /// - `const ConditionContext& getConditionContext( const EventContext& ) const` + /// - provide a method to handle registration of ConditionAccessor instances for + /// internal bookkeeping (here represented by `void registerConditionAccessor<T>( ConditionAccessor<T>& accessor )`) + /// + /// Component classes that require conditions have to inherit from this class, + /// templated with the component base class. E.g. + /// \code + /// class MyConditionAlgorithm final : ConditionAccessorHolder<Gaudi::Algorithm> { + /// // ... + /// }; + /// \endcode + /// + /// See Gaudi::Examples::Conditions::UserAlg for example. + template <typename Base> + class ConditionAccessorHolder : public Base { + public: + /// Forward to base class constructor + using Base::Base; + + /// Helper to expose this class to specializations without having to spell + /// the whole name (see Gaudi::Examples::Conditions::UserAlg) + using base_class = ConditionAccessorHolder<Base>; + + /// Forward backend specific ConditionContext to specializations. + /// + /// It is used to refer to a specific conditions slot. + using ConditionContext = details::ConditionsStore::ConditionContext; + + /// Forward backend specific ConditionKey to specializations. + /// + /// Used to identify a specific condition in a condition slot. + using ConditionKey = details::ConditionsStore::ConditionKey; + + /// Class wrapping the communication between condition users and conditions backend. + /// + /// Users must use instances of this class as data members, passing `this` to the + /// data member constructor. + /// + /// See Gaudi::Examples::Conditions::UserAlg::m_cond + template <typename T> + class ConditionAccessor { + public: + /// Constructor a ConditionAccessor instance registering it to the current holder. + ConditionAccessor( base_class* owner, const std::string& keyName, const ConditionKey& keyDefault, + const std::string& keyDoc = "" ) + : m_key{owner, keyName, keyDefault, keyDoc} { + owner->registerConditionAccessor( *this ); + } + + /// Helper to access the key of the current accessor. + /// + /// @note can only be modified via the associated property + const ConditionKey& key() const { return m_key; } + + /// Get the condition value for the requested condition slot. + const T& get( const ConditionContext& ctx ) const { + return details::ConditionsStore::instance().getCondition<T>( key(), ctx ); + } + + private: + /// Internal property object for the condition key. + Gaudi::Property<ConditionKey> m_key; + }; + + StatusCode initialize() override { + auto sc = Base::initialize(); + if ( !sc ) return sc; + + // backend-specific initialization + + // trigger delayed registrations + for ( auto& action : m_delayedRegistrations ) { action(); } + m_delayedRegistrations.clear(); + + return sc; + } + + /// Helper to map the event being processed to the corresponding conditions slot. + const ConditionContext& getConditionContext( const EventContext& ctx ) const { + return details::ConditionsStore::instance().getConditionContext( ctx ); + } + + private: + template <typename T> + friend class ConditionAccessor; + + /// Register a ConditionAccessor instance to internal bookkeeping. + template <typename T> + void registerConditionAccessor( ConditionAccessor<T>& accessor ) { + if ( this->targetFSMState() == Gaudi::StateMachine::INITIALIZED ) { + details::ConditionsStore::instance().declareKey( accessor.key() ); + } else { + m_delayedRegistrations.push_back( [this, &accessor]() { registerConditionAccessor( accessor ); } ); + } + } + + /// Helper to postpone the registration to the backend. + /// + /// In Gaudi properties are set during initialize, so, in this example implementation, + /// the registration has to be delayed from construction time to initialization time. + std::list<std::function<void()>> m_delayedRegistrations; + }; +} // namespace Gaudi::Examples::Conditions diff --git a/GaudiExamples/src/ConditionAccessor/Backend.cpp b/GaudiExamples/src/ConditionAccessor/Backend.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2a07f6220b6312044506f2bfa3a053a0b6598eb1 --- /dev/null +++ b/GaudiExamples/src/ConditionAccessor/Backend.cpp @@ -0,0 +1,37 @@ +#include <Gaudi/Examples/Conditions/ConditionAccessorHolder.h> +#include <GaudiKernel/System.h> + +namespace Gaudi::Examples::Conditions::details { + ConditionsStore& ConditionsStore::instance() { + static ConditionsStore store{}; + return store; + } + + void ConditionsStore::declareKey( const ConditionKey& key ) { + if ( std::get<1>( m_requestedKeys.emplace( key ) ) ) { + std::cout << "ConditionsStore: requested condition " << key << '\n'; + } + } + + const ConditionsStore::ConditionContext& ConditionsStore::getConditionContext( const EventContext& ctx ) const { + static ConditionContext current_iov = 0; + + current_iov = ctx.evt() % 2; + return current_iov; + } + + const std::any* ConditionsStore::getConditionAny( const ConditionKey& key, const ConditionContext& ctx ) const { + std::lock_guard<std::mutex> lg{m_storageMtx}; + if ( m_storage.find( ctx ) == end( m_storage ) ) { + std::cout << "ConditionsStore: first access to store " << ctx << '\n'; + auto& store = m_storage[ctx]; + for ( const auto& key : m_requestedKeys ) { + std::cout << "ConditionsStore: prepare condition " << key << '\n'; + store.emplace( key, ctx ); + } + } else { + std::cout << "ConditionsStore: using existing store " << ctx << '\n'; + } + return &m_storage[ctx][key]; + } +} // namespace Gaudi::Examples::Conditions::details diff --git a/GaudiExamples/src/ConditionAccessor/User.cpp b/GaudiExamples/src/ConditionAccessor/User.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ab681fcb7060b9302eb4cd88ad89a49ed2a16c96 --- /dev/null +++ b/GaudiExamples/src/ConditionAccessor/User.cpp @@ -0,0 +1,41 @@ +#include <Gaudi/Algorithm.h> +#include <Gaudi/Examples/Conditions/ConditionAccessorHolder.h> + +namespace Gaudi::Examples::Conditions { + /// Example of a simple algorithm using conditions via the ConditionAccessorHolder + /// pattern. + class UserAlg : public ConditionAccessorHolder<Algorithm> { + /// Delegate to base class constructor. + using base_class::base_class; + + /// Data member to access condition values for the current event. + ConditionAccessor<int> m_cond{this, "MyCondition", "condition/key"}; + + StatusCode initialize() override { + auto status = base_class::initialize(); + if ( !status ) return status; + + info() << "initialize" << endmsg; + + return status; + } + + StatusCode execute( const EventContext& ctx ) const override { + // get the condition slot for the current event + const auto& condCtx = getConditionContext( ctx ); + // get the condition value from the current condition slot. + const auto& cond = m_cond.get( condCtx ); + + info() << "got condition value " << cond << endmsg; + + return StatusCode::SUCCESS; + } + + StatusCode finalize() override { + info() << "finalize" << endmsg; + + return base_class::finalize(); + } + }; + DECLARE_COMPONENT( UserAlg ) +} // namespace Gaudi::Examples::Conditions diff --git a/GaudiExamples/tests/qmtest/gaudiexamples.qms/conditions.qmt b/GaudiExamples/tests/qmtest/gaudiexamples.qms/conditions.qmt new file mode 100644 index 0000000000000000000000000000000000000000..a28dfd9a52d5abcdcfe4c204cf5672e5aa834391 --- /dev/null +++ b/GaudiExamples/tests/qmtest/gaudiexamples.qms/conditions.qmt @@ -0,0 +1,16 @@ +<?xml version="1.0" ?> +<!DOCTYPE extension PUBLIC '-//QM/2.3/Extension//EN' 'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'> +<extension class="GaudiTest.GaudiExeTest" kind="test"> +<argument name="program"><text>gaudirun.py</text></argument> +<argument name="options"><text> +from Gaudi.Configuration import * +from Configurables import Gaudi__Examples__Conditions__UserAlg as UA + +ApplicationMgr(EvtSel="NONE", + EvtMax=5, + TopAlg=[ + UA("ConditionsUser1"), + UA("ConditionsUser2", MyCondition="another/key")]) +</text></argument> +<argument name="reference"><text>refs/ConditionsAccess.ref</text></argument> +</extension> diff --git a/GaudiExamples/tests/qmtest/refs/ConditionsAccess.ref b/GaudiExamples/tests/qmtest/refs/ConditionsAccess.ref new file mode 100644 index 0000000000000000000000000000000000000000..5a117b3ca37abf7a2c3c67bf9519bcc755fcf936 --- /dev/null +++ b/GaudiExamples/tests/qmtest/refs/ConditionsAccess.ref @@ -0,0 +1,46 @@ +# setting LC_ALL to "C" +ApplicationMgr SUCCESS +==================================================================================================================================== + Welcome to ApplicationMgr (GaudiCoreSvc v30r5) + running on marco-XPS-13 on Fri Feb 1 12:41:18 2019 +==================================================================================================================================== +ApplicationMgr INFO Application Manager Configured successfully +ConditionsStore: requested condition condition/key +ConditionsUser1 INFO initialize +ConditionsStore: requested condition another/key +ConditionsUser2 INFO initialize +EventLoopMgr WARNING Unable to locate service "EventSelector" +EventLoopMgr WARNING No events will be processed from external input. +HistogramPersis...WARNING Histograms saving not required. +ApplicationMgr INFO Application Manager Initialized successfully +ApplicationMgr INFO Application Manager Started successfully +ConditionsStore: first access to store 0 +ConditionsStore: prepare condition another/key +ConditionsStore: prepare condition condition/key +ConditionsUser1 INFO got condition value 0 +ConditionsStore: using existing store 0 +ConditionsUser2 INFO got condition value 0 +ConditionsStore: first access to store 1 +ConditionsStore: prepare condition another/key +ConditionsStore: prepare condition condition/key +ConditionsUser1 INFO got condition value 1 +ConditionsStore: using existing store 1 +ConditionsUser2 INFO got condition value 1 +ConditionsStore: using existing store 0 +ConditionsUser1 INFO got condition value 0 +ConditionsStore: using existing store 0 +ConditionsUser2 INFO got condition value 0 +ConditionsStore: using existing store 1 +ConditionsUser1 INFO got condition value 1 +ConditionsStore: using existing store 1 +ConditionsUser2 INFO got condition value 1 +ConditionsStore: using existing store 0 +ConditionsUser1 INFO got condition value 0 +ConditionsStore: using existing store 0 +ConditionsUser2 INFO got condition value 0 +ApplicationMgr INFO Application Manager Stopped successfully +ConditionsUser1 INFO finalize +ConditionsUser2 INFO finalize +EventLoopMgr INFO Histograms converted successfully according to request. +ApplicationMgr INFO Application Manager Finalized successfully +ApplicationMgr INFO Application Manager Terminated successfully