LCOV - code coverage report
Current view: top level - artdaq/DAQrate - SharedMemoryEventManager.cc (source / functions) Coverage Total Hit
Test: artdaq.info.cleaned Lines: 63.4 % 1160 735
Test Date: 2026-10-29 00:35:58 Functions: 65.5 % 223 146

            Line data    Source code
       1              : 
       2              : #include "artdaq/DAQrate/SharedMemoryEventManager.hh"
       3              : #include <sys/wait.h>
       4              : 
       5              : #include <poll.h>
       6              : #include <memory>
       7              : #include <numeric>
       8              : 
       9              : #include "artdaq-core/Core/StatisticsCollection.hh"
      10              : #include "artdaq-core/Data/MetadataFragment.hh"
      11              : #include "artdaq-core/Utilities/TraceLock.hh"
      12              : 
      13              : #define TRACE_NAME (app_name + "_SharedMemoryEventManager").c_str()
      14              : 
      15              : // clang-format off
      16              : #define TLVL_ADDFRAGMENT            32
      17              : #define TLVL_ADDINITFRAGMENT        33
      18              : #define TLVL_BROADCASTFRAGMENT      34
      19              : #define TLVL_BROADCASTFRAGMENTS     35
      20              : #define TLVL_BROADCASTFRAGMENTS_2   36
      21              : #define TLVL_CHECKPENDINGBROADCASTS 37
      22              : #define TLVL_CHECKPENDINGBUFFERS    38
      23              : #define TLVL_CHECKPENDINGBUFFERS_2  39
      24              : #define TLVL_CHECKPENDINGBUFFERS_3  40
      25              : #define TLVL_CHECKPENDINGBUFFERS_4  41
      26              : #define TLVL_COMPLETEBUFFER         42
      27              : #define TLVL_CONSTRUCTOR            43
      28              : #define TLVL_DESTRUCTOR             43
      29              : #define TLVL_DONEWRITINGFRAGMENT    44
      30              : #define TLVL_ENDOFDATA              45
      31              : #define TLVL_ENDOFDATA_2            46
      32              : #define TLVL_ENDRUN                 47
      33              : #define TLVL_GETBUFFER              48
      34              : #define TLVL_GETFRAGMENTCOUNT       49
      35              : #define TLVL_GETSUBRUN              50
      36              : #define TLVL_GETEVENTID             51
      37              : #define TLVL_PARSEARTCOMMANDLINE    52
      38              : #define TLVL_RECONFIGUREART         53
      39              : #define TLVL_RUNART                 54
      40              : #define TLVL_RUNART_2               55
      41              : #define TLVL_SENDINIT               56
      42              : #define TLVL_SENDMETRICS            57
      43              : #define TLVL_SHUTDOWN               58
      44              : #define TLVL_STARTRUN               58
      45              : #define TLVL_UPDATEARTCONFIG        59
      46              : #define TLVL_WRITEFRAGMENTHEADER    60
      47              : #define TLVL_BUFFER                 61
      48              : #define TLVL_BUFLCK                 62
      49              : // clang-format on
      50              : 
      51              : std::mutex artdaq::SharedMemoryEventManager::sequence_id_mutex_;
      52              : std::mutex artdaq::SharedMemoryEventManager::subrun_event_map_mutex_;
      53              : const std::string artdaq::SharedMemoryEventManager::
      54              :     FRAGMENTS_RECEIVED_STAT_KEY("SharedMemoryEventManagerFragmentsReceived");
      55              : const std::string artdaq::SharedMemoryEventManager::
      56              :     EVENTS_RELEASED_STAT_KEY("SharedMemoryEventManagerEventsReleased");
      57              : 
      58           15 : artdaq::SharedMemoryEventManager::SharedMemoryEventManager(const fhicl::ParameterSet& pset, fhicl::ParameterSet art_pset)
      59           30 :     : SharedMemoryManager(pset.get<uint32_t>("shared_memory_key", Globals::SharedMemoryKey(0xEE000000)),
      60           15 :                           pset.get<size_t>("buffer_count"),
      61           75 :                           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"),
      62           30 :                           pset.get<size_t>("stale_buffer_timeout_usec", pset.get<size_t>("event_queue_wait_time", 5) * 1000000),
      63           30 :                           !pset.get<bool>("broadcast_mode", false))
      64           30 :     , num_art_processes_(pset.get<size_t>("art_analyzer_count", 1))
      65           30 :     , num_fragments_per_event_(pset.get<size_t>("expected_fragments_per_event"))
      66           15 :     , queue_size_(pset.get<size_t>("buffer_count"))
      67           15 :     , run_id_(0)
      68           15 :     , subrun_id_(0)
      69           30 :     , max_subrun_event_map_length_(pset.get<size_t>("max_subrun_lookup_table_size", 100))
      70           30 :     , subrun_transition_hold_time_s_(pset.get<double>("subrun_transition_hold_time_s", 0.001))
      71           30 :     , max_event_list_length_(pset.get<size_t>("max_event_list_length", 100))
      72           30 :     , use_sequence_id_for_event_number_(pset.get<bool>("use_sequence_id_for_event_number", true))
      73           30 :     , reset_event_number_for_subruns_(pset.get<bool>("reset_event_number_for_subruns", false))
      74           50 :     , overwrite_mode_(!pset.get<bool>("use_art", true) || pset.get<bool>("overwrite_mode", false) || pset.get<bool>("broadcast_mode", false))
      75           30 :     , shared_memory_ordering_(pset.get<bool>("shared_memory_ordering", false))
      76           60 :     , init_fragment_count_(pset.get<size_t>("init_fragment_count", pset.get<bool>("send_init_fragments", true) ? 1 : 0))
      77           15 :     , running_(false)
      78           15 :     , buffer_writes_pending_()
      79           60 :     , open_event_report_interval_ms_(pset.get<int>("open_event_report_interval_ms", pset.get<int>("incomplete_event_report_interval_ms", -1)))
      80           15 :     , last_open_event_report_time_(std::chrono::steady_clock::now())
      81           15 :     , last_backpressure_report_time_(std::chrono::steady_clock::now())
      82           15 :     , last_fragment_header_write_time_(std::chrono::steady_clock::now())
      83           45 :     , event_timing_(pset.get<size_t>("buffer_count"))
      84           30 :     , broadcast_timeout_ms_(pset.get<int>("fragment_broadcast_timeout_ms", 3000))
      85           15 :     , run_event_count_(0)
      86           15 :     , run_incomplete_event_count_(0)
      87           15 :     , subrun_event_count_(0)
      88           15 :     , subrun_incomplete_event_count_(0)
      89           15 :     , oversize_fragment_count_(0)
      90           30 :     , maximum_oversize_fragment_count_(pset.get<int>("maximum_oversize_fragment_count", 1))
      91           15 :     , restart_art_(false)
      92           30 :     , always_restart_art_(pset.get<bool>("restart_crashed_art_processes", true))
      93           30 :     , manual_art_(pset.get<bool>("manual_art", false))
      94           15 :     , current_art_pset_(art_pset)
      95           45 :     , art_cmdline_(pset.get<std::string>("art_command_line", "art -c #CONFIG_FILE#"))
      96           30 :     , art_process_index_offset_(pset.get<size_t>("art_index_offset", 0))
      97           30 :     , minimum_art_lifetime_s_(pset.get<double>("minimum_art_lifetime_s", 2.0))
      98           30 :     , art_event_processing_time_us_(pset.get<size_t>("expected_art_event_processing_time_us", 1000000))
      99           30 :     , capture_art_stdout_(pset.get<bool>("capture_art_stdout", true))
     100           30 :     , capture_art_stderr_(pset.get<bool>("capture_art_stderr", true))
     101           15 :     , requests_(nullptr)
     102           15 :     , tokens_(nullptr)
     103           15 :     , data_pset_(pset)
     104           60 :     , broadcasts_(pset.get<uint32_t>("broadcast_shared_memory_key", Globals::SharedMemoryKey(0xBB000000)),
     105           45 :                   pset.get<size_t>("broadcast_buffer_count", 10),
     106           15 :                   pset.get<size_t>("broadcast_buffer_size", 0x100000),
     107          345 :                   pset.get<int>("expected_art_event_processing_time_us", 100000) * pset.get<size_t>("buffer_count"), false)
     108              : {
     109           15 :         subrun_event_map_[0] = 1;
     110           15 :         buffer_sequence_ids_.resize(size(), Fragment::InvalidSequenceID);
     111           15 :         SetMinWriteSize(sizeof(detail::RawEventHeader) + sizeof(detail::RawFragmentHeader));
     112           15 :         broadcasts_.SetMinWriteSize(sizeof(detail::RawEventHeader) + sizeof(detail::RawFragmentHeader));
     113              : 
     114           15 :         RegisterWriter();
     115           15 :         broadcasts_.RegisterWriter();
     116              : 
     117           45 :         if (!pset.get<bool>("use_art", true))
     118              :         {
     119           30 :                 TLOG(TLVL_INFO) << "BEGIN SharedMemoryEventManager CONSTRUCTOR with use_art:false";
     120           10 :                 num_art_processes_ = 0;
     121              :         }
     122              :         else
     123              :         {
     124           15 :                 TLOG(TLVL_INFO) << "BEGIN SharedMemoryEventManager CONSTRUCTOR with use_art:true";
     125           10 :                 TLOG(TLVL_CONSTRUCTOR) << "art_pset is " << art_pset.to_string();
     126              :         }
     127              : 
     128           15 :         if (manual_art_)
     129            0 :                 current_art_config_file_ = std::make_shared<art_config_file>(art_pset, GetKey(), GetBroadcastKey());
     130              :         else
     131           15 :                 current_art_config_file_ = std::make_shared<art_config_file>(art_pset);
     132              : 
     133           15 :         if (overwrite_mode_ && num_art_processes_ > 0)
     134              :         {
     135            0 :                 TLOG(TLVL_WARNING) << "Art is configured to run, but overwrite mode is enabled! Check your configuration if this in unintentional!";
     136            0 :         }
     137           15 :         else if (overwrite_mode_)
     138              :         {
     139           30 :                 TLOG(TLVL_INFO) << "Overwrite Mode enabled, no configured art processes at startup";
     140              :         }
     141              : 
     142          103 :         for (size_t ii = 0; ii < size(); ++ii)
     143              :         {
     144           88 :                 buffer_writes_pending_[ii] = 0;
     145              :                 // Make sure the mutexes are created once
     146           88 :                 std::lock_guard<std::mutex> lk(buffer_mutexes_[ii]);
     147           88 :         }
     148              : 
     149           15 :         if (!IsValid())
     150              :         {
     151            0 :                 TLOG(TLVL_ERROR) << "Unable to attach to Shared Memory!";
     152            0 :                 throw cet::exception(app_name + "_SharedMemoryEventManager") << "Unable to attach to Shared Memory!";  // NOLINT(cert-err60-cpp)
     153              :         }
     154              : 
     155           30 :         TLOG(TLVL_CONSTRUCTOR) << "Setting Writer rank to " << my_rank;
     156           15 :         SetRank(my_rank);
     157           30 :         TLOG(TLVL_CONSTRUCTOR) << "Writer Rank is " << GetRank();
     158              : 
     159           15 :         statsHelper_.addMonitoredQuantityName(FRAGMENTS_RECEIVED_STAT_KEY);
     160           15 :         statsHelper_.addMonitoredQuantityName(EVENTS_RELEASED_STAT_KEY);
     161              : 
     162              :         // fetch the monitoring parameters and create the MonitoredQuantity instances
     163           15 :         statsHelper_.createCollectors(pset, 100, 30.0, 60.0, EVENTS_RELEASED_STAT_KEY);
     164              : 
     165           30 :         TLOG(TLVL_CONSTRUCTOR) << "END CONSTRUCTOR";
     166           15 : }
     167              : 
     168           16 : artdaq::SharedMemoryEventManager::~SharedMemoryEventManager() noexcept
     169              : {
     170           30 :         TLOG(TLVL_DESTRUCTOR) << "DESTRUCTOR";
     171           15 :         if (running_)
     172              :         {
     173              :                 try
     174              :                 {
     175            8 :                         endOfData();
     176              :                 }
     177            0 :                 catch (...)
     178              :                 {
     179              :                         // IGNORED
     180            0 :                 }
     181              :         }
     182              : 
     183           15 :         UnregisterWriter();
     184           15 :         broadcasts_.UnregisterWriter();
     185           30 :         TLOG(TLVL_DESTRUCTOR) << "Destructor END";
     186           16 : }
     187              : 
     188         7138 : bool artdaq::SharedMemoryEventManager::AddFragment(detail::RawFragmentHeader frag, void* dataPtr)
     189              : {
     190         7138 :         if (!running_) return true;
     191              : 
     192        14276 :         TLOG(TLVL_ADDFRAGMENT) << "AddFragment(Header, ptr) BEGIN frag.word_count=" << frag.word_count
     193         7138 :                                << ", sequence_id=" << frag.sequence_id;
     194         7138 :         auto buffer = getBufferForSequenceID_(frag.sequence_id, true, frag.timestamp);
     195        14276 :         TLOG(TLVL_ADDFRAGMENT) << "Using buffer " << buffer << " for seqid=" << frag.sequence_id;
     196         7138 :         if (buffer == -1)
     197              :         {
     198         5067 :                 return false;
     199              :         }
     200         2071 :         if (buffer == -2)
     201              :         {
     202            0 :                 TLOG(TLVL_ERROR) << "Dropping event because data taking has already passed this event number: " << frag.sequence_id;
     203            0 :                 return true;
     204              :         }
     205              : 
     206         2071 :         auto hdr = getEventHeader_(buffer);
     207         2071 :         hdr->run_id = run_id_;
     208         2071 :         hdr->subrun_id = GetSubrunForSequenceID(frag.sequence_id);
     209         2071 :         hdr->event_id = GetEventIDForFragment(frag.sequence_id, frag.timestamp);
     210              : 
     211         4142 :         TLOG(TLVL_ADDFRAGMENT) << "AddFragment before Write calls";
     212         2071 :         Write(buffer, dataPtr, frag.word_count * sizeof(RawDataType));
     213              : 
     214         4142 :         TLOG(TLVL_ADDFRAGMENT) << "Checking for complete event";
     215         2071 :         auto fragmentCount = GetFragmentCount(frag.sequence_id);
     216         2071 :         hdr->is_complete = fragmentCount == num_fragments_per_event_ && buffer_writes_pending_[buffer] == 0;
     217         4142 :         TLOG(TLVL_ADDFRAGMENT) << "hdr->is_complete=" << std::boolalpha << hdr->is_complete
     218            0 :                                << ", fragmentCount=" << fragmentCount
     219            0 :                                << ", num_fragments_per_event=" << num_fragments_per_event_
     220         2071 :                                << ", buffer_writes_pending_[buffer]=" << buffer_writes_pending_[buffer];
     221              : 
     222         2071 :         complete_buffer_(buffer);
     223              : 
     224         4142 :         TLOG(TLVL_ADDFRAGMENT) << "AddFragment END";
     225         2071 :         statsHelper_.addSample(FRAGMENTS_RECEIVED_STAT_KEY, frag.word_count * sizeof(RawDataType));
     226         2071 :         return true;
     227              : }
     228              : 
     229         2075 : bool artdaq::SharedMemoryEventManager::AddFragment(FragmentPtr frag, size_t timeout_usec, FragmentPtr& outfrag)
     230              : {
     231         4150 :         TLOG(TLVL_ADDFRAGMENT) << "AddFragment(FragmentPtr) BEGIN";
     232         2075 :         auto hdr = *reinterpret_cast<detail::RawFragmentHeader*>(frag->headerAddress());  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     233         2075 :         auto data = frag->headerAddress();
     234         2075 :         auto start = std::chrono::steady_clock::now();
     235         2075 :         bool sts = false;
     236        11288 :         while (!sts && TimeUtils::GetElapsedTimeMicroseconds(start) < timeout_usec)
     237              :         {
     238         7138 :                 sts = AddFragment(hdr, data);
     239         7138 :                 if (!sts)
     240              :                 {
     241         5067 :                         usleep(1000);
     242              :                 }
     243              :         }
     244         2075 :         if (!sts)
     245              :         {
     246            4 :                 outfrag = std::move(frag);
     247              :         }
     248         4150 :         TLOG(TLVL_ADDFRAGMENT) << "AddFragment(FragmentPtr) RETURN " << std::boolalpha << sts;
     249         2075 :         return sts;
     250              : }
     251              : 
     252           26 : artdaq::RawDataType* artdaq::SharedMemoryEventManager::WriteFragmentHeader(detail::RawFragmentHeader frag, bool dropIfNoBuffersAvailable)
     253              : {
     254           26 :         if (!running_) return nullptr;
     255           50 :         TLOG(TLVL_WRITEFRAGMENTHEADER) << "WriteFragmentHeader BEGIN, seqID=" << frag.sequence_id;
     256           25 :         auto buffer = getBufferForSequenceID_(frag.sequence_id, true, frag.timestamp);
     257              : 
     258           25 :         if (buffer < 0)
     259              :         {
     260            2 :                 if (buffer == -1 && !dropIfNoBuffersAvailable)
     261              :                 {
     262            0 :                         std::unique_lock<std::mutex> bp_lk(sequence_id_mutex_);
     263            0 :                         if (TimeUtils::GetElapsedTime(last_backpressure_report_time_) > 1.0)
     264              :                         {
     265            0 :                                 TLOG(TLVL_WARNING) << app_name << ": Back-pressure condition: All Shared Memory buffers have been full for " << TimeUtils::GetElapsedTime(last_fragment_header_write_time_) << " s! There are " << (GetAttachedCount() - 1) << " art processes connected.";
     266            0 :                                 last_backpressure_report_time_ = std::chrono::steady_clock::now();
     267            0 :                                 if (GetAttachedCount() == 1 && !restart_art_)
     268              :                                 {
     269            0 :                                         TLOG(TLVL_ERROR) << "All art processes have died, and restarting was unsuccessful. Check PMT log file for error messages";
     270            0 :                                         throw cet::exception(app_name + "_SharedMemoryEventManager") << "All art processes have died, and restarting was unsuccessful. Check PMT log file for error messages";  // NOLINT(cert-err60-cpp)
     271              :                                 }
     272              :                         }
     273            0 :                         if (metricMan)
     274              :                         {
     275            0 :                                 metricMan->sendMetric("Back-pressure wait time", TimeUtils::GetElapsedTime(last_fragment_header_write_time_), "s", 1, MetricMode::LastPoint);
     276              :                         }
     277            0 :                         TLOG(TLVL_WRITEFRAGMENTHEADER) << "No shared memory buffers available, seqID=" << frag.sequence_id;
     278            0 :                         return nullptr;
     279            0 :                 }
     280            2 :                 if (buffer == -2)
     281              :                 {
     282            6 :                         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.";
     283              :                 }
     284              :                 else
     285              :                 {
     286            0 :                         TLOG(TLVL_INFO) << "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.";
     287              :                 }
     288            2 :                 dropped_data_.emplace_back(frag, std::make_unique<Fragment>(frag.word_count - frag.num_words()));
     289            2 :                 auto it = dropped_data_.rbegin();
     290              : 
     291            4 :                 TLOG(TLVL_WRITEFRAGMENTHEADER) << "Dropping fragment with sequence id " << frag.sequence_id << " and fragment id " << frag.fragment_id << " into "
     292            2 :                                                << static_cast<void*>(it->second->dataBegin()) << " sz=" << it->second->dataSizeBytes();
     293              : 
     294            2 :                 return it->second->dataBegin();
     295              :         }
     296              : 
     297           23 :         last_backpressure_report_time_ = std::chrono::steady_clock::now();
     298           23 :         last_fragment_header_write_time_ = std::chrono::steady_clock::now();
     299              :         // Increment this as soon as we know we want to use the buffer
     300           23 :         buffer_writes_pending_[buffer]++;
     301              : 
     302           23 :         if (metricMan)
     303              :         {
     304          161 :                 metricMan->sendMetric("Input Fragment Rate", 1, "Fragments/s", 1, MetricMode::Rate);
     305              :         }
     306              : 
     307           46 :         TLOG(TLVL_BUFLCK) << "WriteFragmentHeader: obtaining buffer_mutexes lock for buffer " << buffer << ", seqID=" << frag.sequence_id;
     308              :         ;
     309              : 
     310           23 :         std::unique_lock<std::mutex> lk(buffer_mutexes_.at(buffer));
     311              : 
     312           46 :         TLOG(TLVL_BUFLCK) << "WriteFragmentHeader: obtained buffer_mutexes lock for buffer " << buffer << ", seqID=" << frag.sequence_id;
     313              : 
     314           23 :         auto hdrpos = reinterpret_cast<RawDataType*>(GetWritePos(buffer));  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     315           23 :         Write(buffer, &frag, frag.num_words() * sizeof(RawDataType));
     316              : 
     317           23 :         auto pos = reinterpret_cast<RawDataType*>(GetWritePos(buffer));  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     318           23 :         if (frag.word_count - frag.num_words() > 0)
     319              :         {
     320           23 :                 auto sts = IncrementWritePos(buffer, (frag.word_count - frag.num_words()) * sizeof(RawDataType));
     321              : 
     322           23 :                 if (!sts)
     323              :                 {
     324            0 :                         reinterpret_cast<detail::RawFragmentHeader*>(hdrpos)->word_count = frag.num_words();       // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     325            0 :                         reinterpret_cast<detail::RawFragmentHeader*>(hdrpos)->type = Fragment::ErrorFragmentType;  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     326            0 :                         TLOG(TLVL_ERROR) << "Dropping over-size 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)";
     327            0 :                         dropped_data_.emplace_back(frag, std::make_unique<Fragment>(frag.word_count - frag.num_words()));
     328            0 :                         auto it = dropped_data_.rbegin();
     329              : 
     330            0 :                         oversize_fragment_count_++;
     331              : 
     332            0 :                         if (maximum_oversize_fragment_count_ > 0 && oversize_fragment_count_ >= maximum_oversize_fragment_count_)
     333              :                         {
     334            0 :                                 lk.unlock();
     335            0 :                                 TLOG(TLVL_ERROR) << "Too many over-size Fragments received! Please adjust max_event_size_bytes or max_fragment_size_bytes!";
     336            0 :                                 throw cet::exception("Too many over-size Fragments received! Please adjust max_event_size_bytes or max_fragment_size_bytes!");
     337              :                         }
     338              : 
     339            0 :                         TLOG(TLVL_WRITEFRAGMENTHEADER) << "Dropping over-size fragment with sequence id " << frag.sequence_id << " and fragment id " << frag.fragment_id
     340            0 :                                                        << " into " << static_cast<void*>(it->second->dataBegin());
     341            0 :                         return it->second->dataBegin();
     342              :                 }
     343              :         }
     344           46 :         TLOG(TLVL_WRITEFRAGMENTHEADER) << "WriteFragmentHeader END, seqID=" << frag.sequence_id;
     345           23 :         return pos;
     346           23 : }
     347              : 
     348           25 : void artdaq::SharedMemoryEventManager::DoneWritingFragment(detail::RawFragmentHeader frag)
     349              : {
     350           50 :         TLOG(TLVL_DONEWRITINGFRAGMENT) << "DoneWritingFragment BEGIN";
     351              : 
     352           25 :         auto buffer = getBufferForSequenceID_(frag.sequence_id, false, frag.timestamp);
     353           25 :         if (buffer < 0)
     354              :         {
     355            2 :                 for (auto it = dropped_data_.begin(); it != dropped_data_.end(); ++it)
     356              :                 {
     357            2 :                         if (frag.operator==(it->first))  // TODO, ELF 5/26/2023: Workaround until artdaq_core can be fixed for C++20
     358              :                         {
     359            2 :                                 dropped_data_.erase(it);
     360            2 :                                 return;
     361              :                         }
     362              :                 }
     363            0 :                 if (buffer == -1)
     364              :                 {
     365            0 :                         Detach(true, app_name + "SharedMemoryEventManager",
     366              :                                "getBufferForSequenceID_ returned -1 in DoneWritingFragment. This indicates a possible mismatch between expected Fragment count and the actual number of Fragments received.");
     367              :                 }
     368            0 :                 return;
     369              :         }
     370              : 
     371           23 :         if (!frag.valid)
     372              :         {
     373            0 :                 UpdateFragmentHeader(buffer, frag);
     374              :         }
     375              : 
     376           23 :         statsHelper_.addSample(FRAGMENTS_RECEIVED_STAT_KEY, frag.word_count * sizeof(RawDataType));
     377              :         {
     378           46 :                 TLOG(TLVL_BUFLCK) << "DoneWritingFragment: obtaining buffer_mutexes lock for buffer " << buffer;
     379              : 
     380           23 :                 std::unique_lock<std::mutex> lk(buffer_mutexes_.at(buffer));
     381              : 
     382           46 :                 TLOG(TLVL_BUFLCK) << "DoneWritingFragment: obtained buffer_mutexes lock for buffer " << buffer;
     383              : 
     384           46 :                 TLOG(TLVL_DONEWRITINGFRAGMENT) << "DoneWritingFragment: Received Fragment with sequence ID " << frag.sequence_id << " and fragment id " << frag.fragment_id << " (type " << static_cast<int>(frag.type) << ")";
     385           23 :                 auto hdr = getEventHeader_(buffer);
     386           23 :                 hdr->run_id = run_id_;
     387           23 :                 hdr->subrun_id = GetSubrunForSequenceID(frag.sequence_id);
     388           23 :                 hdr->event_id = GetEventIDForFragment(frag.sequence_id, frag.timestamp);
     389              : 
     390           46 :                 TLOG(TLVL_DONEWRITINGFRAGMENT) << "DoneWritingFragment: Updating buffer touch time";
     391           23 :                 TouchBuffer(buffer);
     392              : 
     393           23 :                 if (buffer_writes_pending_[buffer] > 1)
     394              :                 {
     395            6 :                         TLOG(TLVL_DONEWRITINGFRAGMENT) << "Done writing fragment, but there's another writer. Not doing bookkeeping steps.";
     396            3 :                         buffer_writes_pending_[buffer]--;
     397            3 :                         return;
     398              :                 }
     399           40 :                 TLOG(TLVL_DONEWRITINGFRAGMENT) << "Done writing fragment, and no other writer. Doing bookkeeping steps.";
     400           20 :                 auto frag_count = GetFragmentCount(frag.sequence_id);
     401           20 :                 hdr->is_complete = frag_count >= num_fragments_per_event_;
     402              : 
     403           20 :                 if (frag_count > num_fragments_per_event_)
     404              :                 {
     405            3 :                         TLOG(TLVL_WARNING) << "DoneWritingFragment: This Event has more Fragments ( " << frag_count << " ) than specified in configuration ( " << num_fragments_per_event_ << " )!"
     406            2 :                                            << " This is probably due to a misconfiguration and is *not* a reliable mode!";
     407              :                 }
     408              : 
     409           40 :                 TLOG(TLVL_DONEWRITINGFRAGMENT) << "DoneWritingFragment: Received Fragment with sequence ID " << frag.sequence_id << " and fragment id " << frag.fragment_id << ", count/expected = " << frag_count << "/" << num_fragments_per_event_;
     410              : #if ART_SUPPORTS_DUPLICATE_EVENTS
     411              :                 if (!hdr->is_complete && released_incomplete_events_.count(frag.sequence_id))
     412              :                 {
     413              :                         hdr->is_complete = frag_count >= released_incomplete_events_[frag.sequence_id] && buffer_writes_pending_[buffer] == 0;
     414              :                 }
     415              : #endif
     416              : 
     417           20 :                 complete_buffer_(buffer);
     418              : 
     419              :                 // Move this down here to avoid race condition
     420           20 :                 buffer_writes_pending_[buffer]--;
     421           23 :         }
     422           40 :         TLOG(TLVL_DONEWRITINGFRAGMENT) << "DoneWritingFragment END";
     423              : }
     424              : 
     425         2101 : size_t artdaq::SharedMemoryEventManager::GetFragmentCount(Fragment::sequence_id_t seqID, Fragment::type_t type)
     426              : {
     427         2101 :         return GetFragmentCountInBuffer(getBufferForSequenceID_(seqID, false), type);
     428              : }
     429              : 
     430         2106 : size_t artdaq::SharedMemoryEventManager::GetFragmentCountInBuffer(int buffer, Fragment::type_t type)
     431              : {
     432         2106 :         if (buffer < 0)
     433              :         {
     434            0 :                 return 0;
     435              :         }
     436         2106 :         ResetReadPos(buffer);
     437         2106 :         IncrementReadPos(buffer, sizeof(detail::RawEventHeader));
     438              : 
     439         2106 :         size_t count = 0;
     440              : 
     441         8326 :         while (MoreDataInBuffer(buffer))
     442              :         {
     443         6220 :                 auto fragHdr = reinterpret_cast<artdaq::detail::RawFragmentHeader*>(GetReadPos(buffer));  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     444         6220 :                 IncrementReadPos(buffer, fragHdr->word_count * sizeof(RawDataType));
     445         6220 :                 if (type != Fragment::InvalidFragmentType && fragHdr->type != type)
     446              :                 {
     447              :                         // Skip fragments with the wrong type, as they were over-size and truncated to the header
     448            0 :                         continue;
     449              :                 }
     450        12440 :                 TLOG(TLVL_GETFRAGMENTCOUNT) << "Adding Fragment with size=" << fragHdr->word_count << " to Fragment count";
     451         6220 :                 ++count;
     452              :         }
     453              : 
     454         2106 :         return count;
     455              : }
     456              : 
     457            0 : void artdaq::SharedMemoryEventManager::UpdateFragmentHeader(int buffer, artdaq::detail::RawFragmentHeader hdr)
     458              : {
     459            0 :         if (buffer < 0)
     460              :         {
     461            0 :                 return;
     462              :         }
     463            0 :         ResetReadPos(buffer);
     464            0 :         IncrementReadPos(buffer, sizeof(detail::RawEventHeader));
     465              : 
     466            0 :         while (MoreDataInBuffer(buffer))
     467              :         {
     468            0 :                 auto fragHdr = reinterpret_cast<artdaq::detail::RawFragmentHeader*>(GetReadPos(buffer));  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
     469            0 :                 if (hdr.fragment_id == fragHdr->fragment_id)
     470              :                 {
     471            0 :                         *fragHdr = hdr;
     472            0 :                         break;
     473              :                 }
     474              :         }
     475              : 
     476            0 :         return;
     477              : }
     478              : 
     479            6 : void artdaq::SharedMemoryEventManager::RunArt(size_t process_index, const std::shared_ptr<std::atomic<pid_t>>& pid_out)
     480              : {
     481              :         do
     482              :         {
     483            6 :                 auto start_time = std::chrono::steady_clock::now();
     484            6 :                 send_init_frags_();
     485           18 :                 TLOG(TLVL_INFO) << "Starting art process with config file " << current_art_config_file_->getFileName();
     486              : 
     487            6 :                 pid_t pid = 0;
     488            6 :                 bool piped_output = true;
     489              :                 int stdoutpipefd[2];
     490              :                 int stderrpipefd[2];
     491              : 
     492            6 :                 if (!manual_art_)
     493              :                 {
     494            6 :                         if (pipe(stdoutpipefd) == -1)
     495              :                         {
     496            0 :                                 TLOG(TLVL_ERROR) << "Error creating pipe for art process stdout: " << errno << " (" << strerror(errno) << ").";
     497            0 :                                 piped_output = false;
     498              :                         }
     499            6 :                         if (piped_output && pipe(stderrpipefd) == -1)
     500              :                         {
     501            0 :                                 TLOG(TLVL_ERROR) << "Error creating pipe for art process stderr: " << errno << " (" << strerror(errno) << ").";
     502            0 :                                 close(stdoutpipefd[0]);
     503            0 :                                 close(stdoutpipefd[1]);
     504            0 :                                 piped_output = false;
     505              :                         }
     506            6 :                         pid = fork();
     507            6 :                         if (pid == 0)
     508              :                         { /* child */
     509            0 :                                 if (piped_output)
     510              :                                 {
     511            0 :                                         close(stdoutpipefd[0]);                // Close read end of stdout pipe
     512            0 :                                         close(stderrpipefd[0]);                // Close read end of stderr pipe
     513            0 :                                         dup2(stdoutpipefd[1], STDOUT_FILENO);  // Redirect stdout to pipe
     514            0 :                                         dup2(stderrpipefd[1], STDERR_FILENO);  // Redirect stderr to pipe
     515              :                                 }
     516              : 
     517              :                                 // 23-May-2018, KAB: added the setting of the partition number env var
     518              :                                 // in the environment of the child art process so that Globals.hh
     519              :                                 // will pick it up there and provide it to the artdaq classes that
     520              :                                 // are used in data transfers, etc. within the art process.
     521            0 :                                 std::string envVarKey = "ARTDAQ_PARTITION_NUMBER";
     522            0 :                                 std::string envVarValue = std::to_string(Globals::GetPartitionNumber());
     523            0 :                                 if (setenv(envVarKey.c_str(), envVarValue.c_str(), 1) != 0)
     524              :                                 {
     525            0 :                                         TLOG(TLVL_ERROR) << "Error setting environment variable \"" << envVarKey
     526            0 :                                                          << "\" in the environment of a child art process. "
     527            0 :                                                          << "This may result in incorrect TCP port number "
     528            0 :                                                          << "assignments or other issues, and data may "
     529            0 :                                                          << "not flow through the system correctly.";
     530              :                                 }
     531            0 :                                 envVarKey = "ARTDAQ_APPLICATION_NAME";
     532            0 :                                 envVarValue = app_name;
     533            0 :                                 if (setenv(envVarKey.c_str(), envVarValue.c_str(), 1) != 0)
     534              :                                 {
     535            0 :                                         TLOG(TLVL_RUNART) << "Error setting environment variable \"" << envVarKey
     536            0 :                                                           << "\" in the environment of a child art process. ";
     537              :                                 }
     538            0 :                                 envVarKey = "ARTDAQ_RANK";
     539            0 :                                 envVarValue = std::to_string(my_rank);
     540            0 :                                 if (setenv(envVarKey.c_str(), envVarValue.c_str(), 1) != 0)
     541              :                                 {
     542            0 :                                         TLOG(TLVL_RUNART) << "Error setting environment variable \"" << envVarKey
     543            0 :                                                           << "\" in the environment of a child art process. ";
     544              :                                 }
     545              : 
     546            0 :                                 TLOG(TLVL_RUNART_2) << "Parsing art command line";
     547            0 :                                 auto args = parse_art_command_line_(current_art_config_file_, process_index);
     548              : 
     549            0 :                                 TLOG(TLVL_RUNART_2) << "Calling execvp with application name " << args[0];
     550            0 :                                 execvp(args[0], &args[0]);
     551              : 
     552            0 :                                 TLOG(TLVL_RUNART_2) << "Application exited, cleaning up";
     553            0 :                                 for (auto& arg : args)
     554              :                                 {
     555            0 :                                         delete[] arg;
     556              :                                 }
     557              : 
     558            0 :                                 exit(1);
     559            0 :                         }
     560              :                 }
     561              :                 else
     562              :                 {
     563              :                         // Using cin/cout here to ensure console is active (artdaqDriver)
     564            0 :                         std::cout << "Please run the following command in a separate terminal:" << std::endl
     565            0 :                                   << "art -c " << current_art_config_file_->getFileName() << std::endl
     566            0 :                                   << "Then, in a third terminal, execute: \"ps aux|grep [a]rt -c " << current_art_config_file_->getFileName() << "\" and note the PID of the art process." << std::endl
     567            0 :                                   << "Finally, return to this window and enter the pid: " << std::endl;
     568            0 :                         std::cin >> pid;
     569              :                 }
     570            6 :                 *pid_out = pid;
     571              : 
     572           18 :                 TLOG(TLVL_INFO) << "PID of new art process is " << pid;
     573              :                 {
     574            6 :                         std::unique_lock<std::mutex> lk(art_process_mutex_);
     575            6 :                         art_processes_.insert(pid);
     576            6 :                 }
     577              :                 siginfo_t status;
     578            6 :                 auto sts = 0;
     579            6 :                 if (!manual_art_)
     580              :                 {
     581              :                         // Read output from art process and report it to TRACE
     582            6 :                         if (piped_output)
     583              :                         {
     584            6 :                                 close(stdoutpipefd[1]);  // Close write end of stdout pipe
     585            6 :                                 close(stderrpipefd[1]);  // Close write end of stderr pipe
     586              : 
     587            6 :                                 std::string art_tname = app_name + "_art_stdout";
     588              :                                 char buf[PIPE_BUF];
     589              :                                 struct pollfd fds[2];
     590            6 :                                 fds[0].fd = stdoutpipefd[0];
     591            6 :                                 fds[0].events = POLLIN;
     592            6 :                                 fds[1].fd = stderrpipefd[0];
     593            6 :                                 fds[1].events = POLLIN;
     594              : 
     595              :                                 do
     596              :                                 {
     597         2623 :                                         sts = waitid(P_PID, pid, &status, WEXITED | WNOHANG);
     598         2623 :                                         poll(fds, 2, 1000);
     599         2623 :                                         if (fds[0].revents & POLLIN)
     600              :                                         {
     601          105 :                                                 ssize_t count = read(stdoutpipefd[0], buf, sizeof(buf) - 1);
     602          105 :                                                 if (count > 0 && capture_art_stdout_)
     603              :                                                 {
     604          420 :                                                         TLOG(TLVL_INFO, art_tname) << "art[" << pid << "]" << std::string(buf, count);
     605              :                                                 }
     606              :                                         }
     607         2623 :                                         if (fds[1].revents & POLLIN)
     608              :                                         {
     609            0 :                                                 ssize_t count = read(stderrpipefd[0], buf, sizeof(buf) - 1);
     610            0 :                                                 if (count > 0 && capture_art_stderr_)
     611              :                                                 {
     612            0 :                                                         TLOG(TLVL_ERROR, art_tname) << "art[" << pid << "]" << std::string(buf, count);
     613              :                                                 }
     614              :                                         }
     615         2623 :                                 } while (status.si_code != CLD_DUMPED && status.si_code != CLD_KILLED && status.si_code != CLD_EXITED && sts == 0);
     616            6 :                         }
     617              :                         else
     618              :                         {
     619            0 :                                 sts = waitid(P_PID, pid, &status, WEXITED);
     620              :                         }
     621              :                 }
     622              :                 else
     623              :                 {
     624            0 :                         while (kill(pid, 0) >= 0) usleep(10000);
     625              : 
     626            0 :                         TLOG(TLVL_INFO) << "Faking good exit status, please see art process for actual exit status!";
     627            0 :                         status.si_code = CLD_EXITED;
     628            0 :                         status.si_status = 0;
     629              :                 }
     630           18 :                 TLOG(TLVL_INFO) << "Removing PID " << pid << " from process list";
     631              :                 {
     632            6 :                         std::unique_lock<std::mutex> lk(art_process_mutex_);
     633            6 :                         art_processes_.erase(pid);
     634            6 :                 }
     635            6 :                 if (sts < 0)
     636              :                 {
     637            0 :                         TLOG(TLVL_WARNING) << "Error occurred in waitid for art process " << pid << ": " << errno << " (" << strerror(errno) << ").";
     638              :                 }
     639            6 :                 else if (status.si_code == CLD_EXITED && status.si_status == 0)
     640              :                 {
     641           18 :                         TLOG(TLVL_INFO) << "art process " << pid << " exited normally, " << (restart_art_ ? "restarting" : "not restarting");
     642            6 :                 }
     643              :                 else
     644              :                 {
     645            0 :                         auto art_lifetime = TimeUtils::GetElapsedTime(start_time);
     646            0 :                         if (art_lifetime < minimum_art_lifetime_s_)
     647              :                         {
     648            0 :                                 restart_art_ = false;
     649              :                         }
     650              : 
     651            0 :                         auto exit_type = "exited with status code";
     652            0 :                         switch (status.si_code)
     653              :                         {
     654            0 :                                 case CLD_DUMPED:
     655              :                                 case CLD_KILLED:
     656            0 :                                         exit_type = "was killed with signal";
     657            0 :                                         break;
     658            0 :                                 case CLD_EXITED:
     659              :                                 default:
     660            0 :                                         break;
     661              :                         }
     662              : 
     663            0 :                         TLOG((restart_art_ ? TLVL_WARNING : TLVL_ERROR))
     664            0 :                             << "art process " << pid << " " << exit_type << " " << status.si_status
     665            0 :                             << (status.si_code == CLD_DUMPED ? " (core dumped)" : "")
     666            0 :                             << " after running for " << std::setprecision(2) << std::fixed << art_lifetime << " seconds, "
     667            0 :                             << (restart_art_ ? "restarting" : "not restarting");
     668              :                 }
     669            6 :         } while (restart_art_);
     670            6 : }
     671              : 
     672           17 : bool artdaq::SharedMemoryEventManager::StartArt()
     673              : {
     674           17 :         size_t initialCount = GetAttachedCount();
     675           17 :         restart_art_ = always_restart_art_;
     676           17 :         if (num_art_processes_ == 0)
     677              :         {
     678           11 :                 return true;
     679              :         }
     680           12 :         for (size_t ii = 0; ii < num_art_processes_; ++ii)
     681              :         {
     682            6 :                 StartArtProcess(current_art_pset_, ii);
     683              :         }
     684            6 :         auto startTime = std::chrono::steady_clock::now();
     685            6 :         while (GetAttachedCount() - initialCount != num_art_processes_)
     686              :         {
     687            0 :                 TLOG(TLVL_INFO) << "Waiting for all art processes to connect to shared memory, " << TimeUtils::GetElapsedTime(startTime) << " s elapsed.";
     688            0 :                 std::this_thread::sleep_for(std::chrono::seconds(1));
     689            0 :                 if (!restart_art_)
     690              :                 {
     691            0 :                         TLOG(TLVL_ERROR) << "Error occurred while starting art processes, aborting. Check PMT log for error messages.";
     692            0 :                         return false;
     693              :                 }
     694              :         }
     695            6 :         return true;
     696              : }
     697              : 
     698            6 : pid_t artdaq::SharedMemoryEventManager::StartArtProcess(fhicl::ParameterSet pset, size_t process_index)
     699              : {
     700              :         static std::mutex start_art_mutex;
     701            6 :         std::unique_lock<std::mutex> lk(start_art_mutex);
     702              :         // TraceLock lk(start_art_mutex, 15, "StartArtLock");
     703            6 :         restart_art_ = always_restart_art_;
     704            6 :         auto initialCount = GetAttachedCount();
     705            6 :         auto startTime = std::chrono::steady_clock::now();
     706              : 
     707            6 :         if (pset != current_art_pset_ || !current_art_config_file_)
     708              :         {
     709            0 :                 current_art_pset_ = pset;
     710            0 :                 if (manual_art_)
     711            0 :                         current_art_config_file_ = std::make_shared<art_config_file>(pset, GetKey(), GetBroadcastKey());
     712              :                 else
     713            0 :                         current_art_config_file_ = std::make_shared<art_config_file>(pset);
     714              :         }
     715            6 :         std::shared_ptr<std::atomic<pid_t>> pid(new std::atomic<pid_t>(-1));
     716           12 :         boost::thread thread([this, process_index, pid] { RunArt(process_index, pid); });
     717            6 :         thread.detach();
     718              : 
     719            6 :         auto currentCount = GetAttachedCount() - initialCount;
     720           31 :         while ((currentCount < 1 || *pid <= 0) && (TimeUtils::GetElapsedTime(startTime) < 5 || manual_art_))
     721              :         {
     722           25 :                 usleep(10000);
     723           25 :                 currentCount = GetAttachedCount() - initialCount;
     724              :         }
     725            6 :         if ((currentCount < 1 || *pid <= 0) && manual_art_)
     726              :         {
     727            0 :                 TLOG(TLVL_WARNING) << "Manually-started art process has not connected to shared memory or has bad PID: connected:" << currentCount << ", PID:" << pid;
     728            0 :                 return 0;
     729              :         }
     730            6 :         if (currentCount < 1 || *pid <= 0)
     731              :         {
     732            0 :                 TLOG(TLVL_WARNING) << "art process has not started after 5s. Check art configuration!"
     733            0 :                                    << " (pid=" << *pid << ", attachedCount=" << currentCount << ")";
     734            0 :                 return 0;
     735              :         }
     736              : 
     737           18 :         TLOG(TLVL_INFO) << std::setw(4) << std::fixed << "art initialization took "
     738           12 :                         << TimeUtils::GetElapsedTime(startTime) << " seconds.";
     739              : 
     740            6 :         return *pid;
     741            6 : }
     742              : 
     743            6 : void artdaq::SharedMemoryEventManager::ShutdownArtProcesses(std::set<pid_t>& pids)
     744              : {
     745            6 :         restart_art_ = false;
     746              :         // current_art_config_file_ = nullptr;
     747              :         // current_art_pset_ = fhicl::ParameterSet();
     748              : 
     749          370 :         auto check_pids = [&](bool print) {
     750          370 :                 std::unique_lock<std::mutex> lk(art_process_mutex_);
     751          735 :                 for (auto pid = pids.begin(); pid != pids.end();)
     752              :                 {
     753              :                         // 08-May-2018, KAB: protect against killing invalid PIDS
     754              : 
     755          365 :                         if (*pid <= 0)
     756              :                         {
     757            0 :                                 TLOG(TLVL_WARNING) << "Removing an invalid PID (" << *pid
     758            0 :                                                    << ") from the shutdown list.";
     759            0 :                                 pid = pids.erase(pid);
     760              :                         }
     761          365 :                         else if (kill(*pid, 0) < 0)
     762              :                         {
     763            1 :                                 pid = pids.erase(pid);
     764              :                         }
     765              :                         else
     766              :                         {
     767          364 :                                 if (print)
     768              :                                 {
     769            0 :                                         std::cout << *pid << " ";
     770              :                                 }
     771          364 :                                 ++pid;
     772              :                         }
     773              :                 }
     774          370 :         };
     775          370 :         auto count_pids = [&]() {
     776          370 :                 std::unique_lock<std::mutex> lk(art_process_mutex_);
     777          740 :                 return pids.size();
     778          370 :         };
     779            6 :         check_pids(false);
     780            6 :         if (count_pids() == 0)
     781              :         {
     782            0 :                 TLOG(TLVL_SHUTDOWN) << "All art processes already exited, nothing to do.";
     783            0 :                 usleep(1000);
     784            6 :                 return;
     785              :         }
     786              : 
     787            6 :         if (!manual_art_)
     788              :         {
     789            6 :                 int graceful_wait_ms = art_event_processing_time_us_ * size() * 10 / 1000;
     790            6 :                 int gentle_wait_ms = art_event_processing_time_us_ * size() * 2 / 1000;
     791            6 :                 int int_wait_ms = art_event_processing_time_us_ * size() / 1000;
     792            6 :                 auto shutdown_start = std::chrono::steady_clock::now();
     793              : 
     794              :                 //              if (!overwrite_mode_)
     795              :                 {
     796           12 :                         TLOG(TLVL_SHUTDOWN) << "Waiting up to " << graceful_wait_ms << " ms for all art processes to exit gracefully";
     797          364 :                         for (int ii = 0; ii < graceful_wait_ms; ++ii)
     798              :                         {
     799          364 :                                 usleep(1000);
     800              : 
     801          364 :                                 check_pids(false);
     802          364 :                                 if (count_pids() == 0)
     803              :                                 {
     804           18 :                                         TLOG(TLVL_INFO) << "All art processes exited after " << TimeUtils::GetElapsedTimeMilliseconds(shutdown_start) << " ms.";
     805            6 :                                         return;
     806              :                                 }
     807              :                         }
     808              :                 }
     809              : 
     810              :                 {
     811            0 :                         TLOG(TLVL_SHUTDOWN) << "Gently informing art processes that it is time to shut down";
     812            0 :                         std::unique_lock<std::mutex> lk(art_process_mutex_);
     813            0 :                         for (auto pid : pids)
     814              :                         {
     815            0 :                                 TLOG(TLVL_SHUTDOWN) << "Sending SIGQUIT to pid " << pid;
     816            0 :                                 kill(pid, SIGQUIT);
     817              :                         }
     818            0 :                 }
     819              : 
     820            0 :                 TLOG(TLVL_SHUTDOWN) << "Waiting up to " << gentle_wait_ms << " ms for all art processes to exit from SIGQUIT";
     821            0 :                 for (int ii = 0; ii < gentle_wait_ms; ++ii)
     822              :                 {
     823            0 :                         usleep(1000);
     824              : 
     825            0 :                         check_pids(false);
     826            0 :                         if (count_pids() == 0)
     827              :                         {
     828            0 :                                 TLOG(TLVL_INFO) << "All art processes exited after " << TimeUtils::GetElapsedTimeMilliseconds(shutdown_start) << " ms (SIGQUIT).";
     829            0 :                                 return;
     830              :                         }
     831              :                 }
     832              : 
     833              :                 {
     834            0 :                         TLOG(TLVL_SHUTDOWN) << "Insisting that the art processes shut down";
     835            0 :                         std::unique_lock<std::mutex> lk(art_process_mutex_);
     836            0 :                         for (auto pid : pids)
     837              :                         {
     838            0 :                                 kill(pid, SIGINT);
     839              :                         }
     840            0 :                 }
     841              : 
     842            0 :                 TLOG(TLVL_SHUTDOWN) << "Waiting up to " << int_wait_ms << " ms for all art processes to exit from SIGINT";
     843            0 :                 for (int ii = 0; ii < int_wait_ms; ++ii)
     844              :                 {
     845            0 :                         usleep(1000);
     846              : 
     847            0 :                         check_pids(false);
     848              : 
     849            0 :                         if (count_pids() == 0)
     850              :                         {
     851            0 :                                 TLOG(TLVL_INFO) << "All art processes exited after " << TimeUtils::GetElapsedTimeMilliseconds(shutdown_start) << " ms (SIGINT).";
     852            0 :                                 return;
     853              :                         }
     854              :                 }
     855              : 
     856            0 :                 TLOG(TLVL_SHUTDOWN) << "Killing remaning art processes with extreme prejudice";
     857            0 :                 while (count_pids() > 0)
     858              :                 {
     859              :                         {
     860            0 :                                 std::unique_lock<std::mutex> lk(art_process_mutex_);
     861            0 :                                 kill(*pids.begin(), SIGKILL);
     862            0 :                                 usleep(1000);
     863            0 :                         }
     864            0 :                         check_pids(false);
     865              :                 }
     866            0 :                 TLOG(TLVL_INFO) << "All art processes exited after " << TimeUtils::GetElapsedTimeMilliseconds(shutdown_start) << " ms (SIGKILL).";
     867              :         }
     868              :         else
     869              :         {
     870            0 :                 std::cout << "Please shut down all art processes, then hit return/enter" << std::endl;
     871            0 :                 while (count_pids() > 0)
     872              :                 {
     873            0 :                         std::cout << "The following PIDs are running: ";
     874            0 :                         check_pids(true);
     875            0 :                         std::cout << std::endl;
     876            0 :                         usleep(500000);
     877              :                 }
     878              :         }
     879              : }
     880              : 
     881            1 : void artdaq::SharedMemoryEventManager::ReconfigureArt(fhicl::ParameterSet art_pset, run_id_t newRun, int n_art_processes)
     882              : {
     883            2 :         TLOG(TLVL_RECONFIGUREART) << "ReconfigureArt BEGIN";
     884            1 :         if (restart_art_ || !always_restart_art_)  // Art is running
     885              :         {
     886            0 :                 endOfData();
     887              :         }
     888           11 :         for (size_t ii = 0; ii < broadcasts_.size(); ++ii)
     889              :         {
     890           10 :                 broadcasts_.MarkBufferEmpty(ii, true);
     891              :         }
     892            1 :         if (newRun == 0)
     893              :         {
     894            1 :                 newRun = run_id_ + 1;
     895              :         }
     896              : 
     897            1 :         if (art_pset != current_art_pset_ || !current_art_config_file_)
     898              :         {
     899            1 :                 current_art_pset_ = art_pset;
     900            1 :                 if (manual_art_)
     901            0 :                         current_art_config_file_ = std::make_shared<art_config_file>(art_pset, GetKey(), GetBroadcastKey());
     902              :                 else
     903            1 :                         current_art_config_file_ = std::make_shared<art_config_file>(art_pset);
     904              :         }
     905              : 
     906            1 :         if (n_art_processes != -1)
     907              :         {
     908            0 :                 TLOG(TLVL_INFO) << "Setting number of art processes to " << n_art_processes;
     909            0 :                 num_art_processes_ = n_art_processes;
     910              :         }
     911            1 :         startRun(newRun);
     912            2 :         TLOG(TLVL_RECONFIGUREART) << "ReconfigureArt END";
     913            1 : }
     914              : 
     915           16 : bool artdaq::SharedMemoryEventManager::endOfData()
     916              : {
     917           16 :         running_ = false;
     918              :         {
     919           16 :                 std::lock_guard<std::mutex> lk(init_fragments_mutex_);
     920           16 :                 init_fragment_map_.clear();
     921           16 :         }
     922           16 :         init_frags_sent_ = false;
     923           32 :         TLOG(TLVL_ENDOFDATA) << "SharedMemoryEventManager::endOfData";
     924           16 :         restart_art_ = false;
     925              : 
     926           16 :         auto start = std::chrono::steady_clock::now();
     927          212 :         auto pendingWriteCount = std::accumulate(buffer_writes_pending_.begin(), buffer_writes_pending_.end(), 0, [](int a, auto& b) { return a + b.second.load(); });
     928           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData: Waiting for " << pendingWriteCount << " pending writes to complete";
     929           26 :         while (pendingWriteCount > 0 && TimeUtils::GetElapsedTimeMicroseconds(start) < 1000000)
     930              :         {
     931           10 :                 usleep(10000);
     932           50 :                 pendingWriteCount = std::accumulate(buffer_writes_pending_.begin(), buffer_writes_pending_.end(), 0, [](int a, auto& b) { return a + b.second.load(); });
     933              :         }
     934              : 
     935           16 :         size_t initialStoreSize = GetOpenEventCount();
     936           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData: Flushing " << initialStoreSize
     937           16 :                              << " stale events from the SharedMemoryEventManager.";
     938           16 :         int counter = initialStoreSize;
     939           20 :         while (!active_buffers_.empty() && counter > 0)
     940              :         {
     941            4 :                 complete_buffer_(*active_buffers_.begin());
     942            4 :                 counter--;
     943              :         }
     944           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData: Done flushing, there are now " << GetOpenEventCount()
     945           16 :                              << " stale events in the SharedMemoryEventManager.";
     946              : 
     947           16 :         size_t incomplete_at_shutdown = active_buffers_.size();
     948           16 :         size_t force_flushed_count = 0;
     949              : 
     950              :         // Grace period: wait up to one stale_buffer_timeout for late fragments to arrive and complete events
     951           16 :         if (!active_buffers_.empty())
     952              :         {
     953            4 :                 auto grace_timeout_us = GetBufferTimeout();
     954            8 :                 TLOG(TLVL_ENDOFDATA) << "endOfData: Waiting up to " << (grace_timeout_us / 1000000.0)
     955            4 :                                      << " s for " << active_buffers_.size() << " incomplete events to finish";
     956            4 :                 start = std::chrono::steady_clock::now();
     957         1016 :                 while (!active_buffers_.empty() && TimeUtils::GetElapsedTimeMicroseconds(start) < grace_timeout_us)
     958              :                 {
     959         1012 :                         usleep(10000);
     960         1012 :                         counter = active_buffers_.size();
     961         2024 :                         while (!active_buffers_.empty() && counter > 0)
     962              :                         {
     963         1012 :                                 complete_buffer_(*active_buffers_.begin());
     964         1012 :                                 counter--;
     965              :                         }
     966              :                 }
     967              : 
     968              :                 // Force-flush any remaining incomplete events
     969            4 :                 if (!active_buffers_.empty())
     970              :                 {
     971            8 :                         TLOG(TLVL_ENDOFDATA) << "endOfData: Force-flushing " << active_buffers_.size() << " incomplete events after grace period";
     972            4 :                         std::unique_lock<std::mutex> lk(sequence_id_mutex_);
     973            4 :                         auto buffers_to_flush = active_buffers_;
     974            8 :                         for (auto buf : buffers_to_flush)
     975              :                         {
     976            8 :                                 if (buffer_writes_pending_[buf].load() != 0)
     977              :                                 {
     978            0 :                                         continue;
     979              :                                 }
     980            4 :                                 auto hdr = getEventHeader_(buf);
     981            4 :                                 if (requests_)
     982              :                                 {
     983            4 :                                         requests_->RemoveRequest(hdr->sequence_id);
     984              :                                 }
     985            4 :                                 active_buffers_.erase(buf);
     986            4 :                                 pending_buffers_.insert(buf);
     987            4 :                                 run_incomplete_event_count_++;
     988            4 :                                 force_flushed_count++;
     989            4 :                                 if (released_incomplete_events_.count(hdr->sequence_id) == 0u)
     990              :                                 {
     991            4 :                                         released_incomplete_events_[hdr->sequence_id] = num_fragments_per_event_ - GetFragmentCountInBuffer(buf);
     992              :                                 }
     993              :                                 else
     994              :                                 {
     995            0 :                                         released_incomplete_events_[hdr->sequence_id] -= GetFragmentCountInBuffer(buf);
     996              :                                 }
     997           12 :                                 TLOG(TLVL_WARNING) << "endOfData: Event " << hdr->sequence_id
     998            4 :                                                    << " is incomplete (missing " << released_incomplete_events_[hdr->sequence_id]
     999            8 :                                                    << " Fragments). Force-releasing to art.";
    1000              :                         }
    1001            4 :                         check_pending_buffers_(lk);
    1002            4 :                 }
    1003              :         }
    1004              : 
    1005           16 :         if (incomplete_at_shutdown == 0)
    1006              :         {
    1007           36 :                 TLOG(TLVL_INFO) << "endOfData summary: All events were complete at shutdown.";
    1008              :         }
    1009            4 :         else if (force_flushed_count == 0)
    1010              :         {
    1011            0 :                 TLOG(TLVL_INFO) << "endOfData summary: " << incomplete_at_shutdown << " incomplete events all completed during grace period.";
    1012              :         }
    1013              :         else
    1014              :         {
    1015           12 :                 TLOG(TLVL_INFO) << "endOfData summary: " << force_flushed_count << " of " << incomplete_at_shutdown
    1016            8 :                                 << " incomplete events were force-flushed after grace period.";
    1017              :         }
    1018              : 
    1019           32 :         TLOG(TLVL_ENDOFDATA) << "Waiting for " << (ReadReadyCount() + (size() - WriteReadyCount(overwrite_mode_))) << " outstanding buffers...";
    1020           16 :         start = std::chrono::steady_clock::now();
    1021           16 :         auto lastReadCount = ReadReadyCount() + (size() - WriteReadyCount(overwrite_mode_));
    1022           16 :         auto end_of_data_wait_us = art_event_processing_time_us_ * (lastReadCount > 0 ? lastReadCount : 1);  // size();
    1023              : 
    1024           16 :         auto outstanding_buffer_wait_time = art_event_processing_time_us_ > 100000 ? 100000 : art_event_processing_time_us_;
    1025              : 
    1026              :         // We will wait until no buffer has been read for the end of data wait seconds, or no art processes are left.
    1027           32 :         while (lastReadCount > 0 && (end_of_data_wait_us == 0 || TimeUtils::GetElapsedTimeMicroseconds(start) < end_of_data_wait_us) && get_art_process_count_() > 0)
    1028              :         {
    1029           16 :                 auto temp = ReadReadyCount() + (size() - WriteReadyCount(overwrite_mode_));
    1030           16 :                 if (temp != lastReadCount)
    1031              :                 {
    1032           12 :                         TLOG(TLVL_ENDOFDATA_2) << "Waiting for " << temp << " outstanding buffers...";
    1033            6 :                         lastReadCount = temp;
    1034            6 :                         start = std::chrono::steady_clock::now();
    1035              :                 }
    1036           16 :                 if (lastReadCount > 0)
    1037              :                 {
    1038           20 :                         TLOG(TLVL_ENDOFDATA_2) << "About to sleep " << outstanding_buffer_wait_time << " us - lastReadCount=" << lastReadCount << " size=" << size() << " end_of_data_wait_us=" << end_of_data_wait_us;
    1039           10 :                         usleep(outstanding_buffer_wait_time);
    1040              :                 }
    1041              :         }
    1042              : 
    1043           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData: After wait for outstanding buffers. Still outstanding: " << lastReadCount << ", time waited: "
    1044           16 :                              << TimeUtils::GetElapsedTime(start) << " s / " << (end_of_data_wait_us / 1000000.0) << " s, art process count: " << get_art_process_count_();
    1045              : 
    1046           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData: Broadcasting EndOfData Fragment";
    1047           16 :         FragmentPtrs broadcast;
    1048           16 :         broadcast.emplace_back(Fragment::eodFrag(GetBufferCount()));
    1049           16 :         bool success = broadcastFragments_(broadcast);
    1050           16 :         if (!success)
    1051              :         {
    1052            0 :                 TLOG(TLVL_ENDOFDATA) << "endOfData: Clearing buffers to make room for EndOfData Fragment";
    1053            0 :                 for (size_t ii = 0; ii < broadcasts_.size(); ++ii)
    1054              :                 {
    1055            0 :                         broadcasts_.MarkBufferEmpty(ii, true);
    1056              :                 }
    1057            0 :                 broadcastFragments_(broadcast);
    1058              :         }
    1059           16 :         auto endOfDataProcessingStart = std::chrono::steady_clock::now();
    1060           22 :         while (get_art_process_count_() > 0)
    1061              :         {
    1062           12 :                 TLOG(TLVL_ENDOFDATA) << "There are " << get_art_process_count_() << " art processes remaining. Proceeding to shutdown.";
    1063              : 
    1064            6 :                 ShutdownArtProcesses(art_processes_);
    1065              :         }
    1066           32 :         TLOG(TLVL_ENDOFDATA) << "It took " << TimeUtils::GetElapsedTime(endOfDataProcessingStart) << " s for all art processes to close after sending EndOfData Fragment";
    1067              : 
    1068              :         // Reset the manager ID counters on both shared memory segments. Manager IDs are only
    1069              :         // valid in the range 0-63 (readers/writers are tracked in 64-bit masks), and IDs are
    1070              :         // never recycled on Detach. Without resetting the broadcast segment as well, its
    1071              :         // counter grows by one for every art process started during the lifetime of this
    1072              :         // process, and after 63 art process launches (e.g. ~13 runs with 5 art processes each)
    1073              :         // every new art process logs "Too many processes attached to shared memory" and
    1074              :         // reader/writer tracking on the broadcast segment becomes unreliable.
    1075           16 :         ResetAttachedCount();
    1076           16 :         broadcasts_.ResetAttachedCount();
    1077              : 
    1078           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData: Clearing buffers";
    1079          114 :         for (size_t ii = 0; ii < size(); ++ii)
    1080              :         {
    1081           98 :                 MarkBufferEmpty(ii, true);
    1082              :         }
    1083              : 
    1084           16 :         released_events_.clear();
    1085           16 :         released_incomplete_events_.clear();
    1086           16 :         std::fill(buffer_sequence_ids_.begin(), buffer_sequence_ids_.end(), Fragment::InvalidSequenceID);
    1087              : 
    1088           32 :         TLOG(TLVL_ENDOFDATA) << "endOfData END";
    1089           48 :         TLOG(TLVL_INFO) << "EndOfData Complete. Final Buffer ID is " << GetLastSeenBufferID() << ".";
    1090           16 :         return true;
    1091           16 : }
    1092              : 
    1093           17 : bool artdaq::SharedMemoryEventManager::startRun(run_id_t runID)
    1094              : {
    1095           17 :         running_ = true;
    1096              :         {
    1097           17 :                 std::lock_guard<std::mutex> lk(init_fragments_mutex_);
    1098           17 :                 init_fragment_map_.clear();
    1099           17 :         }
    1100           17 :         init_frags_sent_ = false;
    1101           17 :         statsHelper_.resetStatistics();
    1102           34 :         TLOG(TLVL_STARTRUN) << "startRun: Clearing broadcast buffers";
    1103          187 :         for (size_t ii = 0; ii < broadcasts_.size(); ++ii)
    1104              :         {
    1105          170 :                 broadcasts_.MarkBufferEmpty(ii, true);
    1106              :         }
    1107           17 :         released_events_.clear();
    1108           17 :         released_incomplete_events_.clear();
    1109           17 :         std::fill(buffer_sequence_ids_.begin(), buffer_sequence_ids_.end(), Fragment::InvalidSequenceID);
    1110              :         // If we fail to start the art processes, abort Start
    1111           17 :         if (!StartArt())
    1112              :         {
    1113            0 :                 return false;
    1114              :         }
    1115           17 :         run_id_ = runID;
    1116           17 :         subrun_id_ = 1;
    1117              :         {
    1118           17 :                 std::unique_lock<std::mutex> lk(subrun_event_map_mutex_);
    1119           17 :                 subrun_event_map_.clear();
    1120           17 :                 subrun_event_map_[0] = 1;
    1121           17 :         }
    1122           17 :         run_event_count_ = 0;
    1123           17 :         run_incomplete_event_count_ = 0;
    1124           17 :         requests_ = std::make_unique<RequestSender>(data_pset_);
    1125           17 :         if (requests_)
    1126              :         {
    1127           17 :                 requests_->SetRunNumber(static_cast<uint32_t>(run_id_));
    1128              :         }
    1129           51 :         if (data_pset_.has_key("routing_token_config"))
    1130              :         {
    1131            0 :                 auto rmPset = data_pset_.get<fhicl::ParameterSet>("routing_token_config");
    1132            0 :                 if (rmPset.get<bool>("use_routing_manager", false))
    1133              :                 {
    1134            0 :                         tokens_ = std::make_unique<TokenSender>(rmPset);
    1135            0 :                         tokens_->SetRunNumber(static_cast<uint32_t>(run_id_));
    1136            0 :                         tokens_->SendRoutingToken(queue_size_, run_id_);
    1137              :                 }
    1138            0 :         }
    1139           34 :         TLOG(TLVL_STARTRUN) << "Starting run " << run_id_
    1140            0 :                             << ", max queue size = "
    1141            0 :                             << queue_size_
    1142            0 :                             << ", queue size = "
    1143           17 :                             << GetLockedBufferCount();
    1144           17 :         if (metricMan)
    1145              :         {
    1146          119 :                 metricMan->sendMetric("Run Number", static_cast<uint64_t>(run_id_), "Run", 1, MetricMode::LastPoint | MetricMode::Persist);
    1147              :         }
    1148           17 :         return true;
    1149              : }
    1150              : 
    1151            1 : bool artdaq::SharedMemoryEventManager::endRun()
    1152              : {
    1153            3 :         TLOG(TLVL_INFO) << "Ending run " << run_id_;
    1154            2 :         TLOG(TLVL_ENDRUN) << "Shutting down RequestSender";
    1155            1 :         requests_.reset(nullptr);
    1156            2 :         TLOG(TLVL_ENDRUN) << "Shutting down TokenSender";
    1157            1 :         tokens_.reset(nullptr);
    1158              : 
    1159            3 :         TLOG(TLVL_INFO) << "Run " << run_id_ << " has ended. There were " << run_event_count_ << " events in this run.";
    1160            1 :         run_event_count_ = 0;
    1161            1 :         run_incomplete_event_count_ = 0;
    1162            1 :         oversize_fragment_count_ = 0;
    1163              :         {
    1164            1 :                 std::unique_lock<std::mutex> lk(subrun_event_map_mutex_);
    1165            1 :                 subrun_event_map_.clear();
    1166            1 :                 subrun_event_map_[0] = 1;
    1167            1 :         }
    1168            1 :         return true;
    1169              : }
    1170              : 
    1171            5 : void artdaq::SharedMemoryEventManager::rolloverSubrun(sequence_id_t boundary, subrun_id_t subrun, bool sendFragment)
    1172              : {
    1173              :         // Generated EndOfSubrun Fragments have Sequence ID 0 and should be ignored
    1174            5 :         if (boundary == 0 || boundary == Fragment::InvalidSequenceID)
    1175              :         {
    1176            2 :                 return;
    1177              :         }
    1178              : 
    1179            5 :         std::unique_lock<std::mutex> lk(subrun_event_map_mutex_);
    1180              : 
    1181              :         // Don't re-rollover to an already-defined subrun
    1182            5 :         if (!subrun_event_map_.empty() && subrun_event_map_.rbegin()->second >= subrun)
    1183              :         {
    1184            2 :                 return;
    1185              :         }
    1186            9 :         TLOG(TLVL_INFO) << "Will roll over to subrun " << subrun << " when I reach Sequence ID " << (boundary + 1);
    1187            3 :         subrun_event_map_[boundary + 1] = subrun;
    1188            3 :         while (subrun_event_map_.size() > max_subrun_event_map_length_)
    1189              :         {
    1190            0 :                 subrun_event_map_.erase(subrun_event_map_.begin());
    1191              :         }
    1192              : 
    1193            3 :         if (sendFragment)
    1194              :         {
    1195            0 :                 auto endOfSubrunFrag = artdaq::MetadataFragment::CreateEndOfSubrunFragment(my_rank, boundary, subrun, 0);
    1196            0 :                 BroadcastFragment(endOfSubrunFrag);
    1197            0 :         }
    1198            5 : }
    1199              : 
    1200            2 : void artdaq::SharedMemoryEventManager::rolloverSubrun(bool sendFragment)
    1201              : {
    1202            2 :         Fragment::sequence_id_t seqID = 0;
    1203            2 :         subrun_id_t subrun = 0;
    1204              :         {
    1205            2 :                 std::unique_lock<std::mutex> lk(subrun_event_map_mutex_);
    1206            5 :                 for (auto& it : subrun_event_map_)
    1207              :                 {
    1208            3 :                         if (it.first >= seqID)
    1209              :                         {
    1210            3 :                                 seqID = it.first + 1;
    1211              :                         }
    1212            3 :                         if (it.second >= subrun)
    1213              :                         {
    1214            3 :                                 subrun = it.second + 1;
    1215              :                         }
    1216              :                 }
    1217            2 :         }
    1218            2 :         rolloverSubrun(seqID, subrun, sendFragment);
    1219            2 : }
    1220              : 
    1221            0 : void artdaq::SharedMemoryEventManager::sendMetrics()
    1222              : {
    1223            0 :         if (metricMan)
    1224              :         {
    1225            0 :                 metricMan->sendMetric("Open Event Count", GetOpenEventCount(), "events", 1, MetricMode::LastPoint);
    1226            0 :                 metricMan->sendMetric("Pending Event Count", GetPendingEventCount(), "events", 1, MetricMode::LastPoint);
    1227              :         }
    1228              : 
    1229            0 :         if (open_event_report_interval_ms_ > 0 && GetLockedBufferCount() != 0u)
    1230              :         {
    1231            0 :                 if (TimeUtils::GetElapsedTimeMilliseconds(last_open_event_report_time_) < static_cast<size_t>(open_event_report_interval_ms_))
    1232              :                 {
    1233            0 :                         return;
    1234              :                 }
    1235              : 
    1236            0 :                 last_open_event_report_time_ = std::chrono::steady_clock::now();
    1237            0 :                 std::ostringstream oss;
    1238            0 :                 oss << "Open Events (expecting " << num_fragments_per_event_ << " Fragments): ";
    1239            0 :                 for (auto& ev : active_buffers_)
    1240              :                 {
    1241            0 :                         auto hdr = getEventHeader_(ev);
    1242            0 :                         oss << hdr->sequence_id << " (has " << GetFragmentCount(hdr->sequence_id) << " Fragments), ";
    1243              :                 }
    1244            0 :                 TLOG(TLVL_SENDMETRICS) << oss.str();
    1245            0 :         }
    1246              : }
    1247              : 
    1248         6095 : artdaq::detail::RawEventHeader* artdaq::SharedMemoryEventManager::getEventHeader_(int buffer)
    1249              : {
    1250         6095 :         return reinterpret_cast<detail::RawEventHeader*>(GetBufferStart(buffer));  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
    1251              : }
    1252              : 
    1253         2565 : artdaq::SharedMemoryEventManager::subrun_id_t artdaq::SharedMemoryEventManager::GetSubrunForSequenceID(Fragment::sequence_id_t seqID)
    1254              : {
    1255         2565 :         subrun_id_t subrun = 1;
    1256         2565 :         if (init_fragment_count_ > 0)
    1257              :         {
    1258            6 :                 TLOG(TLVL_GETSUBRUN) << "init_fragment_count > 0 (processing art events): Decoding subrun from sequenceID " << seqID;
    1259            3 :                 subrun = seqID >> 32;
    1260              :         }
    1261              :         else
    1262              :         {
    1263         2562 :                 std::unique_lock<std::mutex> lk(subrun_event_map_mutex_);
    1264              : 
    1265         5124 :                 TLOG(TLVL_GETSUBRUN) << "GetSubrunForSequenceID BEGIN map size = " << subrun_event_map_.size();
    1266         2562 :                 auto it = subrun_event_map_.begin();
    1267              : 
    1268         5127 :                 while (it->first <= seqID && it != subrun_event_map_.end())
    1269              :                 {
    1270         5130 :                         TLOG(TLVL_GETSUBRUN) << "Map has sequence ID " << it->first << ", subrun " << it->second << " (looking for <= " << seqID << ")";
    1271         2565 :                         subrun = it->second;
    1272         2565 :                         ++it;
    1273              :                 }
    1274         2562 :         }
    1275              : 
    1276         5130 :         TLOG(TLVL_GETSUBRUN) << "GetSubrunForSequenceID returning subrun " << subrun << " for sequence ID " << seqID;
    1277         2565 :         return subrun;
    1278              : }
    1279              : 
    1280         2537 : artdaq::SharedMemoryEventManager::event_id_t artdaq::SharedMemoryEventManager::GetEventIDForFragment(Fragment::sequence_id_t seqID, Fragment::timestamp_t timestamp)
    1281              : {
    1282         2537 :         event_id_t event = 1;
    1283         2537 :         if (init_fragment_count_ > 0)
    1284              :         {
    1285            4 :                 TLOG(TLVL_GETEVENTID) << "init_fragment_count > 0 (processing art events): Decoding event ID from sequenceID " << seqID;
    1286            2 :                 event = seqID & 0xFFFFFFFF;
    1287              :         }
    1288              :         else
    1289              :         {
    1290         2535 :                 sequence_id_t subrun_start = 0;
    1291         2535 :                 if (reset_event_number_for_subruns_)
    1292              :                 {
    1293            0 :                         std::unique_lock<std::mutex> lk(subrun_event_map_mutex_);
    1294            0 :                         TLOG(TLVL_GETEVENTID) << "GetEventIDForFragment BEGIN map size = " << subrun_event_map_.size();
    1295            0 :                         auto it = subrun_event_map_.begin();
    1296            0 :                         while (it->first < seqID && it != subrun_event_map_.end())
    1297              :                         {
    1298            0 :                                 TLOG(TLVL_GETEVENTID) << "Map has sequence ID " << it->first << ", event " << it->second << " (looking for <= " << seqID << ")";
    1299            0 :                                 subrun_start = it->first;
    1300            0 :                                 ++it;
    1301              :                         }
    1302            0 :                 }
    1303              : 
    1304         2535 :                 event = use_sequence_id_for_event_number_ ? (seqID - subrun_start) : (timestamp - subrun_start);
    1305              :         }
    1306         5074 :         TLOG(TLVL_GETEVENTID) << "GetEventIDForFragment returning event ID " << event << " for sequence ID " << seqID;
    1307         2537 :         return event;
    1308              : }
    1309              : 
    1310         9289 : int artdaq::SharedMemoryEventManager::getBufferForSequenceID_(Fragment::sequence_id_t seqID, bool create_new, Fragment::timestamp_t timestamp)
    1311              : {
    1312        18578 :         TLOG(TLVL_GETBUFFER) << "getBufferForSequenceID " << seqID << " BEGIN";
    1313              : 
    1314         9289 :         std::unique_lock<std::mutex> lk(sequence_id_mutex_);
    1315              : 
    1316        18578 :         TLOG(TLVL_GETBUFFER) << "getBufferForSequenceID obtained sequence_id_mutex for seqid=" << seqID;
    1317              : 
    1318              : #if !ART_SUPPORTS_DUPLICATE_EVENTS
    1319         9289 :         if (released_incomplete_events_.count(seqID) != 0u)
    1320              :         {
    1321            6 :                 TLOG(TLVL_ERROR) << "Event " << seqID << " has already been marked \"Incomplete\" and sent to art!";
    1322            2 :                 return -2;
    1323              :         }
    1324         9287 :         if (released_events_.count(seqID) != 0u)
    1325              :         {
    1326            6 :                 TLOG(TLVL_ERROR) << "Event " << seqID << " has already been completed and released to art! Check configuration for inconsistent Fragment count per event!";
    1327            2 :                 return -2;
    1328              :         }
    1329              : #endif
    1330        80864 :         for (size_t ii = 0; ii < size(); ++ii)
    1331              :         {
    1332        75354 :                 if (buffer_sequence_ids_[ii] == seqID)
    1333              :                 {
    1334         7550 :                         TLOG(TLVL_GETBUFFER) << "getBufferForSequenceID " << seqID << " returning buffer " << ii << " (already assigned)";
    1335         3775 :                         return ii;
    1336              :                 }
    1337              :         }
    1338              : 
    1339         5510 :         auto buffers = GetBuffersOwnedByManager();
    1340         5513 :         for (auto& buf : buffers)
    1341              :         {
    1342            3 :                 auto hdr = getEventHeader_(buf);
    1343            3 :                 buffer_sequence_ids_[buf] = hdr->sequence_id;  // Update sequence ID cache
    1344            3 :                 if (hdr->sequence_id == seqID)
    1345              :                 {
    1346            0 :                         TLOG(TLVL_GETBUFFER) << "getBufferForSequenceID " << seqID << " returning " << buf;
    1347            0 :                         return buf;
    1348              :                 }
    1349              :         }
    1350              : 
    1351         5510 :         if (!create_new)
    1352              :         {
    1353            0 :                 return -1;
    1354              :         }
    1355              : 
    1356         5510 :         check_pending_buffers_(lk);
    1357         5510 :         int new_buffer = GetBufferForWriting(false, shared_memory_ordering_ ? static_cast<size_t>(seqID) : 0);
    1358              : 
    1359         5510 :         if (new_buffer == -1)
    1360              :         {
    1361         5067 :                 new_buffer = GetBufferForWriting(overwrite_mode_, shared_memory_ordering_ ? static_cast<size_t>(seqID) : 0);
    1362              :         }
    1363              : 
    1364         5510 :         if (new_buffer == -1)
    1365              :         {
    1366         5067 :                 return -1;
    1367              :         }
    1368          886 :         TLOG(TLVL_BUFLCK) << "getBufferForSequenceID_: obtaining buffer_mutexes lock for buffer " << new_buffer;
    1369          443 :         std::unique_lock<std::mutex> buffer_lk(buffer_mutexes_.at(new_buffer));
    1370          886 :         TLOG(TLVL_BUFLCK) << "getBufferForSequenceID_: obtained buffer_mutexes lock for buffer " << new_buffer;
    1371              : 
    1372          443 :         event_timing_[new_buffer] = std::chrono::steady_clock::now();
    1373              : 
    1374          443 :         auto hdr = getEventHeader_(new_buffer);
    1375          443 :         hdr->is_complete = false;
    1376          443 :         hdr->run_id = run_id_;
    1377          443 :         hdr->subrun_id = GetSubrunForSequenceID(seqID);
    1378          443 :         hdr->event_id = GetEventIDForFragment(seqID, timestamp);
    1379          443 :         hdr->sequence_id = seqID;
    1380          443 :         hdr->timestamp = timestamp;
    1381          443 :         buffer_writes_pending_[new_buffer] = 0;
    1382          443 :         buffer_sequence_ids_[new_buffer] = seqID;
    1383          443 :         IncrementWritePos(new_buffer, sizeof(detail::RawEventHeader));
    1384          443 :         Globals::SetMFIteration("Sequence ID " + std::to_string(seqID));
    1385              : 
    1386          886 :         TLOG(TLVL_BUFFER) << "getBufferForSequenceID placing " << new_buffer << " to active.";
    1387          443 :         active_buffers_.insert(new_buffer);
    1388          886 :         TLOG(TLVL_BUFFER) << "Buffer occupancy now (total,full,reading,empty,pending,active)=("
    1389            0 :                           << size() << ","
    1390            0 :                           << ReadReadyCount() << ","
    1391            0 :                           << WriteReadyCount(true) - WriteReadyCount(false) - ReadReadyCount() << ","
    1392            0 :                           << WriteReadyCount(false) << ","
    1393            0 :                           << pending_buffers_.size() << ","
    1394          443 :                           << active_buffers_.size() << ")";
    1395              : 
    1396          443 :         if (requests_)
    1397              :         {
    1398          443 :                 requests_->AddRequest(seqID, timestamp);
    1399              :         }
    1400          886 :         TLOG(TLVL_GETBUFFER) << "getBufferForSequenceID " << seqID << " returning newly initialized buffer " << new_buffer;
    1401          443 :         return new_buffer;
    1402         9289 : }
    1403              : 
    1404            0 : bool artdaq::SharedMemoryEventManager::hasFragments_(int buffer)
    1405              : {
    1406            0 :         if (buffer == -1)
    1407              :         {
    1408            0 :                 return true;
    1409              :         }
    1410            0 :         if (!CheckBuffer(buffer, BufferSemaphoreFlags::Writing))
    1411              :         {
    1412            0 :                 return true;
    1413              :         }
    1414            0 :         ResetReadPos(buffer);
    1415            0 :         IncrementReadPos(buffer, sizeof(detail::RawEventHeader));
    1416            0 :         return MoreDataInBuffer(buffer);
    1417              : }
    1418              : 
    1419         3107 : void artdaq::SharedMemoryEventManager::complete_buffer_(int buffer)
    1420              : {
    1421         3107 :         auto hdr = getEventHeader_(buffer);
    1422         3107 :         if (hdr != nullptr && hdr->is_complete)
    1423              :         {
    1424          876 :                 TLOG(TLVL_COMPLETEBUFFER) << "complete_buffer_: This fragment completes event " << hdr->sequence_id << ".";
    1425              : 
    1426              :                 {
    1427          876 :                         TLOG(TLVL_BUFFER) << "complete_buffer_ moving " << buffer << " from active to pending.";
    1428              : 
    1429          876 :                         TLOG(TLVL_BUFLCK) << "complete_buffer_: obtaining sequence_id_mutex lock for seqid=" << hdr->sequence_id;
    1430          438 :                         std::unique_lock<std::mutex> lk(sequence_id_mutex_);
    1431          876 :                         TLOG(TLVL_BUFLCK) << "complete_buffer_: obtained sequence_id_mutex lock for seqid=" << hdr->sequence_id;
    1432          438 :                         active_buffers_.erase(buffer);
    1433          438 :                         pending_buffers_.insert(buffer);
    1434          438 :                         released_events_.insert(hdr->sequence_id);
    1435          538 :                         while (released_events_.size() > max_event_list_length_)
    1436              :                         {
    1437          100 :                                 released_events_.erase(released_events_.begin());
    1438              :                         }
    1439              : 
    1440          876 :                         TLOG(TLVL_BUFFER) << "Buffer occupancy now (total,full,reading,empty,pending,active)=("
    1441            0 :                                           << size() << ","
    1442            0 :                                           << ReadReadyCount() << ","
    1443            0 :                                           << WriteReadyCount(true) - WriteReadyCount(false) - ReadReadyCount() << ","
    1444            0 :                                           << WriteReadyCount(false) << ","
    1445            0 :                                           << pending_buffers_.size() << ","
    1446          438 :                                           << active_buffers_.size() << ")";
    1447          438 :                         check_pending_buffers_(lk);
    1448          438 :                 }
    1449          438 :                 if (requests_)
    1450              :                 {
    1451          438 :                         requests_->RemoveRequest(hdr->sequence_id);
    1452              :                 }
    1453              :         }
    1454         3107 :         check_pending_broadcasts_();
    1455         3107 : }
    1456              : 
    1457            0 : bool artdaq::SharedMemoryEventManager::bufferComparator(int bufA, int bufB)
    1458              : {
    1459            0 :         return getEventHeader_(bufA) < getEventHeader_(bufB);
    1460              : }
    1461              : 
    1462           10 : void artdaq::SharedMemoryEventManager::CheckPendingBuffers()
    1463              : {
    1464              :         {
    1465           20 :                 TLOG(TLVL_BUFLCK) << "Obtaining sequence_id_mutex_";
    1466           10 :                 std::unique_lock<std::mutex> lk(sequence_id_mutex_);
    1467           20 :                 TLOG(TLVL_BUFLCK) << "Obtained sequence_id_mutex_";
    1468              : 
    1469           10 :                 check_pending_buffers_(lk);
    1470           10 :         }
    1471           10 :         check_pending_broadcasts_();
    1472           10 : }
    1473              : 
    1474         5962 : void artdaq::SharedMemoryEventManager::check_pending_buffers_(std::unique_lock<std::mutex> const& lock)
    1475              : {
    1476        11924 :         TLOG(TLVL_CHECKPENDINGBUFFERS) << "check_pending_buffers_ BEGIN Locked=" << std::boolalpha << lock.owns_lock();
    1477              : 
    1478         5962 :         auto buffers = GetBuffersOwnedByManager();
    1479         6419 :         for (auto buf : buffers)
    1480              :         {
    1481          457 :                 if (ResetBuffer(buf) && (pending_buffers_.count(buf) == 0u))
    1482              :                 {
    1483            2 :                         TLOG(TLVL_CHECKPENDINGBUFFERS) << "check_pending_buffers_ Incomplete buffer detected, buf=" << buf << " active_bufers_.count(buf)=" << active_buffers_.count(buf) << " buffer_writes_pending_[buf]=" << buffer_writes_pending_[buf].load();
    1484            1 :                         auto hdr = getEventHeader_(buf);
    1485            2 :                         if ((active_buffers_.count(buf) != 0u) && buffer_writes_pending_[buf].load() == 0)
    1486              :                         {
    1487            1 :                                 if (requests_)
    1488              :                                 {
    1489            1 :                                         requests_->RemoveRequest(hdr->sequence_id);
    1490              :                                 }
    1491            2 :                                 TLOG(TLVL_BUFFER) << "check_pending_buffers_ moving buffer " << buf << " from active to pending";
    1492            1 :                                 active_buffers_.erase(buf);
    1493            1 :                                 pending_buffers_.insert(buf);
    1494            2 :                                 TLOG(TLVL_BUFFER) << "Buffer occupancy now (total,full,reading,empty,pending,active)=("
    1495            0 :                                                   << size() << ","
    1496            0 :                                                   << ReadReadyCount() << ","
    1497            0 :                                                   << WriteReadyCount(true) - WriteReadyCount(false) - ReadReadyCount() << ","
    1498            0 :                                                   << WriteReadyCount(false) << ","
    1499            0 :                                                   << pending_buffers_.size() << ","
    1500            1 :                                                   << active_buffers_.size() << ")";
    1501              : 
    1502            1 :                                 run_incomplete_event_count_++;
    1503            1 :                                 if (metricMan)
    1504              :                                 {
    1505            7 :                                         metricMan->sendMetric("Incomplete Event Rate", 1, "events/s", 3, MetricMode::Rate);
    1506              :                                 }
    1507            1 :                                 if (released_incomplete_events_.count(hdr->sequence_id) == 0u)
    1508              :                                 {
    1509            1 :                                         released_incomplete_events_[hdr->sequence_id] = num_fragments_per_event_ - GetFragmentCountInBuffer(buf);
    1510              :                                 }
    1511              :                                 else
    1512              :                                 {
    1513            0 :                                         released_incomplete_events_[hdr->sequence_id] -= GetFragmentCountInBuffer(buf);
    1514              :                                 }
    1515              : 
    1516            3 :                                 TLOG(TLVL_WARNING) << "Event " << hdr->sequence_id
    1517            2 :                                                    << " was opened " << TimeUtils::GetElapsedTime(event_timing_[buf]) << " s ago"
    1518            1 :                                                    << " and has timed out (missing " << released_incomplete_events_[hdr->sequence_id] << " Fragments)."
    1519            2 :                                                    << "Scheduling release to art.";
    1520              :                         }
    1521              :                 }
    1522              :         }
    1523              : 
    1524         5962 :         std::list<int> sorted_buffers(pending_buffers_.begin(), pending_buffers_.end());
    1525         5962 :         sorted_buffers.sort([this](int a, int b) { return bufferComparator(a, b); });
    1526              : 
    1527         5962 :         auto available_buffers = WriteReadyCount(overwrite_mode_);
    1528         5962 :         auto counter = 0;
    1529         5962 :         double eventSize = 0;
    1530         5962 :         double eventTime = 0;
    1531         6405 :         for (auto buf : sorted_buffers)
    1532              :         {
    1533          443 :                 auto hdr = getEventHeader_(buf);
    1534          443 :                 auto thisEventSize = BufferDataSize(buf);
    1535              : 
    1536          443 :                 bool currentSubrun = hdr->subrun_id == subrun_id_;
    1537              : 
    1538          443 :                 if (hdr->subrun_id > subrun_id_ && available_buffers > 0 && TimeUtils::GetElapsedTime(last_event_time_) < subrun_transition_hold_time_s_)
    1539              :                 {
    1540            0 :                         TLOG(TLVL_CHECKPENDINGBUFFERS_4) << "Holding event " << std::to_string(hdr->sequence_id) << " (sr=" << hdr->subrun_id << ") in buffer " << buf << ", "
    1541            0 :                                                          << "event_size=" << thisEventSize << ", buffer_size=" << BufferSize();
    1542            0 :                         continue;
    1543            0 :                 }
    1544              : 
    1545          886 :                 TLOG(TLVL_CHECKPENDINGBUFFERS_4) << "Releasing event " << std::to_string(hdr->sequence_id) << " (sr=" << hdr->subrun_id << ") in buffer " << buf << " to art, "
    1546          443 :                                                  << "event_size=" << thisEventSize << ", buffer_size=" << BufferSize();
    1547          443 :                 statsHelper_.addSample(EVENTS_RELEASED_STAT_KEY, thisEventSize);
    1548              : 
    1549          886 :                 TLOG(TLVL_BUFFER) << "check_pending_buffers_ removing buffer " << buf << " moving from pending to full";
    1550          443 :                 MarkBufferFull(buf);
    1551          443 :                 run_event_count_++;
    1552          443 :                 counter++;
    1553          443 :                 eventSize += thisEventSize;
    1554          443 :                 eventTime += TimeUtils::GetElapsedTime(event_timing_[buf]);
    1555          443 :                 pending_buffers_.erase(buf);
    1556          443 :                 if (currentSubrun)
    1557              :                 {
    1558          442 :                         last_event_time_ = std::chrono::steady_clock::now();
    1559              :                 }
    1560              :         }
    1561        11924 :         TLOG(TLVL_BUFFER) << "Buffer occupancy now (total,full,reading,empty,pending,active)=("
    1562            0 :                           << size() << ","
    1563            0 :                           << ReadReadyCount() << ","
    1564            0 :                           << WriteReadyCount(true) - WriteReadyCount(false) - ReadReadyCount() << ","
    1565            0 :                           << WriteReadyCount(false) << ","
    1566            0 :                           << pending_buffers_.size() << ","
    1567         5962 :                           << active_buffers_.size() << ")";
    1568              : 
    1569         5962 :         if (tokens_ && tokens_->RoutingTokenSendsEnabled())
    1570              :         {
    1571            0 :                 TLOG(TLVL_CHECKPENDINGBUFFERS_3) << "Sent tokens: " << tokens_->GetSentTokenCount() << ", Event count: " << run_event_count_;
    1572            0 :                 auto outstanding_tokens = tokens_->GetSentTokenCount() - run_event_count_;
    1573              : 
    1574            0 :                 TLOG(TLVL_CHECKPENDINGBUFFERS_3) << "check_pending_buffers_: outstanding_tokens: " << outstanding_tokens << ", available_buffers: " << available_buffers
    1575            0 :                                                  << ", tokens_to_send: " << available_buffers - outstanding_tokens;
    1576              : 
    1577            0 :                 if (available_buffers > outstanding_tokens)
    1578              :                 {
    1579            0 :                         auto tokens_to_send = available_buffers - outstanding_tokens;
    1580              : 
    1581            0 :                         while (tokens_to_send > 0)
    1582              :                         {
    1583            0 :                                 TLOG(TLVL_CHECKPENDINGBUFFERS_3) << "check_pending_buffers_: Sending a Routing Token";
    1584            0 :                                 tokens_->SendRoutingToken(1, run_id_);
    1585            0 :                                 tokens_to_send--;
    1586              :                         }
    1587              :                 }
    1588              :         }
    1589              : 
    1590         5962 :         if (statsHelper_.readyToReport())
    1591              :         {
    1592            0 :                 std::string statString = buildStatisticsString_();
    1593            0 :                 TLOG(TLVL_INFO) << statString;
    1594            0 :         }
    1595              : 
    1596         5962 :         if (metricMan)
    1597              :         {
    1598        11924 :                 TLOG(TLVL_CHECKPENDINGBUFFERS_2) << "check_pending_buffers_: Sending Metrics";
    1599        35772 :                 metricMan->sendMetric("Event Rate", counter, "Events", 1, MetricMode::Rate);
    1600        35772 :                 metricMan->sendMetric("Data Rate", eventSize, "Bytes", 1, MetricMode::Rate);
    1601         5962 :                 if (counter > 0)
    1602              :                 {
    1603         2658 :                         metricMan->sendMetric("Average Event Size", eventSize / counter, "Bytes", 1, MetricMode::Average);
    1604         3101 :                         metricMan->sendMetric("Average Event Building Time", eventTime / counter, "s", 1, MetricMode::Average);
    1605              :                 }
    1606              : 
    1607        35772 :                 metricMan->sendMetric("Events Released to art this run", run_event_count_, "Events", 1, MetricMode::LastPoint);
    1608        35772 :                 metricMan->sendMetric("Incomplete Events Released to art this run", run_incomplete_event_count_, "Events", 1, MetricMode::LastPoint);
    1609         5962 :                 if (tokens_ && tokens_->RoutingTokenSendsEnabled())
    1610              :                 {
    1611            0 :                         metricMan->sendMetric("Tokens sent", tokens_->GetSentTokenCount(), "Tokens", 2, MetricMode::LastPoint);
    1612              :                 }
    1613              : 
    1614         5962 :                 auto bufferReport = GetBufferReport();
    1615         5962 :                 int full = 0, empty = 0, writing = 0, reading = 0;
    1616        65428 :                 for (auto& buf : bufferReport)
    1617              :                 {
    1618        59466 :                         switch (buf.second)
    1619              :                         {
    1620        54817 :                                 case BufferSemaphoreFlags::Full:
    1621        54817 :                                         full++;
    1622        54817 :                                         break;
    1623         4548 :                                 case BufferSemaphoreFlags::Empty:
    1624         4548 :                                         empty++;
    1625         4548 :                                         break;
    1626           14 :                                 case BufferSemaphoreFlags::Writing:
    1627           14 :                                         writing++;
    1628           14 :                                         break;
    1629           87 :                                 case BufferSemaphoreFlags::Reading:
    1630           87 :                                         reading++;
    1631           87 :                                         break;
    1632              :                         }
    1633              :                 }
    1634         5962 :                 auto total = size();
    1635        11924 :                 TLOG(TLVL_CHECKPENDINGBUFFERS_2) << "Buffer usage: full=" << full << ", empty=" << empty << ", writing=" << writing << ", reading=" << reading << ", total=" << total;
    1636              : 
    1637        35772 :                 metricMan->sendMetric("Shared Memory Full Buffers", full, "buffers", 2, MetricMode::LastPoint);
    1638        35772 :                 metricMan->sendMetric("Shared Memory Available Buffers", empty, "buffers", 2, MetricMode::LastPoint);
    1639        35772 :                 metricMan->sendMetric("Shared Memory Pending Buffers", writing, "buffers", 2, MetricMode::LastPoint);
    1640        35772 :                 metricMan->sendMetric("Shared Memory Reading Buffers", reading, "buffers", 2, MetricMode::LastPoint);
    1641         5962 :                 if (total > 0)
    1642              :                 {
    1643        35772 :                         metricMan->sendMetric("Shared Memory Full %", full * 100 / static_cast<double>(total), "%", 2, MetricMode::LastPoint);
    1644        41734 :                         metricMan->sendMetric("Shared Memory Available %", empty * 100 / static_cast<double>(total), "%", 2, MetricMode::LastPoint);
    1645              :                 }
    1646         5962 :         }
    1647        11924 :         TLOG(TLVL_CHECKPENDINGBUFFERS) << "check_pending_buffers_ END";
    1648         5962 : }
    1649              : 
    1650            0 : void artdaq::SharedMemoryEventManager::BroadcastFragment(FragmentPtr& frag)
    1651              : {
    1652              :         {
    1653            0 :                 std::lock_guard<std::mutex> lk(broadcast_mutex_);
    1654              : 
    1655            0 :                 bool entry_found = false;
    1656            0 :                 for (auto& entry : pending_broadcasts_)
    1657              :                 {
    1658            0 :                         if (entry.type == frag->type() && entry.sequence_id == frag->sequenceID())
    1659              :                         {
    1660            0 :                                 TLOG(TLVL_BROADCASTFRAGMENT) << "Received BroadcastFragment of type " << static_cast<int>(frag->type()) << ", seqID " << frag->sequenceID() << " matching current pending_broadcasts_ entry. frags=" << entry.fragments.size() + 1 << "/" << init_fragment_count_;
    1661            0 :                                 entry.fragments.push_back(std::move(frag));
    1662            0 :                                 entry_found = true;
    1663            0 :                                 break;
    1664              :                         }
    1665              :                 }
    1666            0 :                 if (!entry_found)
    1667              :                 {
    1668            0 :                         TLOG(TLVL_BROADCASTFRAGMENT) << "Received BroadcastFragment of type " << static_cast<int>(frag->type()) << ", seqID " << frag->sequenceID() << ", creating new pending_broadcasts_ entry";
    1669            0 :                         pending_broadcasts_.emplace_back();
    1670            0 :                         pending_broadcasts_.back().deadline = std::chrono::steady_clock::now() + std::chrono::microseconds(GetBufferTimeout());
    1671            0 :                         pending_broadcasts_.back().type = frag->type();
    1672            0 :                         pending_broadcasts_.back().sequence_id = frag->sequenceID();
    1673            0 :                         pending_broadcasts_.back().subrun_id = GetSubrunForSequenceID(frag->sequenceID());
    1674            0 :                         pending_broadcasts_.back().fragments.push_back(std::move(frag));
    1675              :                 }
    1676            0 :         }
    1677            0 :         check_pending_broadcasts_();
    1678            0 : }
    1679              : 
    1680         3117 : void artdaq::SharedMemoryEventManager::check_pending_broadcasts_()
    1681              : {
    1682         3117 :         std::lock_guard<std::mutex> lk(broadcast_mutex_);
    1683              : 
    1684         3117 :         auto entry = pending_broadcasts_.begin();
    1685         3117 :         auto now = std::chrono::steady_clock::now();
    1686         3117 :         while (entry != pending_broadcasts_.end())
    1687              :         {
    1688            0 :                 if (running_)
    1689              :                 {
    1690            0 :                         if ((!init_frags_sent_ || entry->fragments.size() == 0 || (entry->fragments.size() < init_fragment_count_ && now < entry->deadline)))
    1691              :                         {
    1692            0 :                                 entry++;
    1693            0 :                                 continue;
    1694              :                         }
    1695            0 :                         if (entry->fragments.front()->type() == Fragment::EndOfSubrunFragmentType || entry->fragments.front()->type() == artdaq::Fragment::SubrunDataFragmentType)
    1696              :                         {
    1697            0 :                                 if (entry->subrun_id == subrun_id_ && TimeUtils::GetElapsedTime(last_event_time_) < subrun_transition_hold_time_s_)
    1698              :                                 {
    1699            0 :                                         TLOG(TLVL_CHECKPENDINGBROADCASTS) << "Holding entry size = " << entry->fragments.size() << " / " << init_fragment_count_ << ", lead SeqID = " << entry->fragments.front()->sequenceID() << ", subrun=" << entry->subrun_id << " because it is EndOfSubrun and hold time has not expired";
    1700            0 :                                         entry++;
    1701            0 :                                         continue;
    1702            0 :                                 }
    1703              :                         }
    1704              :                 }
    1705              : 
    1706            0 :                 TLOG(TLVL_CHECKPENDINGBROADCASTS) << "Broadcasting entry init_frags_sent_=" << init_frags_sent_ << ", size=" << entry->fragments.size() << "/" << init_fragment_count_ << ", subrun=" << entry->subrun_id << ", lead SeqID=" << entry->fragments.front()->sequenceID() << " deadline delta=" << std::chrono::duration_cast<std::chrono::microseconds>(now - entry->deadline).count() << " us";
    1707            0 :                 broadcastFragments_(entry->fragments);
    1708            0 :                 entry = pending_broadcasts_.erase(entry);
    1709              :         }
    1710         3117 : }
    1711              : 
    1712           22 : bool artdaq::SharedMemoryEventManager::broadcastFragments_(FragmentPtrs& frags)
    1713              : {
    1714           22 :         if (frags.empty())
    1715              :         {
    1716            0 :                 TLOG(TLVL_ERROR) << "Requested broadcast but no Fragments given!";
    1717            0 :                 return false;
    1718              :         }
    1719           22 :         if (!broadcasts_.IsValid())
    1720              :         {
    1721            0 :                 TLOG(TLVL_ERROR) << "Broadcast attempted but broadcast shared memory is unavailable!";
    1722            0 :                 return false;
    1723              :         }
    1724           44 :         TLOG(TLVL_BROADCASTFRAGMENTS) << "Broadcasting " << frags.size() << " Fragments with lead seqID=" << frags.front()->sequenceID()
    1725            0 :                                       << ", type " << detail::RawFragmentHeader::SystemTypeToString(frags.front()->type())
    1726           22 :                                       << ", size=" << frags.front()->sizeBytes() << "B.";
    1727           22 :         auto buffer = broadcasts_.GetBufferForWriting(false);
    1728           44 :         TLOG(TLVL_BROADCASTFRAGMENTS) << "broadcastFragments_: after getting buffer 1st buffer=" << buffer;
    1729           22 :         auto start_time = std::chrono::steady_clock::now();
    1730           22 :         while (buffer == -1 && TimeUtils::GetElapsedTimeMilliseconds(start_time) < static_cast<size_t>(broadcast_timeout_ms_))
    1731              :         {
    1732            0 :                 usleep(10000);
    1733            0 :                 buffer = broadcasts_.GetBufferForWriting(true);  // Go into overwrite mode
    1734              :         }
    1735           44 :         TLOG(TLVL_BROADCASTFRAGMENTS) << "broadcastFragments_: after getting buffer w/timeout, buffer=" << buffer << ", elapsed time=" << TimeUtils::GetElapsedTime(start_time) << " s.";
    1736           22 :         if (buffer == -1)
    1737              :         {
    1738            0 :                 TLOG(TLVL_ERROR) << "Broadcast of fragment type " << frags.front()->typeString() << " failed due to timeout waiting for buffer!";
    1739            0 :                 return false;
    1740              :         }
    1741              : 
    1742           44 :         TLOG(TLVL_BROADCASTFRAGMENTS) << "broadcastFragments_: Filling in RawEventHeader";
    1743           22 :         auto hdr = reinterpret_cast<detail::RawEventHeader*>(broadcasts_.GetBufferStart(buffer));  // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
    1744           22 :         hdr->run_id = run_id_;
    1745           22 :         hdr->subrun_id = GetSubrunForSequenceID(frags.front()->sequenceID());
    1746           22 :         hdr->sequence_id = frags.front()->sequenceID();
    1747           22 :         hdr->is_complete = true;
    1748           22 :         broadcasts_.IncrementWritePos(buffer, sizeof(detail::RawEventHeader));
    1749              : 
    1750           22 :         if (frags.front()->type() == artdaq::Fragment::EndOfSubrunFragmentType || frags.front()->type() == artdaq::Fragment::SubrunDataFragmentType)
    1751              :         {
    1752            0 :                 subrun_id_ = hdr->subrun_id + 1;
    1753              :         }
    1754              : 
    1755           44 :         for (auto& frag : frags)
    1756              :         {
    1757           22 :                 if (frag->sequenceID() != hdr->sequence_id || frag->type() != frags.front()->type())
    1758              :                 {
    1759            0 :                         TLOG(TLVL_WARNING) << "Skipping fragment due to Type/SeqID mismatch! seqID=" << frag->sequenceID() << " (expected " << hdr->sequence_id << "), type=" << static_cast<int>(frag->type()) << " (" << static_cast<int>(frags.front()->type()) << ")";
    1760            0 :                         continue;
    1761            0 :                 }
    1762           44 :                 TLOG(TLVL_BROADCASTFRAGMENTS_2) << "broadcastFragments_ before Fragment Write call seqID=" << frag->sequenceID() << ", fragID=" << frag->fragmentID() << ", type=" << static_cast<int>(frag->type());
    1763           22 :                 broadcasts_.Write(buffer, frag->headerAddress(), frag->size() * sizeof(RawDataType));
    1764              :         }
    1765              : 
    1766           44 :         TLOG(TLVL_BROADCASTFRAGMENTS) << "broadcastFragments_ Marking buffer full";
    1767           22 :         broadcasts_.MarkBufferFull(buffer, -1);
    1768           44 :         TLOG(TLVL_BROADCASTFRAGMENTS) << "broadcastFragments_ Complete";
    1769           22 :         return true;
    1770              : }
    1771              : 
    1772            0 : std::vector<char*> artdaq::SharedMemoryEventManager::parse_art_command_line_(const std::shared_ptr<art_config_file>& config_file, size_t process_index)
    1773              : {
    1774            0 :         auto offset_index = process_index + art_process_index_offset_;
    1775            0 :         TLOG(TLVL_PARSEARTCOMMANDLINE) << "parse_art_command_line_: Parsing command line " << art_cmdline_ << ", config_file: " << config_file->getFileName() << ", index: " << process_index << " (w/offset: " << offset_index << ")";
    1776            0 :         std::string art_cmdline_tmp = art_cmdline_;
    1777            0 :         auto filenameit = art_cmdline_tmp.find("#CONFIG_FILE#");
    1778            0 :         if (filenameit != std::string::npos)
    1779              :         {
    1780            0 :                 art_cmdline_tmp.replace(filenameit, 13, config_file->getFileName());
    1781              :         }
    1782            0 :         auto indexit = art_cmdline_tmp.find("#PROCESS_INDEX#");
    1783            0 :         if (indexit != std::string::npos)
    1784              :         {
    1785            0 :                 art_cmdline_tmp.replace(indexit, 15, std::to_string(offset_index));
    1786              :         }
    1787            0 :         TLOG(TLVL_PARSEARTCOMMANDLINE) << "parse_art_command_line_: After replacing index and config parameters, command line is " << art_cmdline_tmp;
    1788              : 
    1789            0 :         std::istringstream iss(art_cmdline_tmp);
    1790            0 :         auto tokens = std::vector<std::string>{std::istream_iterator<std::string>{iss}, std::istream_iterator<std::string>{}};
    1791            0 :         std::vector<char*> output;
    1792              : 
    1793            0 :         for (auto& token : tokens)
    1794              :         {
    1795            0 :                 TLOG(TLVL_PARSEARTCOMMANDLINE) << "parse_art_command_line_: Adding cmdline token " << token << " to output list";
    1796            0 :                 output.emplace_back(new char[token.length() + 1]);
    1797            0 :                 memcpy(output.back(), token.c_str(), token.length());
    1798            0 :                 output.back()[token.length()] = '\0';  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
    1799              :         }
    1800            0 :         output.emplace_back(nullptr);
    1801              : 
    1802            0 :         return output;
    1803            0 : }
    1804              : 
    1805            6 : void artdaq::SharedMemoryEventManager::send_init_frags_()
    1806              : {
    1807            6 :         std::lock_guard<std::mutex> lk(init_fragments_mutex_);
    1808            6 :         if (init_fragment_map_size_() >= init_fragment_count_ && init_fragment_count_ > 0)
    1809              :         {
    1810            0 :                 TLOG(TLVL_INFO) << "Broadcasting " << init_fragment_map_size_() << " Init Fragment(s) to all art subprocesses...";
    1811              : 
    1812            0 :                 FragmentPtrs init_fragments;
    1813            0 :                 for (auto& fragment_id_pair : init_fragment_map_)
    1814              :                 {
    1815            0 :                         for (auto& ts_pair : fragment_id_pair.second)
    1816              :                         {
    1817            0 :                                 init_fragments.emplace_back(std::make_unique<Fragment>(*ts_pair.second));
    1818              :                         }
    1819              :                 }
    1820              : 
    1821            0 :                 broadcastFragments_(init_fragments);
    1822            0 :                 TLOG(TLVL_SENDINIT) << "Init Fragment sent";
    1823            0 :                 init_frags_sent_ = true;
    1824            0 :         }
    1825            6 :         else if (init_fragment_count_ > 0 && init_fragment_map_size_() == 0)
    1826              :         {
    1827            0 :                 TLOG(TLVL_INFO) << "Cannot send Init Fragment(s) because I haven't yet received any! Set send_init_fragments to false or init_fragment_count to 0 if this process does not receive serialized art events to avoid potentially lengthy timeouts!";
    1828              :         }
    1829            6 :         else if (init_fragment_count_ > 0)
    1830              :         {
    1831            0 :                 TLOG(TLVL_INFO) << "Cannot send Init Fragment(s) because I haven't yet received them (have " << init_fragment_map_size_() << " of " << init_fragment_count_ << ")!";
    1832              :         }
    1833              :         else
    1834              :         {
    1835              :                 // Send an empty Init Fragment so that ArtdaqInput knows that this is a pure-Fragment input
    1836            6 :                 artdaq::FragmentPtrs begin_run_fragments_;
    1837            6 :                 begin_run_fragments_.emplace_back(new artdaq::Fragment());
    1838            6 :                 begin_run_fragments_.back()->setSystemType(artdaq::Fragment::InitFragmentType);
    1839            6 :                 broadcastFragments_(begin_run_fragments_);
    1840            6 :                 init_frags_sent_ = true;
    1841            6 :         }
    1842            6 : }
    1843              : 
    1844            0 : void artdaq::SharedMemoryEventManager::AddInitFragment(FragmentPtr& frag)
    1845              : {
    1846            0 :         std::unique_lock<std::mutex> lk(init_fragments_mutex_);
    1847              : 
    1848            0 :         auto fragId = frag->fragmentID();
    1849            0 :         auto ts = frag->timestamp();
    1850              : 
    1851            0 :         init_fragment_map_[fragId][ts] = std::move(frag);
    1852            0 :         TLOG(TLVL_ADDINITFRAGMENT) << "Received Init Fragment from rank " << fragId << ", art process id " << ts << ". Now have " << init_fragment_map_size_() << " of " << init_fragment_count_;
    1853              : 
    1854              :         // Don't send until all init fragments have been received
    1855            0 :         if (init_fragment_map_size_() >= init_fragment_count_)
    1856              :         {
    1857            0 :                 lk.unlock();
    1858            0 :                 send_init_frags_();
    1859              :         }
    1860            0 : }
    1861              : 
    1862            6 : size_t artdaq::SharedMemoryEventManager::init_fragment_map_size_() const
    1863              : {
    1864            6 :         size_t size = 0;
    1865              : 
    1866            6 :         for (auto& frag_id_pair : init_fragment_map_)
    1867              :         {
    1868            0 :                 size += frag_id_pair.second.size();
    1869              :         }
    1870              : 
    1871            6 :         return size;
    1872              : }
    1873              : 
    1874            0 : void artdaq::SharedMemoryEventManager::UpdateArtConfiguration(fhicl::ParameterSet art_pset)
    1875              : {
    1876            0 :         TLOG(TLVL_UPDATEARTCONFIG) << "UpdateArtConfiguration BEGIN";
    1877            0 :         if (art_pset != current_art_pset_ || !current_art_config_file_)
    1878              :         {
    1879            0 :                 current_art_pset_ = art_pset;
    1880            0 :                 if (manual_art_)
    1881            0 :                         current_art_config_file_ = std::make_shared<art_config_file>(art_pset, GetKey(), GetBroadcastKey());
    1882              :                 else
    1883            0 :                         current_art_config_file_ = std::make_shared<art_config_file>(art_pset);
    1884              :         }
    1885            0 :         TLOG(TLVL_UPDATEARTCONFIG) << "UpdateArtConfiguration END";
    1886            0 : }
    1887              : 
    1888            0 : std::string artdaq::SharedMemoryEventManager::buildStatisticsString_() const
    1889              : {
    1890            0 :         std::ostringstream oss;
    1891            0 :         oss << app_name << " statistics:" << std::endl;
    1892              : 
    1893              :         artdaq::MonitoredQuantityPtr mqPtr =
    1894            0 :             artdaq::StatisticsCollection::getInstance().getMonitoredQuantity(EVENTS_RELEASED_STAT_KEY);
    1895            0 :         if (mqPtr.get() != nullptr)
    1896              :         {
    1897            0 :                 artdaq::MonitoredQuantityStats stats;
    1898            0 :                 mqPtr->getStats(stats);
    1899            0 :                 oss << "  Event statistics: " << stats.recentSampleCount << " events released at " << stats.recentSampleRate
    1900            0 :                     << " events/sec, effective data rate = "
    1901            0 :                     << (stats.recentValueRate / 1024.0 / 1024.0)
    1902            0 :                     << " MB/sec, monitor window = " << stats.recentDuration
    1903            0 :                     << " sec, min::max event size = " << (stats.recentValueMin / 1024.0 / 1024.0)
    1904            0 :                     << "::" << (stats.recentValueMax / 1024.0 / 1024.0) << " MB" << std::endl;
    1905            0 :                 if (stats.recentSampleRate > 0.0)
    1906              :                 {
    1907            0 :                         oss << "  Average time per event: ";
    1908            0 :                         oss << " elapsed time = " << (1.0 / stats.recentSampleRate) << " sec" << std::endl;
    1909              :                 }
    1910            0 :         }
    1911              : 
    1912            0 :         mqPtr = artdaq::StatisticsCollection::getInstance().getMonitoredQuantity(FRAGMENTS_RECEIVED_STAT_KEY);
    1913            0 :         if (mqPtr.get() != nullptr)
    1914              :         {
    1915            0 :                 artdaq::MonitoredQuantityStats stats;
    1916            0 :                 mqPtr->getStats(stats);
    1917            0 :                 oss << "  Fragment statistics: " << stats.recentSampleCount << " fragments received at " << stats.recentSampleRate
    1918            0 :                     << " fragments/sec, effective data rate = "
    1919            0 :                     << (stats.recentValueRate / 1024.0 / 1024.0)
    1920            0 :                     << " MB/sec, monitor window = " << stats.recentDuration
    1921            0 :                     << " sec, min::max fragment size = " << (stats.recentValueMin / 1024.0 / 1024.0)
    1922            0 :                     << "::" << (stats.recentValueMax / 1024.0 / 1024.0) << " MB" << std::endl;
    1923            0 :         }
    1924              : 
    1925            0 :         oss << "  Event counts: Run -- " << run_event_count_ << " Total, " << run_incomplete_event_count_ << " Incomplete."
    1926            0 :             << "  Subrun -- " << subrun_event_count_ << " Total, " << subrun_incomplete_event_count_ << " Incomplete. "
    1927            0 :             << std::endl;
    1928              :         //-----------------------------------------------------------------------------
    1929              :         // P.Murat: add statistics on the SHM buffers
    1930              :         // there are 4 different flags: 0:empty, 1:writing; 2:full 3:reading
    1931              :         // want statistics on all of them
    1932              :         //-----------------------------------------------------------------------------
    1933              :         // auto = std::vector<std::pair<int, artdaq::SharedMemoryManager::BufferSemaphoreFlags>>
    1934            0 :         artdaq::SharedMemoryEventManager* nc_this = (artdaq::SharedMemoryEventManager*)this;
    1935              : 
    1936            0 :         int bsize = nc_this->BufferSize();
    1937              : 
    1938            0 :         auto v = nc_this->GetBufferReport();
    1939              : 
    1940            0 :         int nbb[4] = {0, 0, 0, 0};
    1941              : 
    1942            0 :         int nbuff = v.size();
    1943            0 :         for (int i = 0; i < nbuff; i++)
    1944              :         {
    1945            0 :                 auto x = v[i];
    1946            0 :                 int flag = (int)x.second;
    1947            0 :                 nbb[flag]++;
    1948              :         }
    1949              : 
    1950            0 :         oss << "  Shared Memory: "
    1951            0 :             << nbuff << " buffers of " << bsize << " B, "
    1952            0 :             << nbb[0] << " Empty, " << nbb[1] << " Writing, " << nbb[2] << " Full, " << nbb[3] << " reading"
    1953            0 :             << std::endl;
    1954              : 
    1955            0 :         return oss.str();
    1956            0 : }
        

Generated by: LCOV version 2.0-1