Skip to content

Commit ec69aa0

Browse files
committed
tfbuilder: make sure tfs are built and forwarded in-order
1 parent 6981b4f commit ec69aa0

6 files changed

Lines changed: 136 additions & 49 deletions

File tree

src/TfBuilder/TfBuilderDevice.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ void TfBuilderDevice::InitTask()
171171

172172
bool TfBuilderDevice::start()
173173
{
174-
while (!mRpc->start(mTfDataRegionSize)) {
174+
while (!mRpc->start(mTfDataRegionSize, mFlpInputHandler->getDataQueue())) {
175175
// check if should stop looking for TfScheduler
176176
if (mRpc->isTerminateRequested()) {
177177
mShouldExit = true;

src/TfBuilder/TfBuilderInput.cxx

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ TfBuilderInput::TfBuilderInput(TfBuilderDevice& pStfBuilderDev, std::shared_ptr<
3737
mRpc(pRpc),
3838
mOutStage(pOutStage)
3939
{
40-
mInputFairMQ = std::make_unique<TfBuilderInputFairMQ>(pRpc, pStfBuilderDev.TfBuilderI(), mReceivedDataQueue);
40+
mReceivedDataQueue = std::make_shared<ConcurrentQueue<ReceivedStfMeta>>();
41+
mInputFairMQ = std::make_unique<TfBuilderInputFairMQ>(pRpc, pStfBuilderDev.TfBuilderI(), *mReceivedDataQueue);
4142
}
4243

4344
bool TfBuilderInput::start(std::shared_ptr<ConsulTfBuilder> pConfig)
@@ -53,7 +54,7 @@ bool TfBuilderInput::start(std::shared_ptr<ConsulTfBuilder> pConfig)
5354
mState = RUNNING;
5455

5556
// Start the pacer thread
56-
mReceivedDataQueue.start();
57+
mReceivedDataQueue->start();
5758
mStfPacingThread = create_thread_member("tfb_pace", &TfBuilderInput::StfPacingThread, this);
5859

5960
// Start the deserialize thread
@@ -84,7 +85,7 @@ void TfBuilderInput::stop(std::shared_ptr<ConsulTfBuilder> pConfig)
8485
mInputFairMQ->stop(pConfig);
8586

8687
//Wait for pacer thread
87-
mReceivedDataQueue.stop();
88+
mReceivedDataQueue->stop();
8889
if (mStfPacingThread.joinable()) {
8990
mStfPacingThread.join();
9091
}
@@ -119,30 +120,50 @@ void TfBuilderInput::StfPacingThread()
119120
{
120121
// Deserialization object (stf ID)
121122
IovDeserializer lStfReceiver(mDevice.TfBuilderI());
122-
std::uint64_t lTopoStfId = 0;
123123

124124
while (mState == RUNNING) {
125125

126-
auto lData = mReceivedDataQueue.pop();
126+
auto lData = mReceivedDataQueue->pop();
127127
if (lData == std::nullopt) {
128128
continue;
129129
}
130130
auto &lStfInfo = lData.value();
131131

132-
assert (lStfInfo.mRecvStfdata);
133132
std::uint64_t lTfId = lStfInfo.mStfId;
134133

134+
if (lStfInfo.mType == ReceivedStfMeta::MetaType::ADD) {
135+
// only record the intent to build a TF
136+
std::unique_lock<std::mutex> lQueueLock(mStfMergerQueueLock);
137+
assert (mStfMergeMap.count(lTfId) == 0);
138+
assert (mStfMergeMap.empty() || (mStfMergeMap.rbegin()->first < lTfId));
139+
140+
mStfMergeMap[lTfId].reserve(mNumStfSenders);
141+
continue;
142+
} else if (lStfInfo.mType == ReceivedStfMeta::MetaType::DELETE) {
143+
// remove tf merge intent if no StfSenders were contacted
144+
std::unique_lock<std::mutex> lQueueLock(mStfMergerQueueLock);
145+
assert (mStfMergeMap.count(lTfId) == 1);
146+
assert (mStfMergeMap[lTfId].empty());
147+
148+
mStfMergeMap.erase(lTfId);
149+
continue;
150+
}
151+
152+
assert (lStfInfo.mType == ReceivedStfMeta::MetaType::INFO);
153+
assert (lStfInfo.mRecvStfdata);
154+
135155
// Rename STF id if this is a Topological TF
136156
if (lStfInfo.mStfOrigin == SubTimeFrame::Header::Origin::eReadoutTopology) {
137157
// deserialize here to be able to rename the stf
138158
lStfInfo.mStf = std::move(lStfReceiver.deserialize(lStfInfo.mRecvStfHeaderMeta, *lStfInfo.mRecvStfdata));
139159
lStfInfo.mRecvStfdata = nullptr;
140160

141-
const std::uint64_t lNewTfId = ++lTopoStfId;
161+
const std::uint64_t lNewTfId = mRpc->getIdForTopoTf(lStfInfo.mStfSenderId, lStfInfo.mStfId);
162+
142163
DDDLOG_RL(5000, "Deserialized STF. stf_id={} new_id={}", lStfInfo.mStf->id(), lNewTfId);
143164
lStfInfo.mStf->updateId(lNewTfId);
144165
lTfId = lNewTfId;
145-
lStfInfo.mStfId = lTfId;
166+
lStfInfo.mStfId = lNewTfId;
146167
}
147168

148169
/// TODO: STF receive pacing
@@ -185,29 +206,6 @@ void TfBuilderInput::deserialize_headers(std::vector<ReceivedStfMeta> &pStfs)
185206
}
186207
}
187208

188-
// check if topological (S)TF
189-
bool TfBuilderInput::is_topo_stf(const std::vector<ReceivedStfMeta> &pStfs) const
190-
{
191-
if (pStfs.empty()) {
192-
return false;
193-
}
194-
195-
if (pStfs.size() == 1 && pStfs.begin()->mStf) {
196-
if (pStfs.begin()->mStf->header().mOrigin == SubTimeFrame::Header::Origin::eReadoutTopology) {
197-
return true;
198-
}
199-
}
200-
201-
if (pStfs.size() > 1 && pStfs.begin()->mStf) {
202-
if (pStfs.begin()->mStf->header().mOrigin == SubTimeFrame::Header::Origin::eReadoutTopology) {
203-
EDDLOG_RL(1000, "TfBuilderInput::is_topo_stf: multiple topological STFs with a same ID. stf_id={}",
204-
pStfs.begin()->mStf->id());
205-
}
206-
}
207-
208-
return false;
209-
}
210-
211209
/// FMQ->STF thread
212210
/// This thread can block waiting on free O2 Header memory
213211
void TfBuilderInput::StfDeserializingThread()

src/TfBuilder/TfBuilderInput.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ class TfBuilderInput
4545
bool start(std::shared_ptr<ConsulTfBuilder> pConfig);
4646
void stop(std::shared_ptr<ConsulTfBuilder> pConfig);
4747
void reset() {
48-
mReceivedDataQueue.flush();
48+
mReceivedDataQueue->flush();
4949
mStfsForMerging.flush();
5050
std::unique_lock<std::mutex> lQueueLock(mStfMergerQueueLock);
5151
mStfMergeMap.clear();
5252
mMaxMergedTfId = 0;
5353
}
5454

55+
auto getDataQueue() const { return mReceivedDataQueue; }
56+
5557
void StfPacingThread();
5658
void StfDeserializingThread();
5759
void StfMergerThread();
@@ -72,7 +74,7 @@ class TfBuilderInput
7274
std::unique_ptr<TfBuilderInputFairMQ> mInputFairMQ;
7375

7476
/// Received Stfs from input stage
75-
ConcurrentQueue<ReceivedStfMeta> mReceivedDataQueue;
77+
std::shared_ptr<ConcurrentQueue<ReceivedStfMeta> > mReceivedDataQueue;
7678
std::thread mStfPacingThread;
7779

7880
/// Stf Deserializer (add O2 headers etc)

src/TfBuilder/TfBuilderInputDefs.h

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,20 @@
1414
#ifndef TF_BUILDER_INPUT_DEFS_H_
1515
#define TF_BUILDER_INPUT_DEFS_H_
1616

17-
1817
#include <SubTimeFrameDataModel.h>
1918
#include <ConcurrentQueue.h>
2019

2120
#include <chrono>
2221
#include <vector>
2322
#include <memory>
2423

25-
2624
namespace o2::DataDistribution
2725
{
2826

2927
enum InputRunState { CONFIGURING, RUNNING, TERMINATED };
3028

3129
struct ReceivedStfMeta {
30+
enum MetaType { ADD, DELETE, INFO } mType;
3231
TimeFrameIdType mStfId;
3332
SubTimeFrame::Header::Origin mStfOrigin;
3433
std::chrono::time_point<std::chrono::steady_clock> mTimeReceived;
@@ -38,9 +37,18 @@ struct ReceivedStfMeta {
3837
std::unique_ptr<SubTimeFrame> mStf;
3938
std::string mStfSenderId;
4039

41-
ReceivedStfMeta(const TimeFrameIdType pStfId, const SubTimeFrame::Header::Origin pStfOrigin,
42-
const std::string &pStfSenderId, FairMQMessagePtr &&pRcvHdrMsg, std::unique_ptr<std::vector<FairMQMessagePtr>> &&pRecvStfdata)
43-
: mStfId(pStfId),
40+
ReceivedStfMeta(MetaType pType, const TimeFrameIdType pStfId)
41+
: mType(pType),
42+
mStfId(pStfId)
43+
{ }
44+
45+
ReceivedStfMeta(const TimeFrameIdType pStfId,
46+
const SubTimeFrame::Header::Origin pStfOrigin,
47+
const std::string &pStfSenderId,
48+
FairMQMessagePtr &&pRcvHdrMsg,
49+
std::unique_ptr<std::vector<FairMQMessagePtr>> &&pRecvStfdata)
50+
: mType(INFO),
51+
mStfId(pStfId),
4452
mStfOrigin(pStfOrigin),
4553
mTimeReceived(std::chrono::steady_clock::now()),
4654
mRecvStfHeaderMeta(std::move(pRcvHdrMsg)),

src/TfBuilder/TfBuilderRpc.cxx

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// along with this program. If not, see <https://www.gnu.org/licenses/>.
1313

1414
#include "TfBuilderRpc.h"
15+
#include "TfBuilderInputDefs.h"
1516
#include <grpcpp/grpcpp.h>
1617

1718
#include <MemoryUtils.h>
@@ -39,10 +40,11 @@ void TfBuilderRpcImpl::initDiscovery(const std::string pRpcSrvBindIp, int &lReal
3940
IDDLOG("gRPC server is started. server_ep={}:{}", pRpcSrvBindIp, lRealPort);
4041
}
4142

42-
bool TfBuilderRpcImpl::start(const std::uint64_t pBufferSize)
43+
bool TfBuilderRpcImpl::start(const std::uint64_t pBufferSize, std::shared_ptr<ConcurrentQueue<ReceivedStfMeta> > pRecvQueue)
4344
{
4445
mBufferSize = pBufferSize;
4546
mCurrentTfBufferSize = pBufferSize;
47+
mReceivedDataQueue = pRecvQueue;
4648

4749
// Interact with the scheduler
4850
if (!mTfSchedulerRpcClient.should_retry_start()) {
@@ -313,8 +315,29 @@ ::grpc::Status TfBuilderRpcImpl::BuildTfRequest(::grpc::ServerContext* /*context
313315
return a.mStfDataSize < b.mStfDataSize;
314316
});
315317

318+
// setup renaming of topological Stfs
319+
auto lTopoStfId = mTopoStfId;
320+
if (request->tf_source() == TOPOLOGICAL) {
321+
auto &lStfSenderId = request->stf_size_map().begin()->first;
322+
323+
std::scoped_lock lLock(mTopoTfIdLock);
324+
325+
assert (mTopoTfIdRenameMap[lStfSenderId].count(lTfId) == 0);
326+
327+
mTopoTfIdRenameMap[lStfSenderId][lTfId] = lTopoStfId;
328+
329+
// notify Input stage about new Stf (renamed)
330+
mReceivedDataQueue->push(ReceivedStfMeta(ReceivedStfMeta::MetaType::ADD, lTopoStfId));
331+
332+
mTopoStfId += 1;
333+
} else {
334+
lTopoStfId = lTfId; // set to the actual tf id if not topo
335+
// notify Input stage about new Stf (regular)
336+
mReceivedDataQueue->push(ReceivedStfMeta(ReceivedStfMeta::MetaType::ADD, lTfId));
337+
}
338+
316339
// add the vector to the stf request map
317-
mStfRequestDeque.push(lTfId, std::move(lStfRequestVector));
340+
mStfRequestQueue.push(lTfId, (request->tf_source() == TOPOLOGICAL), lTopoStfId, std::move(lStfRequestVector));
318341

319342
response->set_status(BuildTfResponse::OK);
320343
return ::grpc::Status::OK;
@@ -335,14 +358,22 @@ void TfBuilderRpcImpl::StfRequestThread()
335358
while (mRunning) {
336359
StfRequests lStfRequest;
337360
{
338-
std::optional<std::pair<std::uint64_t, std::vector<StfRequests>> > lReqOpt;
339-
if ((lReqOpt = mStfRequestDeque.pop_wait_for(100ms)) == std::nullopt) {
361+
std::optional<std::tuple<std::uint64_t, bool, std::uint64_t, std::vector<StfRequests>> > lReqOpt;
362+
if ((lReqOpt = mStfRequestQueue.pop_wait_for(100ms)) == std::nullopt) {
340363
continue;
341364
}
342365

343366
assert (lReqOpt);
344-
const auto lTfId = lReqOpt.value().first;
345-
auto &lReqVector = lReqOpt.value().second;
367+
const auto lTfId = std::get<0>(lReqOpt.value());
368+
const auto lIsTopo = std::get<1>(lReqOpt.value());
369+
const auto lTfRenamedId = std::get<2>(lReqOpt.value());
370+
auto &lReqVector = std::get<3>(lReqOpt.value());
371+
372+
std::string lStfSenderIdTopo;
373+
if (lIsTopo) {
374+
assert (lReqVector.size() == 1);
375+
lStfSenderIdTopo = lReqVector.front().mStfSenderId;
376+
}
346377

347378
mMaxNumReqInFlight = std::clamp(mDiscoveryConfig->getUInt64Param(MaxNumStfTransfersKey, MaxNumStfTransferDefault),
348379
std::uint64_t(10), std::uint64_t(200));
@@ -416,7 +447,29 @@ void TfBuilderRpcImpl::StfRequestThread()
416447
}
417448

418449
// set the number of STFs for merging thread
419-
setNumberOfStfs(lTfId, lNumExpectedStfs);
450+
if (!lIsTopo) {
451+
setNumberOfStfs(lTfId, lNumExpectedStfs);
452+
} else {
453+
setNumberOfStfs(lTfRenamedId, lNumExpectedStfs);
454+
}
455+
456+
// cleanup if we reached no StfSenders
457+
if (lNumExpectedStfs == 0) {
458+
if (lIsTopo) {
459+
// Topological: indicate that we're deleting topological (renamed) Id
460+
std::scoped_lock lLock(mTopoTfIdLock);
461+
assert (mTopoTfIdRenameMap[lStfSenderIdTopo].count(lTfId) == 1);
462+
assert (lTfRenamedId == mTopoTfIdRenameMap[lStfSenderIdTopo][lTfId]);
463+
464+
mTopoTfIdRenameMap[lStfSenderIdTopo].erase(lTfId);
465+
466+
// notify Input stage about new Stf (renamed)
467+
mReceivedDataQueue->push(ReceivedStfMeta(ReceivedStfMeta::MetaType::DELETE, lTfRenamedId));
468+
} else {
469+
// notify Input stage not to wait for STFs if we reached none of StfSender
470+
mReceivedDataQueue->push(ReceivedStfMeta(ReceivedStfMeta::MetaType::DELETE, lTfId));
471+
}
472+
}
420473
}
421474
}
422475
// send disconnect update

src/TfBuilder/TfBuilderRpc.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
#ifndef ALICEO2_TF_BUILDER_RPC_H_
1515
#define ALICEO2_TF_BUILDER_RPC_H_
1616

17+
#include "TfBuilderInputDefs.h"
18+
1719
#pragma GCC diagnostic push
1820
#pragma GCC diagnostic ignored "-Wunused-parameter"
1921
#include <grpcpp/grpcpp.h>
@@ -57,7 +59,7 @@ class TfBuilderRpcImpl final : public TfBuilderRpc::Service
5759
TfSchedulerRpcClient& TfSchedRpcCli() { return mTfSchedulerRpcClient; }
5860

5961
void initDiscovery(const std::string pRpcSrvBindIp, int &lRealPort /*[out]*/);
60-
bool start(const std::uint64_t pBufferSize);
62+
bool start(const std::uint64_t pBufferSize, std::shared_ptr<ConcurrentQueue<ReceivedStfMeta> > pRecvQueue);
6163
void stop();
6264

6365
void startAcceptingTfs();
@@ -89,12 +91,17 @@ class TfBuilderRpcImpl final : public TfBuilderRpc::Service
8991

9092
/// reset counters on each new run
9193
void reset_run_counters() {
94+
std::scoped_lock lLock(mTfIdSizesLock, mTopoTfIdLock);
95+
9296
mTfIdSizes.clear();
9397
mCurrentTfBufferSize = mBufferSize;
9498
mLastBuiltTfId = 0;
9599
mNumBufferedTfs = 0;
96100
mNumTfsInBuilding = 0;
97101
mTfBuildRequests->flush();
102+
// Reset Topo Tf Id renaming
103+
mTopoStfId = 1;
104+
mTopoTfIdRenameMap.clear();
98105
}
99106

100107
std::optional<std::uint64_t> getNumberOfStfs(const TimeFrameIdType pTfId) {
@@ -113,6 +120,19 @@ class TfBuilderRpcImpl final : public TfBuilderRpc::Service
113120
}
114121
}
115122

123+
std::uint64_t getIdForTopoTf(const std::string &pStfSenderId, const std::uint64_t pTfId) {
124+
std::scoped_lock lLock(mTopoTfIdLock);
125+
126+
assert (mTopoTfIdRenameMap.count(pStfSenderId) == 1);
127+
assert (mTopoTfIdRenameMap[pStfSenderId].count(pTfId) == 1);
128+
129+
const auto lRet = mTopoTfIdRenameMap[pStfSenderId][pTfId];
130+
mTopoTfIdRenameMap[pStfSenderId].erase(pTfId);
131+
132+
assert (lRet > 0);
133+
return lRet;
134+
}
135+
116136
private:
117137
std::atomic_bool mRunning = false;
118138
std::atomic_bool mTerminateRequested = false;
@@ -125,8 +145,8 @@ class TfBuilderRpcImpl final : public TfBuilderRpc::Service
125145
std::thread mUpdateThread;
126146

127147
// Stf request thread
128-
// std::condition_variable mNewRequestCondition;
129148
std::thread mStfRequestThread;
149+
std::shared_ptr<ConcurrentQueue<ReceivedStfMeta> > mReceivedDataQueue;
130150

131151
struct StfRequests {
132152
std::string mStfSenderId;
@@ -140,7 +160,13 @@ class TfBuilderRpcImpl final : public TfBuilderRpc::Service
140160

141161
std::atomic_int64_t mMaxNumReqInFlight = 64;
142162
std::atomic_int64_t mNumReqInFlight = 0;
143-
ConcurrentFifo<std::pair<std::uint64_t, std::vector<StfRequests>> > mStfRequestDeque; // <tfid, stf_reuests>
163+
164+
// <tfid, topo?, topo_id, stf_requests>
165+
ConcurrentFifo<std::tuple<std::uint64_t, bool, std::uint64_t, std::vector<StfRequests>> > mStfRequestQueue;
166+
167+
std::mutex mTopoTfIdLock;
168+
std::uint64_t mTopoStfId = 1;
169+
std::map<std::string, std::unordered_map<std::uint64_t, std::uint64_t> > mTopoTfIdRenameMap;
144170

145171
// Store how many STFs were requested per TfID. nullopt if requests are not finished
146172
std::mutex mStfsCountMapLock;

0 commit comments

Comments
 (0)