Skip to content

Commit

Permalink
Implement poll-based backend
Browse files Browse the repository at this point in the history
Introduces a new backend for UNIX using the level triggered poll()
syscall instead of epoll or kqueue.  This support is crucial for
embedded systems like the esp32 family but also for alternative
operating systems like Haiku.

This diff does not introduce any new platform support targets itself but
provides the core technical implementation necessary to support these
other targets.  Future PRs will introduce specific platform support
however due to reasons outlined in #1602 (many thanks for this initial
effort BTW!) it is not possible to automate tests for those platforms.
We will instead rely on the fact that Linux can serve as a proxy to
prove that the mio code is working nominally.

Note that only Linux has a sufficiently complex implementation to pass
all tests.  This is due to SIGRDHUP missing on other platforms and is
required for about a dozen or so tests that check is_read_closed().
  • Loading branch information
jasta committed Jul 18, 2023
1 parent 0639d6a commit a6edda7
Show file tree
Hide file tree
Showing 9 changed files with 984 additions and 80 deletions.
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,18 @@ jobs:
run: cargo test --all-features
- name: Tests release build
run: cargo test --release --all-features
TestPoll:
runs-on: ubuntu-latest
timeout-minutes: 10
env:
RUSTFLAGS="--cfg mio_unsupported_force_poll_poll"
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Tests
run: cargo test --all-features
- name: Tests release build
run: cargo test --release --all-features
MinimalVersions:
runs-on: ${{ matrix.os }}
timeout-minutes: 10
Expand Down Expand Up @@ -133,6 +145,7 @@ jobs:
runs-on: ubuntu-latest
needs:
- Test
- TestPoll
- MinimalVersions
- MSRV
- Nightly
Expand Down
10 changes: 3 additions & 7 deletions src/io_source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,9 +142,7 @@ where
) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.associate(registry)?;
registry
.selector()
.register(self.inner.as_raw_fd(), token, interests)
self.state.register(registry, token, interests, self.inner.as_raw_fd())
}

fn reregister(
Expand All @@ -155,15 +153,13 @@ where
) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.check_association(registry)?;
registry
.selector()
.reregister(self.inner.as_raw_fd(), token, interests)
self.state.reregister(registry, token, interests, self.inner.as_raw_fd())
}

fn deregister(&mut self, registry: &Registry) -> io::Result<()> {
#[cfg(debug_assertions)]
self.selector_id.remove_association(registry)?;
registry.selector().deregister(self.inner.as_raw_fd())
self.state.deregister(registry, self.inner.as_raw_fd())
}
}

Expand Down
11 changes: 7 additions & 4 deletions src/poll.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{event, sys, Events, Interest, Token};
#[cfg(unix)]
#[cfg(all(unix, not(mio_unsupported_force_poll_poll)))]
use std::os::unix::io::{AsRawFd, RawFd};
use std::time::Duration;
use std::{fmt, io};
Expand Down Expand Up @@ -411,7 +411,7 @@ impl Poll {
}
}

#[cfg(unix)]
#[cfg(all(unix, not(mio_unsupported_force_poll_poll)))]
impl AsRawFd for Poll {
fn as_raw_fd(&self) -> RawFd {
self.registry.as_raw_fd()
Expand Down Expand Up @@ -696,15 +696,18 @@ impl fmt::Debug for Registry {
}
}

#[cfg(unix)]
#[cfg(all(unix, not(mio_unsupported_force_poll_poll)))]
impl AsRawFd for Registry {
fn as_raw_fd(&self) -> RawFd {
self.selector.as_raw_fd()
}
}

cfg_os_poll! {
#[cfg(unix)]
#[cfg(all(
unix,
not(mio_unsupported_force_poll_poll)
))]
#[test]
pub fn as_raw_fd() {
let poll = Poll::new().unwrap();
Expand Down
68 changes: 54 additions & 14 deletions src/sys/unix/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,65 @@ cfg_os_poll! {
}

cfg_io_source! {
use std::io;

// Both `kqueue` and `epoll` don't need to hold any user space state.
pub(crate) struct IoSourceState;
#[cfg(not(mio_unsupported_force_poll_poll))]
mod stateless_io_source {
use std::io;
use std::os::unix::io::RawFd;
use crate::Registry;
use crate::Token;
use crate::Interest;

impl IoSourceState {
pub fn new() -> IoSourceState {
IoSourceState
}
pub(crate) struct IoSourceState;

impl IoSourceState {
pub fn new() -> IoSourceState {
IoSourceState
}

pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R>
where
F: FnOnce(&T) -> io::Result<R>,
{
// We don't hold state, so we can just call the function and
// return.
f(io)
}

pub fn do_io<T, F, R>(&self, f: F, io: &T) -> io::Result<R>
where
F: FnOnce(&T) -> io::Result<R>,
{
// We don't hold state, so we can just call the function and
// return.
f(io)
pub fn register(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
// Pass through, we don't have any state
registry.selector().register(fd, token, interests)
}

pub fn reregister(
&mut self,
registry: &Registry,
token: Token,
interests: Interest,
fd: RawFd,
) -> io::Result<()> {
// Pass through, we don't have any state
registry.selector().reregister(fd, token, interests)
}

pub fn deregister(&mut self, registry: &Registry, fd: RawFd) -> io::Result<()> {
// Pass through, we don't have any state
registry.selector().deregister(fd)
}
}
}

#[cfg(not(mio_unsupported_force_poll_poll))]
pub(crate) use self::stateless_io_source::IoSourceState;

#[cfg(mio_unsupported_force_poll_poll)]
pub(crate) use self::selector::IoSourceState;
}

cfg_os_ext! {
Expand Down
74 changes: 46 additions & 28 deletions src/sys/unix/selector/mod.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,58 @@
#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
#[cfg(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
)
))]
mod epoll;

#[cfg(any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
#[cfg(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "android",
target_os = "illumos",
target_os = "linux",
target_os = "redox",
)
))]
pub(crate) use self::epoll::{event, Event, Events, Selector};

#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "watchos",
#[cfg(mio_unsupported_force_poll_poll)]
mod poll;

#[cfg(mio_unsupported_force_poll_poll)]
pub(crate) use self::poll::{event, Event, Events, Selector, IoSourceState};

#[cfg(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "watchos",
)
))]
mod kqueue;

#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "watchos",
#[cfg(all(
not(mio_unsupported_force_poll_poll),
any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "ios",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "tvos",
target_os = "watchos",
),
))]
pub(crate) use self::kqueue::{event, Event, Events, Selector};

Expand Down
Loading

0 comments on commit a6edda7

Please sign in to comment.