fix(http): Prevent busy looping
We encountered some issues where the `Conn::ready()` would busy loop on reads. Previously, the `ConnInner::can_read_more()` would not consider whether the previous read got a WouldBlock error, and it didn't consider whether the transport was blocked. Accounting for this additional state fixes the busy loop problem.
This commit is contained in:
		| @@ -37,6 +37,10 @@ struct ConnInner<K: Key, T: Transport, H: MessageHandler<T>> { | ||||
|     key: K, | ||||
|     state: State<H, T>, | ||||
|     transport: T, | ||||
|     /// Records a WouldBlock error when trying to read | ||||
|     /// | ||||
|     /// This flag is used to prevent busy looping | ||||
|     read_would_block: bool, | ||||
| } | ||||
|  | ||||
| impl<K: Key, T: Transport, H: MessageHandler<T>> fmt::Debug for ConnInner<K, T, H> { | ||||
| @@ -153,7 +157,10 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> { | ||||
|                     Ok(head) => head, | ||||
|                     Err(::Error::Io(e)) => match e.kind() { | ||||
|                         io::ErrorKind::WouldBlock | | ||||
|                         io::ErrorKind::Interrupted => return state, | ||||
|                         io::ErrorKind::Interrupted => { | ||||
|                             self.read_would_block = true; | ||||
|                             return state; | ||||
|                         }, | ||||
|                         _ => { | ||||
|                             debug!("io error trying to parse {:?}", e); | ||||
|                             return State::Closed; | ||||
| @@ -250,6 +257,7 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> { | ||||
|                     Err(e) => match e.kind() { | ||||
|                         io::ErrorKind::WouldBlock => { | ||||
|                             // This is the expected case reading in this state | ||||
|                             self.read_would_block = true; | ||||
|                             state | ||||
|                         }, | ||||
|                         _ => { | ||||
| @@ -288,7 +296,10 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> { | ||||
|                         }, | ||||
|                         Err(::Error::Io(e)) => match e.kind() { | ||||
|                             io::ErrorKind::WouldBlock | | ||||
|                             io::ErrorKind::Interrupted => None, | ||||
|                             io::ErrorKind::Interrupted => { | ||||
|                                 self.read_would_block = true; | ||||
|                                 None | ||||
|                             }, | ||||
|                             _ => { | ||||
|                                 debug!("io error trying to parse {:?}", e); | ||||
|                                 return State::Closed; | ||||
| @@ -482,10 +493,15 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> { | ||||
|     } | ||||
|  | ||||
|     fn can_read_more(&self, was_init: bool) -> bool { | ||||
|         match self.state { | ||||
|         let transport_blocked = self.transport.blocked().is_some(); | ||||
|         let read_would_block = self.read_would_block; | ||||
|  | ||||
|         let state_machine_ok = match self.state { | ||||
|             State::Init { .. } => !was_init && !self.buf.is_empty(), | ||||
|             _ => !self.buf.is_empty() | ||||
|         } | ||||
|         }; | ||||
|  | ||||
|         !transport_blocked && !read_would_block && state_machine_ok | ||||
|     } | ||||
|  | ||||
|     fn on_error<F>(&mut self, err: ::Error, factory: &F) where F: MessageHandlerFactory<K, T> { | ||||
| @@ -501,6 +517,8 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> ConnInner<K, T, H> { | ||||
|  | ||||
|     fn on_readable<F>(&mut self, scope: &mut Scope<F>) | ||||
|     where F: MessageHandlerFactory<K, T, Output=H> { | ||||
|         // Clear would_block flag so state is clear going into read | ||||
|         self.read_would_block = false; | ||||
|         trace!("on_readable -> {:?}", self.state); | ||||
|         let state = mem::replace(&mut self.state, State::Closed); | ||||
|         self.state = self.read(scope, state); | ||||
| @@ -549,6 +567,7 @@ impl<K: Key, T: Transport, H: MessageHandler<T>> Conn<K, T, H> { | ||||
|                 timeout_start: Some(now), | ||||
|             }, | ||||
|             transport: transport, | ||||
|             read_would_block: false, | ||||
|         })) | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user