@@ -12,10 +12,11 @@ pub fn spawn_socket_reader(
1212 session_ref : InternalSessionRef < impl OutboundMessage > ,
1313) -> ReaderRef {
1414 let ( dc_sender, dc_receiver) = oneshot:: channel ( ) ;
15+ let ( kill_sender, kill_receiver) = oneshot:: channel ( ) ;
1516 let actor = ReaderActor :: new ( reader, session_ref, dc_sender) ;
16- tokio:: spawn ( run_reader ( actor) ) ;
17+ tokio:: spawn ( run_reader ( actor, kill_receiver ) ) ;
1718
18- ReaderRef :: new ( dc_receiver)
19+ ReaderRef :: new ( dc_receiver, kill_sender )
1920}
2021
2122struct ReaderActor < M , R > {
@@ -38,40 +39,52 @@ impl<M, R: AsyncRead> ReaderActor<M, R> {
3839 }
3940}
4041
41- async fn run_reader < Outbound , R > ( mut actor : ReaderActor < Outbound , R > )
42- where
42+ async fn run_reader < Outbound , R > (
43+ mut actor : ReaderActor < Outbound , R > ,
44+ mut kill_rx : oneshot:: Receiver < ( ) > ,
45+ ) where
4346 Outbound : OutboundMessage ,
4447 R : AsyncRead ,
4548{
4649 let mut parser = Parser :: default ( ) ;
4750 loop {
4851 let mut buf = vec ! [ ] ;
4952
50- match actor. reader . read_buf ( & mut buf) . await {
51- Ok ( 0 ) => {
52- let _ = actor
53- . session_ref
54- . disconnect ( "received EOF" . to_string ( ) )
55- . await ;
56- break ;
57- }
58- Err ( err) => {
59- let _ = actor. session_ref . disconnect ( err. to_string ( ) ) . await ;
60- break ;
61- }
62- Ok ( _) => {
63- let messages = parser. parse ( & buf) ;
64-
65- for msg in messages {
66- if actor
53+ tokio:: select! {
54+ result = actor. reader. read_buf( & mut buf) => match result {
55+ Ok ( 0 ) => {
56+ let _ = actor
6757 . session_ref
68- . new_fix_message_received ( msg)
69- . await
70- . is_err ( )
71- {
72- debug ! ( "reader received message but session has been terminated" ) ;
58+ . disconnect( "received EOF" . to_string( ) )
59+ . await ;
60+ break ;
61+ }
62+ Err ( err) => {
63+ let _ = actor. session_ref. disconnect( err. to_string( ) ) . await ;
64+ break ;
65+ }
66+ Ok ( _) => {
67+ let messages = parser. parse( & buf) ;
68+
69+ for msg in messages {
70+ if actor
71+ . session_ref
72+ . new_fix_message_received( msg)
73+ . await
74+ . is_err( )
75+ {
76+ debug!( "reader received message but session has been terminated" ) ;
77+ }
7378 }
7479 }
80+ } ,
81+ res = & mut kill_rx => {
82+ let reason = match res {
83+ Ok ( ( ) ) => "forced close by watchdog" ,
84+ Err ( _) => "reader handle dropped" ,
85+ } ;
86+ let _ = actor. session_ref. disconnect( reason. to_string( ) ) . await ;
87+ break ;
7588 }
7689 }
7790 }
@@ -228,4 +241,42 @@ mod tests {
228241 // wait for disconnect signal
229242 let _ = reader_ref. wait_for_disconnect ( ) . await ;
230243 }
244+
245+ /// Kill signal terminates the reader even when the peer is silent, and
246+ /// the session observes the watchdog-sourced disconnect reason.
247+ #[ tokio:: test]
248+ async fn kill_signal_terminates_reader ( ) {
249+ let ( _writer, reader) = duplex ( 1024 ) ;
250+ let ( reader_half, _writer_half) = tokio:: io:: split ( reader) ;
251+
252+ let ( session_ref, mut event_receiver) = create_test_session_ref ( ) ;
253+ let reader_ref = spawn_socket_reader ( reader_half, session_ref) ;
254+
255+ // Destructure so we can both fire the kill and later await the disconnect signal.
256+ let ReaderRef {
257+ disconnect_signal,
258+ kill,
259+ } = reader_ref;
260+
261+ kill. send ( ( ) ) . expect ( "kill receiver dropped" ) ;
262+
263+ // Reader should publish the watchdog reason to the session.
264+ match tokio:: time:: timeout (
265+ tokio:: time:: Duration :: from_millis ( 100 ) ,
266+ event_receiver. recv ( ) ,
267+ )
268+ . await
269+ {
270+ Ok ( Some ( SessionEvent :: Disconnected ( reason) ) ) => {
271+ assert_eq ! ( reason, "forced close by watchdog" ) ;
272+ }
273+ other => panic ! ( "expected Disconnected(\" forced close by watchdog\" ), got {other:?}" ) ,
274+ }
275+
276+ // And the disconnect signal should fire shortly after.
277+ tokio:: time:: timeout ( tokio:: time:: Duration :: from_millis ( 100 ) , disconnect_signal)
278+ . await
279+ . expect ( "disconnect signal not fired within timeout" )
280+ . expect ( "disconnect sender dropped without signalling" ) ;
281+ }
231282}
0 commit comments