Skip to content

Commit 2bd5701

Browse files
committed
GPU Workflow: Fix possible deadlock when stopping without receiving EndOfSteam with DoublePipeline
1 parent 9a3938a commit 2bd5701

12 files changed

Lines changed: 115 additions & 50 deletions

GPU/GPUTracking/Base/GPUReconstruction.cxx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ struct GPUReconstructionPipelineContext {
6666
std::queue<GPUReconstructionPipelineQueue*> pipelineQueue;
6767
std::mutex mutex;
6868
std::condition_variable cond;
69+
bool workerRunning = false;
6970
bool terminate = false;
7071
};
7172
} // namespace o2::gpu
@@ -1094,6 +1095,7 @@ void GPUReconstruction::RunPipelineWorker()
10941095
{
10951096
std::unique_lock<std::mutex> lk(mPipelineContext->mutex);
10961097
mPipelineContext->cond.wait(lk, [this] { return this->mPipelineContext->pipelineQueue.size() > 0; });
1098+
mPipelineContext->workerRunning = true;
10971099
}
10981100
GPUReconstructionPipelineQueue* q;
10991101
{
@@ -1111,6 +1113,8 @@ void GPUReconstruction::RunPipelineWorker()
11111113
q->done = true;
11121114
}
11131115
q->c.notify_one();
1116+
mPipelineContext->workerRunning = false;
1117+
mPipelineContext->cond.notify_one();
11141118
}
11151119
if (GetProcessingSettings().debugLevel >= 3) {
11161120
GPUInfo("Pipeline worker ended");
@@ -1122,6 +1126,12 @@ void GPUReconstruction::TerminatePipelineWorker()
11221126
EnqueuePipeline(true);
11231127
}
11241128

1129+
void GPUReconstruction::DrainPipeline()
1130+
{
1131+
std::unique_lock<std::mutex> lk(mPipelineContext->mutex);
1132+
mPipelineContext->cond.wait(lk, [this] { return this->mPipelineContext->pipelineQueue.empty() && !this->mPipelineContext->workerRunning; });
1133+
}
1134+
11251135
int32_t GPUReconstruction::EnqueuePipeline(bool terminate)
11261136
{
11271137
ClearAllocatedMemory(true);

GPU/GPUTracking/Base/GPUReconstruction.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@ class GPUReconstruction
9898
static constexpr GeometryType geometryType = GeometryType::O2;
9999
#endif
100100

101+
enum retValValue : uint32_t { ok = 0,
102+
error = 1,
103+
doExit = 2,
104+
nonFatalErrorCode = 3,
105+
abort = 4 };
101106
static DeviceType GetDeviceType(const char* type);
102107
enum InOutPointerType : uint32_t { CLUSTER_DATA = 0,
103108
SECTOR_OUT_TRACK = 1,
@@ -159,6 +164,7 @@ class GPUReconstruction
159164
int32_t CheckErrorCodes(bool cpuOnly = false, bool forceShowErrors = false, std::vector<std::array<uint32_t, 4>>* fillErrors = nullptr);
160165
void RunPipelineWorker();
161166
void TerminatePipelineWorker();
167+
void DrainPipeline();
162168

163169
// Helpers for memory allocation
164170
GPUMemoryResource& Res(int16_t num) { return mMemoryResources[num]; }

GPU/GPUTracking/Base/GPUReconstructionCPU.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ int32_t GPUReconstructionCPU::RunChains()
245245
retVal = mChains[i]->RunChain();
246246
}
247247
}
248-
if (retVal != 0 && retVal != 2) {
248+
if (retVal != GPUReconstruction::retValValue::ok && retVal != GPUReconstruction::retValValue::doExit) {
249249
return retVal;
250250
}
251251
mTimerTotal.Stop();

GPU/GPUTracking/Global/GPUChainTracking.cxx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -677,7 +677,7 @@ int32_t GPUChainTracking::RunChain()
677677
const bool needQA = GPUQA::QAAvailable() && (GetProcessingSettings().runQA || (GetProcessingSettings().eventDisplay && (mIOPtrs.nMCInfosTPC || GetProcessingSettings().runMC)));
678678
if (needQA && GetQA()->IsInitialized() == false) {
679679
if (GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : gpudatatypes::gpuqa::tasksAutomatic)) {
680-
return 1;
680+
return GPUReconstruction::retValValue::error;
681681
}
682682
}
683683
if (needQA) {
@@ -693,7 +693,7 @@ int32_t GPUChainTracking::RunChain()
693693
mRec->PrepareEvent();
694694
} catch (const std::bad_alloc& e) {
695695
GPUError("Memory Allocation Error");
696-
return (1);
696+
return GPUReconstruction::retValValue::error;
697697
}
698698
mRec->getGeneralStepTimer(GeneralStep::Prepare).Stop();
699699

