3434
3535#include < fairmq/Device.h>
3636#include < fairmq/Channel.h>
37+ #include < fairmq/States.h>
3738
3839using namespace o2 ::framework;
3940using namespace o2 ::header;
@@ -45,6 +46,8 @@ using namespace o2::gpu::gpurecoworkflow_internals;
4546namespace o2 ::gpu
4647{
4748
49+ static const std::string GPURecoWorkflowSpec_FMQCallbackKey = " GPURecoWorkflowSpec_FMQCallbackKey" ;
50+
4851struct pipelinePrepareMessage {
4952 static constexpr size_t MAGIC_WORD = 0X8473957353424134 ;
5053 size_t magicWord = MAGIC_WORD ;
@@ -60,6 +63,7 @@ void GPURecoWorkflowSpec::initPipeline(o2::framework::InitContext& ic)
6063{
6164 if (mSpecConfig .enableDoublePipeline == 1 ) {
6265 mPipeline ->fmqDevice = ic.services ().get <RawDeviceService>().device ();
66+ mPipeline ->fmqDevice ->SubscribeToStateChange (GPURecoWorkflowSpec_FMQCallbackKey, [this ](fair::mq::State s) { receiveFMQStateCallback (s); });
6367 mPolicyOrder = [this ](o2::framework::DataProcessingHeader::StartTime timeslice) {
6468 std::unique_lock lk (mPipeline ->completionPolicyMutex );
6569 mPipeline ->completionPolicyNotify .wait (lk, [pipeline = mPipeline .get ()] { return pipeline->pipelineSenderTerminating || !pipeline->completionPolicyQueue .empty (); });
@@ -233,7 +237,11 @@ int GPURecoWorkflowSpec::handlePipeline(ProcessingContext& pc, GPUTrackingInOutP
233237void GPURecoWorkflowSpec::handlePipelineEndOfStream (EndOfStreamContext& ec)
234238{
235239 if (mSpecConfig .enableDoublePipeline == 1 ) {
236- TerminateThreads (); // TODO: Apparently breaks START / STOP / START
240+ {
241+ std::lock_guard lk (mPipeline ->fmqStateMutex );
242+ mPipeline ->endOfStreamReceived = true ;
243+ }
244+ mPipeline ->fmqStateCheckNotify .notify_all ();
237245 }
238246 if (mSpecConfig .enableDoublePipeline == 2 ) {
239247 auto * device = ec.services ().get <RawDeviceService>().device ();
@@ -247,18 +255,37 @@ void GPURecoWorkflowSpec::handlePipelineEndOfStream(EndOfStreamContext& ec)
247255 }
248256}
249257
258+ void GPURecoWorkflowSpec::receiveFMQStateCallback (fair::mq::State newState)
259+ {
260+ {
261+ std::lock_guard lk (mPipeline ->fmqStateMutex );
262+ if (mPipeline ->fmqState != fair::mq::State::Running && newState == fair::mq::State::Running) {
263+ mPipeline ->endOfStreamReceived = false ;
264+ }
265+ mPipeline ->fmqState = newState;
266+ if (newState == fair::mq::State::Exiting) {
267+ mPipeline ->fmqDevice ->UnsubscribeFromStateChange (GPURecoWorkflowSpec_FMQCallbackKey);
268+ }
269+ }
270+ mPipeline ->fmqStateCheckNotify .notify_all ();
271+ }
272+
250273void GPURecoWorkflowSpec::RunReceiveThread ()
251274{
252275 auto * device = mPipeline ->fmqDevice ;
253- while (device->GetCurrentState () != fair::mq::State::Running) {
254- usleep (300000 );
255- }
256276 while (!mPipeline ->shouldTerminate ) {
257277 bool received = false ;
258278 int recvTimeot = 1000 ;
259279 fair::mq::MessagePtr msg;
260280 LOG (debug) << " Waiting for out of band message" ;
261281 do {
282+ {
283+ std::unique_lock lk (mPipeline ->fmqStateMutex );
284+ mPipeline ->fmqStateCheckNotify .wait (lk, [this ]() { return (mPipeline ->fmqState == fair::mq::State::Running && !mPipeline ->endOfStreamReceived ) || mPipeline ->shouldTerminate ; }); // Do not check mPipeline->fmqDevice->NewStatePending() since we wait for EndOfStream!
285+ }
286+ if (mPipeline ->shouldTerminate ) {
287+ break ;
288+ }
262289 try {
263290 msg = device->NewMessageFor (" gpu-prepare-channel" , 0 , 0 );
264291 do {
@@ -279,8 +306,10 @@ void GPURecoWorkflowSpec::RunReceiveThread()
279306 LOG (fatal) << " Prepare message corrupted, invalid magic word" ;
280307 }
281308 if (m->flagEndOfStream ) {
282- LOG (info) << " Received end-of-stream from out-of-band channel, terminating receive thread" ; // TODO: Breaks START / STOP / START
283- break ;
309+ LOG (info) << " Received end-of-stream from out-of-band channel" ;
310+ mPipeline ->endOfStreamReceived = true ;
311+ mPipeline ->mNTFReceived = 0 ;
312+ continue ;
284313 }
285314
286315 auto context = std::make_unique<GPURecoWorkflow_QueueObject>();
@@ -317,7 +346,7 @@ void GPURecoWorkflowSpec::RunReceiveThread()
317346 context->ptrs .tpcZS = &context->tpcZS ;
318347 context->ptrs .settingsTF = &context->tfSettings ;
319348 context->mTFId = mPipeline ->mNTFReceived ;
320- if (mPipeline ->mNTFReceived ++ >= mPipeline ->workers .size ()) { // Do not inject the first 2 TF , since we need a first round of calib updates from DPL before starting
349+ if (mPipeline ->mNTFReceived ++ >= mPipeline ->workers .size ()) { // Do not inject the first workers.size() TFs , since we need a first round of calib updates from DPL before starting
321350 enqueuePipelinedJob (&context->ptrs , nullptr , context.get (), false );
322351 }
323352 {
@@ -338,18 +367,22 @@ void GPURecoWorkflowSpec::RunReceiveThread()
338367 mPipeline ->completionPolicyNotify .notify_one ();
339368}
340369
341- void GPURecoWorkflowSpec::TerminateThreads ()
370+ void GPURecoWorkflowSpec::ExitPipeline ()
342371{
343- mPipeline ->shouldTerminate = true ;
344- for (unsigned int i = 0 ; i < mPipeline ->workers .size (); i++) {
345- mPipeline ->workers [i].inputQueueNotify .notify_one ();
346- }
347- if (mPipeline ->receiveThread .joinable ()) {
348- mPipeline ->receiveThread .join ();
349- }
350- for (unsigned int i = 0 ; i < mPipeline ->workers .size (); i++) {
351- if (mPipeline ->workers [i].thread .joinable ()) {
352- mPipeline ->workers [i].thread .join ();
372+ if (mSpecConfig .enableDoublePipeline == 1 && mPipeline ->fmqDevice ) {
373+ mPipeline ->fmqDevice = nullptr ;
374+ mPipeline ->shouldTerminate = true ;
375+ mPipeline ->fmqStateCheckNotify .notify_all ();
376+ for (unsigned int i = 0 ; i < mPipeline ->workers .size (); i++) {
377+ mPipeline ->workers [i].inputQueueNotify .notify_one ();
378+ }
379+ if (mPipeline ->receiveThread .joinable ()) {
380+ mPipeline ->receiveThread .join ();
381+ }
382+ for (unsigned int i = 0 ; i < mPipeline ->workers .size (); i++) {
383+ if (mPipeline ->workers [i].thread .joinable ()) {
384+ mPipeline ->workers [i].thread .join ();
385+ }
353386 }
354387 }
355388}
0 commit comments