diff --git a/util/src/io.rs b/util/src/io.rs index 7dde4b4..111959f 100644 --- a/util/src/io.rs +++ b/util/src/io.rs @@ -49,3 +49,62 @@ impl TryIoResultKindHintExt for Result { }) } } + +/// Automatically handles `std::io::ErrorKind::Interrupted`. +/// +/// - If there is no error (i.e. on `Ok(r)`), the function will return `Ok(Some(r))` +/// - `Interrupted` is handled internally, by retrying the IO operation +/// - Other errors are returned as is +pub fn handle_interrupted(mut iofn: F) -> Result, E> +where + E: TryIoErrorKind, + F: FnMut() -> Result, +{ + use io::ErrorKind as E; + loop { + match iofn().try_io_err_kind_hint() { + Ok(v) => return Ok(Some(v)), + Err((_, Some(E::Interrupted))) => continue, // try again + Err((e, _)) => return Err(e), + }; + } +} + +/// Automatically handles `std::io::ErrorKind::{WouldBlock, Interrupted}`. +/// +/// - If there is no error (i.e. on `Ok(r)`), the function will return `Ok(Some(r))` +/// - `Interrupted` is handled internally, by retrying the IO operation +/// - `WouldBlock` is handled by returning `Ok(None)`, +/// - Other errors are returned as is +pub fn nonblocking_handle_io_errors(mut iofn: F) -> Result, E> +where + E: TryIoErrorKind, + F: FnMut() -> Result, +{ + use io::ErrorKind as E; + loop { + match iofn().try_io_err_kind_hint() { + Ok(v) => return Ok(Some(v)), + Err((_, Some(E::WouldBlock))) => return Ok(None), // no data to read + Err((_, Some(E::Interrupted))) => continue, // try again + Err((e, _)) => return Err(e), + }; + } +} + +pub trait ReadNonblockingWithBoringErrorsHandledExt { + /// Convenience wrapper using [nonblocking_handle_io_errors] with [std::io::Read] + fn read_nonblocking_with_boring_errors_handled( + &mut self, + buf: &mut [u8], + ) -> io::Result>; +} + +impl ReadNonblockingWithBoringErrorsHandledExt for T { + fn read_nonblocking_with_boring_errors_handled( + &mut self, + buf: &mut [u8], + ) -> io::Result> { + nonblocking_handle_io_errors(|| self.read(buf)) + } +}