LCOV - code coverage report
Current view: top level - ipm/plugins - ZmqSender.cpp (source / functions) Coverage Total Hit
Test: code.result Lines: 66.7 % 93 62
Test Date: 2026-07-12 15:23:06 Functions: 100.0 % 11 11

            Line data    Source code
       1              : /**
       2              :  *
       3              :  * @file ZmqSender.cpp ZmqSender 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 "ipm/Sender.hpp"
      11              : #include "ipm/ZmqContext.hpp"
      12              : 
      13              : #include "logging/Logging.hpp"
      14              : #include "zmq.hpp"
      15              : 
      16              : #include <string>
      17              : #include <vector>
      18              : 
      19              : namespace dunedaq::ipm {
      20              : class ZmqSender : public Sender
      21              : {
      22              : public:
      23           26 :   ZmqSender()
      24           26 :     : m_socket(ZmqContext::instance().GetContext(), zmq::socket_type::push)
      25              :   {
      26           26 :   }
      27              : 
      28           52 :   ~ZmqSender()
      29           26 :   {
      30              :     // Probably (cpp)zmq does this in the socket dtor anyway, but I guess it doesn't hurt to be explicit
      31           26 :     if (m_connection_info.connection_string != "" && m_socket_connected) {
      32           24 :       try {
      33           48 :         TLOG_DEBUG(TLVL_ZMQSENDER_DESTRUCTOR)
      34           24 :           << m_connection_info.connection_name << ": Disconnecting socket from " << m_connection_info.connection_string;
      35              : 
      36              : 
      37           24 :         m_socket.disconnect(m_connection_info.connection_string);
      38           24 :         m_socket_connected = false;
      39            0 :       } catch (zmq::error_t const& err) {
      40            0 :         ers::error(ZmqOperationError(ERS_HERE,
      41            0 :                                      m_connection_info.connection_name,
      42              :                                      "disconnect",
      43              :                                      "send",
      44            0 :                                      err.what(),
      45            0 :                                      m_connection_info.connection_string));
      46            0 :       }
      47              :     }
      48           26 :     m_socket.close();
      49           52 :   }
      50              : 
      51        20037 :   bool can_send() const noexcept override { return m_socket_connected; }
      52           27 :   std::string connect_for_sends(const ConnectionInfo& connection_info) override
      53              :   {
      54           27 :     m_connection_info = connection_info;
      55           27 :     try {
      56           27 :       m_socket.set(zmq::sockopt::sndtimeo, 0); // Return immediately if we can't send
      57            0 :     } catch (zmq::error_t const& err) {
      58            0 :       throw ZmqOperationError(ERS_HERE,
      59            0 :                               m_connection_info.connection_name,
      60              :                               "set timeout",
      61              :                               "send",
      62            0 :                               err.what(),
      63            0 :                               m_connection_info.connection_string);
      64            0 :     }
      65              : 
      66           27 :     auto hwm = connection_info.capacity;
      67           27 :     if (hwm > 0) {
      68            0 :       try {
      69            0 :         m_socket.set(zmq::sockopt::sndhwm, hwm);
      70            0 :       } catch (zmq::error_t const& err) {
      71            0 :         throw ZmqOperationError(ERS_HERE,
      72            0 :                                 m_connection_info.connection_name,
      73              :                                 "set hwm",
      74              :                                 "send",
      75            0 :                                 err.what(),
      76            0 :                                 m_connection_info.connection_string);
      77            0 :       }
      78              :     }
      79              : 
      80           27 :     auto connection_string = m_connection_info.connection_string;
      81              : 
      82           27 :     TLOG_DEBUG(TLVL_CONNECTIONSTRING) << "Connection String is " << connection_string;
      83           27 :     try {
      84           27 :       m_socket.set(zmq::sockopt::immediate, 1); // Don't queue messages to incomplete connections
      85            0 :     } catch (zmq::error_t const& err) {
      86            0 :       throw ZmqOperationError(
      87            0 :         ERS_HERE, m_connection_info.connection_name, "set immediate mode", "send", err.what(), connection_string);
      88            0 :     }
      89              : 
      90           27 :     try {
      91           27 :       m_socket.connect(connection_string);
      92           24 :       m_connection_info.connection_string = m_socket.get(zmq::sockopt::last_endpoint);
      93           24 :       m_socket_connected = true;
      94            3 :     } catch (zmq::error_t const& err) {
      95            3 :       ers::error(ZmqOperationError(
      96            6 :         ERS_HERE, m_connection_info.connection_name, "connect", "send", err.what(), connection_string));
      97            3 :     }
      98              : 
      99           27 :     if (!m_socket_connected) {
     100            3 :       throw ZmqOperationError(ERS_HERE,
     101            3 :                               m_connection_info.connection_name,
     102              :                               "connect",
     103              :                               "send",
     104              :                               "Operation failed for all resolved connection strings",
     105            6 :                               "");
     106              :     }
     107           48 :     return m_connection_info.connection_string;
     108           27 :   }
     109              : 
     110              : protected:
     111        20028 :   bool send_(const void* message,
     112              :              int N,
     113              :              const duration_t& timeout,
     114              :              std::string const& topic,
     115              :              bool no_tmoexcept_mode) override
     116              :   {
     117        20028 :     TLOG_DEBUG(TLVL_ZMQSENDER_SEND_START)
     118        20028 :       << m_connection_info.connection_name << ": Starting send of " << N << " bytes";
     119        20028 :     auto start_time = std::chrono::steady_clock::now();
     120        20028 :     zmq::send_result_t res{};
     121      9736686 :     do {
     122              : 
     123      9736686 :       zmq::message_t topic_msg(topic.c_str(), topic.size());
     124      9736686 :       try {
     125      9736686 :         res = m_socket.send(topic_msg, zmq::send_flags::sndmore);
     126            0 :       } catch (zmq::error_t const& err) {
     127            0 :         throw ZmqSendError(ERS_HERE, m_connection_info.connection_name, err.what(), topic.size(), topic);
     128            0 :       }
     129              : 
     130      9736686 :       if (!res || res != topic.size()) {
     131      9716659 :         TLOG_DEBUG(TLVL_ZMQSENDER_SEND_ERR) << m_connection_info.connection_name << ": Unable to send message";
     132      9716659 :         continue;
     133      9716659 :       }
     134              : 
     135        20027 :       zmq::message_t msg(message, N);
     136        20027 :       try {
     137        20027 :         res = m_socket.send(msg, zmq::send_flags::none);
     138            0 :       } catch (zmq::error_t const& err) {
     139            0 :         throw ZmqSendError(ERS_HERE, m_connection_info.connection_name, err.what(), N, topic);
     140            0 :       }
     141              : 
     142        20027 :       if (!res && timeout > duration_t::zero()) {
     143            0 :         usleep(1000);
     144              :       }
     145     19493400 :     } while (std::chrono::duration_cast<duration_t>(std::chrono::steady_clock::now() - start_time) < timeout && !res);
     146              : 
     147        20028 :     if (!res && !no_tmoexcept_mode) {
     148            1 :       throw SendTimeoutExpired(ERS_HERE, m_connection_info.connection_name, timeout.count());
     149              :     }
     150              : 
     151        20027 :     TLOG_DEBUG(TLVL_ZMQSENDER_SEND_END) << m_connection_info.connection_name << ": Completed send of " << N
     152        20027 :                                         << " bytes";
     153        20027 :     return res && res == N;
     154              :   }
     155              : 
     156              : private:
     157              :   zmq::socket_t m_socket;
     158              :   bool m_socket_connected{ false };
     159              : };
     160              : 
     161              : } // namespace dunedaq::ipm
     162              : 
     163           26 : DEFINE_DUNE_IPM_SENDER(dunedaq::ipm::ZmqSender)
        

Generated by: LCOV version 2.0-1