artdaq  v3_12_01
DataReceiverManager.cc
1 #include "artdaq/DAQdata/Globals.hh"
2 #define TRACE_NAME (app_name + "_DataReceiverManager").c_str()
3 
4 #include "artdaq/DAQdata/HostMap.hh"
5 #include "artdaq/DAQrate/DataReceiverManager.hh"
6 #include "artdaq/TransferPlugins/MakeTransferPlugin.hh"
7 
8 #include "cetlib_except/exception.h"
9 #include "fhiclcpp/ParameterSet.h"
10 
11 #include <boost/bind.hpp>
12 #include <boost/exception/all.hpp>
13 #include <boost/thread.hpp>
14 
15 #include <chrono>
16 #include <iomanip>
17 #include <thread>
18 #include <utility>
19 
20 artdaq::DataReceiverManager::DataReceiverManager(const fhicl::ParameterSet& pset, std::shared_ptr<SharedMemoryEventManager> shm)
21  : stop_requested_(false)
22  , stop_requested_time_(0)
23  , recv_frag_count_()
24  , recv_frag_size_()
25  , recv_seq_count_()
26  , receive_timeout_(pset.get<size_t>("receive_timeout_usec", 100000))
27  , stop_timeout_ms_(pset.get<size_t>("stop_timeout_ms", 1500))
28  , shm_manager_(std::move(std::move(shm)))
29  , non_reliable_mode_enabled_(pset.get<bool>("non_reliable_mode", false))
30  , non_reliable_mode_retry_count_(pset.get<size_t>("non_reliable_mode_retry_count", -1))
31 {
32  TLOG(TLVL_DEBUG + 32) << "Constructor";
33  auto enabled_srcs = pset.get<std::vector<int>>("enabled_sources", std::vector<int>());
34  auto enabled_srcs_empty = enabled_srcs.empty();
35 
36  if (non_reliable_mode_enabled_)
37  {
38  TLOG(TLVL_WARNING) << "DataReceiverManager is configured to drop data after " << non_reliable_mode_retry_count_
39  << " failed attempts to put data into the SharedMemoryEventManager! If this is unexpected, please check your configuration!";
40  }
41 
42  if (enabled_srcs_empty)
43  {
44  TLOG(TLVL_INFO) << "enabled_sources not specified, assuming all sources enabled.";
45  }
46  else
47  {
48  for (auto& s : enabled_srcs)
49  {
50  enabled_sources_[s] = true;
51  }
52  }
53 
54  hostMap_t host_map = MakeHostMap(pset);
55  auto tcp_receive_buffer_size = pset.get<size_t>("tcp_receive_buffer_size", 0);
56  auto max_fragment_size_words = pset.get<size_t>("max_fragment_size_words", 0);
57 
58  auto srcs = pset.get<fhicl::ParameterSet>("sources", fhicl::ParameterSet());
59  for (auto& s : srcs.get_pset_names())
60  {
61  auto src_pset = srcs.get<fhicl::ParameterSet>(s);
62  host_map = MakeHostMap(src_pset, host_map);
63  }
64  auto host_map_pset = MakeHostMapPset(host_map);
65  fhicl::ParameterSet srcs_mod;
66  for (auto& s : srcs.get_pset_names())
67  {
68  auto src_pset = srcs.get<fhicl::ParameterSet>(s);
69  src_pset.erase("host_map");
70  src_pset.put<std::vector<fhicl::ParameterSet>>("host_map", host_map_pset);
71 
72  if (tcp_receive_buffer_size != 0 && !src_pset.has_key("tcp_receive_buffer_size"))
73  {
74  src_pset.put<size_t>("tcp_receive_buffer_size", tcp_receive_buffer_size);
75  }
76  if (max_fragment_size_words != 0 && !src_pset.has_key("max_fragment_size_words"))
77  {
78  src_pset.put<size_t>("max_fragment_size_words", max_fragment_size_words);
79  }
80 
81  srcs_mod.put<fhicl::ParameterSet>(s, src_pset);
82  }
83 
84  for (auto& s : srcs_mod.get_pset_names())
85  {
86  try
87  {
88  auto transfer = std::unique_ptr<TransferInterface>(MakeTransferPlugin(srcs_mod, s,
90  auto source_rank = transfer->source_rank();
91  if (enabled_srcs_empty)
92  {
93  enabled_sources_[source_rank] = true;
94  }
95  else if (enabled_sources_.count(source_rank) == 0u)
96  {
97  enabled_sources_[source_rank] = false;
98  }
99  running_sources_[source_rank] = false;
100  source_plugins_[source_rank] = std::move(transfer);
101  }
102  catch (const cet::exception& ex)
103  {
104  TLOG(TLVL_WARNING) << "cet::exception caught while setting up source " << s << ": " << ex.what();
105  }
106  catch (const std::exception& ex)
107  {
108  TLOG(TLVL_WARNING) << "std::exception caught while setting up source " << s << ": " << ex.what();
109  }
110  catch (...)
111  {
112  TLOG(TLVL_WARNING) << "Non-cet exception caught while setting up source " << s << ".";
113  }
114  }
115  if (srcs.get_pset_names().empty())
116  {
117  TLOG(TLVL_ERROR) << "No sources configured!";
118  }
119 }
120 
122 {
123  TLOG(TLVL_DEBUG + 33) << "~DataReceiverManager: BEGIN";
124  stop_threads();
125  shm_manager_.reset();
126  TLOG(TLVL_DEBUG + 33) << "Destructor END";
127 }
128 
130 {
131  stop_requested_ = false;
132  if (shm_manager_)
133  {
134  shm_manager_->setRequestMode(artdaq::detail::RequestMessageMode::Normal);
135  }
136  for (auto& source : source_plugins_)
137  {
138  auto& rank = source.first;
139  if ((enabled_sources_.count(rank) != 0u) && enabled_sources_[rank].load())
140  {
141  recv_frag_count_.setSlot(rank, 0);
142  recv_frag_size_.setSlot(rank, 0);
143  recv_seq_count_.setSlot(rank, 0);
144 
145  running_sources_[rank] = true;
146  boost::thread::attributes attrs;
147  attrs.set_stack_size(4096 * 2000); // 2000 KB
148  try
149  {
150  source_threads_[rank] = boost::thread(attrs, boost::bind(&DataReceiverManager::runReceiver_, this, rank));
151  char tname[16]; // Size 16 - see man page pthread_setname_np(3) and/or prctl(2)
152  snprintf(tname, sizeof(tname) - 1, "%d-%d RECV", rank, my_rank); // NOLINT
153  tname[sizeof(tname) - 1] = '\0'; // assure term. snprintf is not too evil :)
154  auto handle = source_threads_[rank].native_handle();
155  pthread_setname_np(handle, tname);
156  }
157  catch (const boost::exception& e)
158  {
159  TLOG(TLVL_ERROR) << "Caught boost::exception starting Receiver " << rank << " thread: " << boost::diagnostic_information(e) << ", errno=" << errno;
160  std::cerr << "Caught boost::exception starting Receiver " << rank << " thread: " << boost::diagnostic_information(e) << ", errno=" << errno << std::endl;
161  exit(5);
162  }
163  }
164  }
165 }
166 
168 {
169  TLOG(TLVL_DEBUG + 33) << "stop_threads: BEGIN: Setting stop_requested to true, frags=" << count() << ", bytes=" << byteCount();
170 
171  stop_requested_time_ = TimeUtils::gettimeofday_us();
172  stop_requested_ = true;
173 
174  auto initial_count = running_sources().size();
175  TLOG(TLVL_DEBUG + 33) << "stop_threads: Waiting for " << initial_count << " running receiver threads to stop";
176  auto wait_start = std::chrono::steady_clock::now();
177  auto last_report = std::chrono::steady_clock::now();
178  while (!running_sources().empty() && TimeUtils::GetElapsedTime(wait_start) < 60.0)
179  {
180  usleep(10000);
181  if (TimeUtils::GetElapsedTime(last_report) > 1.0)
182  {
183  TLOG(TLVL_DEBUG + 32) << "stop_threads: Waited " << TimeUtils::GetElapsedTime(wait_start) << " s for " << initial_count
184  << " receiver threads to end (" << running_sources().size() << " remain)";
185  last_report = std::chrono::steady_clock::now();
186  }
187  }
188  if (!running_sources().empty())
189  {
190  TLOG(TLVL_WARNING) << "stop_threads: Timeout expired while waiting for all receiver threads to end. There are "
191  << running_sources().size() << " threads remaining.";
192  }
193 
194  TLOG(TLVL_DEBUG + 33) << "stop_threads: Joining " << source_threads_.size() << " receiver threads";
195  for (auto& source_thread : source_threads_)
196  {
197  TLOG(TLVL_DEBUG + 33) << "stop_threads: Joining thread for source_rank " << source_thread.first;
198  try
199  {
200  if (source_thread.second.joinable())
201  {
202  source_thread.second.join();
203  }
204  else
205  {
206  TLOG(TLVL_ERROR) << "stop_threads: Thread for source rank " << source_thread.first << " is not joinable!";
207  }
208  }
209  catch (...)
210  {
211  // IGNORED
212  }
213  }
214  source_threads_.clear(); // To prevent error messages from shutdown-after-stop
215 
216  TLOG(TLVL_DEBUG + 33) << "stop_threads: END";
217 }
218 
220 {
221  std::set<int> output;
222  for (auto& src : enabled_sources_)
223  {
224  if (src.second)
225  {
226  output.insert(src.first);
227  }
228  }
229  return output;
230 }
231 
233 {
234  std::set<int> output;
235  for (auto& src : running_sources_)
236  {
237  if (src.second)
238  {
239  output.insert(src.first);
240  }
241  }
242  return output;
243 }
244 
245 void artdaq::DataReceiverManager::runReceiver_(int source_rank)
246 {
247  std::chrono::steady_clock::time_point start_time, after_header, before_body, after_body, end_time = std::chrono::steady_clock::now();
248  int ret;
249  detail::RawFragmentHeader header;
250  size_t endOfDataCount = -1;
251  auto sleep_time = receive_timeout_ / 100 > 100000 ? 100000 : receive_timeout_ / 100;
252  if (sleep_time < 5000)
253  {
254  sleep_time = 5000;
255  }
256  auto max_retries = non_reliable_mode_retry_count_ * ceil(receive_timeout_ / sleep_time);
257 
258  while (!(stop_requested_ && TimeUtils::gettimeofday_us() - stop_requested_time_ > stop_timeout_ms_ * 1000) && (enabled_sources_.count(source_rank) != 0u))
259  {
260  TLOG(TLVL_DEBUG + 35) << "runReceiver_: Begin loop stop_requested_=" << stop_requested_ << ", stop_timeout_ms_=" << stop_timeout_ms_ << ", enabled_sources_.count(source_rank)=" << enabled_sources_.count(source_rank) << ", now - stop_requested_time_=" << (TimeUtils::gettimeofday_us() - stop_requested_time_);
261  std::this_thread::yield();
262 
263  // Don't stop receiving until we haven't received anything for 1 second
264  if (endOfDataCount <= recv_frag_count_.slotCount(source_rank) && !source_plugins_[source_rank]->isRunning())
265  {
266  TLOG(TLVL_DEBUG + 32) << "runReceiver_: End of Data conditions met, ending runReceiver loop";
267  break;
268  }
269 
270  start_time = std::chrono::steady_clock::now();
271 
272  TLOG(TLVL_DEBUG + 35) << "runReceiver_: Calling receiveFragmentHeader tmo=" << receive_timeout_;
273  ret = source_plugins_[source_rank]->receiveFragmentHeader(header, receive_timeout_);
274  TLOG(TLVL_DEBUG + 35) << "runReceiver_: Done with receiveFragmentHeader, ret=" << ret << " (should be " << source_rank << ")";
275  if (ret != source_rank)
276  {
277  if (ret >= 0)
278  {
279  TLOG(TLVL_WARNING) << "Received Fragment from rank " << ret << ", but was expecting one from rank " << source_rank << "!";
280  }
281  else if (ret == TransferInterface::DATA_END)
282  {
283  TLOG(TLVL_ERROR) << "Transfer Plugin returned DATA_END, ending receive loop!";
284  break;
285  }
286  if (*running_sources().begin() == source_rank) // Only do this for the first sender in the running_sources_ map
287  {
288  TLOG(TLVL_DEBUG + 34) << "Calling SMEM::CheckPendingBuffers from DRM receiver thread for " << source_rank << " to make sure that things aren't stuck";
289  shm_manager_->CheckPendingBuffers();
290  }
291 
292  usleep(sleep_time);
293  continue; // Receive timeout or other oddness
294  }
295 
296  after_header = std::chrono::steady_clock::now();
297 
298  if (Fragment::isUserFragmentType(header.type) || header.type == Fragment::DataFragmentType || header.type == Fragment::EmptyFragmentType || header.type == Fragment::ContainerFragmentType)
299  {
300  TLOG(TLVL_DEBUG + 33) << "Received Fragment Header from rank " << source_rank << ", sequence ID " << header.sequence_id << ", timestamp " << header.timestamp;
301  RawDataType* loc = nullptr;
302  size_t retries = 0;
303  auto latency_s = header.getLatency(true);
304  auto latency = latency_s.tv_sec + (latency_s.tv_nsec / 1000000000.0);
305  while (loc == nullptr) //&& TimeUtils::GetElapsedTimeMicroseconds(after_header)) < receive_timeout_)
306  {
307  loc = shm_manager_->WriteFragmentHeader(header);
308 
309  // Break here and outside of the loop to go to the cleanup steps at the end of runReceiver_
310  if (loc == nullptr && stop_requested_)
311  {
312  break;
313  }
314 
315  if (loc == nullptr)
316  {
317  usleep(sleep_time);
318  }
319  retries++;
320  if (non_reliable_mode_enabled_ && retries > max_retries)
321  {
322  loc = shm_manager_->WriteFragmentHeader(header, true);
323  }
324  }
325  // Break here to go to cleanup at the end of runReceiver_
326  if (loc == nullptr && stop_requested_)
327  {
328  break;
329  }
330  if (loc == nullptr)
331  {
332  // Could not enqueue event!
333  TLOG(TLVL_ERROR) << "runReceiver_: Could not get data location for event " << header.sequence_id;
334  continue;
335  }
336  before_body = std::chrono::steady_clock::now();
337 
338  auto hdrLoc = reinterpret_cast<artdaq::detail::RawFragmentHeader*>(loc - artdaq::detail::RawFragmentHeader::num_words()); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
339  TLOG(TLVL_DEBUG + 35) << "runReceiver_: Calling receiveFragmentData from rank " << source_rank << ", sequence ID " << header.sequence_id << ", timestamp " << header.timestamp;
340  auto ret2 = source_plugins_[source_rank]->receiveFragmentData(loc, header.word_count - header.num_words());
341  TLOG(TLVL_DEBUG + 35) << "runReceiver_: Done with receiveFragmentData, ret2=" << ret2 << " (should be " << source_rank << ")";
342 
343  if (ret != ret2)
344  {
345  TLOG(TLVL_ERROR) << "Unexpected return code from receiveFragmentData after receiveFragmentHeader! (Expected: " << ret << ", Got: " << ret2 << ")";
346  TLOG(TLVL_ERROR) << "Error receiving data from rank " << source_rank << ", data has been lost! Event " << header.sequence_id << " will most likely be Incomplete!";
347 
348  // Mark the Fragment as invalid
349  /* \todo Make a RawFragmentHeader field that marks it as invalid while maintaining previous type! */
350  hdrLoc->type = Fragment::ErrorFragmentType;
351 
352  shm_manager_->DoneWritingFragment(header);
353  // throw cet::exception("DataReceiverManager") << "Unexpected return code from receiveFragmentData after receiveFragmentHeader! (Expected: " << ret << ", Got: " << ret2 << ")";
354  continue;
355  }
356 
357  shm_manager_->DoneWritingFragment(header);
358  TLOG(TLVL_DEBUG + 33) << "Done receiving fragment with sequence ID " << header.sequence_id << " from rank " << source_rank;
359 
360  recv_frag_count_.incSlot(source_rank);
361  recv_frag_size_.incSlot(source_rank, header.word_count * sizeof(RawDataType));
362  recv_seq_count_.setSlot(source_rank, header.sequence_id);
363  if (endOfDataCount != static_cast<size_t>(-1))
364  {
365  TLOG(TLVL_DEBUG + 32) << "Received fragment " << header.sequence_id << " from rank " << source_rank
366  << " (" << recv_frag_count_.slotCount(source_rank) << "/" << endOfDataCount << ")";
367  }
368 
369  after_body = std::chrono::steady_clock::now();
370 
371  auto hdr_delta_t = TimeUtils::GetElapsedTime(start_time, after_header);
372  auto store_delta_t = TimeUtils::GetElapsedTime(after_header, before_body);
373  auto data_delta_t = TimeUtils::GetElapsedTime(before_body, after_body);
374  auto delta_t = TimeUtils::GetElapsedTime(start_time, after_body);
375  auto dead_t = TimeUtils::GetElapsedTime(end_time, start_time);
376  auto recv_wait_t = hdr_delta_t - latency;
377 
378  uint64_t data_size = header.word_count * sizeof(RawDataType);
379  auto header_size = header.num_words() * sizeof(RawDataType);
380 
381  if (metricMan)
382  { //&& recv_frag_count_.slotCount(source_rank) % 100 == 0) {
383  TLOG(TLVL_DEBUG + 34) << "runReceiver_: Sending receive stats for rank " << source_rank;
384  metricMan->sendMetric("Total Receive Time From Rank " + std::to_string(source_rank), delta_t, "s", 5, MetricMode::Accumulate);
385  metricMan->sendMetric("Total Receive Size From Rank " + std::to_string(source_rank), data_size, "B", 5, MetricMode::Accumulate);
386  metricMan->sendMetric("Total Receive Rate From Rank " + std::to_string(source_rank), data_size / delta_t, "B/s", 5, MetricMode::Average);
387 
388  metricMan->sendMetric("Header Receive Time From Rank " + std::to_string(source_rank), hdr_delta_t, "s", 5, MetricMode::Accumulate);
389  metricMan->sendMetric("Header Receive Size From Rank " + std::to_string(source_rank), header_size, "B", 5, MetricMode::Accumulate);
390  metricMan->sendMetric("Header Receive Rate From Rank " + std::to_string(source_rank), header_size / hdr_delta_t, "B/s", 5, MetricMode::Average);
391 
392  auto payloadSize = data_size - header_size;
393  metricMan->sendMetric("Data Receive Time From Rank " + std::to_string(source_rank), data_delta_t, "s", 5, MetricMode::Accumulate);
394  metricMan->sendMetric("Data Receive Size From Rank " + std::to_string(source_rank), payloadSize, "B", 5, MetricMode::Accumulate);
395  metricMan->sendMetric("Data Receive Rate From Rank " + std::to_string(source_rank), payloadSize / data_delta_t, "B/s", 5, MetricMode::Average);
396 
397  metricMan->sendMetric("Data Receive Count From Rank " + std::to_string(source_rank), recv_frag_count_.slotCount(source_rank), "fragments", 3, MetricMode::LastPoint);
398 
399  metricMan->sendMetric("Total Shared Memory Wait Time From Rank " + std::to_string(source_rank), store_delta_t, "s", 3, MetricMode::Accumulate);
400  metricMan->sendMetric("Avg Shared Memory Wait Time From Rank " + std::to_string(source_rank), store_delta_t, "s", 3, MetricMode::Average);
401  metricMan->sendMetric("Avg Fragment Wait Time From Rank " + std::to_string(source_rank), dead_t, "s", 3, MetricMode::Average);
402 
403  metricMan->sendMetric("Rank", std::to_string(my_rank), "", 3, MetricMode::LastPoint);
404  metricMan->sendMetric("App Name", app_name, "", 3, MetricMode::LastPoint);
405  metricMan->sendMetric("Fragment Latency at Receive From Rank " + std::to_string(source_rank), latency, "s", 4, MetricMode::Average | MetricMode::Maximum);
406  metricMan->sendMetric("Header Receive Wait Time From Rank" + std::to_string(source_rank), recv_wait_t, "s", 4, MetricMode::Average | MetricMode::Maximum | MetricMode::Minimum);
407 
408  TLOG(TLVL_DEBUG + 34) << "runReceiver_: Done sending receive stats for rank " << source_rank;
409  }
410 
411  end_time = std::chrono::steady_clock::now();
412  }
413  else if (header.type == Fragment::EndOfDataFragmentType || header.type == Fragment::InitFragmentType || header.type == Fragment::EndOfRunFragmentType || header.type == Fragment::EndOfSubrunFragmentType || header.type == Fragment::ShutdownFragmentType)
414  {
415  TLOG(TLVL_DEBUG + 32) << "Received System Fragment from rank " << source_rank << " of type " << detail::RawFragmentHeader::SystemTypeToString(header.type) << ".";
416 
417  FragmentPtr frag(new Fragment(header.word_count - header.num_words()));
418  memcpy(frag->headerAddress(), &header, header.num_words() * sizeof(RawDataType));
419  auto ret3 = source_plugins_[source_rank]->receiveFragmentData(frag->headerAddress() + header.num_words(), header.word_count - header.num_words()); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
420  if (ret3 != source_rank)
421  {
422  TLOG(TLVL_ERROR) << "Unexpected return code from receiveFragmentData after receiveFragmentHeader while receiving System Fragment! (Expected: " << source_rank << ", Got: " << ret3 << ")";
423  throw cet::exception("DataReceiverManager") << "Unexpected return code from receiveFragmentData after receiveFragmentHeader while receiving System Fragment! (Expected: " << source_rank << ", Got: " << ret3 << ")"; // NOLINT(cert-err60-cpp)
424  }
425 
426  switch (header.type)
427  {
428  case Fragment::EndOfDataFragmentType:
429  shm_manager_->setRequestMode(detail::RequestMessageMode::EndOfRun);
430  if (endOfDataCount == static_cast<size_t>(-1))
431  {
432  endOfDataCount = *(frag->dataBegin());
433  }
434  else
435  {
436  endOfDataCount += *(frag->dataBegin());
437  }
438  TLOG(TLVL_DEBUG + 32) << "EndOfData Fragment indicates that " << endOfDataCount << " fragments are expected from rank " << source_rank
439  << " (recvd " << recv_frag_count_.slotCount(source_rank) << ").";
440  break;
441  case Fragment::InitFragmentType:
442  TLOG(TLVL_DEBUG + 32) << "Received Init Fragment from rank " << source_rank << ".";
443  shm_manager_->setRequestMode(detail::RequestMessageMode::Normal);
444  shm_manager_->AddInitFragment(frag);
445  break;
446  case Fragment::EndOfRunFragmentType:
447  shm_manager_->setRequestMode(detail::RequestMessageMode::EndOfRun);
448  // shm_manager_->endRun();
449  break;
450  case Fragment::EndOfSubrunFragmentType:
451  // shm_manager_->setRequestMode(detail::RequestMessageMode::EndOfRun);
452  TLOG(TLVL_DEBUG + 32) << "Received EndOfSubrun Fragment from rank " << source_rank
453  << " with sequence_id " << header.sequence_id << ".";
454  if (header.sequence_id != Fragment::InvalidSequenceID)
455  {
456  shm_manager_->rolloverSubrun(header.sequence_id, header.timestamp);
457  }
458  else
459  {
460  shm_manager_->rolloverSubrun(recv_seq_count_.slotCount(source_rank), header.timestamp);
461  }
462  break;
463  case Fragment::ShutdownFragmentType:
464  shm_manager_->setRequestMode(detail::RequestMessageMode::EndOfRun);
465  break;
466  default:
467  break;
468  }
469  }
470  }
471 
472  source_plugins_[source_rank]->flush_buffers();
473 
474  TLOG(TLVL_DEBUG + 32) << "runReceiver_ " << source_rank << " receive loop exited";
475  running_sources_[source_rank] = false;
476 }
std::set< int > running_sources() const
Get the list of sources which are still receiving data.
End of Run mode (Used to end request processing on receiver)
std::unique_ptr< artdaq::TransferInterface > MakeTransferPlugin(const fhicl::ParameterSet &pset, const std::string &plugin_label, TransferInterface::Role role)
Load a TransferInterface plugin.
This TransferInterface is a Receiver.
DataReceiverManager(const fhicl::ParameterSet &ps, std::shared_ptr< SharedMemoryEventManager > shm)
DataReceiverManager Constructor.
std::set< int > enabled_sources() const
Get the list of enabled sources.
std::vector< fhicl::ParameterSet > MakeHostMapPset(std::map< int, std::string > input)
Create a list of HostMap::HostConfig ParameterSets from a hostMap_t map
Definition: HostMap.hh:49
Value that is to be returned when a Transfer plugin determines that no more data will be arriving...
virtual ~DataReceiverManager()
DataReceiverManager Destructor.
void start_threads()
Start receiver threads for all enabled sources.
std::map< int, std::string > hostMap_t
The host_map is a map associating ranks with artdaq::DestinationInfo objects.
Definition: HostMap.hh:42
hostMap_t MakeHostMap(fhicl::ParameterSet const &pset, hostMap_t map=hostMap_t())
Make a hostMap_t from a HostMap::Config ParameterSet
Definition: HostMap.hh:68
void stop_threads()
Stop receiver threads.