@@ -707,11 +707,11 @@ int32_t GPUChainTracking::RunChain()
707707

708708
if (mIOPtrs.tpcCompressedClusters) {
709709
if (runRecoStep(RecoStep::TPCDecompression, &GPUChainTracking::RunTPCDecompression)) {
710-
return 1;
710+
return GPUReconstruction::retValValue::error;
711711
}
712712
} else if (mIOPtrs.tpcPackedDigits || mIOPtrs.tpcZS) {
713713
if (runRecoStep(RecoStep::TPCClusterFinding, &GPUChainTracking::RunTPCClusterizer, false)) {
714-
return 1;
714+
return GPUReconstruction::retValValue::error;
715715
}
716716
}
717717

@@ -720,17 +720,17 @@ int32_t GPUChainTracking::RunChain()
720720
}
721721

722722
if (mIOPtrs.clustersNative && runRecoStep(RecoStep::TPCConversion, &GPUChainTracking::ConvertNativeToClusterData)) {
723-
return 1;
723+
return GPUReconstruction::retValValue::error;
724724
}
725725

726726
mRec->PushNonPersistentMemory(qStr2Tag("TPCSLCD1")); // 1st stack level for TPC tracking sector data
727727
mTPCSectorScratchOnStack = true;
728728
if (runRecoStep(RecoStep::TPCSectorTracking, &GPUChainTracking::RunTPCTrackingSectors)) {
729-
return 1;
729+
return GPUReconstruction::retValValue::error;
730730
}
731731

732732
if (runRecoStep(RecoStep::TPCMerging, &GPUChainTracking::RunTPCTrackingMerger, false)) {
733-
return 1;
733+
return GPUReconstruction::retValValue::error;
734734
}
735735
if (mTPCSectorScratchOnStack) {
736736
mRec->PopNonPersistentMemory(RecoStep::TPCSectorTracking, qStr2Tag("TPCSLCD1")); // Release 1st stack level, TPC sector data not needed after merger
@@ -750,16 +750,16 @@ int32_t GPUChainTracking::RunChain()
750750
}
751751
}
752752
if (runRecoStep(RecoStep::TPCCompression, &GPUChainTracking::RunTPCCompression)) {
753-
return 1;
753+
return GPUReconstruction::retValValue::error;
754754
}
755755
}
756756

757757
if (runRecoStep(RecoStep::TRDTracking, &GPUChainTracking::RunTRDTracking)) {
758-
return 1;
758+
return GPUReconstruction::retValValue::error;
759759
}
760760

761761
if (runRecoStep(RecoStep::Refit, &GPUChainTracking::RunRefit)) {
762-
return 1;
762+
return GPUReconstruction::retValValue::error;
763763
}
764764

765765
if (!GetProcessingSettings().doublePipeline) { // Synchronize with output copies running asynchronously
@@ -770,9 +770,9 @@ int32_t GPUChainTracking::RunChain()
770770
mRec->SetNActiveThreads(-1);
771771
}
772772

773-
int32_t retVal = 0;
773+
int32_t retVal = GPUReconstruction::retValValue::ok;
774774
if (CheckErrorCodes(false, false, mRec->getErrorCodeOutput())) { // TODO: Eventually, we should use GPUReconstruction::CheckErrorCodes
775-
retVal = 3;
775+
retVal = GPUReconstruction::retValValue::nonFatalErrorCode;
776776
if (!GetProcessingSettings().ignoreNonFatalGPUErrors) {
777777
return retVal;
778778
}
@@ -820,7 +820,7 @@ int32_t GPUChainTracking::RunChainFinalize()
820820
GPUInfo("Starting Event Display...");
821821
if (mEventDisplay->StartDisplay()) {
822822
GPUError("Error starting Event Display");
823-
return (1);
823+
return GPUReconstruction::retValValue::error;
824824
}
825825
mDisplayRunning = true;
826826
} else {
@@ -857,15 +857,15 @@ int32_t GPUChainTracking::RunChainFinalize()
857857
mDisplayRunning = false;
858858
GetProcessingSettings().eventDisplay->DisplayExit();
859859
const_cast<GPUSettingsProcessing&>(GetProcessingSettings()).eventDisplay = nullptr; // TODO: fixme - eventDisplay should probably not be put into ProcessingSettings in the first place
860-
return (2);
860+
return GPUReconstruction::retValValue::doExit;
861861
}
862862
GetProcessingSettings().eventDisplay->setDisplayControl(0);
863863
GPUInfo("Loading next event...");
864864

865865
mEventDisplay->BlockTillNextEvent();
866866
}
867867

868-
return 0;
868+
return GPUReconstruction::retValValue::ok;
869869
}
870870

