artdaq  v3_11_00
FragmentBuffer.cc
1 #include "artdaq/DAQdata/Globals.hh"
2 #define TRACE_NAME (app_name + "_FragmentBuffer").c_str() // include these 2 first -
3 
4 #include "artdaq/DAQrate/FragmentBuffer.hh"
5 
6 #include <boost/exception/all.hpp>
7 #include <boost/throw_exception.hpp>
8 
9 #include <iterator>
10 #include <limits>
11 
12 #include "canvas/Utilities/Exception.h"
13 #include "cetlib_except/exception.h"
14 #include "fhiclcpp/ParameterSet.h"
15 
16 #include "artdaq-core/Data/ContainerFragmentLoader.hh"
17 #include "artdaq-core/Data/Fragment.hh"
18 #include "artdaq-core/Utilities/ExceptionHandler.hh"
19 #include "artdaq-core/Utilities/SimpleLookupPolicy.hh"
20 #include "artdaq-core/Utilities/TimeUtils.hh"
21 
22 #include <sys/poll.h>
23 #include <algorithm>
24 #include <fstream>
25 #include <iomanip>
26 #include <iostream>
27 #include <iterator>
29 
30 #define TLVL_ADDFRAGMENT TLVL_TRACE + 10
31 #define TLVL_CHECKSTOP TLVL_TRACE + 11
32 #define TLVL_WAITFORBUFFERREADY TLVL_TRACE + 15
33 #define TLVL_GETBUFFERSTATS TLVL_TRACE + 16
34 #define TLVL_CHECKDATABUFFER TLVL_TRACE + 17
35 #define TLVL_APPLYREQUESTS TLVL_TRACE + 9
36 #define TLVL_APPLYREQUESTS_VERBOSE TLVL_TRACE + 23
37 #define TLVL_SENDEMPTYFRAGMENTS TLVL_TRACE + 19
38 #define TLVL_CHECKWINDOWS TLVL_TRACE + 14
39 #define TLVL_EMPTYFRAGMENT TLVL_TRACE + 22
40 
41 artdaq::FragmentBuffer::FragmentBuffer(const fhicl::ParameterSet& ps)
42  : next_sequence_id_(1)
43  , requestBuffer_()
44  , bufferModeKeepLatest_(ps.get<bool>("buffer_mode_keep_latest", false))
45  , windowOffset_(ps.get<Fragment::timestamp_t>("request_window_offset", 0))
46  , windowWidth_(ps.get<Fragment::timestamp_t>("request_window_width", 0))
47  , staleTimeout_(ps.get<Fragment::timestamp_t>("stale_fragment_timeout", 0))
48  , expectedType_(ps.get<Fragment::type_t>("expected_fragment_type", Fragment::type_t(Fragment::EmptyFragmentType)))
49  , uniqueWindows_(ps.get<bool>("request_windows_are_unique", true))
50  , missing_request_window_timeout_us_(ps.get<size_t>("missing_request_window_timeout_us", 5000000))
51  , window_close_timeout_us_(ps.get<size_t>("window_close_timeout_us", 2000000))
52  , circularDataBufferMode_(ps.get<bool>("circular_buffer_mode", false))
53  , maxDataBufferDepthFragments_(ps.get<int>("data_buffer_depth_fragments", 1000))
54  , maxDataBufferDepthBytes_(ps.get<size_t>("data_buffer_depth_mb", 1000) * 1024 * 1024)
55  , systemFragmentCount_(0)
56  , should_stop_(false)
57 {
58  auto fragment_ids = ps.get<std::vector<artdaq::Fragment::fragment_id_t>>("fragment_ids", std::vector<artdaq::Fragment::fragment_id_t>());
59 
60  TLOG(TLVL_TRACE) << "artdaq::FragmentBuffer::FragmentBuffer(ps)";
61  int fragment_id = ps.get<int>("fragment_id", -99);
62 
63  if (fragment_id != -99)
64  {
65  if (fragment_ids.size() != 0)
66  {
67  auto report = "Error in FragmentBuffer: can't both define \"fragment_id\" and \"fragment_ids\" in FHiCL document";
68  TLOG(TLVL_ERROR) << report;
69  throw cet::exception("FragmentBufferConfig") << report;
70  }
71  else
72  {
73  fragment_ids.emplace_back(fragment_id);
74  }
75  }
76 
77  for (auto& id : fragment_ids)
78  {
79  dataBuffers_[id] = std::make_shared<DataBuffer>();
80  dataBuffers_[id]->DataBufferDepthBytes = 0;
81  dataBuffers_[id]->DataBufferDepthFragments = 0;
82  dataBuffers_[id]->HighestRequestSeen = 0;
83  dataBuffers_[id]->BufferFragmentKept = false;
84  }
85 
86  std::string modeString = ps.get<std::string>("request_mode", "ignored");
87  if (modeString == "single" || modeString == "Single")
88  {
89  mode_ = RequestMode::Single;
90  }
91  else if (modeString.find("buffer") != std::string::npos || modeString.find("Buffer") != std::string::npos)
92  {
93  mode_ = RequestMode::Buffer;
94  }
95  else if (modeString == "window" || modeString == "Window")
96  {
97  mode_ = RequestMode::Window;
98  }
99  else if (modeString.find("ignore") != std::string::npos || modeString.find("Ignore") != std::string::npos)
100  {
101  mode_ = RequestMode::Ignored;
102  }
103  else if (modeString.find("sequence") != std::string::npos || modeString.find("Sequence") != std::string::npos)
104  {
105  mode_ = RequestMode::SequenceID;
106  }
107  if (mode_ != RequestMode::Ignored && !ps.get<bool>("receive_requests", false))
108  {
109  TLOG(TLVL_WARNING) << "Request Mode was requested as " << modeString << ", but is being set to Ignored because \"receive_requests\" was not set to true";
110  mode_ = RequestMode::Ignored;
111  }
112  TLOG(TLVL_DEBUG) << "Request mode is " << printMode_();
113 }
114 
116 {
117  TLOG(TLVL_INFO) << "Fragment Buffer Destructor; Clearing data buffers";
118  Reset(true);
119 }
120 
122 {
123  should_stop_ = stop;
124  next_sequence_id_ = 1;
125  for (auto& id : dataBuffers_)
126  {
127  std::lock_guard<std::mutex> dlk(id.second->DataBufferMutex);
128  id.second->DataBufferDepthBytes = 0;
129  id.second->DataBufferDepthFragments = 0;
130  id.second->BufferFragmentKept = false;
131  id.second->DataBuffer.clear();
132  }
133 
134  {
135  std::lock_guard<std::mutex> lk(systemFragmentMutex_);
136  systemFragments_.clear();
137  systemFragmentCount_ = 0;
138  }
139 }
140 
142 {
143  std::unordered_map<Fragment::fragment_id_t, FragmentPtrs> frags_by_id;
144  while (!frags.empty())
145  {
146  auto dataIter = frags.begin();
147  auto frag_id = (*dataIter)->fragmentID();
148 
149  if ((*dataIter)->type() == Fragment::EndOfRunFragmentType || (*dataIter)->type() == Fragment::EndOfSubrunFragmentType || (*dataIter)->type() == Fragment::InitFragmentType)
150  {
151  std::lock_guard<std::mutex> lk(systemFragmentMutex_);
152  systemFragments_.emplace_back(std::move(*dataIter));
153  systemFragmentCount_++;
154  frags.erase(dataIter);
155  continue;
156  }
157 
158  if (!dataBuffers_.count(frag_id))
159  {
160  throw cet::exception("FragmentIDs") << "Received Fragment with Fragment ID " << frag_id << ", which is not in the declared Fragment IDs list!";
161  }
162 
163  frags_by_id[frag_id].emplace_back(std::move(*dataIter));
164  frags.erase(dataIter);
165  }
166 
167  auto type_it = frags_by_id.begin();
168  while (type_it != frags_by_id.end())
169  {
170  auto frag_id = type_it->first;
171 
172  waitForDataBufferReady(frag_id);
173  auto dataBuffer = dataBuffers_[frag_id];
174  std::lock_guard<std::mutex> dlk(dataBuffer->DataBufferMutex);
175  switch (mode_)
176  {
177  case RequestMode::Single:
178  {
179  auto dataIter = type_it->second.rbegin();
180  TLOG(TLVL_ADDFRAGMENT) << "Adding Fragment with Fragment ID " << frag_id << ", Sequence ID " << (*dataIter)->sequenceID() << ", and Timestamp " << (*dataIter)->timestamp() << " to buffer";
181  dataBuffer->DataBuffer.clear();
182  dataBuffer->DataBufferDepthBytes = (*dataIter)->sizeBytes();
183  dataBuffer->DataBuffer.emplace_back(std::move(*dataIter));
184  dataBuffer->DataBufferDepthFragments = 1;
185  type_it->second.clear();
186  }
187  break;
188  case RequestMode::Buffer:
189  case RequestMode::Ignored:
190  case RequestMode::Window:
191  case RequestMode::SequenceID:
192  default:
193  while (!type_it->second.empty())
194  {
195  auto dataIter = type_it->second.begin();
196  TLOG(TLVL_ADDFRAGMENT) << "Adding Fragment with Fragment ID " << frag_id << ", Sequence ID " << (*dataIter)->sequenceID() << ", and Timestamp " << (*dataIter)->timestamp() << " to buffer";
197 
198  dataBuffer->DataBufferDepthBytes += (*dataIter)->sizeBytes();
199  dataBuffer->DataBuffer.emplace_back(std::move(*dataIter));
200  type_it->second.erase(dataIter);
201  }
202  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
203  break;
204  }
205  getDataBufferStats(frag_id);
206  ++type_it;
207  }
208  dataCondition_.notify_all();
209 }
210 
212 {
213  TLOG(TLVL_CHECKSTOP) << "CFG::check_stop: should_stop=" << should_stop_.load();
214 
215  if (!should_stop_.load()) return false;
216  if (mode_ == RequestMode::Ignored)
217  {
218  return true;
219  }
220 
221  if (requestBuffer_ != nullptr)
222  {
223  // check_stop returns true if the CFG should stop. We should wait for the Request Buffer to report Request Receiver stopped before stopping.
224  TLOG(TLVL_DEBUG) << "should_stop is true, requestBuffer_->isRunning() is " << std::boolalpha << requestBuffer_->isRunning();
225  if (!requestBuffer_->isRunning())
226  {
227  return true;
228  }
229  }
230  return false;
231 }
232 
234 {
235  switch (mode_)
236  {
237  case RequestMode::Single:
238  return "Single";
239  case RequestMode::Buffer:
240  return "Buffer";
241  case RequestMode::Window:
242  return "Window";
243  case RequestMode::Ignored:
244  return "Ignored";
245  case RequestMode::SequenceID:
246  return "SequenceID";
247  }
248 
249  return "ERROR";
250 }
251 
253 {
254  size_t count = 0;
255  for (auto& id : dataBuffers_) count += id.second->DataBufferDepthFragments;
256  count += systemFragmentCount_.load();
257  return count;
258 }
259 
260 bool artdaq::FragmentBuffer::waitForDataBufferReady(Fragment::fragment_id_t id)
261 {
262  if (!dataBuffers_.count(id))
263  {
264  TLOG(TLVL_ERROR) << "DataBufferError: "
265  << "Error in FragmentBuffer: Cannot wait for data buffer for ID " << id << " because it does not exist!";
266  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot wait for data buffer for ID " << id << " because it does not exist!";
267  }
268  auto startwait = std::chrono::steady_clock::now();
269  auto first = true;
270  auto lastwaittime = 0ULL;
271  auto dataBuffer = dataBuffers_[id];
272 
273  while (dataBufferIsTooLarge(id))
274  {
275  if (!circularDataBufferMode_)
276  {
277  if (should_stop_.load())
278  {
279  TLOG(TLVL_DEBUG) << "Run ended while waiting for buffer to shrink!";
280  getDataBufferStats(id);
281  dataCondition_.notify_all();
282  return false;
283  }
284  auto waittime = TimeUtils::GetElapsedTimeMilliseconds(startwait);
285 
286  if (first || (waittime != lastwaittime && waittime % 1000 == 0))
287  {
288  std::lock_guard<std::mutex> lk(dataBuffer->DataBufferMutex);
289  if (dataBufferIsTooLarge(id))
290  {
291  TLOG(TLVL_WARNING) << "Bad Omen: Data Buffer has exceeded its size limits. "
292  << "(seq_id=" << next_sequence_id_ << ", frag_id=" << id
293  << ", frags=" << dataBuffer->DataBufferDepthFragments << "/" << maxDataBufferDepthFragments_
294  << ", szB=" << dataBuffer->DataBufferDepthBytes << "/" << maxDataBufferDepthBytes_ << ")"
295  << ", timestamps=" << dataBuffer->DataBuffer.front()->timestamp() << "-" << dataBuffer->DataBuffer.back()->timestamp();
296  TLOG(TLVL_TRACE) << "Bad Omen: Possible causes include requests not getting through or Ignored-mode BR issues";
297  }
298  first = false;
299  }
300  if (waittime % 5 && waittime != lastwaittime)
301  {
302  TLOG(TLVL_WAITFORBUFFERREADY) << "getDataLoop: Data Retreival paused for " << waittime << " ms waiting for data buffer to drain";
303  }
304  lastwaittime = waittime;
305  usleep(1000);
306  }
307  else
308  {
309  std::lock_guard<std::mutex> lk(dataBuffer->DataBufferMutex);
310  if (dataBufferIsTooLarge(id))
311  {
312  auto begin = dataBuffer->DataBuffer.begin();
313  if (begin == dataBuffer->DataBuffer.end())
314  {
315  TLOG(TLVL_WARNING) << "Data buffer is reported as too large, but doesn't contain any Fragments! Possible corrupt memory!";
316  continue;
317  }
318  if (*begin)
319  {
320  TLOG(TLVL_WAITFORBUFFERREADY) << "waitForDataBufferReady: Dropping Fragment with timestamp " << (*begin)->timestamp() << " from data buffer (Buffer over-size, circular data buffer mode)";
321 
322  dataBuffer->DataBufferDepthBytes -= (*begin)->sizeBytes();
323  dataBuffer->DataBuffer.erase(begin);
324  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
325  dataBuffer->BufferFragmentKept = false; // If any Fragments are removed from data buffer, then we know we don't have to ignore the first one anymore
326  }
327  }
328  }
329  }
330  return true;
331 }
332 
333 bool artdaq::FragmentBuffer::dataBufferIsTooLarge(Fragment::fragment_id_t id)
334 {
335  if (!dataBuffers_.count(id))
336  {
337  TLOG(TLVL_ERROR) << "DataBufferError: "
338  << "Error in FragmentBuffer: Cannot check size of data buffer for ID " << id << " because it does not exist!";
339  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot check size of data buffer for ID " << id << " because it does not exist!";
340  }
341  auto dataBuffer = dataBuffers_[id];
342  return (maxDataBufferDepthFragments_ > 0 && dataBuffer->DataBufferDepthFragments.load() > maxDataBufferDepthFragments_) ||
343  (maxDataBufferDepthBytes_ > 0 && dataBuffer->DataBufferDepthBytes.load() > maxDataBufferDepthBytes_);
344 }
345 
346 void artdaq::FragmentBuffer::getDataBufferStats(Fragment::fragment_id_t id)
347 {
348  if (!dataBuffers_.count(id))
349  {
350  TLOG(TLVL_ERROR) << "DataBufferError: "
351  << "Error in FragmentBuffer: Cannot get stats of data buffer for ID " << id << " because it does not exist!";
352  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot get stats of data buffer for ID " << id << " because it does not exist!";
353  }
354  auto dataBuffer = dataBuffers_[id];
355 
356  if (metricMan)
357  {
358  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: Sending Metrics";
359  metricMan->sendMetric("Buffer Depth Fragments", dataBuffer->DataBufferDepthFragments.load(), "fragments", 1, MetricMode::LastPoint);
360  metricMan->sendMetric("Buffer Depth Bytes", dataBuffer->DataBufferDepthBytes.load(), "bytes", 1, MetricMode::LastPoint);
361 
362  auto bufferDepthFragmentsPercent = dataBuffer->DataBufferDepthFragments.load() * 100 / static_cast<double>(maxDataBufferDepthFragments_);
363  auto bufferDepthBytesPercent = dataBuffer->DataBufferDepthBytes.load() * 100 / static_cast<double>(maxDataBufferDepthBytes_);
364  metricMan->sendMetric("Fragment Buffer Full %Fragments", bufferDepthFragmentsPercent, "%", 3, MetricMode::LastPoint);
365  metricMan->sendMetric("Fragment Buffer Full %Bytes", bufferDepthBytesPercent, "%", 3, MetricMode::LastPoint);
366  metricMan->sendMetric("Fragment Buffer Full %", bufferDepthFragmentsPercent > bufferDepthBytesPercent ? bufferDepthFragmentsPercent : bufferDepthBytesPercent, "%", 1, MetricMode::LastPoint);
367  }
368  TLOG(TLVL_GETBUFFERSTATS) << "getDataBufferStats: frags=" << dataBuffer->DataBufferDepthFragments.load() << "/" << maxDataBufferDepthFragments_
369  << ", sz=" << dataBuffer->DataBufferDepthBytes.load() << "/" << maxDataBufferDepthBytes_;
370 }
371 
372 void artdaq::FragmentBuffer::checkDataBuffer(Fragment::fragment_id_t id)
373 {
374  if (!dataBuffers_.count(id))
375  {
376  TLOG(TLVL_ERROR) << "DataBufferError: "
377  << "Error in FragmentBuffer: Cannot check data buffer for ID " << id << " because it does not exist!";
378  throw cet::exception("DataBufferError") << "Error in FragmentBuffer: Cannot check data buffer for ID " << id << " because it does not exist!";
379  }
380 
381  if (dataBuffers_[id]->DataBufferDepthFragments > 0 && mode_ != RequestMode::Single && mode_ != RequestMode::Ignored)
382  {
383  auto dataBuffer = dataBuffers_[id];
384  std::lock_guard<std::mutex> lk(dataBuffer->DataBufferMutex);
385 
386  // Eliminate extra fragments
387  while (dataBufferIsTooLarge(id))
388  {
389  auto begin = dataBuffer->DataBuffer.begin();
390  TLOG(TLVL_CHECKDATABUFFER) << "checkDataBuffer: Dropping Fragment with timestamp " << (*begin)->timestamp() << " from data buffer (Buffer over-size)";
391  dataBuffer->DataBufferDepthBytes -= (*begin)->sizeBytes();
392  dataBuffer->DataBuffer.erase(begin);
393  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
394  dataBuffer->BufferFragmentKept = false; // If any Fragments are removed from data buffer, then we know we don't have to ignore the first one anymore
395  }
396 
397  TLOG(TLVL_CHECKDATABUFFER) << "DataBufferDepthFragments is " << dataBuffer->DataBufferDepthFragments << ", DataBuffer.size is " << dataBuffer->DataBuffer.size();
398  if (dataBuffer->DataBufferDepthFragments > 0 && staleTimeout_ > 0)
399  {
400  TLOG(TLVL_CHECKDATABUFFER) << "Determining if Fragments can be dropped from data buffer";
401  Fragment::timestamp_t last = dataBuffer->DataBuffer.back()->timestamp();
402  Fragment::timestamp_t min = last > staleTimeout_ ? last - staleTimeout_ : 0;
403  for (auto it = dataBuffer->DataBuffer.begin(); it != dataBuffer->DataBuffer.end();)
404  {
405  if ((*it)->timestamp() < min)
406  {
407  TLOG(TLVL_CHECKDATABUFFER) << "checkDataBuffer: Dropping Fragment with timestamp " << (*it)->timestamp() << " from data buffer (timeout=" << staleTimeout_ << ", min=" << min << ")";
408  dataBuffer->DataBufferDepthBytes -= (*it)->sizeBytes();
409  dataBuffer->BufferFragmentKept = false; // If any Fragments are removed from data buffer, then we know we don't have to ignore the first one anymore
410  it = dataBuffer->DataBuffer.erase(it);
411  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
412  }
413  else
414  {
415  break;
416  }
417  }
418  }
419  }
420 }
421 
422 void artdaq::FragmentBuffer::applyRequestsIgnoredMode(artdaq::FragmentPtrs& frags)
423 {
424  // dataBuffersMutex_ is held by calling function
425  // We just copy everything that's here into the output.
426  TLOG(TLVL_APPLYREQUESTS) << "Mode is Ignored; Copying data to output";
427  for (auto& id : dataBuffers_)
428  {
429  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
430  if (id.second && !id.second->DataBuffer.empty() && id.second->DataBuffer.back()->sequenceID() >= next_sequence_id_)
431  {
432  next_sequence_id_ = id.second->DataBuffer.back()->sequenceID() + 1;
433  }
434  std::move(id.second->DataBuffer.begin(), id.second->DataBuffer.end(), std::inserter(frags, frags.end()));
435  id.second->DataBufferDepthBytes = 0;
436  id.second->DataBufferDepthFragments = 0;
437  id.second->BufferFragmentKept = false;
438  id.second->DataBuffer.clear();
439  }
440 }
441 
442 void artdaq::FragmentBuffer::applyRequestsSingleMode(artdaq::FragmentPtrs& frags)
443 {
444  // We only care about the latest request received. Send empties for all others.
445  auto requests = requestBuffer_->GetRequests();
446  while (requests.size() > 1)
447  {
448  // std::map is ordered by key => Last sequence ID in the map is the one we care about
449  requestBuffer_->RemoveRequest(requests.begin()->first);
450  requests.erase(requests.begin());
451  }
452  sendEmptyFragments(frags, requests);
453 
454  // If no requests remain after sendEmptyFragments, return
455  if (requests.size() == 0 || !requests.count(next_sequence_id_)) return;
456 
457  for (auto& id : dataBuffers_)
458  {
459  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
460  if (id.second->DataBufferDepthFragments > 0)
461  {
462  assert(id.second->DataBufferDepthFragments == 1);
463  TLOG(TLVL_APPLYREQUESTS) << "Mode is Single; Sending copy of last event (SeqID " << next_sequence_id_ << ")";
464  for (auto& fragptr : id.second->DataBuffer)
465  {
466  // Return the latest data point
467  auto frag = fragptr.get();
468  auto newfrag = std::unique_ptr<artdaq::Fragment>(new Fragment(next_sequence_id_, frag->fragmentID()));
469  newfrag->resize(frag->size() - detail::RawFragmentHeader::num_words());
470  memcpy(newfrag->headerAddress(), frag->headerAddress(), frag->sizeBytes());
471  newfrag->setTimestamp(requests[next_sequence_id_]);
472  newfrag->setSequenceID(next_sequence_id_);
473  frags.push_back(std::move(newfrag));
474  }
475  }
476  else
477  {
478  sendEmptyFragment(frags, next_sequence_id_, id.first, "No data for");
479  }
480  }
481  requestBuffer_->RemoveRequest(next_sequence_id_);
482  ++next_sequence_id_;
483 }
484 
485 void artdaq::FragmentBuffer::applyRequestsBufferMode(artdaq::FragmentPtrs& frags)
486 {
487  // We only care about the latest request received. Send empties for all others.
488  auto requests = requestBuffer_->GetRequests();
489  while (requests.size() > 1)
490  {
491  // std::map is ordered by key => Last sequence ID in the map is the one we care about
492  requestBuffer_->RemoveRequest(requests.begin()->first);
493  requests.erase(requests.begin());
494  }
495  sendEmptyFragments(frags, requests);
496 
497  // If no requests remain after sendEmptyFragments, return
498  if (requests.size() == 0 || !requests.count(next_sequence_id_)) return;
499 
500  for (auto& id : dataBuffers_)
501  {
502  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: Creating ContainerFragment for Buffered Fragments (SeqID " << next_sequence_id_ << ")";
503  frags.emplace_back(new artdaq::Fragment(next_sequence_id_, id.first));
504  frags.back()->setTimestamp(requests[next_sequence_id_]);
505  ContainerFragmentLoader cfl(*frags.back());
506  cfl.set_missing_data(false); // Buffer mode is never missing data, even if there IS no data.
507 
508  // If we kept a Fragment from the previous iteration, but more data has arrived, discard it
509  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
510  if (id.second->BufferFragmentKept && id.second->DataBufferDepthFragments > 1)
511  {
512  id.second->DataBufferDepthBytes -= id.second->DataBuffer.front()->sizeBytes();
513  id.second->DataBuffer.erase(id.second->DataBuffer.begin());
514  id.second->DataBufferDepthFragments = id.second->DataBuffer.size();
515  }
516 
517  // Buffer mode TFGs should simply copy out the whole dataBuffer_ into a ContainerFragment
518  FragmentPtrs fragsToAdd;
519  std::move(id.second->DataBuffer.begin(), --id.second->DataBuffer.end(), std::back_inserter(fragsToAdd));
520  id.second->DataBuffer.erase(id.second->DataBuffer.begin(), --id.second->DataBuffer.end());
521 
522  if (fragsToAdd.size() > 0)
523  {
524  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: Adding " << fragsToAdd.size() << " Fragments to Container (SeqID " << next_sequence_id_ << ")";
525  cfl.addFragments(fragsToAdd);
526  }
527  else
528  {
529  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: No Fragments to add (SeqID " << next_sequence_id_ << ")";
530  }
531 
532  if (id.second->DataBuffer.size() == 1)
533  {
534  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsBufferMode: Adding Fragment with timestamp " << id.second->DataBuffer.front()->timestamp() << " to Container with sequence ID " << next_sequence_id_;
535  cfl.addFragment(id.second->DataBuffer.front());
536  if (bufferModeKeepLatest_)
537  {
538  id.second->BufferFragmentKept = true;
539  id.second->DataBufferDepthBytes = id.second->DataBuffer.front()->sizeBytes();
540  id.second->DataBufferDepthFragments = id.second->DataBuffer.size(); // 1
541  }
542  else
543  {
544  id.second->DataBuffer.clear();
545  id.second->BufferFragmentKept = false;
546  id.second->DataBufferDepthBytes = 0;
547  id.second->DataBufferDepthFragments = 0;
548  }
549  }
550  }
551  requestBuffer_->RemoveRequest(next_sequence_id_);
552  ++next_sequence_id_;
553 }
554 
555 void artdaq::FragmentBuffer::applyRequestsWindowMode_CheckAndFillDataBuffer(artdaq::FragmentPtrs& frags, artdaq::Fragment::fragment_id_t id, artdaq::Fragment::sequence_id_t seq, artdaq::Fragment::timestamp_t ts)
556 {
557  auto dataBuffer = dataBuffers_[id];
558 
559  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Checking that data exists for request window " << seq;
560  Fragment::timestamp_t min = ts > windowOffset_ ? ts - windowOffset_ : 0;
561  Fragment::timestamp_t max = ts + windowWidth_ > windowOffset_ ? ts + windowWidth_ - windowOffset_ : 1;
562 
563  TLOG(TLVL_APPLYREQUESTS) << "ApplyRequestsWindowsMode_CheckAndFillDataBuffer: min is " << min << ", max is " << max
564  << " and first/last points in buffer are " << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.front()->timestamp() : 0)
565  << "/" << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.back()->timestamp() : 0)
566  << " (sz=" << dataBuffer->DataBufferDepthFragments << " [" << dataBuffer->DataBufferDepthBytes.load()
567  << "/" << maxDataBufferDepthBytes_ << "])";
568  bool windowClosed = dataBuffer->DataBufferDepthFragments > 0 && dataBuffer->DataBuffer.back()->timestamp() >= max;
569  bool windowTimeout = !windowClosed && TimeUtils::GetElapsedTimeMicroseconds(requestBuffer_->GetRequestTime(seq)) > window_close_timeout_us_;
570  if (windowTimeout)
571  {
572  TLOG(TLVL_WARNING) << "applyRequestsWindowMode_CheckAndFillDataBuffer: A timeout occurred waiting for data to close the request window ({" << min << "-" << max
573  << "}, buffer={" << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.front()->timestamp() : 0) << "-"
574  << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.back()->timestamp() : 0)
575  << "} ). Time waiting: "
576  << TimeUtils::GetElapsedTimeMicroseconds(requestBuffer_->GetRequestTime(seq)) << " us "
577  << "(> " << window_close_timeout_us_ << " us).";
578  }
579  if (windowClosed || windowTimeout)
580  {
581  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Creating ContainerFragment for Window-requested Fragments (SeqID " << seq << ")";
582  frags.emplace_back(new artdaq::Fragment(seq, id));
583  frags.back()->setTimestamp(ts);
584  ContainerFragmentLoader cfl(*frags.back());
585 
586  // In the spirit of NOvA's MegaPool: (RS = Request start (min), RE = Request End (max))
587  // --- | Buffer Start | --- | Buffer End | ---
588  //1. RS RE | | | |
589  //2. RS | | RE | |
590  //3. RS | | | | RE
591  //4. | | RS RE | |
592  //5. | | RS | | RE
593  //6. | | | | RS RE
594  //
595  // If RE (or RS) is after the end of the buffer, we wait for window_close_timeout_us_. If we're here, then that means that windowClosed is false, and the missing_data flag should be set.
596  // If RS (or RE) is before the start of the buffer, then missing_data should be set to true, as data is assumed to arrive in the buffer in timestamp order
597  // If the dataBuffer has size 0, then windowClosed will be false
598  if (!windowClosed || (dataBuffer->DataBufferDepthFragments > 0 && dataBuffer->DataBuffer.front()->timestamp() > min))
599  {
600  TLOG(TLVL_DEBUG) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Request window starts before and/or ends after the current data buffer, setting ContainerFragment's missing_data flag!"
601  << " (requestWindowRange=[" << min << "," << max << "], "
602  << "buffer={" << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.front()->timestamp() : 0) << "-"
603  << (dataBuffer->DataBufferDepthFragments > 0 ? dataBuffer->DataBuffer.back()->timestamp() : 0) << "} (SeqID " << seq << ")";
604  cfl.set_missing_data(true);
605  }
606 
607  auto it = dataBuffer->DataBuffer.begin();
608  // Likely that it will be closer to the end...
609  if (windowTimeout)
610  {
611  it = dataBuffer->DataBuffer.end();
612  --it;
613  while (it != dataBuffer->DataBuffer.begin())
614  {
615  if ((*it)->timestamp() < min)
616  {
617  break;
618  }
619  --it;
620  }
621  }
622 
623  FragmentPtrs fragsToAdd;
624  // Do a little bit more work to decide which fragments to send for a given request
625  for (; it != dataBuffer->DataBuffer.end();)
626  {
627  Fragment::timestamp_t fragT = (*it)->timestamp();
628  if (fragT < min)
629  {
630  ++it;
631  continue;
632  }
633  if (fragT > max || (fragT == max && windowWidth_ > 0))
634  { break; }
635 
636  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Adding Fragment with timestamp " << (*it)->timestamp() << " to Container (SeqID " << seq << ")";
637  if (uniqueWindows_)
638  {
639  dataBuffer->DataBufferDepthBytes -= (*it)->sizeBytes();
640  fragsToAdd.emplace_back(std::move(*it));
641  it = dataBuffer->DataBuffer.erase(it);
642  }
643  else
644  {
645  fragsToAdd.emplace_back(it->get());
646  ++it;
647  }
648  }
649 
650  if (fragsToAdd.size() > 0)
651  {
652  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: Adding " << fragsToAdd.size() << " Fragments to Container (SeqID " << seq << ")";
653  cfl.addFragments(fragsToAdd);
654 
655  // Don't delete Fragments which are still in the Fragment buffer
656  if (!uniqueWindows_)
657  {
658  for (auto& frag : fragsToAdd)
659  {
660  frag.release();
661  }
662  }
663  fragsToAdd.clear();
664  }
665  else
666  {
667  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode_CheckAndFillDataBuffer: No Fragments to add (SeqID " << seq << ")";
668  }
669 
670  dataBuffer->DataBufferDepthFragments = dataBuffer->DataBuffer.size();
671  dataBuffer->WindowsSent[seq] = std::chrono::steady_clock::now();
672  if (seq > dataBuffer->HighestRequestSeen) dataBuffer->HighestRequestSeen = seq;
673  }
674 }
675 
676 void artdaq::FragmentBuffer::applyRequestsWindowMode(artdaq::FragmentPtrs& frags)
677 {
678  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode BEGIN";
679 
680  auto requests = requestBuffer_->GetRequests();
681 
682  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: Starting request processing for " << requests.size() << " requests";
683  for (auto req = requests.begin(); req != requests.end();)
684  {
685  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsWindowMode: processing request with sequence ID " << req->first << ", timestamp " << req->second;
686 
687  while (req->first < next_sequence_id_ && requests.size() > 0)
688  {
689  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsWindowMode: Clearing passed request for sequence ID " << req->first;
690  requestBuffer_->RemoveRequest(req->first);
691  req = requests.erase(req);
692  }
693  if (requests.size() == 0) break;
694 
695  auto ts = req->second;
696  if (ts == Fragment::InvalidTimestamp)
697  {
698  TLOG(TLVL_ERROR) << "applyRequestsWindowMode: Received InvalidTimestamp in request " << req->first << ", cannot apply! Check that push-mode BRs are filling appropriate timestamps in their Fragments!";
699  req = requests.erase(req);
700  continue;
701  }
702 
703  for (auto& id : dataBuffers_)
704  {
705  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
706  if (!id.second->WindowsSent.count(req->first))
707  {
708  applyRequestsWindowMode_CheckAndFillDataBuffer(frags, id.first, req->first, req->second);
709  }
710  }
711  checkSentWindows(req->first);
712  ++req;
713  }
714 
715  // Check sent windows for requests that can be removed
716  std::set<artdaq::Fragment::sequence_id_t> seqs;
717  for (auto& id : dataBuffers_)
718  {
719  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
720  for (auto& seq : id.second->WindowsSent)
721  {
722  seqs.insert(seq.first);
723  }
724  }
725  for (auto& seq : seqs)
726  {
727  checkSentWindows(seq);
728  }
729 }
730 
732 {
733  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsSequenceIDMode BEGIN";
734 
735  auto requests = requestBuffer_->GetRequests();
736 
737  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsSequenceIDMode: Starting request processing";
738  for (auto req = requests.begin(); req != requests.end();)
739  {
740  TLOG(TLVL_APPLYREQUESTS) << "applyRequestsSequenceIDMode: Checking that data exists for request SequenceID " << req->first;
741 
742  for (auto& id : dataBuffers_)
743  {
744  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
745  if (!id.second->WindowsSent.count(req->first))
746  {
747  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "Searching id " << id.first << " for Fragments with Sequence ID " << req->first;
748  for (auto it = id.second->DataBuffer.begin(); it != id.second->DataBuffer.end();)
749  {
750  auto seq = (*it)->sequenceID();
751  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsSequenceIDMode: Fragment SeqID " << seq << ", request ID " << req->first;
752  if (seq == req->first)
753  {
754  TLOG(TLVL_APPLYREQUESTS_VERBOSE) << "applyRequestsSequenceIDMode: Adding Fragment to output";
755  id.second->WindowsSent[req->first] = std::chrono::steady_clock::now();
756  id.second->DataBufferDepthBytes -= (*it)->sizeBytes();
757  frags.push_back(std::move(*it));
758  it = id.second->DataBuffer.erase(it);
759  id.second->DataBufferDepthFragments = id.second->DataBuffer.size();
760  }
761  else
762  {
763  ++it;
764  }
765  }
766  }
767  if (req->first > id.second->HighestRequestSeen) id.second->HighestRequestSeen = req->first;
768  }
769  checkSentWindows(req->first);
770  ++req;
771  }
772 
773  // Check sent windows for requests that can be removed
774  std::set<artdaq::Fragment::sequence_id_t> seqs;
775  for (auto& id : dataBuffers_)
776  {
777  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
778  for (auto& seq : id.second->WindowsSent)
779  {
780  seqs.insert(seq.first);
781  }
782  }
783  for (auto& seq : seqs)
784  {
785  checkSentWindows(seq);
786  }
787 }
788 
789 bool artdaq::FragmentBuffer::applyRequests(artdaq::FragmentPtrs& frags)
790 {
791  if (check_stop())
792  {
793  return false;
794  }
795 
796  // Wait for data, if in ignored mode, or a request otherwise
797  if (mode_ == RequestMode::Ignored)
798  {
799  auto start_time = std::chrono::steady_clock::now();
800  while (dataBufferFragmentCount_() == 0 && TimeUtils::GetElapsedTime(start_time) < 1.0)
801  {
802  if (check_stop()) return false;
803  std::unique_lock<std::mutex> lock(dataConditionMutex_);
804  dataCondition_.wait_for(lock, std::chrono::milliseconds(10), [this]() { return dataBufferFragmentCount_() > 0; });
805  }
806  }
807  else if (requestBuffer_ == nullptr)
808  {
809  TLOG(TLVL_ERROR) << "Request Buffer must be set (via SetRequestBuffer) before applyRequests/getData can be called!";
810  return false;
811  }
812  else
813  {
814  if ((check_stop() && requestBuffer_->size() == 0)) return false;
815 
816  std::unique_lock<std::mutex> lock(dataConditionMutex_);
817  dataCondition_.wait_for(lock, std::chrono::milliseconds(10));
818 
819  checkDataBuffers();
820 
821  // Wait up to 1000 ms for a request...
822  auto counter = 0;
823 
824  while (requestBuffer_->size() == 0 && counter < 100)
825  {
826  if (check_stop()) return false;
827 
828  checkDataBuffers();
829 
830  requestBuffer_->WaitForRequests(10); // milliseconds
831  counter++;
832  }
833  }
834 
835  if (systemFragmentCount_.load() > 0)
836  {
837  std::lock_guard<std::mutex> lk(systemFragmentMutex_);
838  TLOG(TLVL_INFO) << "Copying " << systemFragmentCount_.load() << " System Fragments into output";
839 
840  std::move(systemFragments_.begin(), systemFragments_.end(), std::inserter(frags, frags.end()));
841  systemFragments_.clear();
842  systemFragmentCount_ = 0;
843  }
844 
845  switch (mode_)
846  {
847  case RequestMode::Single:
848  applyRequestsSingleMode(frags);
849  break;
850  case RequestMode::Window:
851  applyRequestsWindowMode(frags);
852  break;
853  case RequestMode::Buffer:
854  applyRequestsBufferMode(frags);
855  break;
856  case RequestMode::SequenceID:
857  applyRequestsSequenceIDMode(frags);
858  break;
859  case RequestMode::Ignored:
860  default:
861  applyRequestsIgnoredMode(frags);
862  break;
863  }
864 
865  getDataBuffersStats();
866 
867  if (frags.size() > 0)
868  TLOG(TLVL_APPLYREQUESTS) << "Finished Processing requests, returning " << frags.size() << " fragments, current ev_counter is " << next_sequence_id_;
869  return true;
870 }
871 
872 bool artdaq::FragmentBuffer::sendEmptyFragment(artdaq::FragmentPtrs& frags, size_t seqId, Fragment::fragment_id_t fragmentId, std::string desc)
873 {
874  TLOG(TLVL_EMPTYFRAGMENT) << desc << " sequence ID " << seqId << ", sending empty fragment";
875  auto frag = new Fragment();
876  frag->setSequenceID(seqId);
877  frag->setFragmentID(fragmentId);
878  frag->setSystemType(Fragment::EmptyFragmentType);
879  frags.emplace_back(FragmentPtr(frag));
880  return true;
881 }
882 
883 void artdaq::FragmentBuffer::sendEmptyFragments(artdaq::FragmentPtrs& frags, std::map<Fragment::sequence_id_t, Fragment::timestamp_t>& requests)
884 {
885  if (requests.size() > 0)
886  {
887  TLOG(TLVL_SENDEMPTYFRAGMENTS) << "Sending Empty Fragments for Sequence IDs from " << next_sequence_id_ << " up to but not including " << requests.begin()->first;
888  while (requests.begin()->first > next_sequence_id_)
889  {
890  for (auto& fid : dataBuffers_)
891  {
892  sendEmptyFragment(frags, next_sequence_id_, fid.first, "Missed request for");
893  }
894  ++next_sequence_id_;
895  }
896  }
897 }
898 
899 void artdaq::FragmentBuffer::checkSentWindows(artdaq::Fragment::sequence_id_t seq)
900 {
901  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Checking if request " << seq << " can be removed from request list";
902  bool seqComplete = true;
903  bool seqTimeout = false;
904  for (auto& id : dataBuffers_)
905  {
906  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
907  if (!id.second->WindowsSent.count(seq) || id.second->HighestRequestSeen < seq)
908  {
909  seqComplete = false;
910  }
911  if (id.second->WindowsSent.count(seq) && TimeUtils::GetElapsedTimeMicroseconds(id.second->WindowsSent[seq]) > missing_request_window_timeout_us_)
912  {
913  seqTimeout = true;
914  }
915  }
916  if (seqComplete)
917  {
918  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Request " << seq << " is complete, removing from requestBuffer_.";
919  requestBuffer_->RemoveRequest(seq);
920 
921  if (next_sequence_id_ == seq)
922  {
923  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Sequence ID matches ev_counter, incrementing ev_counter (" << next_sequence_id_ << ")";
924 
925  for (auto& id : dataBuffers_)
926  {
927  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
928  id.second->WindowsSent.erase(seq);
929  }
930 
931  ++next_sequence_id_;
932  }
933  }
934  if (seqTimeout)
935  {
936  TLOG(TLVL_CHECKWINDOWS) << "checkSentWindows: Sent Window history indicates that requests between " << next_sequence_id_ << " and " << seq << " have timed out.";
937  while (next_sequence_id_ <= seq)
938  {
939  if (next_sequence_id_ < seq) TLOG(TLVL_CHECKWINDOWS) << "Missed request for sequence ID " << next_sequence_id_ << "! Will not send any data for this sequence ID!";
940  requestBuffer_->RemoveRequest(next_sequence_id_);
941 
942  for (auto& id : dataBuffers_)
943  {
944  std::lock_guard<std::mutex> lk(id.second->DataBufferMutex);
945  id.second->WindowsSent.erase(next_sequence_id_);
946  }
947 
948  ++next_sequence_id_;
949  }
950  }
951 }
void AddFragmentsToBuffer(FragmentPtrs frags)
Add Fragments to the FragmentBuffer.
size_t dataBufferFragmentCount_()
Get the total number of Fragments in all data buffers.
std::string printMode_()
Return the string representation of the current RequestMode.
bool applyRequests(FragmentPtrs &frags)
See if any requests have been received, and add the corresponding data Fragment objects to the output...
FragmentBuffer(const fhicl::ParameterSet &ps)
FragmentBuffer Constructor.
bool waitForDataBufferReady(Fragment::fragment_id_t id)
Wait for the data buffer to drain (dataBufferIsTooLarge returns false), periodically reporting status...
void applyRequestsIgnoredMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Ignored. Precondition: dataBufferMutex_ and reque...
void applyRequestsWindowMode_CheckAndFillDataBuffer(artdaq::FragmentPtrs &frags, artdaq::Fragment::fragment_id_t id, artdaq::Fragment::sequence_id_t seq, artdaq::Fragment::timestamp_t ts)
bool check_stop()
Routine used by applyRequests to make sure that all outstanding requests have been fulfilled before r...
void Reset(bool stop)
Reset the FragmentBuffer (flushes all Fragments from buffers)
void checkSentWindows(Fragment::sequence_id_t seq)
Check the windows_sent_ooo_ map for sequence IDs that may be removed.
artdaq::Fragment::fragment_id_t fragment_id() const
Get the Fragment ID of this Fragment generator.
void checkDataBuffer(Fragment::fragment_id_t id)
Perform data buffer pruning operations for the given buffer. If the RequestMode is Single...
bool dataBufferIsTooLarge(Fragment::fragment_id_t id)
Test the configured constraints on the data buffer.
bool sendEmptyFragment(FragmentPtrs &frags, size_t sequenceId, Fragment::fragment_id_t fragmentId, std::string desc)
Send an EmptyFragmentType Fragment.
void applyRequestsBufferMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Buffer. Precondition: dataBufferMutex_ and reques...
void applyRequestsWindowMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Window. Precondition: dataBufferMutex_ and reques...
virtual ~FragmentBuffer()
FragmentBuffer Destructor.
void sendEmptyFragments(FragmentPtrs &frags, std::map< Fragment::sequence_id_t, Fragment::timestamp_t > &requests)
This function is for Buffered and Single request modes, as they can only respond to one data request ...
void applyRequestsSequenceIDMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode SequenceID. Precondition: dataBufferMutex_ and re...
void applyRequestsSingleMode(artdaq::FragmentPtrs &frags)
Create fragments using data buffer for request mode Single. Precondition: dataBufferMutex_ and reques...
void getDataBufferStats(Fragment::fragment_id_t id)
Calculate the size of the dataBuffer and report appropriate metrics.