From ea5a38a5001c2b472f7eb503eff14ad0fe179d43 Mon Sep 17 00:00:00 2001 From: John Nunley Date: Mon, 8 Jan 2024 16:34:13 -0800 Subject: [PATCH] feat(windows): AFD failure now sources underlying I/O error Previously, if AFD failed to initialize `polling` would return a custom I/O error with a string error, containing the formatted version of the underlying system error. However, this means that information about the underlying system error is lost to the user. This commit makes it so the returned `io::Error` wraps a user inaccessible type: `AfdError`. This `AfdError`, when stringified, returns a similar error message as what was previously returned. In addition when `.source()` is used it returns the underlying system error. Closes #174 Signed-off-by: John Nunley --- src/iocp/mod.rs | 66 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/src/iocp/mod.rs b/src/iocp/mod.rs index 3800a2c..5168e76 100644 --- a/src/iocp/mod.rs +++ b/src/iocp/mod.rs @@ -121,17 +121,19 @@ impl Poller { pub(super) fn new() -> io::Result { // Make sure AFD is able to be used. if let Err(e) = afd::NtdllImports::force_load() { - return Err(crate::unsupported_error(format!( - "Failed to initialize unstable Windows functions: {}\nThis usually only happens for old Windows or Wine.", - e - ))); + return Err(io::Error::new( + io::ErrorKind::Unsupported, + AfdError::new("failed to initialize unstable Windows functions", e), + )); } // Create and destroy a single AFD to test if we support it. - Afd::::new().map_err(|e| crate::unsupported_error(format!( - "Failed to initialize \\Device\\Afd: {}\nThis usually only happens for old Windows or Wine.", - e, - )))?; + Afd::::new().map_err(|e| { + io::Error::new( + io::ErrorKind::Unsupported, + AfdError::new("failed to initialize \\Device\\Afd", e), + ) + })?; let port = IoCompletionPort::new(0)?; tracing::trace!(handle = ?port, "new"); @@ -1326,6 +1328,54 @@ fn dur2timeout(dur: Duration) -> u32 { .unwrap_or(INFINITE) } +/// An error type that wraps around failing to open AFD. +struct AfdError { + /// String description of what happened. + description: &'static str, + + /// The underlying system error. + system: io::Error, +} + +impl AfdError { + #[inline] + fn new(description: &'static str, system: io::Error) -> Self { + Self { + description, + system, + } + } +} + +impl fmt::Debug for AfdError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AfdError") + .field("description", &self.description) + .field("system", &self.system) + .field("note", &"probably caused by old Windows or Wine") + .finish() + } +} + +impl fmt::Display for AfdError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}: {}\nThis error is usually caused by running on old Windows or Wine", + self.description, &self.system + ) + } +} + +impl std::error::Error for AfdError { + #[inline] + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + Some(&self.system) + } +} + struct CallOnDrop(F); impl Drop for CallOnDrop {