The Gaudi Framework  master (34daa81a)
Loading...
Searching...
No Matches
IncidentSvc.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2025 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\***********************************************************************************/
11
12#include "IncidentSvc.h"
20#include <GaudiKernel/SmartIF.h>
21
23
24namespace {
25 static const std::string s_unknown = "<unknown>";
26 // Helper to get the name of the listener
27 inline const std::string& getListenerName( IIncidentListener* lis ) {
28 SmartIF<INamedInterface> iNamed( lis );
29 return iNamed ? iNamed->name() : s_unknown;
30 }
31} // namespace
32
33#define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
34#define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
35
36#define DEBMSG ON_DEBUG debug()
37#define VERMSG ON_VERBOSE verbose()
38
39IncidentSvc::IncidentSvc( const std::string& name, ISvcLocator* svc ) : base_class( name, svc ) {}
40IncidentSvc::~IncidentSvc() { auto lock = std::scoped_lock{ m_listenerMapMutex }; }
42 DEBMSG << m_timer.outputUserTime( "Incident timing: Mean(+-rms)/Min/Max:%3%(+-%4%)/%6%/%7%[ms] ", System::milliSec )
43 << m_timer.outputUserTime( "Total:%2%[s]", System::Sec ) << endmsg;
44
45 m_firedIncidents.clear();
46
47 // Finalize this specific service
48 return Service::finalize();
49}
50void IncidentSvc::addListener( IIncidentListener* lis, const std::string& type, long prio, bool rethrow,
51 bool singleShot ) {
52 static const std::string all{ "ALL" };
53 auto lock = std::scoped_lock{ m_listenerMapMutex };
54
55 const std::string& ltype = ( !type.empty() ? type : all );
56
57 // find if the type already exists
58 auto itMap = m_listenerMap.find( ltype );
59 if ( itMap == m_listenerMap.end() ) {
60 // if not found, create and insert now a list of listeners
61 auto p = m_listenerMap.insert( { ltype, std::make_unique<ListenerList>() } );
62 if ( !p.second ) { /* OOPS */
63 }
64 itMap = p.first;
65 }
66 auto& llist = *itMap->second;
67 // add Listener ordered by priority -- higher priority first,
68 // and then add behind listeneres with the same priority
69 // -- so we skip over all items with higher or same priority
70 auto i = std::partition_point( std::begin( llist ), std::end( llist ),
71 [&]( const Listener& j ) { return j.priority >= prio; } );
72 // We insert before the current position
73 DEBMSG << "Adding [" << type << "] listener '" << getListenerName( lis ) << "' with priority " << prio << endmsg;
74 llist.emplace( i, IIncidentSvc::Listener{ lis, prio, rethrow, singleShot } );
75}
76IncidentSvc::ListenerMap::iterator
77IncidentSvc::removeListenerFromList( ListenerMap::iterator i, IIncidentListener* item, bool scheduleRemoval ) {
78 auto match = [&]( ListenerList::const_reference j ) { return !item || item == j.iListener; };
79
80 auto& c = *( i->second );
81 if ( !scheduleRemoval ) {
82 ON_DEBUG std::for_each( std::begin( c ), std::end( c ), [&]( ListenerList::const_reference j ) {
83 if ( match( j ) )
84 debug() << "Removing [" << i->first << "] listener '" << getListenerName( j.iListener ) << "'" << endmsg;
85 } );
86 c.erase( std::remove_if( std::begin( c ), std::end( c ), match ), std::end( c ) );
87 } else {
88 std::for_each( std::begin( c ), std::end( c ), [&]( Listener& i ) {
89 if ( match( i ) ) i.singleShot = true; // will trigger removal as soon as it is safe
90 } );
91 }
92 return c.empty() ? m_listenerMap.erase( i ) : std::next( i );
93}
94void IncidentSvc::removeListener( IIncidentListener* lis, const std::string& type ) {
95 auto lock = std::scoped_lock{ m_listenerMapMutex };
96
97 bool scheduleForRemoval = ( m_currentIncidentType && type == *m_currentIncidentType );
98 if ( type.empty() ) {
99 auto i = std::begin( m_listenerMap );
100 while ( i != std::end( m_listenerMap ) ) { i = removeListenerFromList( i, lis, scheduleForRemoval ); }
101 } else {
102 auto i = m_listenerMap.find( type );
103 if ( i != m_listenerMap.end() ) removeListenerFromList( i, lis, scheduleForRemoval );
104 }
105}
106namespace {
108 constexpr struct isSingleShot_t {
109 bool operator()( const IncidentSvc::Listener& l ) const { return l.singleShot; }
110 } isSingleShot{};
111} // namespace
112void IncidentSvc::i_fireIncident( const Incident& incident, const std::string& listenerType ) {
113
114 auto lock = std::scoped_lock{ m_listenerMapMutex };
115
116 // Wouldn't it be better to write a small 'ReturnCode' service which
117 // looks for these 'special' incidents and does whatever needs to
118 // be done instead of making a special case here?
119
120 // Special case: FailInputFile incident must set the application return code
121 if ( incident.type() == IncidentType::FailInputFile || incident.type() == IncidentType::CorruptedInputFile ) {
122 auto appmgr = serviceLocator()->as<IProperty>();
123 Gaudi::setAppReturnCode( appmgr, incident.type() == IncidentType::FailInputFile
126 .ignore();
127 }
128
129 auto ilisteners = m_listenerMap.find( listenerType );
130 if ( m_listenerMap.end() == ilisteners ) return;
131
132 // setting this pointer will avoid that a call to removeListener() during
133 // the loop triggers a segfault
134 m_currentIncidentType = &incident.type();
135 std::string curIncTyp;
136 if ( m_currentIncidentType != nullptr ) {
137 curIncTyp = *m_currentIncidentType;
138 } else {
139 curIncTyp = "UNKNOWN";
140 }
141
142 bool firedSingleShot = false;
143
144 auto& listeners = *ilisteners->second;
145
146 for ( auto& listener : listeners ) {
147
148 VERMSG << "Calling '" << getListenerName( listener.iListener ) << "' for incident [" << incident.type() << "]"
149 << endmsg;
150
151 // handle exceptions if they occur
152 try {
153 listener.iListener->handle( incident );
154 } catch ( const GaudiException& exc ) {
155 error() << "Exception with tag=" << exc.tag()
156 << " is caught"
157 " handling incident "
158 << curIncTyp << " in listener " << getListenerName( listener.iListener ) << endmsg;
159 error() << exc << endmsg;
160 if ( listener.rethrow ) { throw exc; }
161 } catch ( const std::exception& exc ) {
162 error() << "Standard std::exception is caught"
163 " handling incident "
164 << curIncTyp << " in listener " << getListenerName( listener.iListener ) << endmsg;
165 error() << exc.what() << endmsg;
166 if ( listener.rethrow ) { throw exc; }
167 } catch ( ... ) {
168 error() << "UNKNOWN Exception is caught"
169 " handling incident "
170 << curIncTyp << " in listener " << getListenerName( listener.iListener ) << endmsg;
171 if ( listener.rethrow ) { throw; }
172 }
173 // check wheter one of the listeners is singleShot
174 firedSingleShot |= listener.singleShot;
175 }
176 if ( firedSingleShot ) {
177 // remove all the singleshot listeners that got their shot...
178 listeners.erase( std::remove_if( std::begin( listeners ), std::end( listeners ), isSingleShot ),
179 std::end( listeners ) );
180 if ( listeners.empty() ) m_listenerMap.erase( ilisteners );
181 }
182
183 m_currentIncidentType = nullptr;
184}
185void IncidentSvc::fireIncident( const Incident& incident ) {
186
188
189 // Call specific listeners
190 i_fireIncident( incident, incident.type() );
191 // Try listeners registered for ALL incidents
192 if ( incident.type() != "ALL" ) { // avoid double calls if somebody fires the incident "ALL"
193 i_fireIncident( incident, "ALL" );
194 }
195}
196void IncidentSvc::fireIncident( std::unique_ptr<Incident> incident ) {
197
198 const EventContext& ctx = incident->context();
199 DEBMSG << "Async incident '" << incident->type() << "' fired on context " << ctx << endmsg;
200
201 // create or get incident queue for slot
202 auto [incItr, inserted1] = m_firedIncidents.insert( { ctx.slot(), IncQueue_t() } );
203 // save or get current event for slot
204 auto [slotItr, inserted2] = m_slotEvent.insert( { ctx.slot(), ctx.evt() } );
205
206 // if new event in slot, clear all remaining old incidents
207 if ( slotItr->second != ctx.evt() ) {
208 slotItr->second = ctx.evt();
209
210 if ( msgLevel( MSG::DEBUG ) and !incItr->second.empty() ) {
211 debug() << "Clearing remaining obsolete incidents from slot " << ctx.slot() << ":";
212 std::unique_ptr<Incident> inc;
213 while ( incItr->second.try_pop( inc ) ) { debug() << " " << inc->type() << "(" << inc->context() << ")"; }
214 debug() << endmsg;
215 }
216 incItr->second.clear();
217 }
218 incItr->second.push( std::move( incident ) );
219}
220
221void IncidentSvc::getListeners( std::vector<IIncidentListener*>& l, const std::string& type ) const {
222 static const std::string ALL{ "ALL" };
223 auto lock = std::scoped_lock{ m_listenerMapMutex };
224
225 const std::string& ltype = ( !type.empty() ? type : ALL );
226
227 l.clear();
228 auto i = m_listenerMap.find( ltype );
229 if ( i != m_listenerMap.end() ) {
230 l.reserve( i->second->size() );
231 std::transform( std::begin( *i->second ), std::end( *i->second ), std::back_inserter( l ),
232 []( const Listener& j ) { return j.iListener; } );
233 }
234}
235
238 if ( ctx ) {
239 auto incs = m_firedIncidents.find( ctx->slot() );
240 if ( incs != m_firedIncidents.end() ) {
241 std::unique_ptr<Incident> inc;
242
243 DEBMSG << "Collecting listeners fired on context " << *ctx << endmsg;
244 while ( incs->second.try_pop( inc ) ) {
245 // ensure incident is for this event (should not be necessary)
246 if ( inc->context().evt() == ctx->evt() ) {
247 std::scoped_lock lock( m_listenerMapMutex );
248 auto i = m_listenerMap.find( inc->type() );
249 if ( i != m_listenerMap.end() ) {
250 p.emplace_back( std::move( inc ), std::vector<Listener>{ i->second->begin(), i->second->end() } );
251 }
252 }
253 }
254 }
255 }
256 return p;
257}
#define VERMSG
#define DEBMSG
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition MsgStream.h:198
#define ON_DEBUG
#define DECLARE_COMPONENT(type)
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
MSG::Level msgLevel() const
get the cached level (originally extracted from the embedded MsgStream)
This class represents an entry point to all the event specific data.
Helper object, useful for measurement of CPU-performance of highly-recursive structures,...
Define general base for Gaudi exception.
const char * what() const override
method from std::exception
virtual const std::string & tag() const
name tag for the exception, or exception type
The interface implemented by any class wanting to listen to Incidents.
std::vector< std::pair< std::unique_ptr< Incident >, std::vector< Listener > > > IncidentPack
List of incidents and their listeners.
The IProperty is the basic interface for all components which have properties that can be set or get.
Definition IProperty.h:32
The ISvcLocator is the interface implemented by the Service Factory in the Application Manager to loc...
Definition ISvcLocator.h:42
SmartIF< IFace > as()
Definition ISvcLocator.h:64
Base class for all Incidents (computing events).
Definition Incident.h:24
const std::string & type() const
Access to the incident type.
Definition Incident.h:43
Default implementation of the IIncidentSvc interface.
Definition IncidentSvc.h:35
void removeListener(IIncidentListener *l, const std::string &type="") override
~IncidentSvc() override
StatusCode finalize() override
tbb::concurrent_queue< std::unique_ptr< Incident > > IncQueue_t
Definition IncidentSvc.h:80
ListenerMap::iterator removeListenerFromList(ListenerMap::iterator, IIncidentListener *item, bool scheduleRemoval)
IIncidentSvc::IncidentPack getIncidents(const EventContext *ctx) override
bool m_timerLock
Definition IncidentSvc.h:77
tbb::concurrent_unordered_map< EventContext::ContextID_t, EventContext::ContextEvt_t > m_slotEvent
Event ID for each slot.
Definition IncidentSvc.h:84
tbb::concurrent_unordered_map< EventContext::ContextID_t, IncQueue_t > m_firedIncidents
Definition IncidentSvc.h:81
void addListener(IIncidentListener *lis, const std::string &type="", long priority=0, bool rethrow=false, bool singleShot=false) override
const std::string * m_currentIncidentType
Incident being fired.
Definition IncidentSvc.h:70
ChronoEntity m_timer
timer & it's lock
Definition IncidentSvc.h:76
void i_fireIncident(const Incident &incident, const std::string &type)
Internal function to allow incidents listening to all events.
ListenerMap m_listenerMap
List of auditor names.
Definition IncidentSvc.h:66
std::recursive_mutex m_listenerMapMutex
Mutex to synchronize access to m_listenerMap.
Definition IncidentSvc.h:73
IncidentSvc(const std::string &name, ISvcLocator *svc)
void fireIncident(const Incident &incident) override
void getListeners(std::vector< IIncidentListener * > &lis, const std::string &type="") const override
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator.
Definition Service.cpp:336
StatusCode finalize() override
Definition Service.cpp:223
const std::string & name() const override
Retrieve name of the service.
Definition Service.cpp:333
Small smart pointer class with automatic reference counting for IInterface.
Definition SmartIF.h:28
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
const StatusCode & ignore() const
Allow discarding a StatusCode without warning.
Definition StatusCode.h:139
constexpr int FailInput
constexpr int CorruptedInput
StatusCode setAppReturnCode(SmartIF< IProperty > &appmgr, int value, bool force=false)
Set the application return code.
@ DEBUG
Definition IMessageSvc.h:22
@ milliSec
Definition Timing.h:45
@ Sec
Definition Timing.h:45
Listener properties.