871871
int32_t GPUChainTracking::FinalizePipelinedProcessing()

GPU/GPUTracking/Global/GPUChainTracking.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ class GPUChainTracking : public GPUChain
195195
void SetCalibObjects(const GPUCalibObjects& obj);
196196
void SetUpdateCalibObjects(const GPUCalibObjectsConst& obj, const GPUNewCalibValues& vals);
197197
void SetSubOutputControl(int32_t i, GPUOutputControl* v) { mSubOutputControls[i] = v; }
198-
void SetFinalInputCallback(std::function<void()> v) { mWaitForFinalInputs = v; }
198+
void SetFinalInputCallback(std::function<int32_t()> v) { mWaitForFinalInputs = v; }
199199

200200
const GPUSettingsDisplay* mConfigDisplay = nullptr; // Abstract pointer to Standalone Display Configuration Structure
201201
const GPUSettingsQA* mConfigQA = nullptr; // Abstract pointer to Standalone QA Configuration Structure
@@ -321,7 +321,7 @@ class GPUChainTracking : public GPUChain
321321
std::mutex mMutexUpdateCalib;
322322
std::unique_ptr<GPUChainTrackingFinalContext> mPipelineFinalizationCtx;
323323
GPUChainTrackingFinalContext* mPipelineNotifyCtx = nullptr;
324-
std::function<void()> mWaitForFinalInputs;
324+
std::function<int32_t()> mWaitForFinalInputs;
325325

326326
int32_t OutputStream() const { return mRec->NStreams() - 2; }
327327
};

GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -769,7 +769,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput)
769769
#endif
770770

771771
if (RunTPCClusterizer_prepare(mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer, extraADCs)) {
772-
return 1;
772+
return GPUReconstruction::retValValue::error;
773773
}
774774
if (GetProcessingSettings().autoAdjustHostThreads && !doGPU) {
775775
mRec->SetNActiveThreads(mRec->MemoryScalers()->nTPCdigits / 6000);
@@ -1471,7 +1471,9 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput)
14711471
notifyForeignChainFinished();
14721472
}
14731473
if (mWaitForFinalInputs && iSectorBase >= 30 && (int32_t)iSectorBase < 30 + GetProcessingSettings().nTPCClustererLanes) {
1474-
mWaitForFinalInputs();
1474+
if (mWaitForFinalInputs()) {
1475+
return GPUReconstruction::retValValue::abort;
1476+
}
14751477
synchronizeCalibUpdate = DoQueuedUpdates(0, false);
14761478
}
14771479
}

GPU/GPUTracking/Interface/GPUO2Interface.cxx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ void GPUO2Interface::Deinitialize()
137137
mNContexts = 0;
138138
}
139139

140+
void GPUO2Interface::DrainPipeline()
141+
{
142+
mCtx[0].mRec->DrainPipeline();
143+
}
144+
140145
void GPUO2Interface::DumpEvent(int32_t nEvent, GPUTrackingInOutPointers* data, uint32_t iThread, const char* dir)
141146
{
142147
const auto oldPtrs = mCtx[iThread].mChain->mIOPtrs;
@@ -185,19 +190,23 @@ int32_t GPUO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterface
185190
}
186191
};
187192

