Skip to content

Commit

Permalink
Add add_outlives method to Poller
Browse files Browse the repository at this point in the history
The previous implementation only implemented unsafe methods for registering file descriptors with Poller. This implements an add_outlives method that can guarantee safety by accepting a reference to an AsSource which outlives the Poller.
Since file descriptors are guaranteed to be valid through the lifetime of the Poller, there is no need to ever delete to maintain soundness.
  • Loading branch information
BrankoKrstic committed Dec 26, 2023
1 parent b57a7c3 commit 5c429bd
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 9 deletions.
35 changes: 27 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,13 +349,15 @@ impl Event {
}

/// Waits for I/O events.
pub struct Poller {
pub struct Poller<'a> {
poller: sys::Poller,
lock: Mutex<()>,
notified: AtomicBool,
// Being contravariant over 'a requires borrowed file descriptors outlive the `Poller`
_marker: PhantomData<fn(&'a ())>,
}

impl Poller {
impl<'a> Poller<'a> {
/// Creates a new poller.
///
/// # Examples
Expand All @@ -366,11 +368,12 @@ impl Poller {
/// let poller = Poller::new()?;
/// # std::io::Result::Ok(())
/// ```
pub fn new() -> io::Result<Poller> {
pub fn new() -> io::Result<Poller<'a>> {
Ok(Poller {
poller: sys::Poller::new()?,
lock: Mutex::new(()),
notified: AtomicBool::new(false),
_marker: PhantomData,
})
}

Expand Down Expand Up @@ -478,6 +481,22 @@ impl Poller {
self.poller.add(source.raw(), interest, mode)
}

/// Adds a file descriptor or socket to the poller safely.
///
/// This is identical to the `add()` function, but ensures safety
/// by requiring the descriptor to always outlive the `Poller`.
///
/// # Errors
///
/// If the operating system does not support the specified mode, this function
/// will return an error.
pub fn add_outlives<T: AsSource>(&self, source: &'a T, interest: Event) -> io::Result<()> {
// SAFETY: Lifetime parameter ensures AsSource will live at least as long as the Poller
// It is unsound for AsSource to reference descriptors it doesn't own,
// so assume reference from `source.as_fd()` will be valid until self is dropped
unsafe { self.add(source.as_fd().as_raw_fd(), interest) }
}

/// Modifies the interest in a file descriptor or socket.
///
/// This method has the same behavior as [`add()`][`Poller::add()`] except it modifies the
Expand Down Expand Up @@ -915,13 +934,13 @@ mod raw_fd_impl {
use crate::Poller;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};

impl AsRawFd for Poller {
impl AsRawFd for Poller<'_> {
fn as_raw_fd(&self) -> RawFd {
self.poller.as_raw_fd()
}
}

impl AsFd for Poller {
impl AsFd for Poller<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
self.poller.as_fd()
}
Expand All @@ -947,7 +966,7 @@ mod raw_handle_impl {
}
}

impl fmt::Debug for Poller {
impl fmt::Debug for Poller<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.poller.fmt(f)
}
Expand Down Expand Up @@ -1026,8 +1045,8 @@ fn _assert_send_and_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

assert_send::<Poller>();
assert_sync::<Poller>();
assert_send::<Poller<'_>>();
assert_sync::<Poller<'_>>();

assert_send::<Event>();
assert_sync::<Event>();
Expand Down
2 changes: 1 addition & 1 deletion src/os.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ mod __private {
#[doc(hidden)]
pub trait PollerSealed {}

impl PollerSealed for crate::Poller {}
impl PollerSealed for crate::Poller<'_> {}
}

0 comments on commit 5c429bd

Please sign in to comment.