From 4ea70d0a1c215cbe0918a1064a3433ac685d6a9f Mon Sep 17 00:00:00 2001 From: Ramya Gurunathan Date: Thu, 14 May 2026 14:28:53 -0400 Subject: [PATCH] #75 - Fix Socket UDP --mode both deadlock on peer learning In bidirectional UDP runs (`--mode both`), the server thread spun trying to send before any inbound packet had arrived, so the server had no learned peer and every send attempt logged `UDP server has no learned peer yet; cannot transmit` at ERROR. The log spam back-pressured the client and capped throughput at ~1000 packets / 30s. - examples/socket_bench.cpp: gate the server worker's TX path on having received at least one packet (only when receive is enabled), so the client transmits eagerly and the server learns the peer before it tries to send. - src/managers/socket/daqiri_socket_mgr.{h,cpp}: throttle the "no learned peer" log to fire once per missing-peer episode and downgrade it from ERROR to DEBUG. A new `udp_peer_missing_logged` latch on EndpointState is cleared when the RX loop learns/relearns a peer. Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Ramya Gurunathan --- examples/socket_bench.cpp | 7 ++++++- src/managers/socket/daqiri_socket_mgr.cpp | 6 +++++- src/managers/socket/daqiri_socket_mgr.h | 1 + 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/socket_bench.cpp b/examples/socket_bench.cpp index 690e15f..99c43d1 100644 --- a/examples/socket_bench.cpp +++ b/examples/socket_bench.cpp @@ -96,7 +96,12 @@ void socket_worker(const SocketBenchConfig& cfg, std::atomic& stop, Socket const bool recv_done = !cfg.receive || stats.received_packets >= static_cast(cfg.iterations); if (send_done && recv_done) { break; } - if (cfg.send && !send_done) { + // UDP servers must learn the peer address from an inbound packet before + // they can transmit, so withhold the server's first send until at least + // one RX packet has arrived. The client always transmits eagerly. + const bool waiting_for_peer = cfg.server && cfg.receive && stats.received_packets == 0; + + if (cfg.send && !send_done && !waiting_for_peer) { auto* msg = daqiri::create_tx_burst_params(); daqiri::set_header(msg, port, queue, 1, 1); diff --git a/src/managers/socket/daqiri_socket_mgr.cpp b/src/managers/socket/daqiri_socket_mgr.cpp index 3ccec89..3e4776b 100644 --- a/src/managers/socket/daqiri_socket_mgr.cpp +++ b/src/managers/socket/daqiri_socket_mgr.cpp @@ -739,7 +739,10 @@ bool SocketMgr::send_udp_burst(EndpointState& ep, BurstParams* burst, size_t* se if (ep.socket_cfg.mode_ == SocketMode::SERVER) { std::lock_guard lock(state_mutex_); if (!ep.udp_peer_valid) { - DAQIRI_LOG_ERROR("UDP server has no learned peer yet; cannot transmit"); + if (!ep.udp_peer_missing_logged) { + DAQIRI_LOG_DEBUG("UDP server has no learned peer yet; cannot transmit"); + ep.udp_peer_missing_logged = true; + } return false; } peer = ep.udp_peer_addr; @@ -1285,6 +1288,7 @@ void SocketMgr::udp_rx_loop(int if_index) { std::lock_guard lock(state_mutex_); ep->udp_peer_addr = peers[static_cast(received - 1)]; ep->udp_peer_valid = true; + ep->udp_peer_missing_logged = false; } for (int i = 0; i < received; ++i) { diff --git a/src/managers/socket/daqiri_socket_mgr.h b/src/managers/socket/daqiri_socket_mgr.h index 0027ba1..e56abb8 100644 --- a/src/managers/socket/daqiri_socket_mgr.h +++ b/src/managers/socket/daqiri_socket_mgr.h @@ -146,6 +146,7 @@ class SocketMgr : public Manager { std::shared_ptr rx_queue_state; sockaddr_in udp_peer_addr{}; bool udp_peer_valid = false; + bool udp_peer_missing_logged = false; uintptr_t primary_conn_id = 0; };