00001
00002 #define TRACE_NAME (app_name + "_SharedMemoryEventManager").c_str()
00003
00004 #include "artdaq/DAQrate/SharedMemoryEventManager.hh"
00005 #include "artdaq-core/Core/StatisticsCollection.hh"
00006 #include "artdaq-core/Utilities/TraceLock.hh"
00007 #include <sys/wait.h>
00008
00009 std::mutex artdaq::SharedMemoryEventManager::sequence_id_mutex_;
00010
00011 artdaq::SharedMemoryEventManager::SharedMemoryEventManager(fhicl::ParameterSet pset, fhicl::ParameterSet art_pset)
00012 : SharedMemoryManager(pset.get<uint32_t>("shared_memory_key", 0xBEE70000 + getpid()),
00013 pset.get<size_t>("buffer_count"),
00014 pset.has_key("max_event_size_bytes") ? pset.get<size_t>("max_event_size_bytes") : pset.get<size_t>("expected_fragments_per_event") * pset.get<size_t>("max_fragment_size_bytes"),
00015 pset.get<size_t>("stale_buffer_timeout_usec", pset.get<size_t>("event_queue_wait_time", 5) * 1000000),
00016 !pset.get<bool>("broadcast_mode", false))
00017 , num_art_processes_(pset.get<size_t>("art_analyzer_count", 1))
00018 , num_fragments_per_event_(pset.get<size_t>("expected_fragments_per_event"))
00019 , queue_size_(pset.get<size_t>("buffer_count"))
00020 , run_id_(0)
00021 , subrun_id_(0)
00022 , subrun_rollover_event_(Fragment::InvalidSequenceID)
00023 , last_released_event_(0)
00024 , update_run_ids_(pset.get<bool>("update_run_ids_on_new_fragment", true))
00025 , use_sequence_id_for_event_number_(pset.get<bool>("use_sequence_id_for_event_number", true))
00026 , overwrite_mode_(!pset.get<bool>("use_art", true) || pset.get<bool>("overwrite_mode", false) || pset.get<bool>("broadcast_mode", false))
00027 , send_init_fragments_(pset.get<bool>("send_init_fragments", true))
00028 , running_(false)
00029 , buffer_writes_pending_()
00030 , incomplete_event_report_interval_ms_(pset.get<int>("incomplete_event_report_interval_ms", -1))
00031 , last_incomplete_event_report_time_(std::chrono::steady_clock::now())
00032 , last_shmem_buffer_metric_update_(std::chrono::steady_clock::now())
00033 , broadcast_timeout_ms_(pset.get<int>("fragment_broadcast_timeout_ms", 3000))
00034 , run_event_count_(0)
00035 , run_incomplete_event_count_(0)
00036 , subrun_event_count_(0)
00037 , subrun_incomplete_event_count_(0)
00038 , oversize_fragment_count_(0)
00039 , maximum_oversize_fragment_count_(pset.get<int>("maximum_oversize_fragment_count", 1))
00040 , art_processes_()
00041 , restart_art_(false)
00042 , always_restart_art_(pset.get<bool>("restart_crashed_art_processes", true))
00043 , manual_art_(pset.get<bool>("manual_art", false))
00044 , current_art_pset_(art_pset)
00045 , minimum_art_lifetime_s_(pset.get<double>("minimum_art_lifetime_s", 2.0))
00046 , art_event_processing_time_us_(pset.get<size_t>("expected_art_event_processing_time_us", 100000))
00047 , requests_(nullptr)
00048 , data_pset_(pset)
00049 , broadcasts_(pset.get<uint32_t>("broadcast_shared_memory_key", 0xCEE70000 + getpid()),
00050 pset.get<size_t>("broadcast_buffer_count", 10),
00051 pset.get<size_t>("broadcast_buffer_size", 0x100000),
00052 pset.get<int>("expected_art_event_processing_time_us", 100000) * pset.get<size_t>("buffer_count"), false)
00053 {
00054 SetMinWriteSize(sizeof(detail::RawEventHeader) + sizeof(detail::RawFragmentHeader));
00055 broadcasts_.SetMinWriteSize(sizeof(detail::RawEventHeader) + sizeof(detail::RawFragmentHeader));
00056
00057 if (pset.get<bool>("use_art", true) == false)
00058 {
00059 TLOG(TLVL_INFO) << "BEGIN SharedMemoryEventManager CONSTRUCTOR with use_art:false";
00060 num_art_processes_ = 0;
00061 }
00062 else
00063 {
00064 TLOG(TLVL_INFO) << "BEGIN SharedMemoryEventManager CONSTRUCTOR with use_art:true";
00065 TLOG(TLVL_TRACE) << "art_pset is " << art_pset.to_string();
00066 }
00067 current_art_config_file_ = std::make_shared<art_config_file>(art_pset);
00068
00069 if (overwrite_mode_ && num_art_processes_ > 0)
00070 {
00071 TLOG(TLVL_WARNING) << "Art is configured to run, but overwrite mode is enabled! Check your configuration if this in unintentional!";
00072 }
00073 else if (overwrite_mode_)
00074 {
00075 TLOG(TLVL_INFO) << "Overwrite Mode enabled, no configured art processes at startup";
00076 }
00077
00078 for (size_t ii = 0; ii < size(); ++ii)
00079 {
00080 buffer_writes_pending_[ii] = 0;
00081 }
00082
00083 if (!IsValid()) throw cet::exception(app_name + "_SharedMemoryEventManager") << "Unable to attach to Shared Memory!";
00084
00085 TLOG(TLVL_TRACE) << "Setting Writer rank to " << my_rank;
00086 SetRank(my_rank);
00087 TLOG(TLVL_DEBUG) << "Writer Rank is " << GetRank();
00088
00089
00090 TLOG(TLVL_TRACE) << "END CONSTRUCTOR";
00091 }
00092
00093 artdaq::SharedMemoryEventManager::~SharedMemoryEventManager()
00094 {
00095 TLOG(TLVL_TRACE) << "DESTRUCTOR";
00096 if (running_) endOfData();
00097 TLOG(TLVL_TRACE) << "Destructor END";
00098 }
00099
00100 bool artdaq::SharedMemoryEventManager::AddFragment(detail::RawFragmentHeader frag, void* dataPtr)
00101 {
00102 TLOG(TLVL_TRACE) << "AddFragment(Header, ptr) BEGIN frag.word_count=" << frag.word_count
00103 << ", sequence_id=" << frag.sequence_id;
00104 auto buffer = getBufferForSequenceID_(frag.sequence_id, true, frag.timestamp);
00105 TLOG(TLVL_TRACE) << "Using buffer " << buffer;
00106 if (buffer == -1) return false;
00107 if (buffer == -2)
00108 {
00109 TLOG(TLVL_ERROR) << "Dropping event because data taking has already passed this event number: " << frag.sequence_id;
00110 return true;
00111 }
00112
00113 auto hdr = getEventHeader_(buffer);
00114 if (update_run_ids_)
00115 {
00116 hdr->run_id = run_id_;
00117 hdr->subrun_id = subrun_id_;
00118 }
00119
00120 TLOG(TLVL_TRACE) << "AddFragment before Write calls";
00121 Write(buffer, dataPtr, frag.word_count * sizeof(RawDataType));
00122
00123 TLOG(TLVL_TRACE) << "Checking for complete event";
00124 auto fragmentCount = GetFragmentCount(frag.sequence_id);
00125 hdr->is_complete = fragmentCount == num_fragments_per_event_ && buffer_writes_pending_[buffer] == 0;
00126 TLOG(TLVL_TRACE) << "hdr->is_complete=" << std::boolalpha << hdr->is_complete
00127 << ", fragmentCount=" << fragmentCount
00128 << ", num_fragments_per_event=" << num_fragments_per_event_
00129 << ", buffer_writes_pending_[buffer]=" << buffer_writes_pending_[buffer];
00130
00131 complete_buffer_(buffer);
00132 if (requests_) requests_->SendRequest(true);
00133
00134 TLOG(TLVL_TRACE) << "AddFragment END";
00135 return true;
00136 }
00137
00138 bool artdaq::SharedMemoryEventManager::AddFragment(FragmentPtr frag, size_t timeout_usec, FragmentPtr& outfrag)
00139 {
00140 TLOG(TLVL_TRACE) << "AddFragment(FragmentPtr) BEGIN";
00141 auto hdr = *reinterpret_cast<detail::RawFragmentHeader*>(frag->headerAddress());
00142 auto data = frag->headerAddress();
00143 auto start = std::chrono::steady_clock::now();
00144 bool sts = false;
00145 while (!sts && TimeUtils::GetElapsedTimeMicroseconds(start) < timeout_usec)
00146 {
00147 sts = AddFragment(hdr, data);
00148 if (!sts) usleep(1000);
00149 }
00150 if (!sts)
00151 {
00152 outfrag = std::move(frag);
00153 }
00154 TLOG(TLVL_TRACE) << "AddFragment(FragmentPtr) RETURN " << std::boolalpha << sts;
00155 return sts;
00156 }
00157
00158 artdaq::RawDataType* artdaq::SharedMemoryEventManager::WriteFragmentHeader(detail::RawFragmentHeader frag, bool dropIfNoBuffersAvailable)
00159 {
00160 TLOG(14) << "WriteFragmentHeader BEGIN";
00161 auto buffer = getBufferForSequenceID_(frag.sequence_id, true, frag.timestamp);
00162
00163 if (buffer < 0)
00164 {
00165 if (buffer == -1 && !dropIfNoBuffersAvailable) return nullptr;
00166 if (buffer == -2)
00167 {
00168 TLOG(TLVL_ERROR) << "Dropping fragment with sequence id " << frag.sequence_id << " and fragment id " << frag.fragment_id << " because data taking has already passed this event.";
00169 }
00170 else
00171 {
00172 TLOG(TLVL_ERROR) << "Dropping fragment with sequence id " << frag.sequence_id << " and fragment id " << frag.fragment_id << " because there is no room in the queue and reliable mode is off.";
00173 }
00174 dropped_data_.reset(new Fragment(frag.word_count - frag.num_words()));
00175 return dropped_data_->dataBegin();
00176 }
00177
00178 if (metricMan)
00179 {
00180 metricMan->sendMetric("Input Fragment Rate", 1, "Fragments/s", 1, MetricMode::Rate);
00181 }
00182
00183 buffer_writes_pending_[buffer]++;
00184 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00185
00186 auto hdrpos = reinterpret_cast<RawDataType*>(GetWritePos(buffer));
00187 Write(buffer, &frag, frag.num_words() * sizeof(RawDataType));
00188
00189 auto pos = reinterpret_cast<RawDataType*>(GetWritePos(buffer));
00190 if (frag.word_count - frag.num_words() > 0)
00191 {
00192 auto sts = IncrementWritePos(buffer, (frag.word_count - frag.num_words()) * sizeof(RawDataType));
00193
00194 if (!sts)
00195 {
00196 reinterpret_cast<detail::RawFragmentHeader*>(hdrpos)->word_count = frag.num_words();
00197 reinterpret_cast<detail::RawFragmentHeader*>(hdrpos)->type = Fragment::InvalidFragmentType;
00198 TLOG(TLVL_ERROR) << "Dropping fragment with sequence id " << frag.sequence_id << " and fragment id " << frag.fragment_id << " because there is no room in the current buffer for this Fragment! (Keeping header)";
00199 dropped_data_.reset(new Fragment(frag.word_count - frag.num_words()));
00200
00201 oversize_fragment_count_++;
00202
00203 if (maximum_oversize_fragment_count_ > 0 && oversize_fragment_count_ >= maximum_oversize_fragment_count_)
00204 {
00205 throw cet::exception("Too many over-size Fragments received! Please adjust max_event_size_bytes or max_fragment_size_bytes!");
00206 }
00207
00208 return dropped_data_->dataBegin();
00209 }
00210 }
00211 TLOG(14) << "WriteFragmentHeader END";
00212 return pos;
00213
00214 }
00215
00216 void artdaq::SharedMemoryEventManager::DoneWritingFragment(detail::RawFragmentHeader frag)
00217 {
00218 TLOG(TLVL_TRACE) << "DoneWritingFragment BEGIN";
00219 auto buffer = getBufferForSequenceID_(frag.sequence_id, false, frag.timestamp);
00220 if (buffer == -1) Detach(true, "SharedMemoryEventManager", "getBufferForSequenceID_ returned -1 when it REALLY shouldn't have! Check program logic!");
00221 if (buffer == -2) return;
00222 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
00223
00224
00225 TLOG(TLVL_DEBUG) << "DoneWritingFragment: Received Fragment with sequence ID " << frag.sequence_id << " and fragment id " << frag.fragment_id << " (type " << (int)frag.type << ")";
00226 auto hdr = getEventHeader_(buffer);
00227 if (update_run_ids_)
00228 {
00229 hdr->run_id = run_id_;
00230 hdr->subrun_id = subrun_id_;
00231 }
00232
00233 buffer_writes_pending_[buffer]--;
00234 if (buffer_writes_pending_[buffer] != 0)
00235 {
00236 TLOG(TLVL_TRACE) << "Done writing fragment, but there's another writer. Not doing bookkeeping steps.";
00237 return;
00238 }
00239 auto frag_count = GetFragmentCount(frag.sequence_id);
00240 hdr->is_complete = frag_count == num_fragments_per_event_;
00241 #if ART_SUPPORTS_DUPLICATE_EVENTS
00242 if (!hdr->is_complete && released_incomplete_events_.count(frag.sequence_id))
00243 {
00244 hdr->is_complete = frag_count == released_incomplete_events_[frag.sequence_id] && buffer_writes_pending_[buffer] == 0;
00245 }
00246 #endif
00247
00248 complete_buffer_(buffer);
00249 if (requests_) requests_->SendRequest(true);
00250 TLOG(TLVL_TRACE) << "DoneWritingFragment END";
00251 }
00252
00253 size_t artdaq::SharedMemoryEventManager::GetFragmentCount(Fragment::sequence_id_t seqID, Fragment::type_t type)
00254 {
00255 return GetFragmentCountInBuffer(getBufferForSequenceID_(seqID, false), type);
00256 }
00257
00258 size_t artdaq::SharedMemoryEventManager::GetFragmentCountInBuffer(int buffer, Fragment::type_t type)
00259 {
00260 if (buffer == -1) return 0;
00261 ResetReadPos(buffer);
00262 IncrementReadPos(buffer, sizeof(detail::RawEventHeader));
00263
00264 size_t count = 0;
00265
00266 while (MoreDataInBuffer(buffer))
00267 {
00268 auto fragHdr = reinterpret_cast<artdaq::detail::RawFragmentHeader*>(GetReadPos(buffer));
00269 IncrementReadPos(buffer, fragHdr->word_count * sizeof(RawDataType));
00270 if (type != Fragment::InvalidFragmentType && fragHdr->type != type) continue;
00271 TLOG(TLVL_TRACE) << "Adding Fragment with size=" << fragHdr->word_count << " to Fragment count";
00272 ++count;
00273 }
00274
00275 return count;
00276 }
00277
00278 void artdaq::SharedMemoryEventManager::RunArt(std::shared_ptr<art_config_file> config_file, pid_t& pid_out)
00279 {
00280 do
00281 {
00282 auto start_time = std::chrono::steady_clock::now();
00283 send_init_frag_();
00284 TLOG(TLVL_INFO) << "Starting art process with config file " << config_file->getFileName();
00285
00286 char* filename = new char[config_file->getFileName().length() + 1];
00287 strcpy(filename, config_file->getFileName().c_str());
00288
00289 std::vector<char*> args{ (char*)"art", (char*)"-c", filename, NULL };
00290 pid_t pid = 0;
00291
00292 if (!manual_art_)
00293 {
00294 pid = fork();
00295 if (pid == 0)
00296 {
00297
00298
00299
00300
00301 std::string envVarKey = "ARTDAQ_PARTITION_NUMBER";
00302 std::string envVarValue = std::to_string(Globals::GetPartitionNumber());
00303 if (setenv(envVarKey.c_str(), envVarValue.c_str(), 1) != 0)
00304 {
00305 TLOG(TLVL_ERROR) << "Error setting environment variable \"" << envVarKey
00306 << "\" in the environment of a child art process. "
00307 << "This may result in incorrect TCP port number "
00308 << "assignments or other issues, and data may "
00309 << "not flow through the system correctly.";
00310 }
00311
00312 execvp("art", &args[0]);
00313 delete[] filename;
00314 exit(1);
00315 }
00316 delete[] filename;
00317 }
00318 else
00319 {
00320
00321 std::cout << "Please run the following command in a separate terminal:" << std::endl
00322 << "art -c " << config_file->getFileName() << std::endl
00323 << "Then, in a third terminal, execute: \"ps aux|grep [a]rt -c " << config_file->getFileName() << "\" and note the PID of the art process." << std::endl
00324 << "Finally, return to this window and enter the pid: " << std::endl;
00325 std::cin >> pid;
00326 }
00327 pid_out = pid;
00328
00329 TLOG(TLVL_INFO) << "PID of new art process is " << pid;
00330 art_processes_.insert(pid);
00331 siginfo_t status;
00332 auto sts = waitid(P_PID, pid, &status, WEXITED);
00333 TLOG(TLVL_INFO) << "Removing PID " << pid << " from process list";
00334 art_processes_.erase(pid);
00335 if (sts < 0)
00336 {
00337 TLOG(TLVL_WARNING) << "Error occurred in waitid for art process " << pid << ": " << errno << " (" << strerror(errno) << ").";
00338 }
00339 else if (status.si_code == CLD_EXITED && status.si_status == 0)
00340 {
00341 TLOG(TLVL_INFO) << "art process " << pid << " exited normally, " << (restart_art_ ? "restarting" : "not restarting");
00342 }
00343 else
00344 {
00345 auto art_lifetime = TimeUtils::GetElapsedTime(start_time);
00346 if (art_lifetime < minimum_art_lifetime_s_) restart_art_ = false;
00347
00348 auto exit_type = "exited with status code";
00349 switch (status.si_code)
00350 {
00351 case CLD_DUMPED:
00352 case CLD_KILLED:
00353 exit_type = "was killed with signal";
00354 break;
00355 case CLD_EXITED:
00356 default:
00357 break;
00358 }
00359
00360 TLOG((restart_art_ ? TLVL_WARNING : TLVL_ERROR))
00361 << "art process " << pid << " " << exit_type << " " << status.si_status
00362 << (status.si_code == CLD_DUMPED ? " (core dumped)" : "")
00363 << " after " << std::setprecision(2) << art_lifetime << " seconds, "
00364 << (restart_art_ ? "restarting" : "not restarting");
00365 }
00366 } while (restart_art_);
00367 }
00368
00369 void artdaq::SharedMemoryEventManager::StartArt()
00370 {
00371 restart_art_ = always_restart_art_;
00372 if (num_art_processes_ == 0) return;
00373 for (size_t ii = 0; ii < num_art_processes_; ++ii)
00374 {
00375 StartArtProcess(current_art_pset_);
00376 }
00377 }
00378
00379 pid_t artdaq::SharedMemoryEventManager::StartArtProcess(fhicl::ParameterSet pset)
00380 {
00381 static std::mutex start_art_mutex;
00382 std::unique_lock<std::mutex> lk(start_art_mutex);
00383
00384 restart_art_ = always_restart_art_;
00385 auto initialCount = GetAttachedCount();
00386 auto startTime = std::chrono::steady_clock::now();
00387
00388 if (pset != current_art_pset_ || !current_art_config_file_)
00389 {
00390 current_art_pset_ = pset;
00391 current_art_config_file_ = std::make_shared<art_config_file>(pset);
00392 }
00393 pid_t pid = -1;
00394 boost::thread thread([&] { RunArt(current_art_config_file_, pid); });
00395 thread.detach();
00396
00397 auto currentCount = GetAttachedCount() - initialCount;
00398 while ((currentCount < 1 || pid <= 0) && (TimeUtils::GetElapsedTime(startTime) < 5 || manual_art_))
00399 {
00400 usleep(10000);
00401 currentCount = GetAttachedCount() - initialCount;
00402 }
00403 if ((currentCount < 1 || pid <= 0) && manual_art_)
00404 {
00405 TLOG(TLVL_WARNING) << "Manually-started art process has not connected to shared memory or has bad PID: connected:" << currentCount << ", PID:" << pid;
00406 return 0;
00407 }
00408 else if (currentCount < 1 || pid <= 0)
00409 {
00410 TLOG(TLVL_WARNING) << "art process has not started after 5s. Check art configuration!"
00411 << " (pid=" << pid << ", attachedCount=" << currentCount << ")";
00412 return 0;
00413 }
00414 else
00415 {
00416 TLOG(TLVL_INFO) << std::setw(4) << std::fixed << "art initialization took "
00417 << TimeUtils::GetElapsedTime(startTime) << " seconds.";
00418
00419 return pid;
00420 }
00421
00422 }
00423
00424 void artdaq::SharedMemoryEventManager::ShutdownArtProcesses(std::set<pid_t> pids)
00425 {
00426 restart_art_ = false;
00427
00428
00429
00430 auto check_pids = [&](bool print) {
00431
00432 for (auto pid = pids.begin(); pid != pids.end();)
00433 {
00434
00435 if (*pid <= 0)
00436 {
00437 TLOG(TLVL_WARNING) << "Removing an invalid PID (" << *pid
00438 << ") from the shutdown list.";
00439 pid = pids.erase(pid);
00440 }
00441 else if (kill(*pid, 0) < 0)
00442 {
00443 pid = pids.erase(pid);
00444 }
00445 else
00446 {
00447 if (print) std::cout << *pid << " ";
00448 ++pid;
00449 }
00450 }
00451 };
00452 check_pids(false);
00453 if (pids.size() == 0)
00454 {
00455 TLOG(14) << "All art processes already exited, nothing to do.";
00456 usleep(1000);
00457 return;
00458 }
00459
00460 if (!manual_art_)
00461 {
00462 TLOG(TLVL_TRACE) << "Gently informing art processes that it is time to shut down";
00463 for (auto pid : pids)
00464 {
00465 TLOG(TLVL_TRACE) << "Sending SIGQUIT to pid " << pid;
00466 kill(pid, SIGQUIT);
00467 }
00468
00469 int graceful_wait_ms = 5000;
00470 int int_wait_ms = 1000;
00471
00472 TLOG(TLVL_TRACE) << "Waiting up to " << graceful_wait_ms << " ms for all art processes to exit gracefully";
00473 for (int ii = 0; ii < graceful_wait_ms; ++ii)
00474 {
00475 usleep(1000);
00476
00477 check_pids(false);
00478 if (pids.size() == 0)
00479 {
00480 TLOG(TLVL_TRACE) << "All art processes exited after " << ii << " ms.";
00481 return;
00482 }
00483 }
00484
00485 TLOG(TLVL_TRACE) << "Insisting that the art processes shut down";
00486 for (auto pid : pids)
00487 {
00488 kill(pid, SIGINT);
00489 }
00490
00491 TLOG(TLVL_TRACE) << "Waiting up to " << int_wait_ms << " ms for all art processes to exit";
00492 for (int ii = graceful_wait_ms; ii < graceful_wait_ms + int_wait_ms; ++ii)
00493 {
00494 usleep(1000);
00495
00496 check_pids(false);
00497
00498 if (pids.size() == 0)
00499 {
00500 TLOG(TLVL_TRACE) << "All art processes exited after " << ii << " ms.";
00501 return;
00502 }
00503 }
00504
00505 TLOG(TLVL_TRACE) << "Killing remaning art processes with extreme prejudice";
00506 while (pids.size() > 0)
00507 {
00508 kill(*pids.begin(), SIGKILL);
00509 usleep(1000);
00510
00511 check_pids(false);
00512 }
00513 }
00514 else
00515 {
00516 std::cout << "Please shut down all art processes, then hit return/enter" << std::endl;
00517 while (pids.size() > 0)
00518 {
00519 std::cout << "The following PIDs are running: ";
00520 check_pids(true);
00521 std::cout << std::endl;
00522 std::string ignored;
00523 std::cin >> ignored;
00524 }
00525 }
00526 }
00527
00528 void artdaq::SharedMemoryEventManager::ReconfigureArt(fhicl::ParameterSet art_pset, run_id_t newRun, int n_art_processes)
00529 {
00530 TLOG(TLVL_DEBUG) << "ReconfigureArt BEGIN";
00531 if (restart_art_ || !always_restart_art_)
00532 {
00533 endOfData();
00534 }
00535 for (size_t ii = 0; ii < broadcasts_.size(); ++ii)
00536 {
00537 broadcasts_.MarkBufferEmpty(ii, true);
00538 }
00539 if (newRun == 0) newRun = run_id_ + 1;
00540
00541 if (art_pset != current_art_pset_ || !current_art_config_file_)
00542 {
00543 current_art_pset_ = art_pset;
00544 current_art_config_file_ = std::make_shared<art_config_file>(art_pset);
00545 }
00546
00547 if (n_art_processes != -1)
00548 {
00549 TLOG(TLVL_INFO) << "Setting number of art processes to " << n_art_processes;
00550 num_art_processes_ = n_art_processes;
00551 }
00552 startRun(newRun);
00553 TLOG(TLVL_DEBUG) << "ReconfigureArt END";
00554 }
00555
00556 bool artdaq::SharedMemoryEventManager::endOfData()
00557 {
00558 init_fragment_.reset(nullptr);
00559 TLOG(TLVL_TRACE) << "SharedMemoryEventManager::endOfData";
00560 restart_art_ = false;
00561
00562 size_t initialStoreSize = GetIncompleteEventCount();
00563 TLOG(TLVL_TRACE) << "endOfData: Flushing " << initialStoreSize
00564 << " stale events from the SharedMemoryEventManager.";
00565 int counter = initialStoreSize;
00566 while (active_buffers_.size() > 0 && counter > 0)
00567 {
00568 complete_buffer_(*active_buffers_.begin());
00569 counter--;
00570 }
00571 TLOG(TLVL_TRACE) << "endOfData: Done flushing, there are now " << GetIncompleteEventCount()
00572 << " stale events in the SharedMemoryEventManager.";
00573
00574
00575 TLOG(TLVL_TRACE) << "Waiting for " << (ReadReadyCount() + (size() - WriteReadyCount(overwrite_mode_))) << " outstanding buffers...";
00576 auto start = std::chrono::steady_clock::now();
00577 auto lastReadCount = ReadReadyCount() + (size() - WriteReadyCount(overwrite_mode_));
00578 auto end_of_data_wait_us = art_event_processing_time_us_ * size();
00579
00580
00581 while (lastReadCount > 0 && (end_of_data_wait_us == 0 || TimeUtils::GetElapsedTimeMicroseconds(start) < end_of_data_wait_us) && art_processes_.size() > 0)
00582 {
00583 auto temp = ReadReadyCount() + (size() - WriteReadyCount(overwrite_mode_));
00584 if (temp != lastReadCount)
00585 {
00586 TLOG(TLVL_TRACE) << "Waiting for " << temp << " outstanding buffers...";
00587 lastReadCount = temp;
00588 start = std::chrono::steady_clock::now();
00589 }
00590 if (lastReadCount > 0) usleep(art_event_processing_time_us_);
00591 }
00592 TLOG(TLVL_TRACE) << "endOfData: After wait for outstanding buffers. Still outstanding: " << lastReadCount << ", time waited: " << TimeUtils::GetElapsedTime(start) << " s / " << (end_of_data_wait_us / 1000000.0) << " s, art process count: " << art_processes_.size();
00593
00594 TLOG(TLVL_TRACE) << "endOfData: Broadcasting EndOfData Fragment";
00595 FragmentPtr outFrag = Fragment::eodFrag(GetBufferCount());
00596 bool success = broadcastFragment_(std::move(outFrag), outFrag);
00597 if (!success)
00598 {
00599 TLOG(TLVL_TRACE) << "endOfData: Clearing buffers to make room for EndOfData Fragment";
00600 for (size_t ii = 0; ii < broadcasts_.size(); ++ii)
00601 {
00602 broadcasts_.MarkBufferEmpty(ii, true);
00603 }
00604 broadcastFragment_(std::move(outFrag), outFrag);
00605 }
00606 auto endOfDataProcessingStart = std::chrono::steady_clock::now();
00607
00608 if (art_processes_.size() > 0)
00609 {
00610 TLOG(TLVL_DEBUG) << "Allowing " << art_processes_.size() << " art processes the chance to end gracefully";
00611 if (end_of_data_wait_us == 0)
00612 {
00613 TLOG(TLVL_DEBUG) << "Expected art event processing time not specified. Waiting up to 100s for art to end gracefully.";
00614 end_of_data_wait_us = 100 * 1000000;
00615 }
00616
00617 auto sleep_count = (end_of_data_wait_us / 10000) + 1;
00618 for (size_t ii = 0; ii < sleep_count; ++ii)
00619 {
00620 usleep(10000);
00621 if (art_processes_.size() == 0) break;
00622 }
00623 }
00624
00625 while (art_processes_.size() > 0)
00626 {
00627 TLOG(TLVL_DEBUG) << "There are " << art_processes_.size() << " art processes remaining. Proceeding to shutdown.";
00628 ShutdownArtProcesses(art_processes_);
00629 }
00630 TLOG(TLVL_INFO) << "It took " << TimeUtils::GetElapsedTime(endOfDataProcessingStart) << " s for all art processes to close after sending EndOfData Fragment";
00631
00632 ResetAttachedCount();
00633
00634 TLOG(TLVL_TRACE) << "endOfData: Clearing buffers";
00635 for (size_t ii = 0; ii < size(); ++ii)
00636 {
00637 MarkBufferEmpty(ii, true);
00638 }
00639
00640
00641
00642
00643
00644
00645 released_incomplete_events_.clear();
00646
00647 TLOG(TLVL_TRACE) << "endOfData: Shutting down RequestReceiver";
00648 requests_.reset(nullptr);
00649
00650 TLOG(TLVL_TRACE) << "endOfData END";
00651 TLOG(TLVL_INFO) << "EndOfData Complete. There were " << GetLastSeenBufferID() << " buffers processed.";
00652 running_ = false;
00653 return true;
00654 }
00655
00656 void artdaq::SharedMemoryEventManager::startRun(run_id_t runID)
00657 {
00658 running_ = true;
00659 init_fragment_.reset(nullptr);
00660 TLOG(TLVL_TRACE) << "startRun: Clearing broadcast buffers";
00661 for (size_t ii = 0; ii < broadcasts_.size(); ++ii)
00662 {
00663 broadcasts_.MarkBufferEmpty(ii, true);
00664 }
00665 StartArt();
00666 run_id_ = runID;
00667 subrun_id_ = 1;
00668 subrun_rollover_event_ = Fragment::InvalidSequenceID;
00669 last_released_event_ = 0;
00670 requests_.reset(new RequestSender(data_pset_));
00671 if (requests_) requests_->SendRoutingToken(queue_size_);
00672 TLOG(TLVL_DEBUG) << "Starting run " << run_id_
00673 << ", max queue size = "
00674 << queue_size_
00675 << ", queue size = "
00676 << GetLockedBufferCount();
00677 if (metricMan)
00678 {
00679 double runSubrun = run_id_ + ((double)subrun_id_ / 10000);
00680 metricMan->sendMetric("Run Number", runSubrun, "Run:Subrun", 1, MetricMode::LastPoint);
00681 }
00682 }
00683
00684 void artdaq::SharedMemoryEventManager::startSubrun()
00685 {
00686 ++subrun_id_;
00687 subrun_rollover_event_ = Fragment::InvalidSequenceID;
00688 if (metricMan)
00689 {
00690 double runSubrun = run_id_ + ((double)subrun_id_ / 10000);
00691 metricMan->sendMetric("Run Number", runSubrun, "Run:Subrun", 1, MetricMode::LastPoint);
00692 }
00693 }
00694
00695 bool artdaq::SharedMemoryEventManager::endRun()
00696 {
00697 TLOG(TLVL_INFO) << "Ending run " << run_id_;
00698 FragmentPtr endOfRunFrag(new
00699 Fragment(static_cast<size_t>
00700 (ceil(sizeof(my_rank) /
00701 static_cast<double>(sizeof(Fragment::value_type))))));
00702
00703 TLOG(TLVL_DEBUG) << "Broadcasting EndOfRun Fragment";
00704 endOfRunFrag->setSystemType(Fragment::EndOfRunFragmentType);
00705 *endOfRunFrag->dataBegin() = my_rank;
00706 broadcastFragment_(std::move(endOfRunFrag), endOfRunFrag);
00707
00708 TLOG(TLVL_INFO) << "Run " << run_id_ << " has ended. There were " << run_event_count_ << " events in this run.";
00709 run_event_count_ = 0;
00710 run_incomplete_event_count_ = 0;
00711 oversize_fragment_count_ = 0;
00712 return true;
00713 }
00714
00715 bool artdaq::SharedMemoryEventManager::endSubrun()
00716 {
00717 TLOG(TLVL_INFO) << "Ending subrun " << subrun_id_;
00718 std::unique_ptr<artdaq::Fragment>
00719 endOfSubrunFrag(new
00720 Fragment(static_cast<size_t>
00721 (ceil(sizeof(my_rank) /
00722 static_cast<double>(sizeof(Fragment::value_type))))));
00723
00724 TLOG(TLVL_DEBUG) << "Broadcasting EndOfSubrun Fragment";
00725 endOfSubrunFrag->setSequenceID(subrun_rollover_event_);
00726 endOfSubrunFrag->setSystemType(Fragment::EndOfSubrunFragmentType);
00727 *endOfSubrunFrag->dataBegin() = my_rank;
00728
00729 broadcastFragment_(std::move(endOfSubrunFrag), endOfSubrunFrag);
00730
00731 TLOG(TLVL_INFO) << "Subrun " << subrun_id_ << " in run " << run_id_ << " has ended. There were " << subrun_event_count_ << " events in this subrun.";
00732 subrun_event_count_ = 0;
00733 subrun_incomplete_event_count_ = 0;
00734
00735 return true;
00736 }
00737
00738 void artdaq::SharedMemoryEventManager::rolloverSubrun(sequence_id_t boundary)
00739 {
00740
00741 if (boundary == 0 || boundary == Fragment::InvalidSequenceID) return;
00742
00743 if (boundary < last_released_event_)
00744 {
00745 auto logLevel = TLVL_ERROR;
00746 bool processAnyway = false;
00747 if (last_released_event_ - boundary < 100)
00748 {
00749 logLevel = TLVL_WARNING;
00750 processAnyway = true;
00751 }
00752 TLOG(logLevel) << "Subrun rollover requested for event that is in the past. (delta = " << (last_released_event_ - boundary) << ").";
00753 if (!processAnyway) return;
00754 }
00755 TLOG(TLVL_INFO) << "Will roll over when I reach Sequence ID " << boundary;
00756
00757
00758
00759
00760
00761
00762
00763
00764 if (boundary == last_released_event_ + 1) {
00765 TLOG(TLVL_INFO) << "rolloverSubrun: Last released event had sequence id " << last_released_event_ << \
00766 ", boundary is sequence id " << boundary << ", so will start a new subrun here";
00767 endSubrun();
00768 startSubrun();
00769 subrun_rollover_event_ = std::numeric_limits<sequence_id_t>::max();
00770 }
00771 else {
00772 subrun_rollover_event_ = boundary;
00773 }
00774 }
00775
00776 void artdaq::SharedMemoryEventManager::sendMetrics()
00777 {
00778 if (metricMan)
00779 {
00780 metricMan->sendMetric("Incomplete Event Count", GetIncompleteEventCount(), "events", 1, MetricMode::LastPoint);
00781 metricMan->sendMetric("Pending Event Count", GetPendingEventCount(), "events", 1, MetricMode::LastPoint);
00782 }
00783
00784 if (incomplete_event_report_interval_ms_ > 0 && GetLockedBufferCount())
00785 {
00786 if (TimeUtils::GetElapsedTimeMilliseconds(last_incomplete_event_report_time_) < static_cast<size_t>(incomplete_event_report_interval_ms_))
00787 return;
00788
00789 last_incomplete_event_report_time_ = std::chrono::steady_clock::now();
00790 std::ostringstream oss;
00791 oss << "Incomplete Events (" << num_fragments_per_event_ << "): ";
00792 for (auto& ev : active_buffers_)
00793 {
00794 auto hdr = getEventHeader_(ev);
00795 oss << hdr->sequence_id << " (" << GetFragmentCount(hdr->sequence_id) << "), ";
00796 }
00797 TLOG(TLVL_DEBUG) << oss.str();
00798 }
00799 }
00800
00801 bool artdaq::SharedMemoryEventManager::broadcastFragment_(FragmentPtr frag, FragmentPtr& outFrag)
00802 {
00803 TLOG(TLVL_DEBUG) << "Broadcasting Fragment with seqID=" << frag->sequenceID() << ", type " << detail::RawFragmentHeader::SystemTypeToString(frag->type()) << ", size=" << frag->sizeBytes() << "B.";
00804 auto buffer = broadcasts_.GetBufferForWriting(false);
00805 TLOG(TLVL_DEBUG) << "broadcastFragment_: after getting buffer 1st buffer=" << buffer;
00806 auto start_time = std::chrono::steady_clock::now();
00807 while (buffer == -1 && TimeUtils::GetElapsedTimeMilliseconds(start_time) < static_cast<size_t>(broadcast_timeout_ms_))
00808 {
00809 usleep(10000);
00810 buffer = broadcasts_.GetBufferForWriting(false);
00811 }
00812 TLOG(TLVL_DEBUG) << "broadcastFragment_: after getting buffer w/timeout, buffer=" << buffer << ", elapsed time=" << TimeUtils::GetElapsedTime(start_time) << " s.";
00813 if (buffer == -1)
00814 {
00815 TLOG(TLVL_ERROR) << "Broadcast of fragment type " << frag->typeString() << " failed due to timeout waiting for buffer!";
00816 outFrag.swap(frag);
00817 return false;
00818 }
00819
00820 TLOG(TLVL_DEBUG) << "broadcastFragment_: Filling in RawEventHeader";
00821 auto hdr = reinterpret_cast<detail::RawEventHeader*>(broadcasts_.GetBufferStart(buffer));
00822 hdr->run_id = run_id_;
00823 hdr->subrun_id = subrun_id_;
00824 hdr->sequence_id = frag->sequenceID();
00825 hdr->is_complete = true;
00826 broadcasts_.IncrementWritePos(buffer, sizeof(detail::RawEventHeader));
00827
00828 TLOG(TLVL_DEBUG) << "broadcastFragment_ before Write calls";
00829 broadcasts_.Write(buffer, frag->headerAddress(), frag->size() * sizeof(RawDataType));
00830
00831 TLOG(TLVL_DEBUG) << "broadcastFragment_ Marking buffer full";
00832 broadcasts_.MarkBufferFull(buffer, -1);
00833 outFrag.swap(frag);
00834 TLOG(TLVL_DEBUG) << "broadcastFragment_ Complete";
00835 return true;
00836 }
00837
00838 artdaq::detail::RawEventHeader* artdaq::SharedMemoryEventManager::getEventHeader_(int buffer)
00839 {
00840 return reinterpret_cast<detail::RawEventHeader*>(GetBufferStart(buffer));
00841 }
00842
00843 int artdaq::SharedMemoryEventManager::getBufferForSequenceID_(Fragment::sequence_id_t seqID, bool create_new, Fragment::timestamp_t timestamp)
00844 {
00845 std::unique_lock<std::mutex> lk(sequence_id_mutex_);
00846 TLOG(14) << "getBufferForSequenceID " << seqID << " BEGIN";
00847 auto buffers = GetBuffersOwnedByManager();
00848 for (auto& buf : buffers)
00849 {
00850 auto hdr = getEventHeader_(buf);
00851 if (hdr->sequence_id == seqID)
00852 {
00853 TLOG(14) << "getBufferForSequenceID " << seqID << " returning " << buf;
00854 return buf;
00855 }
00856 }
00857
00858 #if !ART_SUPPORTS_DUPLICATE_EVENTS
00859 if (released_incomplete_events_.count(seqID))
00860 {
00861 TLOG(TLVL_ERROR) << "Event " << seqID << " has already been marked \"Incomplete\" and sent to art!";
00862 return -2;
00863 }
00864 #endif
00865
00866 if (!create_new) return -1;
00867
00868 check_pending_buffers_(lk);
00869 int new_buffer = GetBufferForWriting(false);
00870
00871 if (new_buffer == -1)
00872 {
00873 new_buffer = GetBufferForWriting(overwrite_mode_);
00874 }
00875
00876 if (new_buffer == -1) return -1;
00877 std::unique_lock<std::mutex> buffer_lk(buffer_mutexes_[new_buffer]);
00878
00879 auto hdr = getEventHeader_(new_buffer);
00880 hdr->is_complete = false;
00881 hdr->run_id = run_id_;
00882 hdr->subrun_id = subrun_id_;
00883 hdr->event_id = use_sequence_id_for_event_number_ ? static_cast<uint32_t>(seqID) : static_cast<uint32_t>(timestamp);
00884 hdr->sequence_id = seqID;
00885 buffer_writes_pending_[new_buffer] = 0;
00886 IncrementWritePos(new_buffer, sizeof(detail::RawEventHeader));
00887 SetMFIteration("Sequence ID " + std::to_string(seqID));
00888
00889 active_buffers_.insert(new_buffer);
00890
00891 if (requests_)
00892 {
00893 if (timestamp != Fragment::InvalidTimestamp)
00894 {
00895 requests_->AddRequest(seqID, timestamp);
00896 }
00897 requests_->SendRequest();
00898 }
00899 TLOG(14) << "getBufferForSequenceID " << seqID << " returning newly initialized buffer " << new_buffer;
00900 return new_buffer;
00901 }
00902
00903 bool artdaq::SharedMemoryEventManager::hasFragments_(int buffer)
00904 {
00905 if (buffer == -1) return true;
00906 if (!CheckBuffer(buffer, BufferSemaphoreFlags::Writing))
00907 {
00908 return true;
00909 }
00910 ResetReadPos(buffer);
00911 IncrementReadPos(buffer, sizeof(detail::RawEventHeader));
00912 return MoreDataInBuffer(buffer);
00913 }
00914
00915 void artdaq::SharedMemoryEventManager::complete_buffer_(int buffer)
00916 {
00917 auto hdr = getEventHeader_(buffer);
00918 if (hdr->is_complete)
00919 {
00920 TLOG(TLVL_DEBUG) << "complete_buffer_: This fragment completes event " << hdr->sequence_id << ".";
00921
00922 if (requests_)
00923 {
00924 requests_->RemoveRequest(hdr->sequence_id);
00925 requests_->SendRoutingToken(1);
00926 }
00927 {
00928 std::unique_lock<std::mutex> lk(sequence_id_mutex_);
00929 active_buffers_.erase(buffer);
00930 pending_buffers_.insert(buffer);
00931 }
00932 }
00933 check_pending_buffers_();
00934 }
00935
00936 bool artdaq::SharedMemoryEventManager::bufferComparator(int bufA, int bufB)
00937 {
00938 return getEventHeader_(bufA)->sequence_id < getEventHeader_(bufB)->sequence_id;
00939 }
00940
00941 void artdaq::SharedMemoryEventManager::check_pending_buffers_(std::unique_lock<std::mutex> const& lock)
00942 {
00943 TLOG(TLVL_TRACE) << "check_pending_buffers_ BEGIN Locked=" << std::boolalpha << lock.owns_lock();
00944
00945 auto buffers = GetBuffersOwnedByManager();
00946 for (auto buf : buffers)
00947 {
00948 if (ResetBuffer(buf) && !pending_buffers_.count(buf))
00949 {
00950 auto hdr = getEventHeader_(buf);
00951 if (active_buffers_.count(buf))
00952 {
00953 if (requests_)
00954 {
00955 requests_->RemoveRequest(hdr->sequence_id);
00956 requests_->SendRoutingToken(1);
00957 }
00958 active_buffers_.erase(buf);
00959 pending_buffers_.insert(buf);
00960 subrun_incomplete_event_count_++;
00961 run_incomplete_event_count_++;
00962 if (metricMan) metricMan->sendMetric("Incomplete Event Rate", 1, "events/s", 3, MetricMode::Rate);
00963 if (!released_incomplete_events_.count(hdr->sequence_id))
00964 {
00965 released_incomplete_events_[hdr->sequence_id] = num_fragments_per_event_ - GetFragmentCountInBuffer(buf);
00966 }
00967 else
00968 {
00969 released_incomplete_events_[hdr->sequence_id] -= GetFragmentCountInBuffer(buf);
00970 }
00971 TLOG(TLVL_WARNING) << "Active event " << hdr->sequence_id << " is stale. Scheduling release of incomplete event (missing " << released_incomplete_events_[hdr->sequence_id] << " Fragments) to art.";
00972 }
00973
00974 }
00975 }
00976
00977 Fragment::sequence_id_t lowestSeqId = Fragment::InvalidSequenceID;
00978
00979
00980 if (ReadyForWrite(false))
00981 {
00982 for (auto buf : active_buffers_)
00983 {
00984 auto hdr = getEventHeader_(buf);
00985 TLOG(TLVL_TRACE) << "Buffer: " << buf << ", SeqID: " << hdr->sequence_id << ", ACTIVE";
00986 if (hdr->sequence_id < lowestSeqId)
00987 {
00988 lowestSeqId = hdr->sequence_id;
00989 }
00990 }
00991 TLOG(TLVL_TRACE) << "Lowest SeqID held: " << lowestSeqId;
00992 }
00993
00994 std::list<int> sorted_buffers(pending_buffers_.begin(), pending_buffers_.end());
00995 sorted_buffers.sort([this](int a, int b) { return bufferComparator(a, b); });
00996
00997 auto counter = 0;
00998 double eventSize = 0;
00999 for (auto buf : sorted_buffers)
01000 {
01001 auto hdr = getEventHeader_(buf);
01002 if (hdr->sequence_id > lowestSeqId) break;
01003
01004 if (hdr->sequence_id >= subrun_rollover_event_)
01005 {
01006 TLOG(TLVL_INFO) << "Subrun rollover reached at event " << hdr->sequence_id << " (boundary=" << subrun_rollover_event_ << "), last released event is " << last_released_event_ << ".";
01007 endSubrun();
01008 startSubrun();
01009 }
01010 if (hdr->sequence_id > last_released_event_) last_released_event_ = hdr->sequence_id;
01011
01012 TLOG(TLVL_DEBUG) << "Releasing event " << hdr->sequence_id << " in buffer " << buf << " to art.";
01013 MarkBufferFull(buf);
01014 subrun_event_count_++;
01015 run_event_count_++;
01016 counter++;
01017 eventSize += BufferDataSize(buf);
01018 pending_buffers_.erase(buf);
01019 }
01020 eventSize /= counter;
01021
01022 TLOG(TLVL_TRACE) << "check_pending_buffers_: Sending Metrics";
01023 if (metricMan)
01024 {
01025
01026 metricMan->sendMetric("Event Rate", counter, "Events/s", 1, MetricMode::Rate);
01027 metricMan->sendMetric("Events Released to art this run", run_event_count_, "Events", 1, MetricMode::LastPoint);
01028 metricMan->sendMetric("Incomplete Events Released to art this run", run_incomplete_event_count_, "Events", 1, MetricMode::LastPoint);
01029 metricMan->sendMetric("Events Released to art this subrun", subrun_event_count_, "Events", 2, MetricMode::LastPoint);
01030 metricMan->sendMetric("Incomplete Events Released to art this subrun", subrun_incomplete_event_count_, "Events", 2, MetricMode::LastPoint);
01031 metricMan->sendMetric("Event Size", eventSize, "Bytes", 1, MetricMode::Average);
01032
01033 if (TimeUtils::GetElapsedTimeMilliseconds(last_shmem_buffer_metric_update_) > 500)
01034 {
01035 last_shmem_buffer_metric_update_ = std::chrono::steady_clock::now();
01036 auto full = ReadReadyCount();
01037 auto empty = WriteReadyCount(overwrite_mode_);
01038 auto total = size();
01039
01040 metricMan->sendMetric("Shared Memory Full Buffers", full, "buffers", 2, MetricMode::LastPoint);
01041 metricMan->sendMetric("Shared Memory Available Buffers", empty, "buffers", 2, MetricMode::LastPoint);
01042 metricMan->sendMetric("Shared Memory Full %", full * 100 / static_cast<double>(total), "%", 2, MetricMode::LastPoint);
01043 metricMan->sendMetric("Shared Memory Available %", empty * 100 / static_cast<double>(total), "%", 2, MetricMode::LastPoint);
01044 }
01045 }
01046 TLOG(TLVL_TRACE) << "check_pending_buffers_ END";
01047 }
01048
01049 void artdaq::SharedMemoryEventManager::send_init_frag_()
01050 {
01051 if (init_fragment_ != nullptr)
01052 {
01053 TLOG(TLVL_TRACE) << "Sending init Fragment to art...";
01054
01055 #if 0
01056 std::string fileName = "receiveInitMessage_" + std::to_string(my_rank) + ".bin";
01057 std::fstream ostream(fileName.c_str(), std::ios::out | std::ios::binary);
01058 ostream.write(reinterpret_cast<char*>(init_fragment_->dataBeginBytes()), init_fragment_->dataSizeBytes());
01059 ostream.close();
01060 #endif
01061
01062 broadcastFragment_(std::move(init_fragment_), init_fragment_);
01063 TLOG(TLVL_TRACE) << "Init Fragment sent";
01064 }
01065 else if (send_init_fragments_)
01066 {
01067 TLOG(TLVL_WARNING) << "Cannot send init fragment because I haven't yet received one!";
01068 }
01069 }
01070
01071 void artdaq::SharedMemoryEventManager::SetInitFragment(FragmentPtr frag)
01072 {
01073 if (!init_fragment_ || init_fragment_ == nullptr)
01074 {
01075 init_fragment_.swap(frag);
01076 send_init_frag_();
01077 }
01078 }
01079
01080 #if MESSAGEFACILITY_HEX_VERSION >= 0x20103
01081 FHICL_PROVIDE_ALLOWED_CONFIGURATION(artdaq::SharedMemoryEventManager)
01082 #endif