From 2572207cc0e4260e5a6c4429bf4397f26936d51f Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 19 Jul 2025 22:17:10 +0900 Subject: [PATCH 1/6] Make tracing optional --- .github/workflows/ci.yml | 8 +++++--- Cargo.toml | 2 +- src/driver.rs | 14 ++++++++++++++ src/reactor.rs | 10 ++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b49f73f..023e4bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,10 +48,12 @@ jobs: - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} + - name: Install cargo-hack + uses: taiki-e/install-action@v2 + with: + tool: cargo-hack - run: cargo build --all --all-features --all-targets - - name: Run cargo check (without dev-dependencies to catch missing feature flags) - if: startsWith(matrix.rust, 'nightly') - run: cargo check -Z features=dev_dep + - run: cargo hack build --feature-powerset --no-dev-deps - name: Add rust-src if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') run: rustup component add rust-src diff --git a/Cargo.toml b/Cargo.toml index 5d2f087..d3c4106 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ parking = "2.0.0" polling = "3.0.0" rustix = { version = "1.0.7", default-features = false, features = ["fs", "net", "std"] } slab = "0.4.2" -tracing = { version = "0.1.37", default-features = false } +tracing = { version = "0.1.37", default-features = false, optional = true } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.59.0", features = ["Win32_Foundation"] } diff --git a/src/driver.rs b/src/driver.rs index 834fc0d..d3c3539 100644 --- a/src/driver.rs +++ b/src/driver.rs @@ -44,7 +44,9 @@ pub(crate) fn init() { /// The main loop for the "async-io" thread. fn main_loop(parker: parking::Parker) { + #[cfg(feature = "tracing")] let span = tracing::trace_span!("async_io::main_loop"); + #[cfg(feature = "tracing")] let _enter = span.enter(); // The last observed reactor tick. @@ -65,6 +67,7 @@ fn main_loop(parker: parking::Parker) { }; if let Some(mut reactor_lock) = reactor_lock { + #[cfg(feature = "tracing")] tracing::trace!("waiting on I/O"); reactor_lock.react(None).ok(); last_tick = Reactor::get().ticker(); @@ -80,8 +83,10 @@ fn main_loop(parker: parking::Parker) { .get(sleeps as usize) .unwrap_or(&10_000); + #[cfg(feature = "tracing")] tracing::trace!("sleeping for {} us", delay_us); if parker.park_timeout(Duration::from_micros(*delay_us)) { + #[cfg(feature = "tracing")] tracing::trace!("notified"); // If notified before timeout, reset the last tick and the sleep counter. @@ -109,7 +114,9 @@ fn main_loop(parker: parking::Parker) { /// }); /// ``` pub fn block_on(future: impl Future) -> T { + #[cfg(feature = "tracing")] let span = tracing::trace_span!("async_io::block_on"); + #[cfg(feature = "tracing")] let _enter = span.enter(); // Increment `BLOCK_ON_COUNT` so that the "async-io" thread becomes less aggressive. @@ -200,12 +207,14 @@ pub fn block_on(future: impl Future) -> T { // Ensure the cached parker is reset to the unnotified state for future block_on calls, // in case this future called wake and then immediately returned Poll::Ready. p.park_timeout(Duration::from_secs(0)); + #[cfg(feature = "tracing")] tracing::trace!("completed"); return t; } // Check if a notification was received. if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] tracing::trace!("notified"); // Try grabbing a lock on the reactor to process I/O events. @@ -239,22 +248,26 @@ pub fn block_on(future: impl Future) -> T { // Check if a notification has been received before `io_blocked` was updated // because in that case the reactor won't receive a wakeup. if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] tracing::trace!("notified"); break; } // Wait for I/O events. + #[cfg(feature = "tracing")] tracing::trace!("waiting on I/O"); reactor_lock.react(None).ok(); // Check if a notification has been received. if p.park_timeout(Duration::from_secs(0)) { + #[cfg(feature = "tracing")] tracing::trace!("notified"); break; } // Check if this thread been handling I/O events for a long time. if start.elapsed() > Duration::from_micros(500) { + #[cfg(feature = "tracing")] tracing::trace!("stops hogging the reactor"); // This thread is clearly processing I/O events for some other threads @@ -274,6 +287,7 @@ pub fn block_on(future: impl Future) -> T { } } else { // Wait for an actual notification. + #[cfg(feature = "tracing")] tracing::trace!("sleep until notification"); p.park(); } diff --git a/src/reactor.rs b/src/reactor.rs index c298dd9..95e1b67 100644 --- a/src/reactor.rs +++ b/src/reactor.rs @@ -204,7 +204,9 @@ impl Reactor { /// /// Returns the duration until the next timer before this method was called. fn process_timers(&self, wakers: &mut Vec) -> Option { + #[cfg(feature = "tracing")] let span = tracing::trace_span!("process_timers"); + #[cfg(feature = "tracing")] let _enter = span.enter(); let mut timers = self.timers.lock().unwrap(); @@ -235,6 +237,7 @@ impl Reactor { drop(timers); // Add wakers to the list. + #[cfg(feature = "tracing")] tracing::trace!("{} ready wakers", ready.len()); for (_, waker) in ready { @@ -271,7 +274,9 @@ pub(crate) struct ReactorLock<'a> { impl ReactorLock<'_> { /// Processes new events, blocking until the first event or the timeout. pub(crate) fn react(&mut self, timeout: Option) -> io::Result<()> { + #[cfg(feature = "tracing")] let span = tracing::trace_span!("react"); + #[cfg(feature = "tracing")] let _enter = span.enter(); let mut wakers = Vec::new(); @@ -353,6 +358,7 @@ impl ReactorLock<'_> { }; // Wake up ready tasks. + #[cfg(feature = "tracing")] tracing::trace!("{} ready wakers", wakers.len()); for waker in wakers { // Don't let a panicking waker blow everything up. @@ -518,6 +524,7 @@ impl Future for Readable<'_, T> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] tracing::trace!(fd = ?self.0.handle.source.registration, "readable"); Poll::Ready(Ok(())) } @@ -538,6 +545,7 @@ impl Future for ReadableOwned { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] tracing::trace!(fd = ?self.0.handle.source.registration, "readable_owned"); Poll::Ready(Ok(())) } @@ -558,6 +566,7 @@ impl Future for Writable<'_, T> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] tracing::trace!(fd = ?self.0.handle.source.registration, "writable"); Poll::Ready(Ok(())) } @@ -578,6 +587,7 @@ impl Future for WritableOwned { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; + #[cfg(feature = "tracing")] tracing::trace!(fd = ?self.0.handle.source.registration, "writable_owned"); Poll::Ready(Ok(())) } From 471cdb81b0c941637c220c9e987dff0ec03e3552 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 19 Jul 2025 22:18:19 +0900 Subject: [PATCH 2/6] ci: Add minimal-versions check --- .github/workflows/ci.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 023e4bf..cb7600b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -48,10 +48,10 @@ jobs: - name: Install Rust # --no-self-update is necessary because the windows environment cannot self-update rustup.exe. run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} - - name: Install cargo-hack + - name: Install cargo-hack and cargo-minimal-versions uses: taiki-e/install-action@v2 with: - tool: cargo-hack + tool: cargo-hack,cargo-minimal-versions - run: cargo build --all --all-features --all-targets - run: cargo hack build --feature-powerset --no-dev-deps - name: Add rust-src @@ -60,6 +60,7 @@ jobs: - name: Check ESP-IDF if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') run: cargo check -Z build-std --target riscv32imc-esp-espidf + - run: cargo minimal-versions build --all --all-features - run: cargo test # Copied from: https://github.com/rust-lang/stacker/pull/19/files From 257157c0bc17dfe6382923d44deca59ff9334854 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 19 Jul 2025 22:24:29 +0900 Subject: [PATCH 3/6] ci: Sync cross test/build config from polling --- .github/workflows/ci.yml | 46 ++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb7600b..0da854c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -54,12 +54,6 @@ jobs: tool: cargo-hack,cargo-minimal-versions - run: cargo build --all --all-features --all-targets - run: cargo hack build --feature-powerset --no-dev-deps - - name: Add rust-src - if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') - run: rustup component add rust-src - - name: Check ESP-IDF - if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') - run: cargo check -Z build-std --target riscv32imc-esp-espidf - run: cargo minimal-versions build --all --all-features - run: cargo test @@ -86,12 +80,16 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest] + rust: [nightly, stable] steps: - uses: actions/checkout@v4 - name: Install Rust - run: rustup update stable + run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - name: Install cross uses: taiki-e/install-action@cross + - name: Add rust-src + if: startsWith(matrix.rust, 'nightly') + run: rustup component add rust-src # We don't test BSDs, since we already test them in Cirrus. - name: Android if: startsWith(matrix.os, 'ubuntu') @@ -116,6 +114,36 @@ jobs: run: | rustup target add x86_64-unknown-illumos cargo build --target x86_64-unknown-illumos + - name: Redox + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: | + rustup target add x86_64-unknown-redox + cargo check --target x86_64-unknown-redox + # TODO: + # - name: HermitOS + # if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + # run: cargo check -Z build-std --target x86_64-unknown-hermit + # TODO: + # - 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 + - name: Check ESP-IDF + if: startsWith(matrix.rust, 'nightly') && startsWith(matrix.os, 'ubuntu') + run: cargo check -Z build-std --target riscv32imc-esp-espidf + + wine: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + - name: Install Rust + run: rustup update stable + - uses: taiki-e/setup-cross-toolchain-action@v1 + with: + target: x86_64-pc-windows-gnu + - run: cargo test --target x86_64-pc-windows-gnu msrv: runs-on: ${{ matrix.os }} @@ -128,6 +156,10 @@ jobs: - name: Install cargo-hack uses: taiki-e/install-action@cargo-hack - run: cargo hack build --no-dev-deps --rust-version + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-freebsd + if: startsWith(matrix.os, 'ubuntu') + - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-netbsd + if: startsWith(matrix.os, 'ubuntu') clippy: runs-on: ubuntu-latest From 0c41ab45952cebe968346911e6fb1f0eec50c963 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 19 Jul 2025 22:26:46 +0900 Subject: [PATCH 4/6] ci: Use reusable workflows for clippy --- .github/workflows/ci.yml | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0da854c..3a4449a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,11 @@ defaults: jobs: fmt: uses: smol-rs/.github/.github/workflows/fmt.yml@main + clippy: + uses: smol-rs/.github/.github/workflows/clippy.yml@main + with: + # macOS for kqueue, Windows for windows. + additional-targets: aarch64-apple-darwin x86_64-pc-windows-msvc security_audit: uses: smol-rs/.github/.github/workflows/security_audit.yml@main permissions: @@ -160,11 +165,3 @@ jobs: if: startsWith(matrix.os, 'ubuntu') - run: cargo hack build --no-dev-deps --rust-version --target x86_64-unknown-netbsd if: startsWith(matrix.os, 'ubuntu') - - clippy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Install Rust - run: rustup update stable - - run: cargo clippy --all-features --all-targets From f9dae5b7a29606261af435ac43f5e0e1a78da8d2 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 19 Jul 2025 22:38:58 +0900 Subject: [PATCH 5/6] Fix build failure with minimal-versions ``` error[E0599]: no function or associated item named `from_pid` found for struct `Process` in the current scope --> src/reactor/kqueue.rs:66:35 | 66 | unsafe { Process::from_pid(*pid, ProcessOps::Exit) }, | ^^^^^^^^ function or associated item not found in `Process<'_>` | ``` --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index d3c4106..2295d52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,7 +32,7 @@ concurrent-queue = "2.2.0" futures-io = { version = "0.3.28", default-features = false, features = ["std"] } futures-lite = { version = "2.0.0", default-features = false } parking = "2.0.0" -polling = "3.0.0" +polling = "3.4.0" rustix = { version = "1.0.7", default-features = false, features = ["fs", "net", "std"] } slab = "0.4.2" tracing = { version = "0.1.37", default-features = false, optional = true } From 804e14316740370b66e6c01bec7840ba697c0be8 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 19 Jul 2025 22:41:54 +0900 Subject: [PATCH 6/6] Fix clippy::uninlined_format_args warning ``` error: variables can be used directly in the `format!` string --> examples/linux-inotify.rs:55:17 | 55 | println!("{:?}", event); | ^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args = note: `-D clippy::uninlined-format-args` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::uninlined_format_args)]` help: change this to | 55 - println!("{:?}", event); 55 + println!("{event:?}"); | error: variables can be used directly in the `format!` string --> benches/io.rs:35:30 | 35 | group.bench_function(format!("TcpStream.{}", driver_name), move |b| { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args = note: `-D clippy::uninlined-format-args` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::uninlined_format_args)]` help: change this to | 35 - group.bench_function(format!("TcpStream.{}", driver_name), move |b| { 35 + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { | error: variables can be used directly in the `format!` string --> benches/io.rs:58:34 | 58 | group.bench_function(format!("UnixStream.{}", driver_name), |b| { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args help: change this to | 58 - group.bench_function(format!("UnixStream.{}", driver_name), |b| { 58 + group.bench_function(format!("UnixStream.{driver_name}"), |b| { | error: variables can be used directly in the `format!` string --> benches/io.rs:82:30 | 82 | group.bench_function(format!("TcpStream.{}", driver_name), move |b| { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args help: change this to | 82 - group.bench_function(format!("TcpStream.{}", driver_name), move |b| { 82 + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { | error: variables can be used directly in the `format!` string --> benches/io.rs:108:31 | 108 | let socket_addr = format!("/tmp/async-io-bench-{}.sock", id); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args help: change this to | 108 - let socket_addr = format!("/tmp/async-io-bench-{}.sock", id); 108 + let socket_addr = format!("/tmp/async-io-bench-{id}.sock"); | error: variables can be used directly in the `format!` string --> benches/io.rs:111:34 | 111 | group.bench_function(format!("UnixStream.{}", driver_name), |b| { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args help: change this to | 111 - group.bench_function(format!("UnixStream.{}", driver_name), |b| { 111 + group.bench_function(format!("UnixStream.{driver_name}"), |b| { | error: variables can be used directly in the `format!` string --> benches/io.rs:145:30 | 145 | group.bench_function(format!("UdpSocket.{}", driver_name), |b| { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args help: change this to | 145 - group.bench_function(format!("UdpSocket.{}", driver_name), |b| { 145 + group.bench_function(format!("UdpSocket.{driver_name}"), |b| { | error: variables can be used directly in the `format!` string --> benches/timer.rs:27:13 | 27 | format!("register_timer.({} previous timers)", prev_timer_count), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#uninlined_format_args = note: `-D clippy::uninlined-format-args` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::uninlined_format_args)]` help: change this to | 27 - format!("register_timer.({} previous timers)", prev_timer_count), 27 + format!("register_timer.({prev_timer_count} previous timers)"), | ``` --- benches/io.rs | 12 ++++++------ benches/timer.rs | 2 +- examples/linux-inotify.rs | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/benches/io.rs b/benches/io.rs index a0a1a65..3163322 100644 --- a/benches/io.rs +++ b/benches/io.rs @@ -32,7 +32,7 @@ fn read_and_write(b: &mut Criterion) { (listener, reader, writer) }; - group.bench_function(format!("TcpStream.{}", driver_name), move |b| { + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { let (_listener, mut reader, mut writer) = init_reader_writer(); let mut buf = vec![0x42; TCP_AMOUNT]; @@ -55,7 +55,7 @@ fn read_and_write(b: &mut Criterion) { use std::os::unix::net::UnixStream; const UNIX_AMOUNT: usize = 1024; - group.bench_function(format!("UnixStream.{}", driver_name), |b| { + group.bench_function(format!("UnixStream.{driver_name}"), |b| { let (mut reader, mut writer) = Async::::pair().unwrap(); let mut buf = vec![0x42; UNIX_AMOUNT]; @@ -79,7 +79,7 @@ fn connect_and_accept(c: &mut Criterion) { for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { // Benchmark the TCP streams. - group.bench_function(format!("TcpStream.{}", driver_name), move |b| { + group.bench_function(format!("TcpStream.{driver_name}"), move |b| { let socket_addr = SocketAddr::new("127.0.0.1".parse::().unwrap().into(), 12345); let listener = Async::::bind(socket_addr).unwrap(); @@ -105,10 +105,10 @@ fn connect_and_accept(c: &mut Criterion) { getrandom::fill(&mut id).unwrap(); let id = u64::from_ne_bytes(id); - let socket_addr = format!("/tmp/async-io-bench-{}.sock", id); + let socket_addr = format!("/tmp/async-io-bench-{id}.sock"); let listener = Async::::bind(&socket_addr).unwrap(); - group.bench_function(format!("UnixStream.{}", driver_name), |b| { + group.bench_function(format!("UnixStream.{driver_name}"), |b| { b.iter(|| { block_on( async { @@ -142,7 +142,7 @@ fn udp_send_recv(c: &mut Criterion) { let mut buf = vec![0x42; UDP_AMOUNT]; for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { - group.bench_function(format!("UdpSocket.{}", driver_name), |b| { + group.bench_function(format!("UdpSocket.{driver_name}"), |b| { b.iter(|| { let buf = &mut buf; diff --git a/benches/timer.rs b/benches/timer.rs index 54af9ff..60ae076 100644 --- a/benches/timer.rs +++ b/benches/timer.rs @@ -24,7 +24,7 @@ fn register_timer(c: &mut Criterion) { // Benchmark registering a timer. group.bench_function( - format!("register_timer.({} previous timers)", prev_timer_count), + format!("register_timer.({prev_timer_count} previous timers)"), |b| { b.iter(|| { let timer = make_timer(); diff --git a/examples/linux-inotify.rs b/examples/linux-inotify.rs index 121b482..e2d89b2 100644 --- a/examples/linux-inotify.rs +++ b/examples/linux-inotify.rs @@ -52,7 +52,7 @@ fn main() -> std::io::Result<()> { // Wait for events in a loop and print them on the screen. loop { for event in unsafe { inotify.read_with_mut(read_op).await? } { - println!("{:?}", event); + println!("{event:?}"); } } })