diff --git a/.cirrus.yml b/.cirrus.yml index b3bea56..43ce4ee 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -14,7 +14,7 @@ env: freebsd_task: name: test ($TARGET) freebsd_instance: - image_family: freebsd-13-4 + image_family: freebsd-14-3 matrix: - env: TARGET: x86_64-unknown-freebsd diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f06bbb0..49263d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,6 +82,8 @@ jobs: - run: cargo minimal-versions build --all --all-features - name: Clone async-io run: git clone https://github.com/smol-rs/async-io.git + # Remove this line after polling makes a new release. + - run: cd async-io && git fetch origin notgull/safe && git checkout notgull/safe # The async-io Cargo.toml already has a patch section at the bottom, so we # can just add this. - name: Patch polling @@ -134,15 +136,19 @@ jobs: run: | rustup target add x86_64-unknown-redox cargo check --target x86_64-unknown-redox - - name: HermitOS - if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') - run: cargo check -Z build-std --target x86_64-unknown-hermit + # Bring this back once Hermit stdlib bugs are fixed + # See: https://github.com/rust-lang/rust/issues/150294 + #- name: HermitOS + # if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + # run: cargo check -Z build-std --target x86_64-unknown-hermit - name: Check haiku if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') run: cargo check -Z build-std --target x86_64-unknown-haiku - - name: Check vita - if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') - run: cargo check -Z build-std --target armv7-sony-vita-newlibeabihf + # Bring this back once Vita stdlib bugs are fixed + # See: https://github.com/rust-lang/rust/pull/150297 + #- name: Check vita + # if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + # run: cargo check -Z build-std --target armv7-sony-vita-newlibeabihf - name: Check ESP-IDF if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') run: cargo check -Z build-std --target riscv32imc-esp-espidf diff --git a/examples/tcp_client.rs b/examples/tcp_client.rs index 431c0ae..6e6b3dc 100644 --- a/examples/tcp_client.rs +++ b/examples/tcp_client.rs @@ -6,9 +6,7 @@ use socket2::Type; fn main() -> io::Result<()> { let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; let poller = polling::Poller::new()?; - unsafe { - poller.add(&socket, Event::new(0, true, true))?; - } + poller.add(&socket, Event::new(0, true, true))?; let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); socket.set_nonblocking(true)?; let _ = socket.connect(&addr.into()); diff --git a/examples/two-listeners.rs b/examples/two-listeners.rs index bf54eee..510ad84 100644 --- a/examples/two-listeners.rs +++ b/examples/two-listeners.rs @@ -10,10 +10,8 @@ fn main() -> io::Result<()> { l2.set_nonblocking(true)?; let poller = Poller::new()?; - unsafe { - poller.add(&l1, Event::readable(1))?; - poller.add(&l2, Event::readable(2))?; - } + poller.add(&l1, Event::readable(1))?; + poller.add(&l2, Event::readable(2))?; println!("You can connect to the server using `nc`:"); println!(" $ nc 127.0.0.1 8001"); diff --git a/src/epoll.rs b/src/epoll.rs index 4137cf1..bfcda56 100644 --- a/src/epoll.rs +++ b/src/epoll.rs @@ -97,12 +97,7 @@ impl Poller { } /// Adds a new file descriptor. - /// - /// # Safety - /// - /// The `fd` must be a valid file descriptor. The usual condition of remaining registered in - /// the `Poller` doesn't apply to `epoll`. - pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { #[cfg(feature = "tracing")] let span = tracing::trace_span!( "add", diff --git a/src/iocp/mod.rs b/src/iocp/mod.rs index facbe06..366c2a7 100644 --- a/src/iocp/mod.rs +++ b/src/iocp/mod.rs @@ -165,16 +165,7 @@ impl Poller { } /// Add a new source to the poller. - /// - /// # Safety - /// - /// The socket must be a valid socket and must last until it is deleted. - pub(super) unsafe fn add( - &self, - socket: RawSocket, - interest: Event, - mode: PollMode, - ) -> io::Result<()> { + pub(super) fn add(&self, socket: RawSocket, interest: Event, mode: PollMode) -> io::Result<()> { #[cfg(feature = "tracing")] let span = tracing::trace_span!( "add", diff --git a/src/kqueue.rs b/src/kqueue.rs index 8af4f4b..dd43c29 100644 --- a/src/kqueue.rs +++ b/src/kqueue.rs @@ -82,15 +82,13 @@ impl Poller { } /// Adds a new file descriptor. - /// - /// # Safety - /// - /// The file descriptor must be valid and it must last until it is deleted. - pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { self.add_source(SourceId::Fd(fd))?; // File descriptors don't need to be added explicitly, so just modify the interest. - self.modify(BorrowedFd::borrow_raw(fd), ev, mode) + // SAFETY: Move to BorrowedFd at next breaking change. + // See also: https://github.com/smol-rs/polling/issues/255 + self.modify(unsafe { BorrowedFd::borrow_raw(fd) }, ev, mode) } /// Modifies an existing file descriptor. diff --git a/src/lib.rs b/src/lib.rs index f4e8c69..3232a62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,9 +28,7 @@ //! //! // Create a poller and register interest in readability on the socket. //! let poller = Poller::new()?; -//! unsafe { -//! poller.add(&socket, Event::readable(key))?; -//! } +//! poller.add(&socket, Event::readable(key))?; //! //! // The event loop. //! let mut events = Events::new(); @@ -343,16 +341,13 @@ impl Event { /// /// ``` /// use std::{io, net}; - /// // Assuming polling and socket2 are included as dependencies in Cargo.toml /// use polling::Event; /// use socket2::Type; /// /// fn main() -> io::Result<()> { /// let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; /// let poller = polling::Poller::new()?; - /// unsafe { - /// poller.add(&socket, Event::new(0, true, true))?; - /// } + /// poller.add(&socket, Event::new(0, true, true))?; /// let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); /// socket.set_nonblocking(true)?; /// let _ = socket.connect(&addr.into()); @@ -495,12 +490,6 @@ impl Poller { /// will act as if the source was registered with another [`Poller`], with the same caveats /// as above. /// - /// # Safety - /// - /// The source must be [`delete()`]d from this `Poller` before it is dropped. - /// - /// [`delete()`]: Poller::delete - /// /// # Errors /// /// This method returns an error in the following situations: @@ -520,13 +509,12 @@ impl Poller { /// let key = 7; /// /// let poller = Poller::new()?; - /// unsafe { - /// poller.add(&source, Event::all(key))?; - /// } + /// poller.add(&source, Event::all(key))?; /// poller.delete(&source)?; /// # std::io::Result::Ok(()) /// ``` - pub unsafe fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> { + // TODO: At the next breaking change, change `AsRawSource` to `AsSource` + pub fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> { self.add_with_mode(source, interest, PollMode::Oneshot) } @@ -535,17 +523,11 @@ impl Poller { /// This is identical to the `add()` function, but allows specifying the /// polling mode to use for this socket. /// - /// # Safety - /// - /// The source must be [`delete()`]d from this `Poller` before it is dropped. - /// - /// [`delete()`]: Poller::delete - /// /// # Errors /// /// If the operating system does not support the specified mode, this function /// will return an error. - pub unsafe fn add_with_mode( + pub fn add_with_mode( &self, source: impl AsRawSource, interest: Event, @@ -588,7 +570,7 @@ impl Poller { /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # let key = 7; /// # let poller = Poller::new()?; - /// # unsafe { poller.add(&source, Event::none(key))?; } + /// # poller.add(&source, Event::none(key))?; /// poller.modify(&source, Event::all(key))?; /// # std::io::Result::Ok(()) /// ``` @@ -600,7 +582,7 @@ impl Poller { /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # let key = 7; /// # let poller = Poller::new()?; - /// # unsafe { poller.add(&source, Event::none(key))?; } + /// # poller.add(&source, Event::none(key))?; /// poller.modify(&source, Event::readable(key))?; /// # poller.delete(&source)?; /// # std::io::Result::Ok(()) @@ -613,7 +595,7 @@ impl Poller { /// # let poller = Poller::new()?; /// # let key = 7; /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; - /// # unsafe { poller.add(&source, Event::none(key))? }; + /// # poller.add(&source, Event::none(key))?; /// poller.modify(&source, Event::writable(key))?; /// # poller.delete(&source)?; /// # std::io::Result::Ok(()) @@ -626,7 +608,7 @@ impl Poller { /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # let key = 7; /// # let poller = Poller::new()?; - /// # unsafe { poller.add(&source, Event::none(key))?; } + /// # poller.add(&source, Event::none(key))?; /// poller.modify(&source, Event::none(key))?; /// # poller.delete(&source)?; /// # std::io::Result::Ok(()) @@ -681,7 +663,7 @@ impl Poller { /// let key = 7; /// /// let poller = Poller::new()?; - /// unsafe { poller.add(&socket, Event::all(key))?; } + /// poller.add(&socket, Event::all(key))?; /// poller.delete(&socket)?; /// # std::io::Result::Ok(()) /// ``` @@ -719,9 +701,7 @@ impl Poller { /// let key = 7; /// /// let poller = Poller::new()?; - /// unsafe { - /// poller.add(&socket, Event::all(key))?; - /// } + /// poller.add(&socket, Event::all(key))?; /// /// let mut events = Events::new(); /// let n = poller.wait(&mut events, Some(Duration::from_secs(1)))?; diff --git a/src/os/iocp.rs b/src/os/iocp.rs index 3370118..4e9823f 100644 --- a/src/os/iocp.rs +++ b/src/os/iocp.rs @@ -65,10 +65,6 @@ pub trait PollerIocpExt: PollerSealed { /// /// Once the object has been signalled, the poller will emit the `interest` event. /// - /// # Safety - /// - /// The added handle must not be dropped before it is deleted. - /// /// # Examples /// /// ```no_run @@ -98,7 +94,7 @@ pub trait PollerIocpExt: PollerSealed { /// assert_eq!(events.len(), 1); /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); /// ``` - unsafe fn add_waitable( + fn add_waitable( &self, handle: impl AsRawWaitable, interest: Event, @@ -198,7 +194,7 @@ impl PollerIocpExt for Poller { self.poller.post(packet) } - unsafe fn add_waitable( + fn add_waitable( &self, handle: impl AsRawWaitable, event: Event, diff --git a/src/port.rs b/src/port.rs index 8f4f5a0..952d8ad 100644 --- a/src/port.rs +++ b/src/port.rs @@ -45,13 +45,9 @@ impl Poller { } /// Adds a file descriptor. - /// - /// # Safety - /// - /// The `fd` must be a valid file descriptor and it must last until it is deleted. - pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { + pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { // File descriptors don't need to be added explicitly, so just modify the interest. - self.modify(BorrowedFd::borrow_raw(fd), ev, mode) + self.modify(unsafe { BorrowedFd::borrow_raw(fd) }, ev, mode) } /// Modifies an existing file descriptor. diff --git a/tests/concurrent_modification.rs b/tests/concurrent_modification.rs index f4598c0..bc90cfa 100644 --- a/tests/concurrent_modification.rs +++ b/tests/concurrent_modification.rs @@ -20,9 +20,7 @@ fn concurrent_add() -> io::Result<()> { }) .add(|| { thread::sleep(Duration::from_millis(100)); - unsafe { - poller.add(&reader, Event::readable(0))?; - } + poller.add(&reader, Event::readable(0))?; writer.write_all(&[1])?; Ok(()) }) @@ -46,9 +44,7 @@ fn concurrent_add() -> io::Result<()> { fn concurrent_modify() -> io::Result<()> { let (reader, mut writer) = tcp_pair()?; let poller = Poller::new()?; - unsafe { - poller.add(&reader, Event::none(0))?; - } + poller.add(&reader, Event::none(0))?; let mut events = Events::new(); @@ -84,9 +80,7 @@ fn concurrent_interruption() -> io::Result<()> { let (reader, _writer) = tcp_pair()?; let poller = Poller::new()?; - unsafe { - poller.add(&reader, Event::none(0))?; - } + poller.add(&reader, Event::none(0))?; let mut events = Events::new(); let events_borrow = &mut events; diff --git a/tests/io.rs b/tests/io.rs index 8156abc..25eff26 100644 --- a/tests/io.rs +++ b/tests/io.rs @@ -8,9 +8,7 @@ use std::time::Duration; fn basic_io() { let poller = Poller::new().unwrap(); let (read, mut write) = tcp_pair().unwrap(); - unsafe { - poller.add(&read, Event::readable(1)).unwrap(); - } + poller.add(&read, Event::readable(1)).unwrap(); // Nothing should be available at first. let mut events = Events::new(); @@ -50,7 +48,8 @@ fn insert_twice() { let read = Arc::new(read); let poller = Poller::new().unwrap(); - unsafe { + + { #[cfg(unix)] let read = read.as_raw_fd(); #[cfg(windows)] @@ -101,15 +100,13 @@ fn append_events() { // Add the sockets to the poller. let poller = Poller::new().unwrap(); - unsafe { - for (read, _write) in &pairs { - #[cfg(unix)] - let read = read.as_raw_fd(); - #[cfg(windows)] - let read = read.as_raw_socket(); - - poller.add(read, Event::readable(1)).unwrap(); - } + for (read, _write) in &pairs { + #[cfg(unix)] + let read = read.as_raw_fd(); + #[cfg(windows)] + let read = read.as_raw_socket(); + + poller.add(read, Event::readable(1)).unwrap(); } // Trigger read events on the sockets and reuse the event list to test diff --git a/tests/many_connections.rs b/tests/many_connections.rs index ed392f5..0ffdb51 100644 --- a/tests/many_connections.rs +++ b/tests/many_connections.rs @@ -22,9 +22,7 @@ fn many_connections() { let poller = polling::Poller::new().unwrap(); for (i, reader, _) in connections.iter() { - unsafe { - poller.add(reader, polling::Event::readable(*i)).unwrap(); - } + poller.add(reader, polling::Event::readable(*i)).unwrap(); } let mut events = Events::new(); diff --git a/tests/multiple_pollers.rs b/tests/multiple_pollers.rs index 18f0efd..9e0374b 100644 --- a/tests/multiple_pollers.rs +++ b/tests/multiple_pollers.rs @@ -18,14 +18,13 @@ fn level_triggered() { // Register the source into both pollers. let (mut reader, mut writer) = tcp_pair().unwrap(); - unsafe { - poller1 - .add_with_mode(&reader, Event::readable(1), PollMode::Level) - .unwrap(); - poller2 - .add_with_mode(&reader, Event::readable(2), PollMode::Level) - .unwrap(); - } + + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Level) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Level) + .unwrap(); // Neither poller should have any events. assert_eq!( @@ -139,14 +138,12 @@ fn edge_triggered() { // Register the source into both pollers. let (mut reader, mut writer) = tcp_pair().unwrap(); - unsafe { - poller1 - .add_with_mode(&reader, Event::readable(1), PollMode::Edge) - .unwrap(); - poller2 - .add_with_mode(&reader, Event::readable(2), PollMode::Edge) - .unwrap(); - } + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Edge) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Edge) + .unwrap(); // Neither poller should have any events. assert_eq!( @@ -256,14 +253,12 @@ fn oneshot_triggered() { // Register the source into both pollers. let (mut reader, mut writer) = tcp_pair().unwrap(); - unsafe { - poller1 - .add_with_mode(&reader, Event::readable(1), PollMode::Oneshot) - .unwrap(); - poller2 - .add_with_mode(&reader, Event::readable(2), PollMode::Oneshot) - .unwrap(); - } + poller1 + .add_with_mode(&reader, Event::readable(1), PollMode::Oneshot) + .unwrap(); + poller2 + .add_with_mode(&reader, Event::readable(2), PollMode::Oneshot) + .unwrap(); // Neither poller should have any events. assert_eq!( diff --git a/tests/other_modes.rs b/tests/other_modes.rs index c3cf79f..4ca0590 100644 --- a/tests/other_modes.rs +++ b/tests/other_modes.rs @@ -16,7 +16,8 @@ fn level_triggered() { // Create our poller and register our streams. let poller = Poller::new().unwrap(); - if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Level) } + if poller + .add_with_mode(&reader, Event::readable(reader_token), PollMode::Level) .is_err() { // Only panic if we're on a platform that should support level mode. @@ -104,10 +105,11 @@ fn edge_triggered() { // Create our poller and register our streams. let poller = Poller::new().unwrap(); - if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Edge) } + if poller + .add_with_mode(&reader, Event::readable(reader_token), PollMode::Edge) .is_err() { - // Only panic if we're on a platform that should support level mode. + // Only panic if we're on a platform that should support edge mode. cfg_if::cfg_if! { if #[cfg(all( any( @@ -191,7 +193,7 @@ fn edge_oneshot_triggered() { // Create our poller and register our streams. let poller = Poller::new().unwrap(); - if unsafe { + if { poller.add_with_mode( &reader, Event::readable(reader_token), diff --git a/tests/windows_waitable.rs b/tests/windows_waitable.rs index c9e0e8f..4d388ff 100644 --- a/tests/windows_waitable.rs +++ b/tests/windows_waitable.rs @@ -79,11 +79,9 @@ fn smoke() { let event = EventHandle::new(true).unwrap(); - unsafe { - poller - .add_waitable(&event, Event::all(0), PollMode::Oneshot) - .unwrap(); - } + poller + .add_waitable(&event, Event::all(0), PollMode::Oneshot) + .unwrap(); let mut events = Events::new(); poller