@@ -272,6 +272,15 @@ pub struct Client {
272272 pub interrupt : Arc < Interrupt > ,
273273}
274274
275+ /// How long a blocking read waits before the loop re-reads the cancel flag and the
276+ /// deadline. The socket timeout never bounded a statement: `read_message` checks
277+ /// `Interrupt` on the `WouldBlock` arm and loops, so this is poll granularity, and a
278+ /// long value only delays how soon a stop or a timeout is noticed.
279+ const POLL_INTERVAL : std:: time:: Duration = std:: time:: Duration :: from_millis ( 200 ) ;
280+
281+ /// The connect and TLS handshake run before the poll loop owns the socket.
282+ const HANDSHAKE_TIMEOUT : std:: time:: Duration = std:: time:: Duration :: from_secs ( 10 ) ;
283+
275284impl Client {
276285 /// Create a new client for the given host and port.
277286 pub fn new ( host : & str , port : u16 ) -> Self {
@@ -306,8 +315,11 @@ impl Client {
306315 fn connect_stream ( & mut self , use_ssl : bool ) -> Result < ( ) > {
307316 let addr = format ! ( "{}:{}" , self . host, self . port) ;
308317 let stream = TcpStream :: connect ( & addr) ?;
309- stream. set_read_timeout ( Some ( std:: time:: Duration :: from_secs ( 10 ) ) ) ?;
310- stream. set_write_timeout ( Some ( std:: time:: Duration :: from_secs ( 10 ) ) ) ?;
318+ // The TLS handshake below reads through `native-tls`, which does not retry a
319+ // WouldBlock, so it keeps the long timeout. The poll interval is applied to the
320+ // established stream instead.
321+ stream. set_read_timeout ( Some ( HANDSHAKE_TIMEOUT ) ) ?;
322+ stream. set_write_timeout ( Some ( HANDSHAKE_TIMEOUT ) ) ?;
311323
312324 if use_ssl {
313325 let connector = TlsConnector :: new ( )
@@ -319,6 +331,9 @@ impl Client {
319331 } else {
320332 self . stream = Some ( Stream :: Tcp ( stream) ) ;
321333 }
334+ if let Some ( stream) = self . stream . as_mut ( ) {
335+ stream. set_read_timeout ( Some ( POLL_INTERVAL ) ) ?;
336+ }
322337 Ok ( ( ) )
323338 }
324339
@@ -1180,9 +1195,7 @@ impl Client {
11801195 match stream. read ( & mut tmp) {
11811196 Ok ( 0 ) => return None ,
11821197 Ok ( n) => buf. extend_from_slice ( & tmp[ ..n] ) ,
1183- Err ( e) if e. kind ( ) == ErrorKind :: WouldBlock || e. raw_os_error ( ) == Some ( 35 ) => {
1184- std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
1185- }
1198+ Err ( e) if e. kind ( ) == ErrorKind :: WouldBlock || e. raw_os_error ( ) == Some ( 35 ) => { }
11861199 Err ( e) => return Some ( Err ( Error :: Io ( e) ) ) ,
11871200 }
11881201 }
@@ -1210,9 +1223,7 @@ impl Client {
12101223 ) ) )
12111224 }
12121225 Ok ( n) => buf. extend_from_slice ( & tmp[ ..n] ) ,
1213- Err ( e) if e. kind ( ) == ErrorKind :: WouldBlock || e. raw_os_error ( ) == Some ( 35 ) => {
1214- std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
1215- }
1226+ Err ( e) if e. kind ( ) == ErrorKind :: WouldBlock || e. raw_os_error ( ) == Some ( 35 ) => { }
12161227 Err ( e) => return Some ( Err ( Error :: Io ( e) ) ) ,
12171228 }
12181229 }
@@ -1369,7 +1380,6 @@ impl Client {
13691380 Ok ( n) => break n,
13701381 Err ( e) if e. kind ( ) == ErrorKind :: WouldBlock || e. raw_os_error ( ) == Some ( 35 ) => {
13711382 interrupt. check ( deadline) ?;
1372- std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
13731383 continue ;
13741384 }
13751385 Err ( e) => return Err ( Error :: Io ( e) ) ,
@@ -1400,7 +1410,6 @@ impl Client {
14001410 Ok ( n) => break n,
14011411 Err ( e) if e. kind ( ) == ErrorKind :: WouldBlock || e. raw_os_error ( ) == Some ( 35 ) => {
14021412 interrupt. check ( deadline) ?;
1403- std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 10 ) ) ;
14041413 continue ;
14051414 }
14061415 Err ( e) => return Err ( Error :: Io ( e) ) ,
@@ -1707,4 +1716,15 @@ mod tests {
17071716 assert ! ( checked_lob_len( -1 ) . is_err( ) ) ;
17081717 assert ! ( checked_lob_len( MAX_LOB_BYTES as i64 + 1 ) . is_err( ) ) ;
17091718 }
1719+
1720+ /// A long read timeout is how soon a stop or a deadline is noticed, because
1721+ /// `read_message` only consults `Interrupt` on the `WouldBlock` arm. At ten seconds a
1722+ /// caller's cancel landed nine and a half seconds late and a one second statement
1723+ /// deadline fired at ten. `try_read_message`'s own shorter deadlines were unreachable
1724+ /// for the same reason.
1725+ #[ test]
1726+ fn the_poll_interval_stays_short_enough_to_notice_a_stop ( ) {
1727+ assert ! ( POLL_INTERVAL <= std:: time:: Duration :: from_millis( 250 ) ) ;
1728+ assert ! ( POLL_INTERVAL < HANDSHAKE_TIMEOUT ) ;
1729+ }
17101730}
0 commit comments