diff --git a/GaudiCommonSvc/CMakeLists.txt b/GaudiCommonSvc/CMakeLists.txt index 0fc495d0287ccee882584865286bc6c16370b4bc..089ea51263a85c116e8d92ca2d3ae51f7d70832e 100644 --- a/GaudiCommonSvc/CMakeLists.txt +++ b/GaudiCommonSvc/CMakeLists.txt @@ -70,3 +70,22 @@ if(GAUDI_USE_AIDA) src/HistogramPersistencySvc/HistogramPersistencySvc.cpp) target_link_libraries(GaudiCommonSvc PRIVATE AIDA::aida GaudiCommonSvcLib) endif() + +if(BUILD_TESTING) + gaudi_add_executable(test_AlgContextSvc + SOURCES + src/AlgContextSvc.cpp + LINK + Boost::headers + Catch2::Catch2WithMain + Gaudi::GaudiKernel + TEST + ) + target_compile_definitions(test_AlgContextSvc PRIVATE BUILD_UNIT_TESTS) + catch_discover_tests(test_AlgContextSvc + TEST_PREFIX GaudiCommonSvc. + PROPERTIES + LABELS "Gaudi" + LABELS "Gaudi.GaudiCommonSvc" + ) +endif() diff --git a/GaudiCommonSvc/src/AlgContextSvc.cpp b/GaudiCommonSvc/src/AlgContextSvc.cpp index 769d2d435393144573e963ee436e0b947cd25f51..1ab934d57f877d7a5f24d810452687bf88ca903b 100644 --- a/GaudiCommonSvc/src/AlgContextSvc.cpp +++ b/GaudiCommonSvc/src/AlgContextSvc.cpp @@ -1,5 +1,5 @@ /***********************************************************************************\ -* (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations * +* (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations * * * * This software is distributed under the terms of the Apache version 2 licence, * * copied verbatim in the file "LICENSE". * @@ -8,36 +8,71 @@ * granted to it by virtue of its status as an Intergovernmental Organization * * or submit itself to any jurisdiction. * \***********************************************************************************/ -// ============================================================================ -// Include files -// ============================================================================ -// Local -// ============================================================================ -#include "AlgContextSvc.h" -// ============================================================================ -// GaudiKernel -// ============================================================================ -#include "GaudiKernel/ConcurrencyFlags.h" -#include "GaudiKernel/IIncidentSvc.h" -#include "GaudiKernel/ISvcLocator.h" -#include "GaudiKernel/MsgStream.h" +#include <GaudiKernel/ConcurrencyFlags.h> +#include <GaudiKernel/IAlgContextSvc.h> +#include <GaudiKernel/IAlgorithm.h> +#include <GaudiKernel/IIncidentListener.h> +#include <GaudiKernel/IIncidentSvc.h> +#include <GaudiKernel/ISvcLocator.h> +#include <GaudiKernel/MsgStream.h> +#include <GaudiKernel/Service.h> +#include <GaudiKernel/StatusCode.h> +#include <boost/thread.hpp> +#include <vector> // ============================================================================ -/** @file - * Implementation firl for class AlgContextSvc +/** @class AlgContextSvc + * Simple implementation of interface IAlgContextSvc + * for Algorithm Context Service * @author ATLAS Collaboration - * @author modified by Vanya BELYAEV ibelyaev@physics.syr.edu - * @author modified by Sami Kama - * @date 2017-03-17 (modified) - */ -// ============================================================================ -/** Instantiation of a static factory class used by clients to create - * instances of this service + * @author modified by Vanya BELYAEV ibelyaev@physics.sye.edu + * @author incident listening removed by Benedikt Hegner + * @author S. Kama. Added multi-context incident based queueing to support + * Serial-MT cases + * @date 2007-03-07 (modified) */ +class AlgContextSvc : public extends<Service, IAlgContextSvc, IIncidentListener> { +public: + /// set the currently executing algorithm ("push_back") @see IAlgContextSvc + StatusCode setCurrentAlg( IAlgorithm* a, const EventContext& context ) override; + /// remove the algorithm ("pop_back") @see IAlgContextSvc + StatusCode unSetCurrentAlg( IAlgorithm* a, const EventContext& context ) override; + /// accessor to current algorithm: @see IAlgContextSvc + IAlgorithm* currentAlg() const override; + /// get the stack of executed algorithms @see IAlgContextSvc + const IAlgContextSvc::Algorithms& algorithms() const override { + if ( !m_algorithms.get() ) { + static IAlgContextSvc::Algorithms empty; + return empty; + } + return *m_algorithms; + } + +public: + void handle( const Incident& ) override; + +public: + StatusCode initialize() override; + StatusCode start() override; + StatusCode finalize() override; + +public: + using extends::extends; + +private: + /// the stack of current algorithms + boost::thread_specific_ptr<IAlgContextSvc::Algorithms> m_algorithms; + /// pointer to Incident Service + SmartIF<IIncidentSvc> m_inc = nullptr; + + Gaudi::Property<bool> m_check{ this, "Check", true, "Flag to perform more checks" }; + Gaudi::Property<bool> m_bypassInc{ this, "BypassIncidents", false, + "Flag to bypass begin/endevent incident requirement" }; + std::vector<int> m_inEvtLoop; +}; + DECLARE_COMPONENT( AlgContextSvc ) -// ============================================================================ -// standard initialization of the service -// ============================================================================ + StatusCode AlgContextSvc::initialize() { // Initialize the base class StatusCode sc = Service::initialize(); @@ -89,9 +124,6 @@ StatusCode AlgContextSvc::start() { return sc; } -// ============================================================================ -// standard finalization of the service @see IService -// ============================================================================ StatusCode AlgContextSvc::finalize() { if ( m_algorithms.get() && !m_algorithms->empty() ) { warning() << "Non-empty stack of algorithms #" << m_algorithms->size() << endmsg; @@ -104,9 +136,7 @@ StatusCode AlgContextSvc::finalize() { // finalize the base class return Service::finalize(); } -// ============================================================================ -// set the currently executing algorithm ("push_back") @see IAlgContextSvc -// ============================================================================ + StatusCode AlgContextSvc::setCurrentAlg( IAlgorithm* a, const EventContext& context ) { if ( !a ) { warning() << "IAlgorithm* points to NULL" << endmsg; @@ -122,9 +152,7 @@ StatusCode AlgContextSvc::setCurrentAlg( IAlgorithm* a, const EventContext& cont return StatusCode::SUCCESS; } -// ============================================================================ -// remove the algorithm ("pop_back") @see IAlgContextSvc -// ============================================================================ + StatusCode AlgContextSvc::unSetCurrentAlg( IAlgorithm* a, const EventContext& context ) { // check whether thread-local algorithm list already exists // if not, create it @@ -150,17 +178,13 @@ StatusCode AlgContextSvc::unSetCurrentAlg( IAlgorithm* a, const EventContext& co } return StatusCode::SUCCESS; } -// ============================================================================ -/// accessor to current algorithm: @see IAlgContextSvc -// ============================================================================ + IAlgorithm* AlgContextSvc::currentAlg() const { return ( m_algorithms.get() && !m_algorithms->empty() ) ? m_algorithms->back() : nullptr; } -// ============================================================================ -// handle incident @see IIncidentListener -// ============================================================================ + void AlgContextSvc::handle( const Incident& inc ) { - // some false sharing is possible but it should be negligable + // some false sharing is possible but it should be negligible auto currSlot = inc.context().slot(); if ( currSlot == EventContext::INVALID_CONTEXT_ID ) { currSlot = 0; } if ( inc.type() == "BeginEvent" ) { @@ -181,7 +205,71 @@ void AlgContextSvc::handle( const Incident& inc ) { // } // } } -// ============================================================================ -// ============================================================================ -/// The END -// ============================================================================ + +// From here on, we have unit tests. +#if defined( BUILD_UNIT_TESTS ) +# if __has_include( <catch2/catch.hpp>) +// Catch2 v2 +# include <catch2/catch.hpp> +# else +// Catch2 v3 +# include <catch2/catch_test_macros.hpp> +# endif + +# include <Gaudi/Algorithm.h> + +namespace mock { + struct ServiceLocator : implements<ISvcLocator> { + std::list<IService*> m_services; + SmartIF<IService> m_null_svc; + + const std::list<IService*>& getServices() const override { return m_services; } + bool existsService( std::string_view /* name */ ) const override { return false; } + SmartIF<IService>& service( const Gaudi::Utils::TypeNameString& /* typeName */, + const bool /* createIf */ ) override { + return m_null_svc; + } + }; + struct Algorithm : Gaudi::Algorithm { + using Gaudi::Algorithm::Algorithm; + StatusCode execute( const EventContext& ) const override { return StatusCode::SUCCESS; } + }; +} // namespace mock + +TEST_CASE( "AlgContextSvc basic operations" ) { + SmartIF<ISvcLocator> svcLoc{ new mock::ServiceLocator }; + AlgContextSvc acs{ "AlgContextSvc", svcLoc }; + REQUIRE( acs.setProperty( "BypassIncidents", true ).isSuccess() ); // do not try to invoke incident svc + + // check that algorithms() never returns a nullptr + // (see https://gitlab.cern.ch/gaudi/Gaudi/-/issues/304) + auto empty_algorithms = &acs.algorithms(); + REQUIRE( empty_algorithms != nullptr ); + CHECK( empty_algorithms->empty() ); + + mock::Algorithm alg{ "dummy", svcLoc }; + EventContext ctx; + + // add an algorithm + REQUIRE( acs.setCurrentAlg( &alg, ctx ).isSuccess() ); + { + auto algorithms = &acs.algorithms(); + + // what we get before adding the first algorithm is a dummy static instance + // see https://gitlab.cern.ch/gaudi/Gaudi/-/issues/304#note_7930366 + CHECK( empty_algorithms != algorithms ); + + REQUIRE( algorithms != nullptr ); + REQUIRE( algorithms->size() == 1 ); + CHECK( algorithms->at( 0 ) == &alg ); + } + + // removing the algorithm results in a an empty list + REQUIRE( acs.unSetCurrentAlg( &alg, ctx ).isSuccess() ); + { + auto algorithms = &acs.algorithms(); + REQUIRE( algorithms != nullptr ); + REQUIRE( algorithms->empty() ); + } +} +#endif diff --git a/GaudiCommonSvc/src/AlgContextSvc.h b/GaudiCommonSvc/src/AlgContextSvc.h deleted file mode 100644 index 862c6b5d612c690fdc2c9f590b687b7a31662873..0000000000000000000000000000000000000000 --- a/GaudiCommonSvc/src/AlgContextSvc.h +++ /dev/null @@ -1,89 +0,0 @@ -/***********************************************************************************\ -* (c) Copyright 1998-2019 CERN for the benefit of the LHCb and ATLAS collaborations * -* * -* This software is distributed under the terms of the Apache version 2 licence, * -* copied verbatim in the file "LICENSE". * -* * -* In applying this licence, CERN does not waive the privileges and immunities * -* granted to it by virtue of its status as an Intergovernmental Organization * -* or submit itself to any jurisdiction. * -\***********************************************************************************/ -// ============================================================================ -#ifndef GAUDISVC_ALGCONTEXTSVC_H -#define GAUDISVC_ALGCONTEXTSVC_H 1 -// ============================================================================ -// Include files -// ============================================================================ -#include <vector> -// ============================================================================ -// GaudiKernel -// ============================================================================ -#include "GaudiKernel/IAlgContextSvc.h" -#include "GaudiKernel/IAlgorithm.h" -#include "GaudiKernel/IIncidentListener.h" -#include "GaudiKernel/Service.h" -#include "GaudiKernel/StatusCode.h" -#include <boost/thread.hpp> - -// ============================================================================ -// Forward declarations -// ============================================================================ -class IIncidentSvc; -// ============================================================================ -/** @class AlgContexSvc - * Simple implementation of interface IAlgContextSvc - * for Algorithm Context Service - * @author ATLAS Collaboration - * @author modified by Vanya BELYAEV ibelyaev@physics.sye.edu - * @author incident listening removed by Benedikt Hegner - * @author S. Kama. Added multi-context incident based queueing to support - * Serial-MT cases - * @date 2007-03-07 (modified) - */ -class AlgContextSvc : public extends<Service, IAlgContextSvc, IIncidentListener> { -public: - /// set the currently executing algorithm ("push_back") @see IAlgContextSvc - StatusCode setCurrentAlg( IAlgorithm* a, const EventContext& context ) override; - /// remove the algorithm ("pop_back") @see IAlgContextSvc - StatusCode unSetCurrentAlg( IAlgorithm* a, const EventContext& context ) override; - /// accessor to current algorithm: @see IAlgContextSvc - IAlgorithm* currentAlg() const override; - /// get the stack of executed algorithms @see IAlgContextSvc - const IAlgContextSvc::Algorithms& algorithms() const override { return *m_algorithms; } - -public: - /// handle incident @see IIncidentListener - void handle( const Incident& ) override; - -public: - /// standard initialization of the service @see IService - StatusCode initialize() override; - StatusCode start() override; - /// standard finalization of the service @see IService - StatusCode finalize() override; - -public: - using extends::extends; - -private: - // default/copy constructor & asignment are deleted - AlgContextSvc() = delete; - AlgContextSvc( const AlgContextSvc& ) = delete; - AlgContextSvc& operator=( const AlgContextSvc& ) = delete; - -private: - // the stack of current algorithms - boost::thread_specific_ptr<IAlgContextSvc::Algorithms> m_algorithms; ///< the stack of current algorithms - // pointer to Incident Service - SmartIF<IIncidentSvc> m_inc = nullptr; ///< pointer to Incident Service - - Gaudi::Property<bool> m_check{ this, "Check", true, "Flag to perform more checks" }; - Gaudi::Property<bool> m_bypassInc{ this, "BypassIncidents", false, - "Flag to bypass begin/endevent incident requirement" }; - std::vector<int> m_inEvtLoop; -}; - -// ============================================================================ -// The END -// ============================================================================ -#endif // GAUDISVC_ALGCONTEXTSVC_H