LCOV - code coverage report
Current view: top level - ipm/plugins - ZmqReceiver.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 73.6 % 125 92
Test Date: 2026-10-11 15:05:20 Functions: 100.0 % 15 15

            Line data    Source code
       1              : /**
       2              :  *
       3              :  * @file ZmqReceiver.cpp ZmqReceiver messaging class definitions
       4              :  *
       5              :  * This is part of the DUNE DAQ Application Framework, copyright 2020.
       6              :  * Licensing/copyright details are in the COPYING file that you should have
       7              :  * received with this code.
       8              :  */
       9              : 
      10              : #include "CallbackAdapter.hpp"
      11              : #include "ipm/Receiver.hpp"
      12              : #include "ipm/ZmqContext.hpp"
      13              : 
      14              : #include "logging/Logging.hpp"
      15              : #include "utilities/ZmqUri.hpp"
      16              : 
      17              : #include <string>
      18              : #include <vector>
      19              : 
      20              : namespace dunedaq::ipm {
      21              : 
      22              : class ZmqReceiver : public Receiver
      23              : {
      24              : public:
      25           26 :   ZmqReceiver()
      26           26 :     : m_socket(ZmqContext::instance().GetContext(), zmq::socket_type::pull)
      27              :   {
      28           26 :   }
      29              : 
      30           52 :   ~ZmqReceiver()
      31           26 :   {
      32           26 :     unregister_callback();
      33              :     // Probably (cpp)zmq does this in the socket dtor anyway, but I guess it doesn't hurt to be explicit
      34           26 :     if (m_connection_info.connection_string != "" && m_socket_connected) {
      35           24 :       try {
      36           24 :         m_socket.unbind(m_connection_info.connection_string);
      37           24 :         m_socket_connected = false;
      38            0 :       } catch (zmq::error_t const& err) {
      39            0 :         ers::error(ZmqOperationError(ERS_HERE,
      40            0 :                                      m_connection_info.connection_name,
      41              :                                      "unbind",
      42              :                                      "receive",
      43            0 :                                      err.what(),
      44            0 :                                      m_connection_info.connection_string));
      45            0 :       }
      46              :     }
      47           26 :     m_socket.close();
      48           52 :   }
      49              : 
      50           27 :   std::string connect_for_receives(const ConnectionInfo& connection_info) override
      51              :   {
      52           27 :     m_connection_info = connection_info;
      53           27 :     try {
      54           27 :       m_socket.set(zmq::sockopt::rcvtimeo, 0); // Return immediately if we can't receive
      55            0 :     } catch (zmq::error_t const& err) {
      56            0 :       throw ZmqOperationError(ERS_HERE,
      57            0 :                               m_connection_info.connection_name,
      58              :                               "set timeout",
      59              :                               "receive",
      60            0 :                               err.what(),
      61            0 :                               m_connection_info.connection_string);
      62            0 :     }
      63              : 
      64           27 :     try {
      65           27 :       m_socket.set(zmq::sockopt::linger, 0); // Close connection immediately when close is called
      66            0 :     } catch (zmq::error_t const& err) {
      67            0 :       throw ZmqOperationError(ERS_HERE,
      68            0 :                               m_connection_info.connection_name,
      69              :                               "set linger",
      70              :                               "receive",
      71            0 :                               err.what(),
      72            0 :                               m_connection_info.connection_string);
      73            0 :     }
      74              : 
      75           27 :     std::vector<std::string> resolved;
      76           27 :     try {
      77           27 :       utilities::ZmqUri this_uri(m_connection_info.connection_string);
      78           26 :       resolved = this_uri.get_uri_ip_addresses();
      79           27 :     } catch (utilities::InvalidUri const& err) {
      80            1 :       throw ZmqOperationError(ERS_HERE,
      81            1 :                               m_connection_info.connection_name,
      82              :                               "resolve connection_string",
      83              :                               "receive",
      84              :                               "An invalid URI was passed",
      85              :                               m_connection_info.connection_string,
      86            2 :                               err);
      87            1 :     }
      88              : 
      89           26 :     if (resolved.size() == 0) {
      90            1 :       throw ZmqOperationError(ERS_HERE,
      91            1 :                               m_connection_info.connection_name,
      92              :                               "resolve connection_string",
      93              :                               "receive",
      94              :                               "Unable to resolve connection_string",
      95            2 :                               m_connection_info.connection_string);
      96              :     }
      97           26 :     for (auto& connection_string : resolved) {
      98           25 :       TLOG_DEBUG(TLVL_CONNECTIONSTRING) << m_connection_info.connection_name << ": Connection String is "
      99           25 :                                         << connection_string;
     100           25 :       try {
     101           25 :         m_socket.bind(connection_string);
     102           24 :         m_connection_info.connection_string = m_socket.get(zmq::sockopt::last_endpoint);
     103           24 :         m_socket_connected = true;
     104           24 :         break;
     105            1 :       } catch (zmq::error_t const& err) {
     106            1 :         ers::error(ZmqOperationError(
     107            2 :           ERS_HERE, m_connection_info.connection_name, "bind", "receive", err.what(), connection_string));
     108            1 :       }
     109              :     }
     110           25 :     if (!m_socket_connected) {
     111            1 :       throw ZmqOperationError(ERS_HERE,
     112            1 :                               m_connection_info.connection_name,
     113              :                               "bind",
     114              :                               "receive",
     115              :                               "Bind failed for all resolved connection strings",
     116            2 :                               "");
     117              :     }
     118              : 
     119           24 :     m_callback_adapter.set_receiver(this);
     120              : 
     121           48 :     return m_connection_info.connection_string;
     122           27 :   }
     123              : 
     124        20270 :   bool can_receive() const noexcept override { return m_socket_connected; }
     125              : 
     126            1 :   void register_callback(std::function<void(Response&)> callback) override
     127              :   {
     128            1 :     m_callback_adapter.set_callback(callback);
     129            1 :   }
     130           27 :   void unregister_callback() override { m_callback_adapter.clear_callback(); }
     131              : 
     132            4 :   bool data_pending() override
     133              :   {
     134            4 :     try {
     135            4 :       auto events = m_socket.get(zmq::sockopt::events);
     136            4 :       return (events & ZMQ_POLLIN) != 0;
     137            0 :     } catch (zmq::error_t const& err) {
     138            0 :       ers::error(ZmqOperationError(ERS_HERE,
     139            0 :                                    m_connection_info.connection_name,
     140              :                                    "get events sockopt",
     141              :                                    "data_pending",
     142            0 :                                    err.what(),
     143            0 :                                    m_connection_info.connection_string));
     144            0 :     }
     145            0 :     return false;
     146              :   }
     147              : 
     148              : protected:
     149        20260 :   Receiver::Response receive_(const duration_t& timeout, bool no_tmoexcept_mode) override
     150              :   {
     151        20260 :     Receiver::Response output;
     152        20260 :     zmq::message_t hdr;
     153        20260 :     zmq::message_t msg;
     154        20260 :     zmq::recv_result_t res{};
     155              : 
     156        20260 :     auto start_time = std::chrono::steady_clock::now();
     157        25047 :     do {
     158              : 
     159        25047 :       try {
     160        25047 :         TLOG_DEBUG(TLVL_ZMQRECEIVER_RECV_HDR) << m_connection_info.connection_name << ": Going to receive header";
     161        25041 :         res = m_socket.recv(hdr);
     162        25045 :         TLOG_DEBUG(TLVL_ZMQRECEIVER_RECV_HDR_2) << m_connection_info.connection_name << ": Recv res=" << res.value_or(0)
     163        25043 :                                                 << " for header (hdr.size() == " << hdr.size() << ")";
     164            0 :       } catch (zmq::error_t const& err) {
     165            0 :         throw ZmqReceiveError(ERS_HERE, m_connection_info.connection_name, err.what(), "header");
     166            0 :       }
     167        25045 :       if (res || hdr.more()) {
     168        20026 :         TLOG_DEBUG(TLVL_ZMQRECEIVER_RECV_DATA) << m_connection_info.connection_name << ": Going to receive data";
     169        20026 :         output.metadata.resize(hdr.size());
     170        20026 :         memcpy(&output.metadata[0], hdr.data(), hdr.size());
     171              : 
     172              :         // ZMQ guarantees that the entire message has arrived
     173              : 
     174        20026 :         try {
     175        20026 :           res = m_socket.recv(msg);
     176            0 :         } catch (zmq::error_t const& err) {
     177            0 :           throw ZmqReceiveError(ERS_HERE, m_connection_info.connection_name, err.what(), "data");
     178            0 :         }
     179        20026 :         TLOG_DEBUG(TLVL_ZMQRECEIVER_RECV_DATA_2)
     180            0 :           << m_connection_info.connection_name << ": Recv res=" << res.value_or(0)
     181        20026 :           << " for data (msg.size() == " << msg.size() << ")";
     182        20026 :         output.data.resize(msg.size());
     183        20026 :         memcpy(&output.data[0], msg.data(), msg.size());
     184         5010 :       } else if (timeout > duration_t::zero()) {
     185         5010 :         usleep(1000);
     186              :       }
     187        25041 :     } while (std::chrono::duration_cast<duration_t>(std::chrono::steady_clock::now() - start_time) < timeout &&
     188        45071 :              res.value_or(0) == 0);
     189              : 
     190        20260 :     if (res.value_or(0) == 0 && !no_tmoexcept_mode) {
     191            5 :       throw ReceiveTimeoutExpired(ERS_HERE, m_connection_info.connection_name, timeout.count());
     192              :     }
     193              : 
     194        20255 :     TLOG_DEBUG(TLVL_ZMQRECEIVER_RECV_END)
     195            0 :       << m_connection_info.connection_name << ": Returning output with metadata size " << output.metadata.size()
     196        20255 :       << " and data size " << output.data.size();
     197        20255 :     return output;
     198        20270 :   }
     199              : 
     200              : private:
     201              :   zmq::socket_t m_socket;
     202              :   bool m_socket_connected{ false };
     203              :   CallbackAdapter m_callback_adapter;
     204              : };
     205              : } // namespace dunedaq::ipm
     206              : 
     207           26 : DEFINE_DUNE_IPM_RECEIVER(dunedaq::ipm::ZmqReceiver)
        

Generated by: LCOV version 2.0-1