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