188-
auto inputWaitCallback = [this, iThread, inputUpdateCallback, &data, &outputs, &setOutputs]() {
193+
auto inputWaitCallback = [this, iThread, inputUpdateCallback, &data, &outputs, &setOutputs]() -> int32_t {
189194
GPUTrackingInOutPointers* updatedData;
190195
GPUInterfaceOutputs* updatedOutputs;
196+
int32_t retVal = 0;
191197
if (inputUpdateCallback->callback) {
192-
inputUpdateCallback->callback(updatedData, updatedOutputs);
193-
mCtx[iThread].mChain->mIOPtrs = *updatedData;
194-
outputs = updatedOutputs;
195-
data = updatedData;
196-
setOutputs(outputs);
198+
retVal = inputUpdateCallback->callback(updatedData, updatedOutputs);
199+
if (retVal == 0) {
200+
mCtx[iThread].mChain->mIOPtrs = *updatedData;
201+
outputs = updatedOutputs;
202+
data = updatedData;
203+
setOutputs(outputs);
204+
}
197205
}
198206
if (inputUpdateCallback->notifyCallback) {
199207
inputUpdateCallback->notifyCallback();
200208
}
209+
return retVal;
201210
};
202211

203212
if (inputUpdateCallback) {
@@ -210,8 +219,8 @@ int32_t GPUO2Interface::RunTracking(GPUTrackingInOutPointers* data, GPUInterface
210219
}
211220

212221
int32_t retVal = mCtx[iThread].mRec->RunChains();
213-
if (retVal == 2) {
214-
retVal = 0; // 2 signals end of event display, ignore
222+
if (retVal == GPUReconstruction::retValValue::doExit) {
223+
retVal = GPUReconstruction::retValValue::ok; // Ignore exit signal from event display
215224
}
216225
if (mConfig->configQA.shipToQC && mCtx[iThread].mChain->QARanForTF()) {
217226
outputs->qa.hist1 = &mCtx[iThread].mChain->GetQA()->getHistograms1D();

GPU/GPUTracking/Interface/GPUO2Interface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ class GPUO2Interface
7171

7272
int32_t Initialize(const GPUO2InterfaceConfiguration& config);
7373
void Deinitialize();
74+
void DrainPipeline();
7475

7576
int32_t RunTracking(GPUTrackingInOutPointers* data, GPUInterfaceOutputs* outputs = nullptr, uint32_t iThread = 0, GPUInterfaceInputUpdate* inputUpdateCallback = nullptr);
7677
void Clear(bool clearOutputs, uint32_t iThread = 0);

GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ struct GPUInterfaceOutputs : public GPUTrackingOutputs {
5858
};
5959

6060
struct GPUInterfaceInputUpdate {
61-
std::function<void(GPUTrackingInOutPointers*& data, GPUInterfaceOutputs*& outputs)> callback; // Callback which provides final data ptrs / outputRegions after Clusterization stage
62-
std::function<void()> notifyCallback; // Callback called to notify that Clusterization state has finished without update
61+
std::function<int32_t(GPUTrackingInOutPointers*& data, GPUInterfaceOutputs*& outputs)> callback; // Callback which provides final data ptrs / outputRegions after Clusterization stage
62+
std::function<void()> notifyCallback; // Callback called to notify that Clusterization state has finished without update
6363
};
6464

6565
// Full configuration structure with all available settings of GPU...

GPU/GPUTracking/Standalone/Benchmark/standalone.cxx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -685,11 +685,11 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU
685685
}
686686
}
687687

688-
if (tmpRetVal == 0 || tmpRetVal == 2) {
688+
if (tmpRetVal == GPUReconstruction::retValValue::ok || tmpRetVal == GPUReconstruction::retValValue::doExit) {
689689
OutputStat(chainTrackingUse, iRun == 0 ? nTracksTotal : nullptr, iRun == 0 ? nClustersTotal : nullptr);
690690
}
691691

692-
if (tmpRetVal == 0 && configStandalone.testSyncAsync) {
692+
if (tmpRetVal == GPUReconstruction::retValValue::ok && configStandalone.testSyncAsync) {
693693
vecpod<char> compressedTmpMem(chainTracking->mIOPtrs.tpcCompressedClusters->totalDataSize);
694694
memcpy(compressedTmpMem.data(), (const void*)chainTracking->mIOPtrs.tpcCompressedClusters, chainTracking->mIOPtrs.tpcCompressedClusters->totalDataSize);
695695
o2::tpc::CompressedClusters tmp(*chainTracking->mIOPtrs.tpcCompressedClusters);
@@ -717,7 +717,7 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU
717717
recAsync->SetResetTimers(iRun < configStandalone.runsInit);
718718
}
719719
tmpRetVal = recAsync->RunChains();
720-
if (tmpRetVal == 0 || tmpRetVal == 2) {
720+
if (tmpRetVal == GPUReconstruction::retValValue::ok || tmpRetVal == GPUReconstruction::retValValue::doExit) {
721721
OutputStat(chainTrackingAsync, nullptr, nullptr);
722722
}
723723
recAsync->ClearAllocatedMemory();
@@ -726,14 +726,14 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU
726726
recUse->ClearAllocatedMemory();
727727
}
728728

729-
if (tmpRetVal == 2) {
729+
if (tmpRetVal == GPUReconstruction::retValValue::doExit) {
730730
configStandalone.continueOnError = 0; // Forced exit from event display loop
731731
configStandalone.noprompt = 1;
732732
}
733-
if (tmpRetVal == 3 && configStandalone.proc.ignoreNonFatalGPUErrors) {
733+
if (tmpRetVal == GPUReconstruction::retValValue::nonFatalErrorCode && configStandalone.proc.ignoreNonFatalGPUErrors) {
734734
printf("GPU Standalone Benchmark: Non-FATAL GPU error occured, ignoring\n");
735735
} else if (tmpRetVal && !configStandalone.continueOnError) {
736-
if (tmpRetVal != 2) {
736+
if (tmpRetVal != GPUReconstruction::retValValue::doExit) {
737737
printf("GPU Standalone Benchmark: Error occured\n");
738738
}
739739
return 1;

0 commit comments

Comments
 (0)