The Gaudi Framework  master (b9786168)
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)
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.