The Gaudi Framework  v40r0 (475e45c1)
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 #ifdef _WIN32
12 // Avoid conflicts between windows and the message service.
13 # define NOMSG
14 # define NOGDI
15 #endif
16 
17 #include "IncidentSvc.h"
22 #include <GaudiKernel/Incident.h>
24 #include <GaudiKernel/MsgStream.h>
25 #include <GaudiKernel/SmartIF.h>
26 
28 
29 namespace {
30  static const std::string s_unknown = "<unknown>";
31  // Helper to get the name of the listener
32  inline const std::string& getListenerName( IIncidentListener* lis ) {
33  SmartIF<INamedInterface> iNamed( lis );
34  return iNamed ? iNamed->name() : s_unknown;
35  }
36 } // namespace
37 
38 #define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
39 #define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
40 
41 #define DEBMSG ON_DEBUG debug()
42 #define VERMSG ON_VERBOSE verbose()
43 
44 IncidentSvc::IncidentSvc( const std::string& name, ISvcLocator* svc ) : base_class( name, svc ) {}
45 IncidentSvc::~IncidentSvc() { auto lock = std::scoped_lock{ m_listenerMapMutex }; }
47  DEBMSG << m_timer.outputUserTime( "Incident timing: Mean(+-rms)/Min/Max:%3%(+-%4%)/%6%/%7%[ms] ", System::milliSec )
48  << m_timer.outputUserTime( "Total:%2%[s]", System::Sec ) << endmsg;
49 
50  m_firedIncidents.clear();
51 
52  // Finalize this specific service
53  return Service::finalize();
54 }
55 void IncidentSvc::addListener( IIncidentListener* lis, const std::string& type, long prio, bool rethrow,
56  bool singleShot ) {
57  static const std::string all{ "ALL" };
58  auto lock = std::scoped_lock{ m_listenerMapMutex };
59 
60  const std::string& ltype = ( !type.empty() ? type : all );
61 
62  // find if the type already exists
63  auto itMap = m_listenerMap.find( ltype );
64  if ( itMap == m_listenerMap.end() ) {
65  // if not found, create and insert now a list of listeners
66  auto p = m_listenerMap.insert( { ltype, std::make_unique<ListenerList>() } );
67  if ( !p.second ) { /* OOPS */
68  }
69  itMap = p.first;
70  }
71  auto& llist = *itMap->second;
72  // add Listener ordered by priority -- higher priority first,
73  // and then add behind listeneres with the same priority
74  // -- so we skip over all items with higher or same priority
75  auto i = std::partition_point( std::begin( llist ), std::end( llist ),
76  [&]( const Listener& j ) { return j.priority >= prio; } );
77  // We insert before the current position
78  DEBMSG << "Adding [" << type << "] listener '" << getListenerName( lis ) << "' with priority " << prio << endmsg;
79  llist.emplace( i, IIncidentSvc::Listener{ lis, prio, rethrow, singleShot } );
80 }
82 IncidentSvc::removeListenerFromList( ListenerMap::iterator i, IIncidentListener* item, bool scheduleRemoval ) {
83  auto match = [&]( ListenerList::const_reference j ) { return !item || item == j.iListener; };
84 
85  auto& c = *( i->second );
86  if ( !scheduleRemoval ) {
87  ON_DEBUG std::for_each( std::begin( c ), std::end( c ), [&]( ListenerList::const_reference j ) {
88  if ( match( j ) )
89  debug() << "Removing [" << i->first << "] listener '" << getListenerName( j.iListener ) << "'" << endmsg;
90  } );
91  c.erase( std::remove_if( std::begin( c ), std::end( c ), match ), std::end( c ) );
92  } else {
93  std::for_each( std::begin( c ), std::end( c ), [&]( Listener& i ) {
94  if ( match( i ) ) i.singleShot = true; // will trigger removal as soon as it is safe
95  } );
96  }
97  return c.empty() ? m_listenerMap.erase( i ) : std::next( i );
98 }
99 void IncidentSvc::removeListener( IIncidentListener* lis, const std::string& type ) {
100  auto lock = std::scoped_lock{ m_listenerMapMutex };
101 
102  bool scheduleForRemoval = ( m_currentIncidentType && type == *m_currentIncidentType );
103  if ( type.empty() ) {
104  auto i = std::begin( m_listenerMap );
105  while ( i != std::end( m_listenerMap ) ) { i = removeListenerFromList( i, lis, scheduleForRemoval ); }
106  } else {
107  auto i = m_listenerMap.find( type );
108  if ( i != m_listenerMap.end() ) removeListenerFromList( i, lis, scheduleForRemoval );
109  }
110 }
111 namespace {
113  constexpr struct isSingleShot_t {
114  bool operator()( const IncidentSvc::Listener& l ) const { return l.singleShot; }
115  } isSingleShot{};
116 } // namespace
117 void IncidentSvc::i_fireIncident( const Incident& incident, const std::string& listenerType ) {
118 
119  auto lock = std::scoped_lock{ m_listenerMapMutex };
120 
121  // Wouldn't it be better to write a small 'ReturnCode' service which
122  // looks for these 'special' incidents and does whatever needs to
123  // be done instead of making a special case here?
124 
125  // Special case: FailInputFile incident must set the application return code
126  if ( incident.type() == IncidentType::FailInputFile || incident.type() == IncidentType::CorruptedInputFile ) {
127  auto appmgr = serviceLocator()->as<IProperty>();
128  Gaudi::setAppReturnCode( appmgr, incident.type() == IncidentType::FailInputFile
131  .ignore();
132  }
133 
134  auto ilisteners = m_listenerMap.find( listenerType );
135  if ( m_listenerMap.end() == ilisteners ) return;
136 
137  // setting this pointer will avoid that a call to removeListener() during
138  // the loop triggers a segfault
139  m_currentIncidentType = &incident.type();
140  std::string curIncTyp;
141  if ( m_currentIncidentType != nullptr ) {
142  curIncTyp = *m_currentIncidentType;
143  } else {
144  curIncTyp = "UNKNOWN";
145  }
146 
147  bool firedSingleShot = false;
148 
149  auto& listeners = *ilisteners->second;
150 
151  for ( auto& listener : listeners ) {
152 
153  VERMSG << "Calling '" << getListenerName( listener.iListener ) << "' for incident [" << incident.type() << "]"
154  << endmsg;
155 
156  // handle exceptions if they occur
157  try {
158  listener.iListener->handle( incident );
159  } catch ( const GaudiException& exc ) {
160  error() << "Exception with tag=" << exc.tag()
161  << " is caught"
162  " handling incident "
163  << curIncTyp << " in listener " << getListenerName( listener.iListener ) << endmsg;
164  error() << exc << endmsg;
165  if ( listener.rethrow ) { throw exc; }
166  } catch ( const std::exception& exc ) {
167  error() << "Standard std::exception is caught"
168  " handling incident "
169  << curIncTyp << " in listener " << getListenerName( listener.iListener ) << endmsg;
170  error() << exc.what() << endmsg;
171  if ( listener.rethrow ) { throw exc; }
172  } catch ( ... ) {
173  error() << "UNKNOWN Exception is caught"
174  " handling incident "
175  << curIncTyp << " in listener " << getListenerName( listener.iListener ) << endmsg;
176  if ( listener.rethrow ) { throw; }
177  }
178  // check wheter one of the listeners is singleShot
179  firedSingleShot |= listener.singleShot;
180  }
181  if ( firedSingleShot ) {
182  // remove all the singleshot listeners that got their shot...
183  listeners.erase( std::remove_if( std::begin( listeners ), std::end( listeners ), isSingleShot ),
184  std::end( listeners ) );
185  if ( listeners.empty() ) m_listenerMap.erase( ilisteners );
186  }
187 
188  m_currentIncidentType = nullptr;
189 }
190 void IncidentSvc::fireIncident( const Incident& incident ) {
191 
193 
194  // Call specific listeners
195  i_fireIncident( incident, incident.type() );
196  // Try listeners registered for ALL incidents
197  if ( incident.type() != "ALL" ) { // avoid double calls if somebody fires the incident "ALL"
198  i_fireIncident( incident, "ALL" );
199  }
200 }
201 void IncidentSvc::fireIncident( std::unique_ptr<Incident> incident ) {
202 
203  const EventContext& ctx = incident->context();
204  DEBMSG << "Async incident '" << incident->type() << "' fired on context " << ctx << endmsg;
205 
206  // create or get incident queue for slot
207  auto [incItr, inserted1] = m_firedIncidents.insert( { ctx.slot(), IncQueue_t() } );
208  // save or get current event for slot
209  auto [slotItr, inserted2] = m_slotEvent.insert( { ctx.slot(), ctx.evt() } );
210 
211  // if new event in slot, clear all remaining old incidents
212  if ( slotItr->second != ctx.evt() ) {
213  slotItr->second = ctx.evt();
214 
215  if ( msgLevel( MSG::DEBUG ) and !incItr->second.empty() ) {
216  debug() << "Clearing remaining obsolete incidents from slot " << ctx.slot() << ":";
217  std::unique_ptr<Incident> inc;
218  while ( incItr->second.try_pop( inc ) ) { debug() << " " << inc->type() << "(" << inc->context() << ")"; }
219  debug() << endmsg;
220  }
221  incItr->second.clear();
222  }
223  incItr->second.push( std::move( incident ) );
224 }
225 
226 void IncidentSvc::getListeners( std::vector<IIncidentListener*>& l, const std::string& type ) const {
227  static const std::string ALL{ "ALL" };
228  auto lock = std::scoped_lock{ m_listenerMapMutex };
229 
230  const std::string& ltype = ( !type.empty() ? type : ALL );
231 
232  l.clear();
233  auto i = m_listenerMap.find( ltype );
234  if ( i != m_listenerMap.end() ) {
235  l.reserve( i->second->size() );
236  std::transform( std::begin( *i->second ), std::end( *i->second ), std::back_inserter( l ),
237  []( const Listener& j ) { return j.iListener; } );
238  }
239 }
240 
243  if ( ctx ) {
244  auto incs = m_firedIncidents.find( ctx->slot() );
245  if ( incs != m_firedIncidents.end() ) {
246  std::unique_ptr<Incident> inc;
247 
248  DEBMSG << "Collecting listeners fired on context " << *ctx << endmsg;
249  while ( incs->second.try_pop( inc ) ) {
250  // ensure incident is for this event (should not be necessary)
251  if ( inc->context().evt() == ctx->evt() ) {
252  std::scoped_lock lock( m_listenerMapMutex );
253  auto i = m_listenerMap.find( inc->type() );
254  if ( i != m_listenerMap.end() ) {
255  p.emplace_back( std::move( inc ), std::vector<Listener>{ i->second->begin(), i->second->end() } );
256  }
257  }
258  }
259  }
260  }
261  return p;
262 }
MSG::DEBUG
@ DEBUG
Definition: IMessageSvc.h:22
DEBMSG
#define DEBMSG
Definition: IncidentSvc.cpp:41
IIncidentSvc::Listener
Listener properties.
Definition: IIncidentSvc.h:57
System::milliSec
@ milliSec
Definition: Timing.h:54
IncidentSvc::~IncidentSvc
~IncidentSvc() override
Definition: IncidentSvc.cpp:45
ON_DEBUG
#define ON_DEBUG
Definition: IncidentSvc.cpp:38
AppReturnCode.h
IncidentSvc::IncQueue_t
tbb::concurrent_queue< std::unique_ptr< Incident > > IncQueue_t
Definition: IncidentSvc.h:80
GaudiUtils::Map::find
iterator find(const key_type &key)
Definition: Map.h:133
GaudiException.h
ISvcLocator
Definition: ISvcLocator.h:42
GaudiException
Definition: GaudiException.h:29
VERMSG
#define VERMSG
Definition: IncidentSvc.cpp:42
IncidentSvc::i_fireIncident
void i_fireIncident(const Incident &incident, const std::string &type)
Internal function to allow incidents listening to all events.
Definition: IncidentSvc.cpp:117
IncidentSvc::m_firedIncidents
tbb::concurrent_unordered_map< EventContext::ContextID_t, IncQueue_t > m_firedIncidents
Definition: IncidentSvc.h:81
GaudiKernel.Constants.ALL
ALL
Definition: Constants.py:28
gaudirun.c
c
Definition: gaudirun.py:525
IncidentSvc::m_timerLock
bool m_timerLock
Definition: IncidentSvc.h:77
LockedChrono.h
CommonMessaging< implements< IService, IProperty, IStateful > >::msgLevel
MSG::Level msgLevel() const
get the cached level (originally extracted from the embedded MsgStream)
Definition: CommonMessaging.h:147
Service::finalize
StatusCode finalize() override
Definition: Service.cpp:223
Gaudi::ReturnCode::FailInput
constexpr int FailInput
Definition: AppReturnCode.h:32
IncidentSvc::m_timer
ChronoEntity m_timer
timer & it's lock
Definition: IncidentSvc.h:76
IncidentSvc
Default implementation of the IIncidentSvc interface.
Definition: IncidentSvc.h:35
GaudiPartProp.decorators.all
all
decorate service
Definition: decorators.py:53
IProperty
Definition: IProperty.h:32
IIncidentListener
Definition: IIncidentListener.h:23
SmartIF.h
Incident::context
EventContext context() const
Access to the EventContext of the source of the incident.
Definition: Incident.h:55
Gaudi::Utils::begin
AttribStringParser::Iterator begin(const AttribStringParser &parser)
Definition: AttribStringParser.h:135
GaudiPython.Pythonizations.ctx
ctx
Definition: Pythonizations.py:578
StatusCode
Definition: StatusCode.h:64
Gaudi::cxx::for_each
void for_each(ContainerOfSynced &c, Fun &&f)
Definition: SynchronizedValue.h:98
Gaudi::setAppReturnCode
StatusCode setAppReturnCode(SmartIF< IProperty > &appmgr, int value, bool force=false)
Set the application return code.
Definition: AppReturnCode.h:58
IncidentSvc::addListener
void addListener(IIncidentListener *lis, const std::string &type="", long priority=0, bool rethrow=false, bool singleShot=false) override
Definition: IncidentSvc.cpp:55
ProduceConsume.j
j
Definition: ProduceConsume.py:104
ChronoEntity::outputUserTime
std::string outputUserTime() const
print the chrono ;
Definition: ChronoEntity.cpp:68
CommonMessaging
Definition: CommonMessaging.h:65
Gaudi::Utils::LockedChrono
Definition: LockedChrono.h:53
Gaudi::ReturnCode::CorruptedInput
constexpr int CorruptedInput
Definition: AppReturnCode.h:37
IncidentSvc::removeListenerFromList
ListenerMap::iterator removeListenerFromList(ListenerMap::iterator, IIncidentListener *item, bool scheduleRemoval)
Definition: IncidentSvc.cpp:82
SmartIF
Definition: IConverter.h:22
MsgStream::clear
void clear(STATE_TYPE _i=std::ios_base::failbit)
Definition: MsgStream.h:177
endmsg
MsgStream & endmsg(MsgStream &s)
MsgStream Modifier: endmsg. Calls the output method of the MsgStream.
Definition: MsgStream.h:198
GaudiException::tag
virtual const std::string & tag() const
name tag for the exception, or exception type
Definition: GaudiException.h:75
IncidentSvc::m_slotEvent
tbb::concurrent_unordered_map< EventContext::ContextID_t, EventContext::ContextEvt_t > m_slotEvent
Event ID for each slot.
Definition: IncidentSvc.h:84
GaudiUtils::Map::end
iterator end()
Definition: Map.h:122
IIncidentSvc::IncidentPack
std::vector< std::pair< std::unique_ptr< Incident >, std::vector< Listener > > > IncidentPack
List of incidents and their listeners.
Definition: IIncidentSvc.h:65
GaudiUtils::Map::erase
iterator erase(const_iterator pos)
Definition: Map.h:168
StatusCode::ignore
const StatusCode & ignore() const
Allow discarding a StatusCode without warning.
Definition: StatusCode.h:139
SmartIF::as
SmartIF< IFace > as() const
return a new SmartIF instance to another interface
Definition: SmartIF.h:110
IncidentSvc::fireIncident
void fireIncident(const Incident &incident) override
Definition: IncidentSvc.cpp:190
gaudirun.type
type
Definition: gaudirun.py:160
System::Sec
@ Sec
Definition: Timing.h:54
ConditionsStallTest.name
name
Definition: ConditionsStallTest.py:77
gaudirun.l
dictionary l
Definition: gaudirun.py:583
IncidentSvc.h
IIncidentListener.h
DECLARE_COMPONENT
#define DECLARE_COMPONENT(type)
Definition: PluginServiceV1.h:45
IncidentSvc::m_listenerMapMutex
std::recursive_mutex m_listenerMapMutex
Mutex to synchronize access to m_listenerMap.
Definition: IncidentSvc.h:73
GaudiUtils::Map< Gaudi::StringKey, std::unique_ptr< ListenerList >, std::unordered_map< Gaudi::StringKey, std::unique_ptr< ListenerList >, Hash< Gaudi::StringKey > > >::iterator
map_type::iterator iterator
Definition: Map.h:97
EventContext
Definition: EventContext.h:34
GaudiUtils::Map::insert
std::pair< iterator, bool > insert(ValueType &&val)
Definition: Map.h:154
IncidentSvc::removeListener
void removeListener(IIncidentListener *l, const std::string &type="") override
Definition: IncidentSvc.cpp:99
Incident::type
const std::string & type() const
Access to the incident type.
Definition: Incident.h:43
IncidentSvc::IncidentSvc
IncidentSvc(const std::string &name, ISvcLocator *svc)
Definition: IncidentSvc.cpp:44
IOTest.end
end
Definition: IOTest.py:125
ISvcLocator.h
Incident.h
IncidentSvc::m_currentIncidentType
const std::string * m_currentIncidentType
Incident being fired.
Definition: IncidentSvc.h:70
Incident
Definition: Incident.h:24
IncidentSvc::getIncidents
IIncidentSvc::IncidentPack getIncidents(const EventContext *ctx) override
Definition: IncidentSvc.cpp:241
IncidentSvc::getListeners
void getListeners(std::vector< IIncidentListener * > &lis, const std::string &type="") const override
Definition: IncidentSvc.cpp:226
EventContext::evt
ContextEvt_t evt() const
Definition: EventContext.h:50
IncidentSvc::m_listenerMap
ListenerMap m_listenerMap
List of auditor names.
Definition: IncidentSvc.h:66
MsgStream.h
Service::serviceLocator
SmartIF< ISvcLocator > & serviceLocator() const override
Retrieve pointer to service locator
Definition: Service.cpp:336
IncidentSvc::finalize
StatusCode finalize() override
Definition: IncidentSvc.cpp:46