The Gaudi Framework  master (9f3f8895)
Loading...
Searching...
No Matches
AvalancheSchedulerSvc.cpp
Go to the documentation of this file.
1/***********************************************************************************\
2* (c) Copyright 1998-2026 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\***********************************************************************************/
12#include "AlgTask.h"
13#include "FiberManager.h"
14#include "GraphDumper.h"
15#include "ThreadPoolSvc.h"
16
17// Framework includes
18#include <Gaudi/Algorithm.h> // can be removed ASA dynamic casts to Algorithm are removed
25
26// C++
27#include <algorithm>
28#include <chrono>
29#include <fstream>
30#include <future>
31#include <map>
32#include <queue>
33#include <regex>
34#include <semaphore>
35#include <sstream>
36#include <string_view>
37#include <thread>
38#include <unordered_set>
39
40// External libs
41#include <boost/algorithm/string.hpp>
42#include <boost/thread.hpp>
43#include <boost/tokenizer.hpp>
44
45// Instantiation of a static factory class used by clients to create instances of this service
47
48#define ON_DEBUG if ( msgLevel( MSG::DEBUG ) )
49#define ON_VERBOSE if ( msgLevel( MSG::VERBOSE ) )
50
51namespace {
53 constexpr std::chrono::seconds s_dumpStateTimeout{ 30 };
54
55 struct DataObjIDSorter {
56 bool operator()( const DataObjID* a, const DataObjID* b ) { return a->fullKey() < b->fullKey(); }
57 };
58
59 // Sort a DataObjIDColl in a well-defined, reproducible manner.
60 // Used for making debugging dumps.
61 std::vector<const DataObjID*> sortedDataObjIDColl( const DataObjIDColl& coll ) {
62 std::vector<const DataObjID*> v;
63 v.reserve( coll.size() );
64 for ( const DataObjID& id : coll ) v.push_back( &id );
65 std::sort( v.begin(), v.end(), DataObjIDSorter() );
66 return v;
67 }
68
69 bool subSlotAlgsInStates( const EventSlot& slot, std::initializer_list<AlgsExecutionStates::State> testStates ) {
70 return std::any_of( slot.allSubSlots.begin(), slot.allSubSlots.end(),
71 [testStates]( const EventSlot& ss ) { return ss.algsStates.containsAny( testStates ); } );
72 }
73} // namespace
74
75//---------------------------------------------------------------------------
76
82
84
85 // Initialise mother class (read properties, ...)
87 if ( sc.isFailure() ) warning() << "Base class could not be initialized" << endmsg;
88
89 // Get hold of the TBBSvc. This should initialize the thread pool
90 m_threadPoolSvc = serviceLocator()->service( "ThreadPoolSvc" );
91 if ( !m_threadPoolSvc.isValid() ) {
92 fatal() << "Error retrieving ThreadPoolSvc" << endmsg;
94 }
95 auto castTPS = dynamic_cast<ThreadPoolSvc*>( m_threadPoolSvc.get() );
96 if ( !castTPS ) {
97 fatal() << "Cannot cast ThreadPoolSvc" << endmsg;
99 }
100 m_arena = castTPS->getArena();
101 if ( !m_arena ) {
102 fatal() << "Cannot find valid TBB task_arena" << endmsg;
103 return StatusCode::FAILURE;
104 }
105
106 // Activate the scheduler in another thread.
107 info() << "Activating scheduler in a separate thread" << endmsg;
108 std::binary_semaphore fiber_manager_initalized{ 0 };
109 m_thread = std::thread( [this, &fiber_manager_initalized]() {
110 // Initialize FiberManager
111 this->m_fiberManager =
112 std::make_unique<FiberManager>( this->m_numOffloadThreads.value(), this->m_fiberStackSize.value() );
113 fiber_manager_initalized.release();
114 this->activate();
115 } );
116 // Wait for initialization to complete
117 fiber_manager_initalized.acquire();
118
119 while ( m_isActive != ACTIVE ) {
120 if ( m_isActive == FAILURE ) {
121 fatal() << "Terminating initialization" << endmsg;
122 return StatusCode::FAILURE;
123 } else {
124 ON_DEBUG debug() << "Waiting for AvalancheSchedulerSvc to activate" << endmsg;
125 sleep( 1 );
126 }
127 }
128
129 if ( m_enableCondSvc ) {
130 // Get hold of the CondSvc
131 m_condSvc = serviceLocator()->service( "CondSvc" );
132 if ( !m_condSvc.isValid() ) {
133 warning() << "No CondSvc found, or not enabled. "
134 << "Will not manage CondAlgorithms" << endmsg;
135 m_enableCondSvc = false;
136 }
137 }
138
139 // Get the algo resource pool
140 m_algResourcePool = serviceLocator()->service( "AlgResourcePool" );
141 if ( !m_algResourcePool.isValid() ) {
142 fatal() << "Error retrieving AlgoResourcePool" << endmsg;
143 return StatusCode::FAILURE;
144 }
145
146 m_algExecStateSvc = serviceLocator()->service( "AlgExecStateSvc" );
147 if ( !m_algExecStateSvc.isValid() ) {
148 fatal() << "Error retrieving AlgExecStateSvc" << endmsg;
149 return StatusCode::FAILURE;
150 }
151
152 // Get Whiteboard
154 if ( !m_whiteboard.isValid() ) {
155 fatal() << "Error retrieving EventDataSvc interface IHiveWhiteBoard." << endmsg;
156 return StatusCode::FAILURE;
157 }
158
159 // Set the MaxEventsInFlight parameters from the number of WB stores
160 m_maxEventsInFlight = m_whiteboard->getNumberOfStores();
161
162 // Set the number of free slots
164
165 // Get the list of algorithms
166 const std::list<IAlgorithm*>& algos = m_algResourcePool->getFlatAlgList();
167 const unsigned int algsNumber = algos.size();
168 if ( algsNumber != 0 ) {
169 info() << "Found " << algsNumber << " algorithms" << endmsg;
170 } else {
171 error() << "No algorithms found" << endmsg;
172 return StatusCode::FAILURE;
173 }
174
175 /* Dependencies
176 1) Look for handles in algo, if none
177 2) Assume none are required
178 */
179
180 DataObjIDColl globalInp, globalOutp;
181
182 auto hasAsynchronousAlgorithms = false;
183
184 // figure out all outputs and check for asynchronous algorithms
185 std::map<std::string, DataObjIDColl> algosOutputDependenciesMap;
186 for ( IAlgorithm* ialgoPtr : algos ) {
187 Gaudi::Algorithm* algoPtr = dynamic_cast<Gaudi::Algorithm*>( ialgoPtr );
188 if ( !algoPtr ) {
189 fatal() << "Could not convert IAlgorithm into Gaudi::Algorithm: this will result in a crash." << endmsg;
190 return StatusCode::FAILURE;
191 }
192
193 DataObjIDColl algoOutputs;
194 for ( auto id : algoPtr->outputDataObjs() ) {
195 globalOutp.insert( id );
196 algoOutputs.insert( id );
197 }
198 algosOutputDependenciesMap[algoPtr->name()] = algoOutputs;
199
200 hasAsynchronousAlgorithms = hasAsynchronousAlgorithms || algoPtr->isAsynchronous();
201 }
202
203 if ( m_numOffloadThreads <= 0 && hasAsynchronousAlgorithms ) {
204 fatal() << "Found asynchronous algorithms, but NumOffloadThreads is " << m_numOffloadThreads
205 << "; no fiber pool threads will be available to execute them." << endmsg;
206 return StatusCode::FAILURE;
207 }
208
209 std::ostringstream ostdd;
210 ostdd << "Data Dependencies for Algorithms:";
211
212 std::map<std::string, DataObjIDColl> algosInputDependenciesMap;
213 for ( IAlgorithm* ialgoPtr : algos ) {
214 Gaudi::Algorithm* algoPtr = dynamic_cast<Gaudi::Algorithm*>( ialgoPtr );
215 if ( nullptr == algoPtr ) {
216 fatal() << "Could not convert IAlgorithm into Gaudi::Algorithm for " << ialgoPtr->name()
217 << ": this will result in a crash." << endmsg;
218 return StatusCode::FAILURE;
219 }
220
221 DataObjIDColl i1, i2;
222 DHHVisitor avis( i1, i2 );
223 algoPtr->acceptDHVisitor( &avis );
224
225 ostdd << "\n " << algoPtr->name();
226
227 auto write_owners = [&avis, &ostdd]( const DataObjID& id ) {
228 auto owners = avis.owners_names_of( id );
229 if ( !owners.empty() ) { GaudiUtils::operator<<( ostdd << ' ', owners ); }
230 };
231
232 DataObjIDColl algoDependencies;
233 if ( !algoPtr->inputDataObjs().empty() || !algoPtr->outputDataObjs().empty() ) {
234 for ( const DataObjID* idp : sortedDataObjIDColl( algoPtr->inputDataObjs() ) ) {
235 DataObjID id = *idp;
236 ostdd << "\n o INPUT " << id;
237 write_owners( id );
238 algoDependencies.insert( id );
239 globalInp.insert( id );
240 }
241 for ( const DataObjID* id : sortedDataObjIDColl( algoPtr->outputDataObjs() ) ) {
242 ostdd << "\n o OUTPUT " << *id;
243 write_owners( *id );
244 if ( id->key().find( ":" ) != std::string::npos ) {
245 error() << " in Alg " << algoPtr->name() << " alternatives are NOT allowed for outputs! id: " << *id
246 << endmsg;
247 m_showDataDeps = true;
248 }
249 }
250 } else {
251 ostdd << "\n none";
252 }
253 algosInputDependenciesMap[algoPtr->name()] = algoDependencies;
254 }
255
256 if ( m_showDataDeps ) { info() << ostdd.str() << endmsg; }
257
258 // If requested, dump a graph of the data dependencies in a .dot, .md or .graphml file
259 if ( not m_dataDepsGraphFile.empty() ) {
260 if ( dumpDataDepsGraphFile( algosInputDependenciesMap, algosOutputDependenciesMap ).isFailure() ) {
261 return StatusCode::FAILURE;
262 }
263 }
264
265 // Check if we have unmet global input dependencies, and, optionally, heal them
266 // WARNING: this step must be done BEFORE the Precedence Service is initialized
267 DataObjIDColl unmetDepInp, unusedOutp;
268 if ( m_checkDeps || m_checkOutput ) {
269 std::set<std::string> requiredInputKeys;
270 for ( auto o : globalInp ) {
271 // track aliases
272 // (assuming there should be no items with different class and same key corresponding to different objects)
273 requiredInputKeys.insert( o.key() );
274 if ( globalOutp.find( o ) == globalOutp.end() ) unmetDepInp.insert( o );
275 }
276 if ( m_checkOutput ) {
277 for ( auto o : globalOutp ) {
278 if ( globalInp.find( o ) == globalInp.end() && requiredInputKeys.find( o.key() ) == requiredInputKeys.end() ) {
279 // check ignores
280 bool ignored{};
281 for ( const std::string& algoName : m_checkOutputIgnoreList ) {
282 auto it = algosOutputDependenciesMap.find( algoName );
283 if ( it != algosOutputDependenciesMap.end() ) {
284 if ( it->second.find( o ) != it->second.end() ) {
285 ignored = true;
286 break;
287 }
288 }
289 }
290 if ( !ignored ) { unusedOutp.insert( o ); }
291 }
292 }
293 }
294 }
295
296 if ( m_checkDeps ) {
297 if ( unmetDepInp.size() > 0 ) {
298
299 auto printUnmet = [&]( auto msg ) {
300 for ( const DataObjID* o : sortedDataObjIDColl( unmetDepInp ) ) {
301 msg << " o " << *o << " required by Algorithm: " << endmsg;
302
303 for ( const auto& p : algosInputDependenciesMap )
304 if ( p.second.find( *o ) != p.second.end() ) msg << " * " << p.first << endmsg;
305 }
306 };
307
308 if ( !m_useDataLoader.empty() ) {
309
310 // Find the DataLoader Alg
311 IAlgorithm* dataLoaderAlg( nullptr );
312 for ( IAlgorithm* algo : algos )
313 if ( m_useDataLoader == algo->name() ) {
314 dataLoaderAlg = algo;
315 break;
316 }
317
318 if ( dataLoaderAlg == nullptr ) {
319 fatal() << "No DataLoader Algorithm \"" << m_useDataLoader.value()
320 << "\" found, and unmet INPUT dependencies "
321 << "detected:" << endmsg;
322 printUnmet( fatal() );
323 return StatusCode::FAILURE;
324 }
325
326 info() << "Will attribute the following unmet INPUT dependencies to \"" << dataLoaderAlg->type() << "/"
327 << dataLoaderAlg->name() << "\" Algorithm" << endmsg;
328 printUnmet( info() );
329
330 // Set the property Load of DataLoader Alg
331 Gaudi::Algorithm* dataAlg = dynamic_cast<Gaudi::Algorithm*>( dataLoaderAlg );
332 if ( !dataAlg ) {
333 fatal() << "Unable to dcast DataLoader \"" << m_useDataLoader.value() << "\" IAlg to Gaudi::Algorithm"
334 << endmsg;
335 return StatusCode::FAILURE;
336 }
337
338 for ( auto& id : unmetDepInp ) {
339 ON_DEBUG debug() << "adding OUTPUT dep \"" << id << "\" to " << dataLoaderAlg->type() << "/"
340 << dataLoaderAlg->name() << endmsg;
342 }
343
344 } else {
345 fatal() << "Auto DataLoading not requested, "
346 << "and the following unmet INPUT dependencies were found:" << endmsg;
347 printUnmet( fatal() );
348 return StatusCode::FAILURE;
349 }
350
351 } else {
352 info() << "No unmet INPUT data dependencies were found" << endmsg;
353 }
354 }
355
356 if ( m_checkOutput ) {
357 if ( unusedOutp.size() > 0 ) {
358
359 auto printUnusedOutp = [&]( auto msg ) {
360 for ( const DataObjID* o : sortedDataObjIDColl( unusedOutp ) ) {
361 msg << " o " << *o << " produced by Algorithm: " << endmsg;
362
363 for ( const auto& p : algosOutputDependenciesMap )
364 if ( p.second.find( *o ) != p.second.end() ) msg << " * " << p.first << endmsg;
365 }
366 };
367
368 fatal() << "The following unused OUTPUT items were found:" << endmsg;
369 printUnusedOutp( fatal() );
370 return StatusCode::FAILURE;
371 } else {
372 info() << "No unused OUTPUT items were found" << endmsg;
373 }
374 }
375
376 // Get the precedence service
377 m_precSvc = serviceLocator()->service( "PrecedenceSvc" );
378 if ( !m_precSvc.isValid() ) {
379 fatal() << "Error retrieving PrecedenceSvc" << endmsg;
380 return StatusCode::FAILURE;
381 }
382 const PrecedenceSvc* precSvc = dynamic_cast<const PrecedenceSvc*>( m_precSvc.get() );
383 if ( !precSvc ) {
384 fatal() << "Unable to dcast PrecedenceSvc" << endmsg;
385 return StatusCode::FAILURE;
386 }
387
388 // Fill the containers to convert algo names to index
389 m_algname_vect.resize( algsNumber );
390 for ( IAlgorithm* algo : algos ) {
391 const std::string& name = algo->name();
392 auto index = precSvc->getRules()->getAlgorithmNode( name )->getAlgoIndex();
393 m_algname_index_map[name] = index;
394 m_algname_vect.at( index ) = name;
395 }
396
397 // Shortcut for the message service
398 SmartIF<IMessageSvc> messageSvc( serviceLocator() );
399 if ( !messageSvc.isValid() ) error() << "Error retrieving MessageSvc interface IMessageSvc." << endmsg;
400
402 for ( size_t i = 0; i < m_maxEventsInFlight; ++i ) {
403 m_eventSlots.emplace_back( algsNumber, precSvc->getRules()->getControlFlowNodeCounter(), messageSvc );
404 m_eventSlots.back().complete = true;
405 }
406
407 // Clearly inform about the level of concurrency
408 info() << "Concurrency level information:" << endmsg;
409 info() << " o Number of events in flight: " << m_maxEventsInFlight << endmsg;
410 info() << " o TBB thread pool size: " << m_threadPoolSize << endmsg;
411 info() << " o Fiber thread pool size: " << m_numOffloadThreads << endmsg;
412
413 // Inform about task scheduling prescriptions
414 info() << "Task scheduling settings:" << endmsg;
415 info() << " o Avalanche generation mode: "
416 << ( m_optimizationMode.empty() ? "disabled" : m_optimizationMode.toString() ) << endmsg;
417 info() << " o Scheduling of condition tasks: " << ( m_enableCondSvc ? "enabled" : "disabled" ) << endmsg;
418
419 if ( m_showControlFlow ) m_precSvc->dumpControlFlow();
420
421 if ( m_showDataFlow ) m_precSvc->dumpDataFlow();
422
423 // Simulate execution flow
424 if ( m_simulateExecution ) sc = m_precSvc->simulate( m_eventSlots[0] );
425
426 return sc;
427}
428//---------------------------------------------------------------------------
429
434
436 if ( sc.isFailure() ) warning() << "Base class could not be finalized" << endmsg;
437
438 sc = deactivate();
439 if ( sc.isFailure() ) warning() << "Scheduler could not be deactivated" << endmsg;
440
441 debug() << "Deleting FiberManager" << endmsg;
442 m_fiberManager.reset();
443
444 info() << "Joining Scheduler thread" << endmsg;
445 m_thread.join();
446
447 // Final error check after thread pool termination
448 if ( m_isActive == FAILURE ) {
449 error() << "problems in scheduler thread" << endmsg;
450 return StatusCode::FAILURE;
451 }
452
453 return sc;
454}
455//---------------------------------------------------------------------------
456
468
469 ON_DEBUG debug() << "AvalancheSchedulerSvc::activate()" << endmsg;
470
471 if ( m_threadPoolSvc->initPool( m_threadPoolSize, m_maxParallelismExtra ).isFailure() ) {
472 error() << "problems initializing ThreadPoolSvc" << endmsg;
474 return;
475 }
476
477 // Wait for actions pushed into the queue by finishing tasks.
478 action thisAction;
480
482
483 // Continue to wait if the scheduler is running or there is something to do
484 ON_DEBUG debug() << "Start checking the actionsQueue" << endmsg;
485 while ( m_isActive == ACTIVE || m_actionsQueue.size() != 0 ) {
486 m_actionsQueue.pop( thisAction );
487 sc = thisAction();
488 ON_VERBOSE {
489 if ( sc.isFailure() )
490 verbose() << "Action did not succeed (which is not bad per se)." << endmsg;
491 else
492 verbose() << "Action succeeded." << endmsg;
493 }
494 else sc.ignore();
495
496 // If all queued actions have been processed, update the slot states
497 if ( m_needsUpdate.load() && m_actionsQueue.empty() ) {
498 sc = iterate();
499 ON_VERBOSE {
500 if ( sc.isFailure() )
501 verbose() << "Iteration did not succeed (which is not bad per se)." << endmsg;
502 else
503 verbose() << "Iteration succeeded." << endmsg;
504 }
505 else sc.ignore();
506 }
507 }
508
509 ON_DEBUG debug() << "Terminating thread-pool resources" << endmsg;
510 if ( m_threadPoolSvc->terminatePool().isFailure() ) {
511 error() << "Problems terminating thread pool" << endmsg;
513 }
514}
515
516//---------------------------------------------------------------------------
517
525
526 if ( m_isActive == ACTIVE ) {
527
528 // Set the number of slots available to an error code
529 m_freeSlots.store( 0 );
530
531 // Empty queue
532 action thisAction;
533 while ( m_actionsQueue.try_pop( thisAction ) ) {};
534
535 // This would be the last action
536 m_actionsQueue.push( [this]() -> StatusCode {
537 ON_VERBOSE verbose() << "Deactivating scheduler" << endmsg;
539 return StatusCode::SUCCESS;
540 } );
541 }
542
543 return StatusCode::SUCCESS;
544}
545
546//---------------------------------------------------------------------------
547
548// EventSlot management
556
557 if ( !eventContext ) {
558 fatal() << "Event context is nullptr" << endmsg;
559 return StatusCode::FAILURE;
560 }
561
562 if ( m_freeSlots.load() == 0 ) {
563 ON_DEBUG debug() << "A free processing slot could not be found." << endmsg;
564 return StatusCode::FAILURE;
565 }
566
567 // no problem as push new event is only called from one thread (event loop manager)
568 --m_freeSlots;
569
570 auto action = [this, eventContext]() -> StatusCode {
571 // Event processing slot forced to be the same as the wb slot
572 const unsigned int thisSlotNum = eventContext->slot();
573 EventSlot& thisSlot = m_eventSlots[thisSlotNum];
574 if ( !thisSlot.complete ) {
575 fatal() << "The slot " << thisSlotNum << " is supposed to be a finished event but it's not" << endmsg;
576 return StatusCode::FAILURE;
577 }
578
579 ON_DEBUG debug() << "Executing event " << eventContext->evt() << " on slot " << thisSlotNum << endmsg;
580 thisSlot.reset( eventContext );
581
582 // Result status code:
584
585 // promote to CR and DR the initial set of algorithms
586 Cause cs = { Cause::source::Root, "RootDecisionHub" };
587 if ( m_precSvc->iterate( thisSlot, cs ).isFailure() ) {
588 error() << "Failed to call IPrecedenceSvc::iterate for slot " << thisSlotNum << endmsg;
589 result = StatusCode::FAILURE;
590 }
591
592 if ( this->iterate().isFailure() ) {
593 error() << "Failed to call AvalancheSchedulerSvc::updateStates for slot " << thisSlotNum << endmsg;
594 result = StatusCode::FAILURE;
595 }
596
597 return result;
598 }; // end of lambda
599
600 // Kick off scheduling
601 ON_VERBOSE {
602 verbose() << "Pushing the action to update the scheduler for slot " << eventContext->slot() << endmsg;
603 verbose() << "Free slots available " << m_freeSlots.load() << endmsg;
604 }
605
606 m_actionsQueue.push( action );
607
608 return StatusCode::SUCCESS;
609}
610
611//---------------------------------------------------------------------------
612
613StatusCode AvalancheSchedulerSvc::pushNewEvents( std::vector<EventContext*>& eventContexts ) {
614 StatusCode sc;
615 for ( auto context : eventContexts ) {
616 sc = pushNewEvent( context );
617 if ( sc != StatusCode::SUCCESS ) return sc;
618 }
619 return sc;
620}
621
622//---------------------------------------------------------------------------
623
624unsigned int AvalancheSchedulerSvc::freeSlots() { return std::max( m_freeSlots.load(), 0 ); }
625
626//---------------------------------------------------------------------------
627
629 // The state of the event slots is only modified by the scheduler thread (through the actions in
630 // m_actionsQueue), so, unless we are already on it or it is not running, let it produce the dump
631 // instead of reading the slots concurrently from the calling thread (typically the EventWatchdog).
632 if ( m_isActive != ACTIVE || std::this_thread::get_id() == m_thread.get_id() ) {
633 dumpSchedulerState( -1 );
634 return;
635 }
636
637 auto done = std::make_shared<std::promise<void>>();
638 auto future = done->get_future();
639 m_actionsQueue.push( [this, done]() -> StatusCode {
640 dumpSchedulerState( -1 );
641 done->set_value();
642 return StatusCode::SUCCESS;
643 } );
644
645 // Do not block the caller for ever if the scheduler thread is stuck: the dump stays queued
646 // and is produced as soon as the scheduler thread gets to it.
647 if ( future.wait_for( s_dumpStateTimeout ) != std::future_status::ready ) {
648 warning() << "The scheduler thread did not produce the state dump within " << s_dumpStateTimeout.count()
649 << "s (it may be blocked): the dump will appear when it proceeds" << endmsg;
650 }
651}
652
653//---------------------------------------------------------------------------
658
659 // ON_DEBUG debug() << "popFinishedEvent: queue size: " << m_finishedEvents.size() << endmsg;
660 if ( m_freeSlots.load() == (int)m_maxEventsInFlight || m_isActive == INACTIVE ) {
661 // ON_DEBUG debug() << "freeslots: " << m_freeSlots << "/" << m_maxEventsInFlight
662 // << " active: " << m_isActive << endmsg;
663 return StatusCode::FAILURE;
664 } else {
665 // ON_DEBUG debug() << "freeslots: " << m_freeSlots << "/" << m_maxEventsInFlight
666 // << " active: " << m_isActive << endmsg;
667 m_finishedEvents.pop( eventContext );
668 ++m_freeSlots;
669 ON_DEBUG debug() << "Popped slot " << eventContext->slot() << " (event " << eventContext->evt() << ")" << endmsg;
670 return StatusCode::SUCCESS;
671 }
672}
673
674//---------------------------------------------------------------------------
679
680 if ( m_finishedEvents.try_pop( eventContext ) ) {
681 ON_DEBUG debug() << "Try Pop successful slot " << eventContext->slot() << "(event " << eventContext->evt() << ")"
682 << endmsg;
683 ++m_freeSlots;
684 return StatusCode::SUCCESS;
685 }
686 return StatusCode::FAILURE;
687}
688
689//--------------------------------------------------------------------------
690
699
700 StatusCode global_sc( StatusCode::SUCCESS );
701
702 // Retry algorithms
703 const size_t retries = m_retryQueue.size();
704 for ( unsigned int retryIndex = 0; retryIndex < retries; ++retryIndex ) {
705 TaskSpec retryTS = std::move( m_retryQueue.front() );
706 m_retryQueue.pop();
707 global_sc = schedule( std::move( retryTS ) );
708 }
709
710 // Loop over all slots
711 OccupancySnapshot nextSnap;
712 auto now = std::chrono::system_clock::now();
713 for ( EventSlot& thisSlot : m_eventSlots ) {
714
715 // Ignore slots without a valid context (relevant when populating scheduler for first time)
716 if ( !thisSlot.eventContext ) continue;
717
718 int iSlot = thisSlot.eventContext->slot();
719
720 // Cache the states of the algorithms to improve readability and performance
721 AlgsExecutionStates& thisAlgsStates = thisSlot.algsStates;
722
723 StatusCode partial_sc = StatusCode::FAILURE;
724
725 // Make an occupancy snapshot
726 if ( m_snapshotInterval != std::chrono::duration<int64_t, std::milli>::min() &&
728
729 // Initialise snapshot
730 if ( nextSnap.states.empty() ) {
731 nextSnap.time = now;
732 nextSnap.states.resize( m_eventSlots.size() );
733 }
734
735 // Store alg states
736 std::vector<int>& slotStateTotals = nextSnap.states[iSlot];
737 slotStateTotals.resize( AState::MAXVALUE );
738 for ( uint8_t state = 0; state < AState::MAXVALUE; ++state ) {
739 slotStateTotals[state] = thisSlot.algsStates.sizeOfSubset( AState( state ) );
740 }
741
742 // Add subslot alg states
743 for ( auto& subslot : thisSlot.allSubSlots ) {
744 for ( uint8_t state = 0; state < AState::MAXVALUE; ++state ) {
745 slotStateTotals[state] += subslot.algsStates.sizeOfSubset( AState( state ) );
746 }
747 }
748 }
749
750 // Perform DR->SCHEDULED
751 const auto& drAlgs = thisAlgsStates.algsInState( AState::DATAREADY );
752 for ( uint algIndex : drAlgs ) {
753 const std::string& algName{ index2algname( algIndex ) };
754 unsigned int rank{ m_optimizationMode.empty() ? 0 : m_precSvc->getPriority( algName ) };
755 bool asynchronous{ m_precSvc->isAsynchronous( algName ) };
756
757 partial_sc =
758 schedule( TaskSpec( nullptr, algIndex, algName, rank, asynchronous, iSlot, thisSlot.eventContext.get() ) );
759
760 ON_VERBOSE if ( partial_sc.isFailure() ) verbose()
761 << "Could not apply transition from " << AState::DATAREADY << " for algorithm " << algName
762 << " on processing slot " << iSlot << endmsg;
763 }
764
765 // Check for algorithms ready in sub-slots
766 for ( auto& subslot : thisSlot.allSubSlots ) {
767 const auto& drAlgsSubSlot = subslot.algsStates.algsInState( AState::DATAREADY );
768 for ( uint algIndex : drAlgsSubSlot ) {
769 const std::string& algName{ index2algname( algIndex ) };
770 unsigned int rank{ m_optimizationMode.empty() ? 0 : m_precSvc->getPriority( algName ) };
771 bool asynchronous{ m_precSvc->isAsynchronous( algName ) };
772 partial_sc =
773 schedule( TaskSpec( nullptr, algIndex, algName, rank, asynchronous, iSlot, subslot.eventContext.get() ) );
774 }
775 }
776
778 std::stringstream s;
779 s << "START, " << thisAlgsStates.sizeOfSubset( AState::CONTROLREADY ) << ", "
780 << thisAlgsStates.sizeOfSubset( AState::DATAREADY ) << ", " << thisAlgsStates.sizeOfSubset( AState::SCHEDULED )
781 << ", " << std::chrono::high_resolution_clock::now().time_since_epoch().count() << "\n";
782 auto threads = ( m_threadPoolSize != -1 ) ? std::to_string( m_threadPoolSize )
783 : std::to_string( std::thread::hardware_concurrency() );
784 std::ofstream myfile;
785 myfile.open( "IntraEventFSMOccupancy_" + threads + "T.csv", std::ios::app );
786 myfile << s.str();
787 myfile.close();
788 }
789
790 // Not complete because this would mean that the slot is already free!
791 if ( m_precSvc->CFRulesResolved( thisSlot ) &&
792 !thisSlot.algsStates.containsAny(
793 { AState::CONTROLREADY, AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) &&
794 !subSlotAlgsInStates( thisSlot,
795 { AState::CONTROLREADY, AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) &&
796 !thisSlot.complete ) {
797
798 thisSlot.complete = true;
799 // if the event did not fail, add it to the finished events
800 // otherwise it is taken care of in the error handling
801 if ( m_algExecStateSvc->eventStatus( *thisSlot.eventContext ) == EventStatus::Success ) {
802 ON_DEBUG debug() << "Event " << thisSlot.eventContext->evt() << " finished (slot "
803 << thisSlot.eventContext->slot() << ")." << endmsg;
804 m_finishedEvents.push( thisSlot.eventContext.release() );
805 }
806
807 // now let's return the fully evaluated result of the control flow
808 ON_DEBUG debug() << m_precSvc->printState( thisSlot ) << endmsg;
809
810 thisSlot.eventContext.reset( nullptr );
811
812 } else if ( isStalled( thisSlot ) ) {
813 m_algExecStateSvc->setEventStatus( EventStatus::AlgStall, *thisSlot.eventContext );
814 eventFailed( thisSlot.eventContext.get() ); // can't release yet
815 }
816 partial_sc.ignore();
817 } // end loop on slots
818
819 // Process snapshot
820 if ( !nextSnap.states.empty() ) {
821 m_lastSnapshot = nextSnap.time;
822 m_snapshotCallback( std::move( nextSnap ) );
823 }
824
825 ON_VERBOSE verbose() << "Iteration done." << endmsg;
826 m_needsUpdate.store( false );
827 return global_sc;
828}
829
830//---------------------------------------------------------------------------
831// Update algorithm state and, optionally, revise states of other downstream algorithms
832StatusCode AvalancheSchedulerSvc::revise( unsigned int iAlgo, EventContext* contextPtr, AState state, bool iterate ) {
833 StatusCode sc;
834 auto slotIndex = contextPtr->slot();
835 EventSlot& slot = m_eventSlots[slotIndex];
836 Cause cs = { Cause::source::Task, index2algname( iAlgo ) };
837
838 if ( contextPtr->usesSubSlot() ) {
839 // Sub-slot
840 auto subSlotIndex = contextPtr->subSlot();
841 EventSlot& subSlot = slot.allSubSlots[subSlotIndex];
842
843 sc = subSlot.algsStates.set( iAlgo, state );
844
845 if ( sc.isSuccess() ) {
846 ON_VERBOSE verbose() << "Promoted " << index2algname( iAlgo ) << " to " << state << " [slot:" << slotIndex
847 << ", subslot:" << subSlotIndex << ", event:" << contextPtr->evt() << "]" << endmsg;
848 // Revise states of algorithms downstream the precedence graph
849 if ( iterate ) sc = m_precSvc->iterate( subSlot, cs );
850 }
851 } else {
852 // Event level (standard behaviour)
853 sc = slot.algsStates.set( iAlgo, state );
854
855 if ( sc.isSuccess() ) {
856 ON_VERBOSE verbose() << "Promoted " << index2algname( iAlgo ) << " to " << state << " [slot:" << slotIndex
857 << ", event:" << contextPtr->evt() << "]" << endmsg;
858 // Revise states of algorithms downstream the precedence graph
859 if ( iterate ) sc = m_precSvc->iterate( slot, cs );
860 }
861 }
862 return sc;
863}
864
865//---------------------------------------------------------------------------
866
874
875 if ( !slot.algsStates.containsAny( { AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) &&
876 !subSlotAlgsInStates( slot, { AState::DATAREADY, AState::SCHEDULED, AState::RESOURCELESS } ) ) {
877
878 error() << "*** Stall detected, event context: " << slot.eventContext.get() << endmsg;
879
880 return true;
881 }
882 return false;
883}
884
885//---------------------------------------------------------------------------
886
892 const uint slotIdx = eventContext->slot();
893
894 error() << "Event " << eventContext->evt() << " on slot " << slotIdx << " failed" << endmsg;
895
896 dumpSchedulerState( msgLevel( MSG::VERBOSE ) ? -1 : slotIdx );
897
898 // dump temporal and topological precedence analysis (if enabled in the PrecedenceSvc)
899 m_precSvc->dumpPrecedenceRules( m_eventSlots[slotIdx] );
900
901 // Push into the finished events queue the failed context
902 m_eventSlots[slotIdx].complete = true;
903 m_finishedEvents.push( m_eventSlots[slotIdx].eventContext.release() );
904}
905
906//---------------------------------------------------------------------------
907
913
914 // To have just one big message
915 std::ostringstream outputMS;
916
917 outputMS << "Dumping scheduler state\n"
918 << "=========================================================================================\n"
919 << "++++++++++++++++++++++++++++++++++++ SCHEDULER STATE ++++++++++++++++++++++++++++++++++++\n"
920 << "=========================================================================================\n\n";
921
922 //===========================================================================
923
924 outputMS << "------------------ Last schedule: Task/Event/Slot/Thread/State Mapping "
925 << "------------------\n\n";
926
927 // Figure if TimelineSvc is available (used below to detect threads IDs)
928 auto timelineSvc = serviceLocator()->service<ITimelineSvc>( "TimelineSvc", false );
929 if ( !timelineSvc.isValid() || !timelineSvc->isEnabled() ) {
930 outputMS << "WARNING Enable TimelineSvc in record mode (RecordTimeline = True) to trace the mapping\n";
931 } else {
932
933 // Figure optimal printout layout
934 size_t indt( 0 );
935 for ( auto& slot : m_eventSlots ) {
936
937 const auto& schedAlgs = slot.algsStates.algsInState( AState::SCHEDULED );
938 for ( uint algIndex : schedAlgs ) {
939 if ( index2algname( algIndex ).length() > indt ) indt = index2algname( algIndex ).length();
940 }
941 }
942
943 // Figure the last running schedule across all slots
944 for ( auto& slot : m_eventSlots ) {
945
946 const auto& schedAlgs = slot.algsStates.algsInState( AState::SCHEDULED );
947 for ( uint algIndex : schedAlgs ) {
948
949 const std::string& algoName{ index2algname( algIndex ) };
950
951 outputMS << " task: " << std::setw( indt ) << algoName << " evt/slot: " << slot.eventContext->evt() << "/"
952 << slot.eventContext->slot();
953
954 // Try to get POSIX threads IDs the currently running tasks are scheduled to
955 if ( timelineSvc.isValid() ) {
956 TimelineEvent te{};
957 te.algorithm = algoName;
958 te.slot = slot.eventContext->slot();
959 te.event = slot.eventContext->evt();
960
961 if ( timelineSvc->getLastMatching( te ) )
962 outputMS << " thread.id: 0x" << std::hex << te.thread << std::dec;
963 else
964 outputMS << " thread.id: [unknown]"; // this means a task has just
965 // been signed off as SCHEDULED,
966 // but has not been assigned to a thread yet
967 // (i.e., not running yet)
968 }
969 outputMS << " state: [" << m_algExecStateSvc->algExecState( algoName, *( slot.eventContext ) ) << "]\n";
970 }
971 }
972 }
973
974 //===========================================================================
975
976 outputMS << "\n---------------------------- Task/CF/FSM Mapping "
977 << ( 0 > iSlot ? "[all slots] --" : "[target slot] " ) << "--------------------------\n\n";
978
979 int slotCount = -1;
980 bool wasAlgError = ( iSlot >= 0 ) ? m_eventSlots[iSlot].algsStates.containsAny( { AState::ERROR } ) ||
981 subSlotAlgsInStates( m_eventSlots[iSlot], { AState::ERROR } )
982 : false;
983
984 for ( auto& slot : m_eventSlots ) {
985 ++slotCount;
986 if ( slot.complete ) continue;
987
988 outputMS << "[ slot: "
989 << ( slot.eventContext->valid() ? std::to_string( slot.eventContext->slot() ) : "[ctx invalid]" )
990 << ", event: "
991 << ( slot.eventContext->valid() ? std::to_string( slot.eventContext->evt() ) : "[ctx invalid]" );
992
993 if ( slot.eventContext->eventID().isValid() ) { outputMS << ", eventID: " << slot.eventContext->eventID(); }
994 outputMS << " ]:\n\n";
995
996 if ( 0 > iSlot || iSlot == slotCount ) {
997
998 // If an alg has thrown an error then it's not a failure of the CF/DF graph
999 if ( wasAlgError ) {
1000 outputMS << "ERROR alg(s):";
1001 int errorCount = 0;
1002 const auto& errorAlgs = slot.algsStates.algsInState( AState::ERROR );
1003 for ( uint algIndex : errorAlgs ) {
1004 outputMS << " " << index2algname( algIndex );
1005 ++errorCount;
1006 }
1007 if ( errorCount == 0 ) outputMS << " in subslot(s)";
1008 outputMS << "\n\n";
1009 } else {
1010 // Snapshot of the Control Flow and FSM states
1011 outputMS << m_precSvc->printState( slot ) << "\n";
1012 }
1013
1014 // Mention sub slots (this is expensive if the number of sub-slots is high)
1015 if ( m_verboseSubSlots && !slot.allSubSlots.empty() ) {
1016 outputMS << "\nNumber of sub-slots: " << slot.allSubSlots.size() << "\n\n";
1017 auto slotID = slot.eventContext->valid() ? std::to_string( slot.eventContext->slot() ) : "[ctx invalid]";
1018 for ( auto& ss : slot.allSubSlots ) {
1019 outputMS << "[ slot: " << slotID << ", sub-slot: "
1020 << ( ss.eventContext->valid() ? std::to_string( ss.eventContext->subSlot() ) : "[ctx invalid]" )
1021 << ", entry: " << ss.entryPoint << ", event: "
1022 << ( ss.eventContext->valid() ? std::to_string( ss.eventContext->evt() ) : "[ctx invalid]" )
1023 << " ]:\n\n";
1024 if ( wasAlgError ) {
1025 outputMS << "ERROR alg(s):";
1026 const auto& errorAlgs = ss.algsStates.algsInState( AState::ERROR );
1027 for ( uint algIndex : errorAlgs ) { outputMS << " " << index2algname( algIndex ); }
1028 outputMS << "\n\n";
1029 } else {
1030 // Snapshot of the Control Flow and FSM states in sub slot
1031 outputMS << m_precSvc->printState( ss ) << "\n";
1032 }
1033 }
1034 }
1035 }
1036 }
1037
1038 //===========================================================================
1039
1040 if ( 0 <= iSlot && !wasAlgError ) {
1041 outputMS << "\n------------------------------ Algorithm Execution States -----------------------------\n\n";
1042 m_algExecStateSvc->dump( outputMS, *( m_eventSlots[iSlot].eventContext ) );
1043 }
1044
1045 outputMS << "\n=========================================================================================\n"
1046 << "++++++++++++++++++++++++++++++++++++++ END OF DUMP ++++++++++++++++++++++++++++++++++++++\n"
1047 << "=========================================================================================\n\n";
1048
1049 info() << outputMS.str() << endmsg;
1050}
1051
1052//---------------------------------------------------------------------------
1053
1055
1056 // Check if a free Algorithm instance is available
1057 StatusCode getAlgSC( m_algResourcePool->acquireAlgorithm( ts.algName, ts.algPtr ) );
1058
1059 // If an instance is available, proceed to scheduling
1060 StatusCode sc;
1061 if ( getAlgSC.isSuccess() ) {
1062
1063 // Decide how to schedule the task and schedule it
1064 if ( -100 != m_threadPoolSize ) {
1065
1066 // Cache values before moving the TaskSpec further
1067 unsigned int algIndex{ ts.algIndex };
1068 std::string_view algName( ts.algName );
1069 unsigned int algRank{ ts.algRank };
1070 bool asynchronous{ ts.asynchronous };
1071 int slotIndex{ ts.slotIndex };
1072 EventContext* contextPtr{ ts.contextPtr };
1073
1074 if ( asynchronous ) {
1075 // Add to asynchronous scheduled queue
1076 m_scheduledAsynchronousQueue.push( std::move( ts ) );
1077
1078 // Schedule task
1079 m_fiberManager->schedule( AlgTask( this, serviceLocator(), m_algExecStateSvc, asynchronous ) );
1080 }
1081
1082 if ( !asynchronous ) {
1083 // Add the algorithm to the scheduled queue
1084 m_scheduledQueue.push( std::move( ts ) );
1085
1086 // Prepare a TBB task that will execute the Algorithm according to the above queued specs
1087 m_arena->enqueue( AlgTask( this, serviceLocator(), m_algExecStateSvc, asynchronous ) );
1089 }
1090 sc = revise( algIndex, contextPtr, AState::SCHEDULED );
1091
1092 ON_DEBUG debug() << "Scheduled " << algName << " [slot:" << slotIndex << ", event:" << contextPtr->evt()
1093 << ", rank:" << algRank << ", asynchronous:" << ( asynchronous ? "yes" : "no" )
1094 << "]. Scheduled algorithms: " << m_algosInFlight << endmsg;
1095
1096 } else { // Avoid scheduling via TBB if the pool size is -100. Instead, run here in the scheduler's control thread
1097 // Beojan: I don't think this bit works. ts hasn't been pushed into any queue so AlgTask won't retrieve it
1099 sc = revise( ts.algIndex, ts.contextPtr, AState::SCHEDULED );
1100 AlgTask( this, serviceLocator(), m_algExecStateSvc, ts.asynchronous )();
1102 }
1103 } else { // if no Algorithm instance available, retry later
1104
1105 sc = revise( ts.algIndex, ts.contextPtr, AState::RESOURCELESS );
1106 // Add the algorithm to the retry queue
1107 m_retryQueue.push( std::move( ts ) );
1108 }
1109
1111
1112 return sc;
1113}
1114
1115//---------------------------------------------------------------------------
1116
1121
1122 Gaudi::Hive::setCurrentContext( ts.contextPtr );
1123
1125
1126 const AlgExecStateRef algstate = m_algExecStateSvc->algExecState( ts.algPtr, *( ts.contextPtr ) );
1127 AState state = algstate.execStatus().isSuccess()
1128 ? ( algstate.filterPassed() ? AState::EVTACCEPTED : AState::EVTREJECTED )
1129 : AState::ERROR;
1130
1131 // Update algorithm state and revise the downstream states
1132 auto sc = revise( ts.algIndex, ts.contextPtr, state, true );
1133
1134 ON_DEBUG debug() << "Executed " << ts.algName << " [slot:" << ts.slotIndex << ", event:" << ts.contextPtr->evt()
1135 << ", rank:" << ts.algRank << ", asynchronous:" << ( ts.asynchronous ? "yes" : "no" )
1136 << "]. Scheduled algorithms: " << m_algosInFlight << endmsg;
1137
1138 // Prompt a call to updateStates
1139 m_needsUpdate.store( true );
1140 return sc;
1141}
1142
1143//---------------------------------------------------------------------------
1144
1145// Method to inform the scheduler about event views
1146
1147StatusCode AvalancheSchedulerSvc::scheduleEventView( const EventContext* sourceContext, const std::string& nodeName,
1148 std::unique_ptr<EventContext> viewContext ) {
1149 // Prevent view nesting
1150 if ( sourceContext->usesSubSlot() ) {
1151 fatal() << "Attempted to nest EventViews at node " << nodeName << ": this is not supported" << endmsg;
1152 return StatusCode::FAILURE;
1153 }
1154
1155 ON_VERBOSE verbose() << "Queuing a view for [" << viewContext.get() << "]" << endmsg;
1156
1157 // It's not possible to create an std::functional from a move-capturing lambda
1158 // So, we have to release the unique pointer
1159 auto action = [this, slotIndex = sourceContext->slot(), viewContextPtr = viewContext.release(),
1160 &nodeName]() -> StatusCode {
1161 // Attach the sub-slot to the top-level slot
1162 EventSlot& topSlot = this->m_eventSlots[slotIndex];
1163
1164 if ( viewContextPtr ) {
1165 // Re-create the unique pointer
1166 auto viewContext = std::unique_ptr<EventContext>( viewContextPtr );
1167 topSlot.addSubSlot( std::move( viewContext ), nodeName );
1168 return StatusCode::SUCCESS;
1169 } else {
1170 // Disable the view node if there are no views
1171 topSlot.disableSubSlots( nodeName );
1172 return StatusCode::SUCCESS;
1173 }
1174 };
1175
1176 m_actionsQueue.push( std::move( action ) );
1177
1178 return StatusCode::SUCCESS;
1179}
1180
1181//---------------------------------------------------------------------------
1182
1183// Sample occupancy at fixed interval (ms)
1184// Negative value to deactivate, 0 to snapshot every change
1185// Each sample, apply the callback function to the result
1186
1187void AvalancheSchedulerSvc::recordOccupancy( int samplePeriod, std::function<void( OccupancySnapshot )> callback ) {
1188
1189 auto action = [this, samplePeriod, callback = std::move( callback )]() -> StatusCode {
1190 if ( samplePeriod < 0 ) {
1191 this->m_snapshotInterval = std::chrono::duration<int64_t, std::milli>::min();
1192 } else {
1193 this->m_snapshotInterval = std::chrono::duration<int64_t, std::milli>( samplePeriod );
1194 m_snapshotCallback = std::move( callback );
1195 }
1196 return StatusCode::SUCCESS;
1197 };
1198
1199 m_actionsQueue.push( std::move( action ) );
1200}
1201
1202StatusCode AvalancheSchedulerSvc::dumpDataDepsGraphFile( const std::map<std::string, DataObjIDColl>& inDeps,
1203 const std::map<std::string, DataObjIDColl>& outDeps ) const {
1204 // Both maps should have the same algorithm entries
1205 assert( inDeps.size() == outDeps.size() );
1206
1208 info() << "Dumping data dependencies graph to file: " << g.fileName() << endmsg;
1209
1210 // define algs and objects
1211 std::set<std::size_t> definedObjects;
1212
1213 // Regex for selection of algs and objects
1214 std::regex algNameRegex( m_dataDepsGraphAlgoPattern.value() );
1215 std::regex objNameRegex( m_dataDepsGraphObjectPattern.value() );
1216
1217 // inDeps and outDeps should have the same entries
1218 std::size_t algoIndex = 0ul;
1219 for ( const auto& [algName, ideps] : inDeps ) {
1220 if ( not std::regex_search( algName, algNameRegex ) ) continue;
1221 std::string algIndex = "Alg_" + std::to_string( algoIndex );
1222 g.addNode( algIndex, algName );
1223
1224 // inputs
1225 for ( const auto& dep : ideps ) {
1226 if ( not std::regex_search( dep.fullKey(), objNameRegex ) ) continue;
1227
1228 const auto [itr, inserted] = definedObjects.insert( dep.hash() );
1229 std::string objIndex = "obj_" + std::to_string( dep.hash() );
1230 if ( inserted ) g.addNode( objIndex, dep.key() );
1231
1232 g.addEdge( objIndex, algIndex );
1233 } // loop on ideps
1234
1235 const auto& odeps = outDeps.at( algName );
1236 for ( const auto& dep : odeps ) {
1237 if ( not std::regex_search( dep.fullKey(), objNameRegex ) ) continue;
1238
1239 const auto [itr, inserted] = definedObjects.insert( dep.hash() );
1240 std::string objIndex = "obj_" + std::to_string( dep.hash() );
1241 if ( inserted ) g.addNode( objIndex, dep.key() );
1242
1243 g.addEdge( algIndex, objIndex );
1244 } // loop on odeps
1245
1246 ++algoIndex;
1247 } // loop on inDeps
1248
1249 return StatusCode::SUCCESS;
1250}
#define ON_VERBOSE
std::unordered_set< DataObjID, DataObjID_Hasher > DataObjIDColl
Definition DataObjID.h:121
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)
Provide serialization function (output only) for some common STL classes (vectors,...
wrapper on an Algorithm state.
const StatusCode & execStatus() const
bool filterPassed() const
The AlgsExecutionStates encodes the state machine for the execution of algorithms within a single eve...
const boost::container::flat_set< int > algsInState(State state) const
size_t sizeOfSubset(State state) const
bool containsAny(std::initializer_list< State > l) const
check if the collection contains at least one state of any listed types
StatusCode set(unsigned int iAlgo, State newState)
StatusCode pushNewEvent(EventContext *eventContext) override
Make an event available to the scheduler.
Gaudi::Property< std::vector< std::string > > m_checkOutputIgnoreList
SmartIF< IThreadPoolSvc > m_threadPoolSvc
Gaudi::Property< std::string > m_useDataLoader
void dumpState() override
Dump scheduler state for all slots.
void activate()
Activate scheduler.
Gaudi::Property< std::string > m_optimizationMode
StatusCode popFinishedEvent(EventContext *&eventContext) override
Blocks until an event is available.
Gaudi::Property< bool > m_dumpIntraEventDynamics
std::chrono::system_clock::time_point m_lastSnapshot
std::vector< std::string > m_algname_vect
Vector to bookkeep the information necessary to the index2name conversion.
Gaudi::Property< int > m_threadPoolSize
StatusCode finalize() override
Finalise.
tbb::concurrent_priority_queue< TaskSpec, AlgQueueSort > m_scheduledQueue
Queues for scheduled algorithms.
std::function< void(OccupancySnapshot)> m_snapshotCallback
std::queue< TaskSpec > m_retryQueue
Gaudi::Property< bool > m_verboseSubSlots
tbb::concurrent_bounded_queue< action > m_actionsQueue
Queue where closures are stored and picked for execution.
SmartIF< ICondSvc > m_condSvc
A shortcut to service for Conditions handling.
AlgsExecutionStates::State AState
bool isStalled(const EventSlot &) const
Check if scheduling in a particular slot is in a stall.
SmartIF< IAlgExecStateSvc > m_algExecStateSvc
Algorithm execution state manager.
StatusCode pushNewEvents(std::vector< EventContext * > &eventContexts) override
StatusCode revise(unsigned int iAlgo, EventContext *contextPtr, AState state, bool iterate=false)
tbb::concurrent_bounded_queue< EventContext * > m_finishedEvents
Queue of finished events.
StatusCode deactivate()
Deactivate scheduler.
unsigned int m_algosInFlight
Number of algorithms presently in flight.
Gaudi::Property< std::string > m_dataDepsGraphObjectPattern
Gaudi::Property< bool > m_showDataFlow
StatusCode schedule(TaskSpec &&)
SmartIF< IPrecedenceSvc > m_precSvc
A shortcut to the Precedence Service.
Gaudi::Property< bool > m_checkDeps
std::chrono::duration< int64_t, std::milli > m_snapshotInterval
Gaudi::Property< std::string > m_dataDepsGraphFile
SmartIF< IAlgResourcePool > m_algResourcePool
Cache for the algorithm resource pool.
Gaudi::Property< bool > m_showControlFlow
Gaudi::Property< bool > m_simulateExecution
Gaudi::Property< std::string > m_whiteboardSvcName
StatusCode tryPopFinishedEvent(EventContext *&eventContext) override
Try to fetch an event from the scheduler.
std::unordered_map< std::string, unsigned int > m_algname_index_map
Map to bookkeep the information necessary to the name2index conversion.
std::atomic< bool > m_needsUpdate
virtual StatusCode scheduleEventView(const EventContext *sourceContext, const std::string &nodeName, std::unique_ptr< EventContext > viewContext) override
Method to inform the scheduler about event views.
Gaudi::Property< int > m_maxParallelismExtra
StatusCode signoff(const TaskSpec &)
The call to this method is triggered only from within the AlgTask.
std::function< StatusCode()> action
Gaudi::Property< std::string > m_dataDepsGraphAlgoPattern
Gaudi::Property< int > m_numOffloadThreads
Gaudi::Property< bool > m_checkOutput
std::atomic< ActivationState > m_isActive
Flag to track if the scheduler is active or not.
StatusCode initialize() override
Initialise.
Gaudi::Property< bool > m_enableCondSvc
virtual void recordOccupancy(int samplePeriod, std::function< void(OccupancySnapshot)> callback) override
Sample occupancy at fixed interval (ms) Negative value to deactivate, 0 to snapshot every change Each...
void eventFailed(EventContext *eventContext)
Method to execute if an event failed.
std::atomic_int m_freeSlots
Atomic to account for asyncronous updates by the scheduler wrt the rest.
StatusCode dumpDataDepsGraphFile(const std::map< std::string, DataObjIDColl > &inDeps, const std::map< std::string, DataObjIDColl > &outDeps) const
unsigned int freeSlots() override
Get free slots number.
void dumpSchedulerState(int iSlot)
Dump the state of the scheduler.
std::unique_ptr< FiberManager > m_fiberManager
SmartIF< IHiveWhiteBoard > m_whiteboard
A shortcut to the whiteboard.
tbb::concurrent_priority_queue< TaskSpec, AlgQueueSort > m_scheduledAsynchronousQueue
StatusCode iterate()
Loop on all slots to schedule DATAREADY algorithms and sign off ready events.
Gaudi::Property< bool > m_showDataDeps
std::thread m_thread
The thread in which the activate function runs.
const std::string & index2algname(unsigned int index)
Convert an integer to a name.
std::vector< EventSlot > m_eventSlots
Vector of events slots.
MsgStream & error() const
shortcut for the method msgStream(MSG::ERROR)
MsgStream & verbose() const
shortcut for the method msgStream(MSG::VERBOSE)
MsgStream & warning() const
shortcut for the method msgStream(MSG::WARNING)
MsgStream & fatal() const
shortcut for the method msgStream(MSG::FATAL)
MsgStream & debug() const
shortcut for the method msgStream(MSG::DEBUG)
MsgStream & msg() const
shortcut for the method msgStream(MSG::INFO)
MsgStream & info() const
shortcut for the method msgStream(MSG::INFO)
MSG::Level msgLevel() const
get the cached level (originally extracted from the embedded MsgStream)
std::vector< std::string > owners_names_of(const DataObjID &id, bool with_main=false) const
const DataObjIDColl & outputDataObjs() const override
void addDependency(const DataObjID &id, const Gaudi::DataHandle::Mode &mode) override
const DataObjIDColl & inputDataObjs() const override
std::string fullKey() const
combination of the key and the ClassName, mostly for debugging
This class represents an entry point to all the event specific data.
ContextEvt_t evt() const
ContextID_t subSlot() const
ContextID_t slot() const
bool usesSubSlot() const
Base class from which all concrete algorithm classes should be derived.
Definition Algorithm.h:87
void acceptDHVisitor(IDataHandleVisitor *) const override
const std::string & name() const override
The identifying name of the algorithm object.
bool isAsynchronous() const
Definition Algorithm.h:376
utilities to dump graphs in different formats
Definition GraphDumper.h:30
The IAlgorithm is the interface implemented by the Algorithm base class.
Definition IAlgorithm.h:37
virtual const std::string & type() const =0
The type of the algorithm.
virtual SmartIF< IService > & service(const Gaudi::Utils::TypeNameString &typeName, const bool createIf=true)=0
Returns a smart pointer to a service.
A service to resolve the task execution precedence.
const concurrency::PrecedenceRulesGraph * getRules() const
Precedence rules accessor.
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
StatusCode initialize() override
Definition Service.cpp:118
Small smart pointer class with automatic reference counting for IInterface.
Definition SmartIF.h:28
bool isValid() const
Allow for check if smart pointer is valid.
Definition SmartIF.h:69
This class is used for returning status codes from appropriate routines.
Definition StatusCode.h:64
bool isFailure() const
Definition StatusCode.h:118
void ignore() const
Allow discarding a StatusCode without warning.
Definition StatusCode.h:128
bool isSuccess() const
Definition StatusCode.h:302
constexpr static const auto SUCCESS
Definition StatusCode.h:99
constexpr static const auto FAILURE
Definition StatusCode.h:100
A service which initializes a TBB thread pool.
unsigned int getAlgoIndex() const
Get algorithm index.
unsigned int getControlFlowNodeCounter() const
Get total number of control flow graph nodes.
AlgorithmNode * getAlgorithmNode(const std::string &algoName) const
Get the AlgorithmNode from by algorithm name using graph index.
GAUDI_API void setCurrentContext(const EventContext *ctx)
std::ostream & operator<<(std::ostream &s, const std::pair< T1, T2 > &p)
Serialize an std::pair in a python like format. E.g. "(1, 2)".
@ VERBOSE
Definition IMessageSvc.h:22
Struct to hold entries in the alg queues.
Class representing an event slot.
Definition EventSlot.h:23
std::unique_ptr< EventContext > eventContext
Cache for the eventContext.
Definition EventSlot.h:82
std::vector< EventSlot > allSubSlots
Actual sub-slot instances.
Definition EventSlot.h:99
bool complete
Flags completion of the event.
Definition EventSlot.h:88
void addSubSlot(std::unique_ptr< EventContext > viewContext, const std::string &nodeName)
Add a subslot to the slot (this constructs a new slot and registers it with the parent one).
Definition EventSlot.h:60
void reset(EventContext *theeventContext)
Reset all resources in order to reuse the slot (thread-unsafe).
Definition EventSlot.h:48
AlgsExecutionStates algsStates
Vector of algorithms states.
Definition EventSlot.h:84
void disableSubSlots(const std::string &nodeName)
Disable event views for a given CF view node by registering an empty container Contact B.
Definition EventSlot.h:77
std::string algorithm
pthread_t thread