35#include <fairmq/Device.h>
36#include <fairmq/Channel.h>
37#include <fairmq/States.h>
49static const std::string GPURecoWorkflowSpec_FMQCallbackKey =
"GPURecoWorkflowSpec_FMQCallbackKey";
65 mPipeline->fmqDevice->SubscribeToStateChange(GPURecoWorkflowSpec_FMQCallbackKey, [
this](fair::mq::State s) { receiveFMQStateCallback(s); });
67 std::unique_lock lk(mPipeline->completionPolicyMutex);
68 mPipeline->completionPolicyNotify.wait(lk, [pipeline = mPipeline.get()] { return pipeline->pipelineSenderTerminating || !pipeline->completionPolicyQueue.empty(); });
69 return !mPipeline->completionPolicyQueue.empty() && mPipeline->completionPolicyQueue.front() == timeslice;
71 mPipeline->receiveThread = std::thread([
this]() { RunReceiveThread(); });
72 for (uint32_t
i = 0;
i < mPipeline->workers.size();
i++) {
73 mPipeline->workers[
i].thread = std::thread([
this,
i]() { RunWorkerThread(
i); });
78void GPURecoWorkflowSpec::RunWorkerThread(int32_t
id)
80 LOG(
debug) <<
"Running pipeline worker " <<
id;
81 auto& workerContext = mPipeline->workers[
id];
82 while (!mPipeline->shouldTerminate) {
85 std::unique_lock lk(workerContext.inputQueueMutex);
86 workerContext.inputQueueNotify.wait(lk, [
this, &workerContext]() {
return mPipeline->shouldTerminate || !workerContext.inputQueue.empty(); });
87 if (workerContext.inputQueue.empty()) {
90 context = workerContext.inputQueue.front();
91 workerContext.inputQueue.pop();
106 std::unique_lock lk(mPipeline->mayInjectMutex);
107 mPipeline->mayInjectCondition.wait(lk, [
this, context]() {
return mPipeline->mayInject && mPipeline->mayInjectTFId == context->
mTFId; });
108 mPipeline->mayInjectTFId = mPipeline->mayInjectTFId + 1;
109 mPipeline->mayInject =
false;
122 if (mPipeline->pipelineAbort) {
132 std::lock_guard lk(mPipeline->mayInjectMutex);
133 mPipeline->mayInject =
true;
135 mPipeline->mayInjectCondition.notify_one();
138 mNextThreadIndex = (mNextThreadIndex + 1) % 2;
141 std::lock_guard lk(mPipeline->workers[mNextThreadIndex].inputQueueMutex);
142 mPipeline->workers[mNextThreadIndex].inputQueue.emplace(context);
144 mPipeline->workers[mNextThreadIndex].inputQueueNotify.notify_one();
160 mPipeline->runStarted =
true;
161 mPipeline->stateNotify.notify_all();
166 std::unique_lock lk(mPipeline->queueMutex);
167 mPipeline->queueNotify.wait(lk, [
this] {
return !mPipeline->pipelineQueue.empty(); });
168 context = std::move(mPipeline->pipelineQueue.front());
169 mPipeline->pipelineQueue.pop();
172 if (context->timeSliceId != tinfo.timeslice) {
173 LOG(fatal) <<
"Prepare message for incorrect time frame received, time frames seem out of sync";
176 tpcZSmeta = std::move(context->tpcZSmeta);
177 tpcZS = context->tpcZS;
181 std::lock_guard lk(mPipeline->completionPolicyMutex);
182 if (mPipeline->completionPolicyQueue.empty() || mPipeline->completionPolicyQueue.front() != tinfo.timeslice) {
183 LOG(fatal) <<
"Time frame processed does not equal the timeframe at the top of the queue, time frames seem out of sync";
185 mPipeline->completionPolicyQueue.pop();
190 size_t ptrsTotal = 0;
191 const void* firstPtr =
nullptr;
202 fair::mq::MessagePtr payload(device->NewMessage());
203 payload->Rebuild(prepareBufferSize, fair::mq::Alignment(
sizeof(
size_t)));
204 auto* messageBuffer = (
size_t*)payload->GetData();
212 size_t* ptrBuffer = messageBuffer +
sizeof(preMessage) /
sizeof(
size_t);
213 size_t ptrsCopied = 0;
214 int32_t lastRegion = -1;
220 bool regionFound = lastRegion != -1 && (size_t)curPtr >= (
size_t)mRegionInfos[lastRegion].ptr && (size_t)curPtr < (
size_t)mRegionInfos[lastRegion].ptr + mRegionInfos[lastRegion].size;
222 for (uint32_t l = 0; l < mRegionInfos.size(); l++) {
223 if ((
size_t)curPtr >= (size_t)mRegionInfos[l].
ptr && (
size_t)curPtr < (size_t)mRegionInfos[l].
ptr + mRegionInfos[l].
size) {
231 LOG(fatal) <<
"Found a TPC ZS pointer outside of shared memory";
233 ptrBuffer[ptrsCopied + k] = (size_t)curPtr - (
size_t)mRegionInfos[lastRegion].ptr;
235 ptrBuffer[2 * ptrsTotal + ptrsCopied + k] = mRegionInfos[lastRegion].managed;
236 ptrBuffer[3 * ptrsTotal + ptrsCopied + k] = mRegionInfos[lastRegion].id;
242 auto channel = device->GetChannels().find(
"gpu-prepare-channel");
243 LOG(info) <<
"Sending gpu-reco-workflow prepare message of size " << prepareBufferSize;
244 channel->second[0].Send(payload);
253 mPipeline->endOfStreamDplReceived =
true;
254 mPipeline->stateNotify.notify_all();
258 fair::mq::MessagePtr payload(device->NewMessage());
263 auto channel = device->GetChannels().find(
"gpu-prepare-channel");
264 LOG(info) <<
"Sending end-of-stream message over out-of-bands channel";
265 channel->second[0].Send(payload);
269void GPURecoWorkflowSpec::handlePipelineStop()
273 std::unique_lock lk(mPipeline->queueMutex);
274 mPipeline->pipelineAbort = mPipeline->pipelineQueue.size();
276 if (mPipeline->pipelineAbort) {
277 mPipeline->pipelineQueue.front()->jobInputFinalNotify.notify_one();
278 mGPUReco->DrainPipeline();
280 std::unique_lock lk(mPipeline->queueMutex);
281 mPipeline->pipelineQueue = {};
284 std::lock_guard lk(mPipeline->completionPolicyMutex);
285 mPipeline->completionPolicyQueue = {};
287 mPipeline->pipelineAbort =
false;
289 std::lock_guard lk(mPipeline->stateMutex);
290 mPipeline->endOfStreamAsyncWaiting =
false;
291 mPipeline->mNTFReceived = 0;
292 mPipeline->runStarted =
false;
296 std::unique_lock lk(mPipeline->mayInjectMutex);
297 mPipeline->mayInjectTFId = 0;
302void GPURecoWorkflowSpec::receiveFMQStateCallback(fair::mq::State newState)
305 std::lock_guard lk(mPipeline->stateMutex);
306 if (mPipeline->fmqState != fair::mq::State::Running && newState == fair::mq::State::Running) {
307 mPipeline->endOfStreamAsyncWaiting =
true;
308 mPipeline->endOfStreamDplReceived =
false;
310 mPipeline->fmqPreviousState = mPipeline->fmqState;
311 mPipeline->fmqState = newState;
313 mPipeline->stateNotify.notify_all();
315 std::lock_guard lk(mPipeline->receiveMutex);
316 if (newState == fair::mq::State::Exiting) {
317 mPipeline->fmqDevice->UnsubscribeFromStateChange(GPURecoWorkflowSpec_FMQCallbackKey);
322void GPURecoWorkflowSpec::RunReceiveThread()
324 auto* device = mPipeline->fmqDevice;
325 while (!mPipeline->shouldTerminate) {
326 bool received =
false;
327 int32_t recvTimeot = 1000;
328 fair::mq::MessagePtr
msg;
329 LOG(
debug) <<
"Waiting for out of band message";
330 auto shouldReceive = [
this]() {
return ((mPipeline->fmqState == fair::mq::State::Running || (mPipeline->fmqState == fair::mq::State::Ready && mPipeline->fmqPreviousState == fair::mq::State::Running)) && mPipeline->endOfStreamAsyncWaiting); };
333 std::unique_lock lk(mPipeline->stateMutex);
334 mPipeline->stateNotify.wait(lk, [
this, shouldReceive]() {
return shouldReceive() || mPipeline->shouldTerminate; });
336 if (mPipeline->shouldTerminate) {
341 std::unique_lock lk(mPipeline->receiveMutex);
342 if (!shouldReceive()) {
345 msg = device->NewMessageFor(
"gpu-prepare-channel", 0, 0);
346 received = device->Receive(
msg,
"gpu-prepare-channel", 0, recvTimeot) > 0;
347 }
while (!received && !mPipeline->shouldTerminate);
351 }
while (!received && !mPipeline->shouldTerminate);
352 if (mPipeline->shouldTerminate) {
359 if (
m->magicWord !=
m->MAGIC_WORD) {
360 LOG(fatal) <<
"Prepare message corrupted, invalid magic word";
362 if (
m->flagEndOfStream) {
363 LOG(info) <<
"Received end-of-stream from out-of-band channel";
365 std::lock_guard lk(mPipeline->stateMutex);
366 mPipeline->endOfStreamAsyncWaiting =
false;
367 mPipeline->mNTFReceived = 0;
368 mPipeline->runStarted =
false;
370 mPipeline->stateNotify.notify_all();
375 std::lock_guard lk(mPipeline->completionPolicyMutex);
376 mPipeline->completionPolicyQueue.emplace(
m->timeSliceId);
378 mPipeline->completionPolicyNotify.notify_one();
381 std::unique_lock lk(mPipeline->stateMutex);
382 mPipeline->stateNotify.wait(lk, [
this]() {
return (mPipeline->runStarted && mPipeline->endOfStreamAsyncWaiting) || mPipeline->shouldTerminate; });
383 if (!mPipeline->runStarted) {
388 auto context = std::make_unique<GPURecoWorkflow_QueueObject>();
389 context->timeSliceId =
m->timeSliceId;
390 context->tfSettings =
m->tfSettings;
392 size_t ptrsCopied = 0;
394 context->tpcZSmeta.Pointers[0][0].resize(
m->pointersTotal);
395 context->tpcZSmeta.Sizes[0][0].resize(
m->pointersTotal);
396 int32_t lastRegion = -1;
399 context->tpcZS.sector[
i].count[
j] =
m->pointerCounts[
i][
j];
400 for (uint32_t k = 0; k < context->tpcZS.sector[
i].count[
j]; k++) {
401 bool regionManaged = ptrBuffer[2 *
m->pointersTotal + ptrsCopied + k];
402 size_t regionId = ptrBuffer[3 *
m->pointersTotal + ptrsCopied + k];
403 bool regionFound = lastRegion != -1 && mRegionInfos[lastRegion].managed == regionManaged && mRegionInfos[lastRegion].id == regionId;
405 for (uint32_t l = 0; l < mRegionInfos.size(); l++) {
406 if (mRegionInfos[l].managed == regionManaged && mRegionInfos[l].
id == regionId) {
414 LOG(fatal) <<
"Received ZS Ptr for SHM region (managed " << (int32_t)regionManaged <<
", id " << regionId <<
"), which was not registered for us";
416 context->tpcZSmeta.Pointers[0][0][ptrsCopied + k] = (
void*)(ptrBuffer[ptrsCopied + k] + (
size_t)mRegionInfos[lastRegion].ptr);
417 context->tpcZSmeta.Sizes[0][0][ptrsCopied + k] = ptrBuffer[
m->pointersTotal + ptrsCopied + k];
419 context->tpcZS.sector[
i].zsPtr[
j] = context->tpcZSmeta.Pointers[0][0].data() + ptrsCopied;
420 context->tpcZS.sector[
i].nZSPtr[
j] = context->tpcZSmeta.Sizes[0][0].data() + ptrsCopied;
421 ptrsCopied += context->tpcZS.sector[
i].count[
j];
424 context->ptrs.tpcZS = &context->tpcZS;
425 context->ptrs.settingsTF = &context->tfSettings;
426 context->mTFId = mPipeline->mNTFReceived;
427 if (mPipeline->mNTFReceived++ >= mPipeline->workers.size()) {
428 enqueuePipelinedJob(&context->ptrs,
nullptr, context.get(),
false);
431 std::lock_guard lk(mPipeline->queueMutex);
432 mPipeline->pipelineQueue.emplace(std::move(context));
434 mPipeline->queueNotify.notify_one();
436 mPipeline->pipelineSenderTerminating =
true;
437 mPipeline->completionPolicyNotify.notify_one();
440void GPURecoWorkflowSpec::ExitPipeline()
443 mPipeline->fmqDevice =
nullptr;
444 mPipeline->shouldTerminate =
true;
445 mPipeline->stateNotify.notify_all();
446 for (uint32_t
i = 0;
i < mPipeline->workers.size();
i++) {
447 mPipeline->workers[
i].inputQueueNotify.notify_one();
449 if (mPipeline->receiveThread.joinable()) {
450 mPipeline->receiveThread.join();
452 for (uint32_t
i = 0;
i < mPipeline->workers.size();
i++) {
453 if (mPipeline->workers[
i].thread.joinable()) {
454 mPipeline->workers[
i].thread.join();
Type wrappers for enfording a specific serialization method.
decltype(auto) make(const Output &spec, Args... args)
ServiceRegistryRef services()
ServiceRegistryRef services()
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
ServiceRegistryRef services()
The services registry associated with this processing context.
o2::framework::Outputs outputs()
constexpr o2::header::DataOrigin gDataOriginGPU
Defining ITS Vertex explicitly as messageable.
int32_t enableDoublePipeline
const GPUSettingsTF * settingsTF
const GPUTrackingInOutZS * tpcZS
const void *const * zsPtr[NENDPOINTS]
uint32_t count[NENDPOINTS]
const uint32_t * nZSPtr[NENDPOINTS]
GPUTrackingInOutZSSector sector[NSECTORS]
static constexpr uint32_t NSECTORS
static constexpr uint32_t NENDPOINTS
std::mutex jobInputFinalMutex
volatile int32_t jobThreadIndex
std::unique_ptr< GPUInterfaceInputUpdate > jobInputUpdateCallback
std::condition_variable jobInputFinalNotify
std::condition_variable jobFinishedNotify
GPUInterfaceOutputs * jobOutputRegions
std::mutex jobFinishedMutex
GPUTrackingInOutPointers * jobPtrs
static constexpr size_t MAGIC_WORD
size_t pointerCounts[GPUTrackingInOutZS::NSECTORS][GPUTrackingInOutZS::NENDPOINTS]
DataProcessingHeader::StartTime timeSliceId
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
uint64_t const void const *restrict const msg