@@ -2075,6 +2075,11 @@ impl P2pConnManager {
20752075 tokio:: spawn ( async move {
20762076 peer_connection_listener ( rx, connection, peer_addr, conn_events) . await ;
20772077 } ) ;
2078+ // Yield to allow the spawned peer_connection_listener task to start.
2079+ // This is important because on some runtimes (especially in tests with boxed_local
2080+ // futures), spawned tasks may not be scheduled immediately, causing messages
2081+ // sent to the channel to pile up without being processed.
2082+ tokio:: task:: yield_now ( ) . await ;
20782083 newly_inserted = true ;
20792084 } else {
20802085 tracing:: debug!(
@@ -2669,42 +2674,47 @@ async fn notify_transport_closed(
26692674 }
26702675}
26712676
2677+ /// Listens for messages on a peer connection using drain-then-select pattern.
2678+ ///
2679+ /// On each iteration, drains all pending outbound messages via `try_recv()` before
2680+ /// waiting for either new outbound or inbound messages. This approach:
2681+ /// 1. Ensures queued outbound messages are sent promptly
2682+ /// 2. Avoids starving inbound (which would happen with biased select)
2683+ /// 3. No messages are lost due to poll ordering
26722684async fn peer_connection_listener (
26732685 mut rx : PeerConnChannelRecv ,
26742686 mut conn : PeerConnection ,
26752687 peer_addr : SocketAddr ,
26762688 conn_events : Sender < ConnEvent > ,
26772689) {
2678- const MAX_IMMEDIATE_SENDS : usize = 32 ;
26792690 let remote_addr = conn. remote_addr ( ) ;
26802691 tracing:: debug!(
26812692 to = %remote_addr,
26822693 %peer_addr,
26832694 "[CONN_LIFECYCLE] Starting peer_connection_listener task"
26842695 ) ;
2696+
26852697 loop {
2686- let mut drained = 0 ;
2698+ // Drain all pending outbound messages first before checking inbound.
2699+ // This ensures queued outbound messages are sent promptly without starving
2700+ // the inbound channel (which would happen with biased select).
26872701 loop {
26882702 match rx. try_recv ( ) {
26892703 Ok ( msg) => {
26902704 if let Err ( error) = handle_peer_channel_message ( & mut conn, msg) . await {
26912705 tracing:: debug!(
26922706 to = %remote_addr,
26932707 ?error,
2694- "[CONN_LIFECYCLE] Shutting down connection after send failure "
2708+ "[CONN_LIFECYCLE] Connection closed after channel command "
26952709 ) ;
26962710 notify_transport_closed ( & conn_events, remote_addr, error) . await ;
26972711 return ;
26982712 }
2699- drained += 1 ;
2700- if drained >= MAX_IMMEDIATE_SENDS {
2701- break ;
2702- }
27032713 }
27042714 Err ( TryRecvError :: Empty ) => break ,
27052715 Err ( TryRecvError :: Disconnected ) => {
27062716 tracing:: warn!(
2707- to = %conn . remote_addr( ) ,
2717+ to = %remote_addr,
27082718 "[CONN_LIFECYCLE] peer_connection_listener channel closed without explicit DropConnection"
27092719 ) ;
27102720 notify_transport_closed (
@@ -2718,74 +2728,80 @@ async fn peer_connection_listener(
27182728 }
27192729 }
27202730
2731+ // Now wait for either new outbound or inbound messages fairly
27212732 tokio:: select! {
27222733 msg = rx. recv( ) => {
2723- let Some ( msg) = msg else {
2724- tracing:: warn!(
2725- to = %conn. remote_addr( ) ,
2726- "[CONN_LIFECYCLE] peer_connection_listener channel closed without explicit DropConnection"
2727- ) ;
2728- notify_transport_closed(
2729- & conn_events,
2730- remote_addr,
2731- TransportError :: ConnectionClosed ( remote_addr) ,
2732- )
2733- . await ;
2734- return ;
2735- } ;
2736- if let Err ( error) = handle_peer_channel_message( & mut conn, msg) . await {
2737- tracing:: debug!(
2738- to = %remote_addr,
2739- ?error,
2740- "[CONN_LIFECYCLE] Connection closed after channel command"
2741- ) ;
2742- notify_transport_closed( & conn_events, remote_addr, error) . await ;
2743- return ;
2734+ match msg {
2735+ Some ( msg) => {
2736+ if let Err ( error) = handle_peer_channel_message( & mut conn, msg) . await {
2737+ tracing:: debug!(
2738+ to = %remote_addr,
2739+ ?error,
2740+ "[CONN_LIFECYCLE] Connection closed after channel command"
2741+ ) ;
2742+ notify_transport_closed( & conn_events, remote_addr, error) . await ;
2743+ return ;
2744+ }
2745+ }
2746+ None => {
2747+ tracing:: warn!(
2748+ to = %remote_addr,
2749+ "[CONN_LIFECYCLE] peer_connection_listener channel closed without explicit DropConnection"
2750+ ) ;
2751+ notify_transport_closed(
2752+ & conn_events,
2753+ remote_addr,
2754+ TransportError :: ConnectionClosed ( remote_addr) ,
2755+ )
2756+ . await ;
2757+ return ;
2758+ }
27442759 }
27452760 }
2761+
27462762 msg = conn. recv( ) => {
27472763 match msg {
2748- Ok ( msg) => {
2749- match decode_msg( & msg) {
2750- Ok ( net_message) => {
2751- let tx = * net_message. id( ) ;
2764+ Ok ( msg) => match decode_msg( & msg) {
2765+ Ok ( net_message) => {
2766+ let tx = * net_message. id( ) ;
2767+ tracing:: debug!(
2768+ from = %remote_addr,
2769+ %tx,
2770+ tx_type = ?tx. transaction_type( ) ,
2771+ msg_type = %net_message,
2772+ "[CONN_LIFECYCLE] Received inbound NetMessage from peer"
2773+ ) ;
2774+ if conn_events
2775+ . send( ConnEvent :: InboundMessage ( IncomingMessage :: with_remote(
2776+ net_message,
2777+ remote_addr,
2778+ ) ) )
2779+ . await
2780+ . is_err( )
2781+ {
27522782 tracing:: debug!(
2753- from = %conn. remote_addr( ) ,
2754- %tx,
2755- tx_type = ?tx. transaction_type( ) ,
2756- msg_type = %net_message,
2757- "[CONN_LIFECYCLE] Received inbound NetMessage from peer"
2758- ) ;
2759- if conn_events. send( ConnEvent :: InboundMessage ( IncomingMessage :: with_remote( net_message, remote_addr) ) ) . await . is_err( ) {
2760- tracing:: debug!(
2761- from = %remote_addr,
2762- "[CONN_LIFECYCLE] conn_events receiver dropped; stopping listener"
2763- ) ;
2764- return ;
2765- }
2766- }
2767- Err ( error) => {
2768- tracing:: error!(
2769- from = %conn. remote_addr( ) ,
2770- ?error,
2771- "[CONN_LIFECYCLE] Failed to deserialize inbound message; closing connection"
2783+ from = %remote_addr,
2784+ "[CONN_LIFECYCLE] conn_events receiver dropped; stopping listener"
27722785 ) ;
2773- let transport_error = TransportError :: Other ( anyhow!(
2774- "Failed to deserialize inbound message from {remote_addr}: {error:?}"
2775- ) ) ;
2776- notify_transport_closed(
2777- & conn_events,
2778- remote_addr,
2779- transport_error,
2780- )
2781- . await ;
27822786 return ;
27832787 }
27842788 }
2785- }
2789+ Err ( error) => {
2790+ tracing:: error!(
2791+ from = %remote_addr,
2792+ ?error,
2793+ "[CONN_LIFECYCLE] Failed to deserialize inbound message; closing connection"
2794+ ) ;
2795+ let transport_error = TransportError :: Other ( anyhow!(
2796+ "Failed to deserialize inbound message from {remote_addr}: {error:?}"
2797+ ) ) ;
2798+ notify_transport_closed( & conn_events, remote_addr, transport_error) . await ;
2799+ return ;
2800+ }
2801+ } ,
27862802 Err ( error) => {
27872803 tracing:: debug!(
2788- from = %conn . remote_addr( ) ,
2804+ from = %remote_addr,
27892805 ?error,
27902806 "[CONN_LIFECYCLE] peer_connection_listener terminating after recv error"
27912807 ) ;
0 commit comments