diff --git a/Cargo.toml b/Cargo.toml index c4aa68e..d3b1f0d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,9 +36,6 @@ socket2 = { version = "0.5.3", features = ["all"] } tracing = { version = "0.1.37", default-features = false } waker-fn = "1.1.0" -[build-dependencies] -autocfg = "1" - [dev-dependencies] async-channel = "1" async-net = "1" @@ -54,3 +51,7 @@ timerfd = "1" [target.'cfg(windows)'.dev-dependencies] uds_windows = "1" + +[patch.crates-io] +polling = { git = "https://github.com/smol-rs/polling.git", branch = "notgull/unsafe2" } +async-io = { path = "." } diff --git a/build.rs b/build.rs deleted file mode 100644 index 8292525..0000000 --- a/build.rs +++ /dev/null @@ -1,16 +0,0 @@ -fn main() { - let cfg = match autocfg::AutoCfg::new() { - Ok(cfg) => cfg, - Err(e) => { - println!( - "cargo:warning=async-io: failed to detect compiler features: {}", - e - ); - return; - } - }; - - if !cfg.probe_rustc_version(1, 63) { - autocfg::emit("async_io_no_io_safety"); - } -} diff --git a/examples/linux-inotify.rs b/examples/linux-inotify.rs index 376aff4..121b482 100644 --- a/examples/linux-inotify.rs +++ b/examples/linux-inotify.rs @@ -37,17 +37,21 @@ fn main() -> std::io::Result<()> { future::block_on(async { // Watch events in the current directory. let mut inotify = Async::new(Inotify::init()?)?; - inotify - .get_mut() - .watches() - .add(".", WatchMask::ALL_EVENTS)?; + + // SAFETY: We do not move the inner file descriptor out. + unsafe { + inotify + .get_mut() + .watches() + .add(".", WatchMask::ALL_EVENTS)?; + } println!("Watching for filesystem events in the current directory..."); println!("Try opening a file to trigger some events."); println!(); // Wait for events in a loop and print them on the screen. loop { - for event in inotify.read_with_mut(read_op).await? { + for event in unsafe { inotify.read_with_mut(read_op).await? } { println!("{:?}", event); } } diff --git a/examples/windows-uds.rs b/examples/windows-uds.rs index 0980d1b..d538702 100644 --- a/examples/windows-uds.rs +++ b/examples/windows-uds.rs @@ -6,54 +6,6 @@ //! cargo run --example windows-uds //! ``` -#[cfg(windows)] -fn main() -> std::io::Result<()> { - use std::path::PathBuf; - - use async_io::Async; - use blocking::Unblock; - use futures_lite::{future, io, prelude::*}; - use tempfile::tempdir; - use uds_windows::{UnixListener, UnixStream}; - - async fn client(addr: PathBuf) -> io::Result<()> { - // Connect to the address. - let stream = Async::new(UnixStream::connect(addr)?)?; - println!("Connected to {:?}", stream.get_ref().peer_addr()?); - - // Pipe the stream to stdout. - let mut stdout = Unblock::new(std::io::stdout()); - io::copy(&stream, &mut stdout).await?; - Ok(()) - } - - let dir = tempdir()?; - let path = dir.path().join("socket"); - - future::block_on(async { - // Create a listener. - let listener = Async::new(UnixListener::bind(&path)?)?; - println!("Listening on {:?}", listener.get_ref().local_addr()?); - - future::try_zip( - async { - // Accept the client. - let (stream, _) = listener.read_with(|l| l.accept()).await?; - println!("Accepted a client"); - - // Send a message, drop the stream, and wait for the client. - Async::new(stream)?.write_all(b"Hello!\n").await?; - Ok(()) - }, - client(path), - ) - .await?; - - Ok(()) - }) -} - -#[cfg(not(windows))] fn main() { - println!("This example works only on Windows!"); + println!("TODO: Restore this example from git once uds_windows implements I/O safety"); } diff --git a/src/lib.rs b/src/lib.rs index 3aa368c..1f69aaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,26 +70,22 @@ use std::sync::Arc; use std::task::{Context, Poll, Waker}; use std::time::{Duration, Instant}; -#[cfg(all(not(async_io_no_io_safety), unix))] -use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; #[cfg(unix)] use std::{ - os::unix::io::{AsRawFd, RawFd}, + os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}, os::unix::net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream}, path::Path, }; #[cfg(windows)] -use std::os::windows::io::{AsRawSocket, RawSocket}; -#[cfg(all(not(async_io_no_io_safety), windows))] -use std::os::windows::io::{AsSocket, BorrowedSocket, OwnedSocket}; +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; use futures_io::{AsyncRead, AsyncWrite}; use futures_lite::stream::{self, Stream}; use futures_lite::{future, pin, ready}; use socket2::{Domain, Protocol, SockAddr, Socket, Type}; -use crate::reactor::{Reactor, Source}; +use crate::reactor::{Reactor, Registration, Source}; mod driver; mod reactor; @@ -539,6 +535,9 @@ impl Stream for Timer { /// For higher-level primitives built on top of [`Async`], look into [`async-net`] or /// [`async-process`] (on Unix). /// +/// The most notable caveat is that it is unsafe to access the inner I/O source mutably +/// using this function. +/// /// [`async-net`]: https://github.com/smol-rs/async-net /// [`async-process`]: https://github.com/smol-rs/async-process /// @@ -625,7 +624,7 @@ pub struct Async { impl Unpin for Async {} #[cfg(unix)] -impl Async { +impl Async { /// Creates an async I/O handle. /// /// This method will put the handle in non-blocking mode and register it in @@ -651,14 +650,8 @@ impl Async { /// # std::io::Result::Ok(()) }); /// ``` pub fn new(io: T) -> io::Result> { - let raw = io.as_raw_fd(); - // Put the file descriptor in non-blocking mode. - // - // Safety: We assume `as_raw_fd()` returns a valid fd. When we can - // depend on Rust >= 1.63, where `AsFd` is stabilized, and when - // `TimerFd` implements it, we can remove this unsafe and simplify this. - let fd = unsafe { rustix::fd::BorrowedFd::borrow_raw(raw) }; + let fd = io.as_fd(); cfg_if::cfg_if! { // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux // for now, as with the standard library, because it seems to behave @@ -677,8 +670,12 @@ impl Async { } } + // SAFETY: It is impossible to drop the I/O source while it is registered through + // this type. + let registration = unsafe { Registration::new(fd) }; + Ok(Async { - source: Reactor::get().insert_io(raw)?, + source: Reactor::get().insert_io(registration)?, io: Some(io), }) } @@ -691,15 +688,15 @@ impl AsRawFd for Async { } } -#[cfg(all(not(async_io_no_io_safety), unix))] +#[cfg(unix)] impl AsFd for Async { fn as_fd(&self) -> BorrowedFd<'_> { self.get_ref().as_fd() } } -#[cfg(all(not(async_io_no_io_safety), unix))] -impl> TryFrom for Async { +#[cfg(unix)] +impl> TryFrom for Async { type Error = io::Error; fn try_from(value: OwnedFd) -> Result { @@ -707,7 +704,7 @@ impl> TryFrom for Async { } } -#[cfg(all(not(async_io_no_io_safety), unix))] +#[cfg(unix)] impl> TryFrom> for OwnedFd { type Error = io::Error; @@ -717,7 +714,7 @@ impl> TryFrom> for OwnedFd { } #[cfg(windows)] -impl Async { +impl Async { /// Creates an async I/O handle. /// /// This method will put the handle in non-blocking mode and register it in @@ -743,8 +740,7 @@ impl Async { /// # std::io::Result::Ok(()) }); /// ``` pub fn new(io: T) -> io::Result> { - let sock = io.as_raw_socket(); - let borrowed = unsafe { rustix::fd::BorrowedFd::borrow_raw(sock) }; + let borrowed = io.as_socket(); // Put the socket in non-blocking mode. // @@ -753,8 +749,14 @@ impl Async { // `TimerFd` implements it, we can remove this unsafe and simplify this. rustix::io::ioctl_fionbio(borrowed, true)?; + // Create the registration. + // + // SAFETY: It is impossible to drop the I/O source while it is registered through + // this type. + let registration = unsafe { Registration::new(borrowed) }; + Ok(Async { - source: Reactor::get().insert_io(sock)?, + source: Reactor::get().insert_io(registration)?, io: Some(io), }) } @@ -767,15 +769,15 @@ impl AsRawSocket for Async { } } -#[cfg(all(not(async_io_no_io_safety), windows))] +#[cfg(windows)] impl AsSocket for Async { fn as_socket(&self) -> BorrowedSocket<'_> { self.get_ref().as_socket() } } -#[cfg(all(not(async_io_no_io_safety), windows))] -impl> TryFrom for Async { +#[cfg(windows)] +impl> TryFrom for Async { type Error = io::Error; fn try_from(value: OwnedSocket) -> Result { @@ -783,7 +785,7 @@ impl> TryFrom for Async { } } -#[cfg(all(not(async_io_no_io_safety), windows))] +#[cfg(windows)] impl> TryFrom> for OwnedSocket { type Error = io::Error; @@ -812,6 +814,10 @@ impl Async { /// Gets a mutable reference to the inner I/O handle. /// + /// # Safety + /// + /// The underlying I/O source must not be dropped using this function. + /// /// # Examples /// /// ``` @@ -820,10 +826,10 @@ impl Async { /// /// # futures_lite::future::block_on(async { /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; - /// let inner = listener.get_mut(); + /// let inner = unsafe { listener.get_mut() }; /// # std::io::Result::Ok(()) }); /// ``` - pub fn get_mut(&mut self) -> &mut T { + pub unsafe fn get_mut(&mut self) -> &mut T { self.io.as_mut().unwrap() } @@ -1013,6 +1019,10 @@ impl Async { /// /// The closure receives a mutable reference to the I/O handle. /// + /// # Safety + /// + /// In the closure, the underlying I/O source must not be dropped. + /// /// # Examples /// /// ```no_run @@ -1023,10 +1033,10 @@ impl Async { /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; /// /// // Accept a new client asynchronously. - /// let (stream, addr) = listener.read_with_mut(|l| l.accept()).await?; + /// let (stream, addr) = unsafe { listener.read_with_mut(|l| l.accept()).await? }; /// # std::io::Result::Ok(()) }); /// ``` - pub async fn read_with_mut( + pub async unsafe fn read_with_mut( &mut self, op: impl FnMut(&mut T) -> io::Result, ) -> io::Result { @@ -1081,7 +1091,10 @@ impl Async { /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS /// sends a notification that the I/O handle is writable. /// - /// The closure receives a mutable reference to the I/O handle. + /// # Safety + /// + /// The closure receives a mutable reference to the I/O handle. In the closure, the underlying + /// I/O source must not be dropped. /// /// # Examples /// @@ -1094,10 +1107,10 @@ impl Async { /// socket.get_ref().connect("127.0.0.1:9000")?; /// /// let msg = b"hello"; - /// let len = socket.write_with_mut(|s| s.send(msg)).await?; + /// let len = unsafe { socket.write_with_mut(|s| s.send(msg)).await? }; /// # std::io::Result::Ok(()) }); /// ``` - pub async fn write_with_mut( + pub async unsafe fn write_with_mut( &mut self, op: impl FnMut(&mut T) -> io::Result, ) -> io::Result { @@ -1118,12 +1131,6 @@ impl AsRef for Async { } } -impl AsMut for Async { - fn as_mut(&mut self) -> &mut T { - self.get_mut() - } -} - impl Drop for Async { fn drop(&mut self) { if self.io.is_some() { @@ -1136,14 +1143,45 @@ impl Drop for Async { } } -impl AsyncRead for Async { +/// Types whose I/O trait implementations do not drop the underlying I/O source. +/// +/// # Safety +/// +/// Any I/O trait implementations for this type must not drop the underlying I/O source. +pub unsafe trait IoSafe {} + +// Reference types can't be mutated. +unsafe impl IoSafe for &T {} +unsafe impl IoSafe for std::rc::Rc {} +unsafe impl IoSafe for Arc {} + +// Can be implemented on top of libstd types. +unsafe impl IoSafe for std::fs::File {} +unsafe impl IoSafe for std::io::Stderr {} +unsafe impl IoSafe for std::io::Stdin {} +unsafe impl IoSafe for std::io::Stdout {} +unsafe impl IoSafe for std::io::StderrLock<'_> {} +unsafe impl IoSafe for std::io::StdinLock<'_> {} +unsafe impl IoSafe for std::io::StdoutLock<'_> {} +unsafe impl IoSafe for std::net::TcpStream {} + +#[cfg(unix)] +unsafe impl IoSafe for std::os::unix::net::UnixStream {} + +unsafe impl IoSafe for std::io::BufReader {} +unsafe impl IoSafe for std::io::BufWriter {} +unsafe impl IoSafe for std::io::LineWriter {} +unsafe impl IoSafe for &mut T {} +unsafe impl IoSafe for Box {} + +impl AsyncRead for Async { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { loop { - match (*self).get_mut().read(buf) { + match unsafe { (*self).get_mut() }.read(buf) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } @@ -1157,7 +1195,7 @@ impl AsyncRead for Async { bufs: &mut [IoSliceMut<'_>], ) -> Poll> { loop { - match (*self).get_mut().read_vectored(bufs) { + match unsafe { (*self).get_mut() }.read_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } @@ -1166,6 +1204,8 @@ impl AsyncRead for Async { } } +// Since this is through a reference, we can't mutate the inner I/O source. +// Therefore this is safe! impl AsyncRead for &Async where for<'a> &'a T: Read, @@ -1199,14 +1239,14 @@ where } } -impl AsyncWrite for Async { +impl AsyncWrite for Async { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { loop { - match (*self).get_mut().write(buf) { + match unsafe { (*self).get_mut() }.write(buf) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } @@ -1220,7 +1260,7 @@ impl AsyncWrite for Async { bufs: &[IoSlice<'_>], ) -> Poll> { loop { - match (*self).get_mut().write_vectored(bufs) { + match unsafe { (*self).get_mut() }.write_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } @@ -1230,7 +1270,7 @@ impl AsyncWrite for Async { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { - match (*self).get_mut().flush() { + match unsafe { (*self).get_mut() }.flush() { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } diff --git a/src/os/kqueue.rs b/src/os/kqueue.rs index 78b026e..36d543b 100644 --- a/src/os/kqueue.rs +++ b/src/os/kqueue.rs @@ -8,14 +8,11 @@ use crate::Async; use std::convert::{TryFrom, TryInto}; use std::future::Future; use std::io::{Error, Result}; -use std::os::unix::io::{AsRawFd, RawFd}; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::pin::Pin; use std::process::Child; use std::task::{Context, Poll}; -#[cfg(not(async_io_no_io_safety))] -use std::os::unix::io::{AsFd, BorrowedFd, OwnedFd}; - /// A wrapper around a queueable object that waits until it is ready. /// /// The underlying `kqueue` implementation can be used to poll for events besides file descriptor @@ -34,7 +31,7 @@ impl AsRef for Filter { impl AsMut for Filter { fn as_mut(&mut self) -> &mut T { - self.0.as_mut() + self.get_mut() } } @@ -72,15 +69,13 @@ impl AsRawFd for Filter { } } -#[cfg(not(async_io_no_io_safety))] impl AsFd for Filter { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() } } -#[cfg(not(async_io_no_io_safety))] -impl> TryFrom for Filter { +impl> TryFrom for Filter { type Error = Error; fn try_from(fd: OwnedFd) -> Result { @@ -88,7 +83,6 @@ impl> TryFrom for Filter { } } -#[cfg(not(async_io_no_io_safety))] impl> TryFrom> for OwnedFd { type Error = Error; @@ -117,6 +111,9 @@ impl Filter { /// Gets a mutable reference to the underlying [`Queueable`] object. /// + /// Unlike in [`Async`], this method is safe to call, since dropping the [`Filter`] will + /// not cause any undefined behavior. + /// /// # Examples /// /// ``` @@ -129,7 +126,7 @@ impl Filter { /// # }); /// ``` pub fn get_mut(&mut self) -> &mut T { - self.0.get_mut() + unsafe { self.0.get_mut() } } /// Unwraps the inner [`Queueable`] object. diff --git a/src/reactor.rs b/src/reactor.rs index 3e39534..b93f20d 100644 --- a/src/reactor.rs +++ b/src/reactor.rs @@ -117,13 +117,13 @@ impl Reactor { } /// Registers an I/O source in the reactor. - pub(crate) fn insert_io(&self, raw: impl Into) -> io::Result> { + pub(crate) fn insert_io(&self, raw: Registration) -> io::Result> { // Create an I/O source for this file descriptor. let source = { let mut sources = self.sources.lock().unwrap(); let key = sources.vacant_entry().key(); let source = Arc::new(Source { - registration: raw.into(), + registration: raw, key, state: Default::default(), }); diff --git a/src/reactor/kqueue.rs b/src/reactor/kqueue.rs index 4c575a2..46c47e2 100644 --- a/src/reactor/kqueue.rs +++ b/src/reactor/kqueue.rs @@ -7,7 +7,7 @@ use polling::{Event, PollMode, Poller}; use std::fmt; use std::io::Result; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::process::Child; /// The raw registration into the reactor. @@ -16,6 +16,12 @@ use std::process::Child; #[doc(hidden)] pub enum Registration { /// Raw file descriptor for readability/writability. + /// + /// + /// # Invariant + /// + /// This describes a valid file descriptor that has not been `close`d. It will not be + /// closed while this object is alive. Fd(RawFd), /// Raw signal number for signal delivery. @@ -35,19 +41,24 @@ impl fmt::Debug for Registration { } } -impl From for Registration { - #[inline] - fn from(raw: RawFd) -> Self { - Self::Fd(raw) +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: impl AsFd) -> Self { + Self::Fd(f.as_fd().as_raw_fd()) } -} -impl Registration { /// Registers the object into the reactor. #[inline] pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { match self { - Self::Fd(raw) => poller.add(*raw, Event::none(token)), + Self::Fd(raw) => { + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(*raw, Event::none(token)) } + } Self::Signal(signal) => { poller.add_filter(PollSignal(signal.0), token, PollMode::Oneshot) } @@ -63,7 +74,11 @@ impl Registration { #[inline] pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { match self { - Self::Fd(raw) => poller.modify(*raw, interest), + Self::Fd(raw) => { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; + poller.modify(fd, interest) + } Self::Signal(signal) => { poller.modify_filter(PollSignal(signal.0), interest.key, PollMode::Oneshot) } @@ -79,7 +94,11 @@ impl Registration { #[inline] pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { match self { - Self::Fd(raw) => poller.delete(*raw), + Self::Fd(raw) => { + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; + poller.delete(fd) + } Self::Signal(signal) => poller.delete_filter(PollSignal(signal.0)), Self::Process(process) => poller.delete_filter(Process::new(process, ProcessOps::Exit)), } diff --git a/src/reactor/unix.rs b/src/reactor/unix.rs index 2db2437..b2f9b1b 100644 --- a/src/reactor/unix.rs +++ b/src/reactor/unix.rs @@ -4,12 +4,17 @@ use polling::{Event, Poller}; use std::fmt; use std::io::Result; -use std::os::unix::io::RawFd; +use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; /// The raw registration into the reactor. #[doc(hidden)] pub struct Registration { /// Raw file descriptor on Unix. + /// + /// # Invariant + /// + /// This describes a valid file descriptor that has not been `close`d. It will not be + /// closed while this object is alive. raw: RawFd, } @@ -19,29 +24,38 @@ impl fmt::Debug for Registration { } } -impl From for Registration { - #[inline] - fn from(raw: RawFd) -> Self { - Self { raw } +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: impl AsFd) -> Self { + Self { + raw: f.as_fd().as_raw_fd(), + } } -} -impl Registration { /// Registers the object into the reactor. #[inline] pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { - poller.add(self.raw, Event::none(token)) + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(self.raw, Event::none(token)) } } /// Re-registers the object into the reactor. #[inline] pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { - poller.modify(self.raw, interest) + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; + poller.modify(fd, interest) } /// Deregisters the object from the reactor. #[inline] pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { - poller.delete(self.raw) + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; + poller.delete(fd) } } diff --git a/src/reactor/windows.rs b/src/reactor/windows.rs index 59f247f..1c92f00 100644 --- a/src/reactor/windows.rs +++ b/src/reactor/windows.rs @@ -3,12 +3,17 @@ use polling::{Event, Poller}; use std::fmt; use std::io::Result; -use std::os::windows::io::RawSocket; +use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, RawSocket}; /// The raw registration into the reactor. #[doc(hidden)] pub struct Registration { /// Raw socket handle on Windows. + /// + /// # Invariant + /// + /// This describes a valid socket that has not been `close`d. It will not be + /// closed while this object is alive. raw: RawSocket, } @@ -18,29 +23,38 @@ impl fmt::Debug for Registration { } } -impl From for Registration { - #[inline] - fn from(raw: RawSocket) -> Self { - Self { raw } +impl Registration { + /// Add this file descriptor into the reactor. + /// + /// # Safety + /// + /// The provided file descriptor must be valid and not be closed while this object is alive. + pub(crate) unsafe fn new(f: impl AsSocket) -> Self { + Self { + raw: f.as_socket().as_raw_socket(), + } } -} -impl Registration { /// Registers the object into the reactor. #[inline] pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { - poller.add(self.raw, Event::none(token)) + // SAFETY: This object's existence validates the invariants of Poller::add + unsafe { poller.add(self.raw, Event::none(token)) } } /// Re-registers the object into the reactor. #[inline] pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { - poller.modify(self.raw, interest) + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedSocket::borrow_raw(self.raw) }; + poller.modify(fd, interest) } /// Deregisters the object from the reactor. #[inline] pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { - poller.delete(self.raw) + // SAFETY: self.raw is a valid file descriptor + let fd = unsafe { BorrowedSocket::borrow_raw(self.raw) }; + poller.delete(fd) } }