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 <dev@notgull.net>
This commit is contained in:
John Nunley 2024-01-08 16:34:13 -08:00 committed by GitHub
parent 1f13664bbb
commit ea5a38a500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 58 additions and 8 deletions

View File

@ -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");
@ -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> {