Skip to content

Commit

Permalink
feat(windows): AFD failure now sources underlying I/O error
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
notgull authored Jan 9, 2024
1 parent 1f13664 commit ea5a38a
Showing 1 changed file with 58 additions and 8 deletions.
66 changes: 58 additions & 8 deletions src/iocp/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,17 +121,19 @@ impl Poller {
pub(super) fn new() -> io::Result<Self> {
// 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::<Packet>::new().map_err(|e| crate::unsupported_error(format!(
"Failed to initialize \\Device\\Afd: {}\nThis usually only happens for old Windows or Wine.",
e,
)))?;
Afd::<Packet>::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");
Expand Down Expand Up @@ -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: FnMut()>(F);

impl<F: FnMut()> Drop for CallOnDrop<F> {
Expand Down

0 comments on commit ea5a38a

Please sign in to comment.