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