@@ -45,6 +45,10 @@ pub enum Error {
4545 /// Invalid configuration.
4646 #[ error( "invalid config: {0}" ) ]
4747 Config ( String ) ,
48+ /// The shutdown signal arrived before startup finished. Both halves report
49+ /// this the same way so a stopped run fails identically on either side.
50+ #[ error( "Shutdown before {0}" ) ]
51+ ShutdownDuringStartup ( String ) ,
4852}
4953
5054/// Parameters for [`run_client`].
@@ -171,8 +175,9 @@ async fn wait_for_shutdown_or_failure(
171175///
172176/// # Errors
173177///
174- /// Returns an error if configuration is invalid, the blackhole control port
175- /// is never reachable, or a worker thread reports a fatal error or panics.
178+ /// Returns an error if configuration is invalid, the blackhole control port is
179+ /// never reachable, shutdown arrives before startup finishes, or a worker
180+ /// thread reports a fatal error or panics.
176181pub ( crate ) async fn run_client (
177182 params : ClientParams ,
178183 metric_labels : Vec < ( String , String ) > ,
@@ -278,16 +283,16 @@ pub(crate) async fn run_client(
278283///
279284/// # Errors
280285///
281- /// Returns [`Error::Io`] if the control port never becomes reachable, if
282- /// shutdown fires first, or if the handshake read fails.
286+ /// Returns [`Error::ShutdownDuringStartup`] if shutdown fires first, or
287+ /// [`Error::Io`] if the control port never becomes reachable or the handshake
288+ /// read fails.
283289fn wait_for_blackhole ( control_addr : SocketAddr , shutdown_flag : & AtomicBool ) -> Result < u16 , Error > {
284290 info ! ( "waiting for blackhole control port at {control_addr}" ) ;
285291 let deadline = Instant :: now ( ) + CONTROL_CONNECT_TIMEOUT ;
286292 loop {
287293 if shutdown_flag. load ( Relaxed ) {
288- return Err ( Error :: Io ( io:: Error :: new (
289- ErrorKind :: ConnectionRefused ,
290- format ! ( "shutdown before blackhole control port {control_addr} became reachable" ) ,
294+ return Err ( Error :: ShutdownDuringStartup ( format ! (
295+ "blackhole control port {control_addr} became reachable"
291296 ) ) ) ;
292297 }
293298 match net:: TcpStream :: connect ( control_addr) {
@@ -439,8 +444,9 @@ fn handle_client_event(
439444///
440445/// # Errors
441446///
442- /// Returns an error if binding fails, if a worker thread reports a fatal
443- /// error, or if a worker thread panics.
447+ /// Returns an error if binding fails, if shutdown arrives before the generator
448+ /// connects, if a worker thread reports a fatal error, or if a worker thread
449+ /// panics.
444450pub ( crate ) async fn run_server (
445451 params : ServerParams ,
446452 metric_labels : Vec < ( String , String ) > ,
@@ -510,22 +516,19 @@ pub(crate) async fn run_server(
510516 flag. store ( true , Relaxed ) ;
511517 } ) ;
512518
513- let generator_connected = match wait_for_generator (
519+ if let Err ( e ) = wait_for_generator (
514520 & control_listener,
515521 control_addr,
516522 params. flows ,
517523 & shutdown_flag,
518524 )
519525 . await
520526 {
521- Ok ( connected) => connected,
522- Err ( e) => return Err ( shutdown_and_join ( e, & shutdown_flag, handles) ) ,
523- } ;
527+ return Err ( shutdown_and_join ( e, & shutdown_flag, handles) ) ;
528+ }
524529 drop ( control_listener) ;
525530
526- if generator_connected {
527- wait_for_shutdown_or_failure ( shutdown, & mut fail_rx) . await ;
528- }
531+ wait_for_shutdown_or_failure ( shutdown, & mut fail_rx) . await ;
529532 shutdown_flag. store ( true , Relaxed ) ;
530533
531534 join_workers ( handles) ?;
@@ -639,24 +642,24 @@ async fn prepare_data_listeners(
639642/// Wait for the generator to connect to the control port, then hand it the
640643/// flow count over that connection.
641644///
642- /// Returns `true` once the generator has connected and received the count,
643- /// `false` if shutdown fired before any generator showed up.
644- ///
645645/// # Errors
646646///
647- /// Returns an error if the handshake write fails or `accept` fails for a
647+ /// Returns [`Error::ShutdownDuringStartup`] if shutdown fires before any
648+ /// generator connects, matching how the client reports the same event. Also
649+ /// returns an error if the handshake write fails or `accept` fails for a
648650/// reason other than `WouldBlock`.
649651async fn wait_for_generator (
650652 control_listener : & net:: TcpListener ,
651653 control_addr : SocketAddr ,
652654 flows : u16 ,
653655 shutdown_flag : & AtomicBool ,
654- ) -> Result < bool , Error > {
656+ ) -> Result < ( ) , Error > {
655657 let flows_bytes = flows. to_be_bytes ( ) ;
656658 loop {
657659 if shutdown_flag. load ( Relaxed ) {
658- info ! ( "shutdown before generator connected" ) ;
659- return Ok ( false ) ;
660+ return Err ( Error :: ShutdownDuringStartup (
661+ "the generator connected to the control port" . to_string ( ) ,
662+ ) ) ;
660663 }
661664 match control_listener. accept ( ) {
662665 Ok ( ( mut conn, peer) ) => {
@@ -666,7 +669,7 @@ async fn wait_for_generator(
666669 conn. set_write_timeout ( Some ( HANDSHAKE_TIMEOUT ) ) ?;
667670 conn. write_all ( & flows_bytes) ?;
668671 info ! ( "generator connected from {peer}, sent flows={flows}, data threads running" ) ;
669- return Ok ( true ) ;
672+ return Ok ( ( ) ) ;
670673 }
671674 Err ( ref e) if e. kind ( ) == ErrorKind :: WouldBlock => {
672675 tokio:: time:: sleep ( Duration :: from_millis ( 100 ) ) . await ;
0 commit comments