The Gaudi Framework  master (37c0b60a)
AlgContextSvc.cpp
Go to the documentation of this file.
1 /***********************************************************************************\
2 * (c) Copyright 1998-2024 CERN for the benefit of the LHCb and ATLAS collaborations *
3 * *
4 * This software is distributed under the terms of the Apache version 2 licence, *
5 * copied verbatim in the file "LICENSE". *
6 * *
7 * In applying this licence, CERN does not waive the privileges and immunities *
8 * granted to it by virtue of its status as an Intergovernmental Organization *
9 * or submit itself to any jurisdiction. *
10 \***********************************************************************************/
13 #include <GaudiKernel/IAlgorithm.h>
17 #include <GaudiKernel/MsgStream.h>
18 #include <GaudiKernel/Service.h>
19 #include <GaudiKernel/StatusCode.h>
20 #include <boost/thread.hpp>
21 #include <vector>
22 
23 // ============================================================================
34 class AlgContextSvc : public extends<Service, IAlgContextSvc, IIncidentListener> {
35 public:
37  StatusCode setCurrentAlg( IAlgorithm* a, const EventContext& context ) override;
39  StatusCode unSetCurrentAlg( IAlgorithm* a, const EventContext& context ) override;
41  IAlgorithm* currentAlg() const override;
43  const IAlgContextSvc::Algorithms& algorithms() const override {
44  if ( !m_algorithms.get() ) {
45  static IAlgContextSvc::Algorithms empty;
46  return empty;
47  }
48  return *m_algorithms;
49  }
50 
51 public:
52  void handle( const Incident& ) override;
53 
54 public:
55  StatusCode initialize() override;
56  StatusCode start() override;
57  StatusCode finalize() override;
58 
59 public:
60  using extends::extends;
61 
62 private:
64  boost::thread_specific_ptr<IAlgContextSvc::Algorithms> m_algorithms;
67 
68  Gaudi::Property<bool> m_check{ this, "Check", true, "Flag to perform more checks" };
69  Gaudi::Property<bool> m_bypassInc{ this, "BypassIncidents", false,
70  "Flag to bypass begin/endevent incident requirement" };
72 };
73 
75 
76 StatusCode AlgContextSvc::initialize() {
77  // Initialize the base class
79  if ( sc.isFailure() ) { return sc; }
80  // Incident Service
81  if ( m_inc ) {
82  m_inc->removeListener( this );
83  m_inc.reset();
84  }
85  // perform more checks?
87  numSlots = ( 1 > numSlots ) ? 1 : numSlots;
88  if ( numSlots > 1000 ) {
89  warning() << "Num Slots are greater than 1000. Is this correct? numSlots=" << numSlots << endmsg;
90  numSlots = 1000;
91  warning() << "Setting numSlots to " << numSlots << endmsg;
92  }
93  m_inEvtLoop.resize( numSlots, 0 );
94 
95  if ( m_check ) {
96  m_inc = Service::service( "IncidentSvc", true );
97  if ( !m_inc ) {
98  error() << "Could not locate 'IncidentSvc'" << endmsg;
99  return StatusCode::FAILURE;
100  }
101  m_inc->addListener( this, IncidentType::BeginEvent );
102  m_inc->addListener( this, IncidentType::EndEvent );
103  }
104  if ( m_algorithms.get() && !m_algorithms->empty() ) {
105  warning() << "Non-empty stack of algorithms #" << m_algorithms->size() << endmsg;
106  }
107  return StatusCode::SUCCESS;
108 }
109 
110 // implementation of start
111 // needs to be removed once we have a proper service
112 // for getting configuration information at initialization time
113 // S. Kama
115  auto sc = Service::start();
117  numSlots = ( 1 > numSlots ) ? 1 : numSlots;
118  if ( numSlots > 1000 ) {
119  warning() << "Num Slots are greater than 1000. Is this correct? numSlots=" << numSlots << endmsg;
120  numSlots = 1000;
121  }
122  m_inEvtLoop.resize( numSlots, 0 );
123 
124  return sc;
125 }
126 
128  if ( m_algorithms.get() && !m_algorithms->empty() ) {
129  warning() << "Non-empty stack of algorithms #" << m_algorithms->size() << endmsg;
130  }
131  // Incident Service
132  if ( m_inc ) {
133  m_inc->removeListener( this );
134  m_inc.reset();
135  }
136  // finalize the base class
137  return Service::finalize();
138 }
139 
141  if ( !a ) {
142  warning() << "IAlgorithm* points to NULL" << endmsg;
144  }
145  if ( !m_bypassInc ) {
146  if ( !m_inEvtLoop[context.valid() ? context.slot() : 0] ) return StatusCode::SUCCESS;
147  }
148  // check whether thread-local algorithm list already exists
149  // if not, create it
150  if ( !m_algorithms.get() ) { m_algorithms.reset( new IAlgContextSvc::Algorithms() ); }
151  if ( a->type() != "IncidentProcAlg" ) m_algorithms->push_back( a );
152 
153  return StatusCode::SUCCESS;
154 }
155 
157  // check whether thread-local algorithm list already exists
158  // if not, create it
159  if ( !m_algorithms.get() ) { m_algorithms.reset( new IAlgContextSvc::Algorithms() ); }
160 
161  if ( !a ) {
162  warning() << "IAlgorithm* points to NULL" << endmsg;
164  }
165 
166  if ( !m_bypassInc ) {
167  if ( !m_inEvtLoop[context.valid() ? context.slot() : 0] ) return StatusCode::SUCCESS;
168  }
169 
170  if ( a->type() != "IncidentProcAlg" ) {
171  // if ( m_algorithms->empty() || m_algorithms->back() != a ){
172  // error() << "Algorithm stack is invalid" << endmsg ;
173  // return StatusCode::FAILURE ;
174  // }
175  if ( !m_algorithms->empty() ) {
176  if ( m_algorithms->back() == a ) { m_algorithms->pop_back(); }
177  }
178  }
179  return StatusCode::SUCCESS;
180 }
181 
183  return ( m_algorithms.get() && !m_algorithms->empty() ) ? m_algorithms->back() : nullptr;
184 }
185 
186 void AlgContextSvc::handle( const Incident& inc ) {
187  // some false sharing is possible but it should be negligible
188  auto currSlot = inc.context().slot();
189  if ( currSlot == EventContext::INVALID_CONTEXT_ID ) { currSlot = 0; }
190  if ( inc.type() == "BeginEvent" ) {
191  m_inEvtLoop[currSlot] = 1;
192  } else if ( inc.type() == "EndEvent" ) {
193  m_inEvtLoop[currSlot] = 0;
194  }
195 
196  // This check is invalidated with RTTI AlgContext object.
197  // Whole service needs to be rewritten. Commenting the error until then
198  // to prevent test failures.
199  // if ( m_algorithms.get() && !m_algorithms->empty() ) {
200  // //skip incident processing algorithm endevent incident
201  // if((m_algorithms->size()!=1) ||
202  // (m_algorithms->back()->type()!="IncidentProcAlg")){
203  // error() << "Non-empty stack of algorithms #"
204  // << m_algorithms->size() << endmsg ;
205  // }
206  // }
207 }
208 
209 // From here on, we have unit tests.
210 #if defined( BUILD_UNIT_TESTS )
211 # if __has_include( <catch2/catch.hpp>)
212 // Catch2 v2
213 # include <catch2/catch.hpp>
214 # else
215 // Catch2 v3
216 # include <catch2/catch_test_macros.hpp>
217 # endif
218 
219 # include <Gaudi/Algorithm.h>
220 
221 namespace mock {
222  struct ServiceLocator : implements<ISvcLocator> {
223  std::list<IService*> m_services;
224  SmartIF<IService> m_null_svc;
225 
226  const std::list<IService*>& getServices() const override { return m_services; }
227  bool existsService( std::string_view /* name */ ) const override { return false; }
228  SmartIF<IService>& service( const Gaudi::Utils::TypeNameString& /* typeName */,
229  const bool /* createIf */ ) override {
230  return m_null_svc;
231  }
232  };
233  struct Algorithm : Gaudi::Algorithm {
235  StatusCode execute( const EventContext& ) const override { return StatusCode::SUCCESS; }
236  };
237 } // namespace mock
238 
239 TEST_CASE( "AlgContextSvc basic operations" ) {
240  SmartIF<ISvcLocator> svcLoc{ new mock::ServiceLocator };
241  AlgContextSvc acs{ "AlgContextSvc", svcLoc };
242  REQUIRE( acs.setProperty( "BypassIncidents", true ).isSuccess() ); // do not try to invoke incident svc
243 
244  // check that algorithms() never returns a nullptr
245  // (see https://gitlab.cern.ch/gaudi/Gaudi/-/issues/304)
246  auto empty_algorithms = &acs.algorithms();
247  REQUIRE( empty_algorithms != nullptr );
248  CHECK( empty_algorithms->empty() );
249 
250  mock::Algorithm alg{ "dummy", svcLoc };
252 
253  // add an algorithm
254  REQUIRE( acs.setCurrentAlg( &alg, ctx ).isSuccess() );
255  {
256  auto algorithms = &acs.algorithms();
257 
258  // what we get before adding the first algorithm is a dummy static instance
259  // see https://gitlab.cern.ch/gaudi/Gaudi/-/issues/304#note_7930366
260  CHECK( empty_algorithms != algorithms );
261 
262  REQUIRE( algorithms != nullptr );
263  REQUIRE( algorithms->size() == 1 );
264  CHECK( algorithms->at( 0 ) == &alg );
265  }
266 
267  // removing the algorithm results in a an empty list
268  REQUIRE( acs.unSetCurrentAlg( &alg, ctx ).isSuccess() );
269  {
270  auto algorithms = &acs.algorithms();
271  REQUIRE( algorithms != nullptr );
272  REQUIRE( algorithms->empty() );
273  }
274 }
275 #endif
Histograms_with_global.algorithms
algorithms
Definition: Histograms_with_global.py:19
EventContext::valid
bool valid() const
Definition: EventContext.h:54
std::vector::resize
T resize(T... args)
AlgContextSvc::currentAlg
IAlgorithm * currentAlg() const override
accessor to current algorithm:
Definition: AlgContextSvc.cpp:182
AlgContextSvc::m_check
Gaudi::Property< bool > m_check
Definition: AlgContextSvc.cpp:68
Service::initialize
StatusCode initialize() override
Definition: Service.cpp:118
AlgContextSvc::m_inc
SmartIF< IIncidentSvc > m_inc
pointer to Incident Service
Definition: AlgContextSvc.cpp:66
AlgContextSvc::handle
void handle(const Incident &) override
Definition: AlgContextSvc.cpp:186
std::list< IService * >
AlgContextSvc::m_inEvtLoop
std::vector< int > m_inEvtLoop
Definition: AlgContextSvc.cpp:71
AlgContextSvc::start
StatusCode start() override
Definition: AlgContextSvc.cpp:114
Service::start
StatusCode start() override
Definition: Service.cpp:187
std::vector< IAlgorithm * >
SmartIF::reset
void reset(TYPE *ptr=nullptr)
Set the internal pointer to the passed one disposing of the old one.
Definition: SmartIF.h:96
Algorithm
Alias for backward compatibility.
Definition: Algorithm.h:58
AlgContextSvc::setCurrentAlg
StatusCode setCurrentAlg(IAlgorithm *a, const EventContext &context) override
set the currently executing algorithm ("push_back")
Definition: AlgContextSvc.cpp:140
AlgContextSvc::m_algorithms
boost::thread_specific_ptr< IAlgContextSvc::Algorithms > m_algorithms
the stack of current algorithms
Definition: AlgContextSvc.cpp:64
EventContext::INVALID_CONTEXT_ID
static constexpr ContextID_t INVALID_CONTEXT_ID
Definition: EventContext.h:39
IAlgorithm::type
virtual const std::string & type() const =0
The type of the algorithm.
ConcurrencyFlags.h
StatusCode.h
AlgContextSvc::m_bypassInc
Gaudi::Property< bool > m_bypassInc
Definition: AlgContextSvc.cpp:69
Service::finalize
StatusCode finalize() override
Definition: Service.cpp:222
IIncidentSvc.h
ManySmallAlgs.alg
alg
Definition: ManySmallAlgs.py:81
Gaudi::Concurrency::ConcurrencyFlags::numConcurrentEvents
static GAUDI_API std::size_t numConcurrentEvents()
number of Concurrent Events (for MT)
Definition: ConcurrencyFlags.h:57
Incident::context
EventContext context() const
Access to the EventContext of the source of the incident.
Definition: Incident.h:60
Gaudi::Utils::TypeNameString
Helper class to parse a string of format "type/name".
Definition: TypeNameString.h:20
GaudiPython.Pythonizations.ctx
ctx
Definition: Pythonizations.py:578
StatusCode
Definition: StatusCode.h:65
IAlgorithm
Definition: IAlgorithm.h:38
GaudiPython.Pythonizations.execute
execute
Definition: Pythonizations.py:578
EventContext::slot
ContextID_t slot() const
Definition: EventContext.h:51
Gaudi::Algorithm
Base class from which all concrete algorithm classes should be derived.
Definition: Algorithm.h:90
AlgContextSvc::algorithms
const IAlgContextSvc::Algorithms & algorithms() const override
get the stack of executed algorithms
Definition: AlgContextSvc.cpp:43
AlgContextSvc::unSetCurrentAlg
StatusCode unSetCurrentAlg(IAlgorithm *a, const EventContext &context) override
remove the algorithm ("pop_back")
Definition: AlgContextSvc.cpp:156
Algorithm.h
SmartIF< IIncidentSvc >
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:202
extends
Base class used to extend a class implementing other interfaces.
Definition: extends.h:20
Service.h
AlgContextSvc::finalize
StatusCode finalize() override
Definition: AlgContextSvc.cpp:127
StatusCode::isFailure
bool isFailure() const
Definition: StatusCode.h:129
implements
Base class used to implement the interfaces.
Definition: implements.h:19
StatusCode::SUCCESS
constexpr static const auto SUCCESS
Definition: StatusCode.h:100
IAlgContextSvc.h
AlgContextSvc
Definition: AlgContextSvc.cpp:34
IIncidentListener.h
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:46
EventContext
Definition: EventContext.h:34
Incident::type
const std::string & type() const
Access to the incident type.
Definition: Incident.h:48
Gaudi::Algorithm::Algorithm
Algorithm(std::string name, ISvcLocator *svcloc, std::string version=PACKAGE_VERSION)
Constructor.
Definition: Algorithm.h:101
IAlgorithm.h
StatusCode::FAILURE
constexpr static const auto FAILURE
Definition: StatusCode.h:101
ISvcLocator.h
StatusCode::RECOVERABLE
constexpr static const auto RECOVERABLE
Definition: StatusCode.h:102
Incident
Definition: Incident.h:27
Service::service
StatusCode service(const std::string &name, const T *&psvc, bool createIf=true) const
Access a service by name, creating it if it doesn't already exist.
Definition: Service.h:89
Gaudi::Property< bool >
MsgStream.h
CHECK
#define CHECK(x, y)
Definition: PartitionSwitchTool.cpp:57
AlgContextSvc::initialize
StatusCode initialize() override
Definition: AlgContextSvc.cpp:76