From 3661cbbc92ea72932e4c0ab9c45e8cbe30918212 Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Mon, 24 Nov 2025 20:48:00 +0800 Subject: [PATCH 1/8] add tests --- .github/workflows/ci.yml | 55 ++++++++++++++++++++++ src/lib.rs | 2 + src/tests.rs | 98 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 src/tests.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b72f04c --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,55 @@ +name: CI + +on: [push, pull_request] + +jobs: + ci: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + rust-toolchain: [nightly] + targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + with: + toolchain: ${{ matrix.rust-toolchain }} + components: rust-src, clippy, rustfmt + targets: ${{ matrix.targets }} + - name: Check rust version + run: rustc --version --verbose + - name: Check code format + run: cargo fmt --all -- --check + - name: Clippy + run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default + - name: Build + run: cargo build --target ${{ matrix.targets }} --all-features + - name: Unit test + if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} + run: cargo test --target ${{ matrix.targets }} -- --nocapture + + doc: + runs-on: ubuntu-latest + strategy: + fail-fast: false + permissions: + contents: write + env: + default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} + RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Build docs + continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} + run: | + cargo doc --no-deps --all-features + printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html + - name: Deploy to Github Pages + if: ${{ github.ref == env.default-branch }} + uses: JamesIves/github-pages-deploy-action@v4 + with: + single-commit: true + branch: gh-pages + folder: target/doc \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0b757b1..2166f29 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,6 +15,8 @@ use bitflags::bitflags; use linux_raw_sys::general::*; use spin::Mutex; +mod tests; + bitflags! { /// I/O events. #[derive(Debug, Clone, Copy)] diff --git a/src/tests.rs b/src/tests.rs new file mode 100644 index 0000000..1b000f6 --- /dev/null +++ b/src/tests.rs @@ -0,0 +1,98 @@ +#[cfg(test)] +extern crate std; + +#[cfg(test)] +mod test { + use super::*; + use crate::*; + use std::sync::{ + Arc, + atomic::{AtomicUsize, Ordering}, + }; + use std::task::{Context, RawWaker, RawWakerVTable, Waker}; + + fn make_waker(counter: Arc) -> Waker { + unsafe fn clone(ptr: *const ()) -> RawWaker { + let arc = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; + let arc2 = arc.clone(); + std::mem::forget(arc); + RawWaker::new(Arc::into_raw(arc2) as *const (), &VTABLE) + } + unsafe fn wake(ptr: *const ()) { + let arc = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; + arc.fetch_add(1, Ordering::SeqCst); + } + unsafe fn wake_by_ref(ptr: *const ()) { + let arc = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; + arc.fetch_add(1, Ordering::SeqCst); + std::mem::forget(arc); + } + unsafe fn drop_arc(ptr: *const ()) { + let _ = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; + } + + static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop_arc); + + let raw = RawWaker::new(Arc::into_raw(counter.clone()) as *const (), &VTABLE); + unsafe { Waker::from_raw(raw) } + } + + #[test] + fn register_and_wake() { + let ps = PollSet::new(); + let counter = Arc::new(AtomicUsize::new(0)); + let w = make_waker(counter.clone()); + ps.register(&w); + assert_eq!(ps.wake(), 1); + assert_eq!(counter.load(Ordering::SeqCst), 1); + } + + #[test] + fn empty_return() { + let ps = PollSet::new(); + assert_eq!(ps.wake(), 0); + } + + #[test] + fn full_capacity() { + let ps = PollSet::new(); + let counter = Arc::new(AtomicUsize::new(0)); + for _ in 0..64 { + let w = make_waker(counter.clone()); + let cx = Context::from_waker(&w); + ps.register(cx.waker()); + } + let woke = ps.wake(); + assert_eq!(woke, 64); + assert_eq!(counter.load(Ordering::SeqCst), 64); + } + + #[test] + fn overwrite() { + let ps = PollSet::new(); + let counters = (0..65) + .map(|_| Arc::new(AtomicUsize::new(0))) + .collect::>>(); + for c in &counters { + let w = make_waker(c.clone()); + let cx = Context::from_waker(&w); + ps.register(cx.waker()); + } + assert_eq!(ps.wake(), 64); + let total: usize = counters.iter().map(|c| c.load(Ordering::SeqCst)).sum(); + assert_eq!(total, 65); + } + + #[test] + fn drop_wakes() { + let ps = PollSet::new(); + let counters = Arc::new(AtomicUsize::new(0)); + for _ in 0..10 { + let w = make_waker(counters.clone()); + let cx = Context::from_waker(&w); + ps.register(cx.waker()); + } + drop(ps); + assert_eq!(counters.load(Ordering::SeqCst), 10); + } +} From fe4fce128f274d31f0f51459e87c8c3716f504e2 Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Tue, 25 Nov 2025 01:15:16 +0800 Subject: [PATCH 2/8] refactor make_waker --- .github/workflows/ci.yml | 42 +++++++++++++++++++++------------------- src/lib.rs | 2 -- {src => tests}/tests.rs | 37 ++++++++++++++--------------------- 3 files changed, 36 insertions(+), 45 deletions(-) rename {src => tests}/tests.rs (63%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b72f04c..d868a83 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,25 +31,27 @@ jobs: doc: runs-on: ubuntu-latest - strategy: - fail-fast: false + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - name: Build docs + run: | + cargo doc --all-features --no-deps + printf '' $(cargo tree | head -1 | cut -d' ' -f1 | tr '-' '_') > target/doc/index.html + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: target/doc + + deploy: + runs-on: ubuntu-latest + needs: doc + if: ${{ github.ref == format('refs/heads/{0}', github.event.repository.default_branch) }} permissions: - contents: write - env: - default-branch: ${{ format('refs/heads/{0}', github.event.repository.default_branch) }} - RUSTDOCFLAGS: -D rustdoc::broken_intra_doc_links -D missing-docs + contents: read + pages: write + id-token: write steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - - name: Build docs - continue-on-error: ${{ github.ref != env.default-branch && github.event_name != 'pull_request' }} - run: | - cargo doc --no-deps --all-features - printf '' $(cargo tree | head -1 | cut -d' ' -f1) > target/doc/index.html - - name: Deploy to Github Pages - if: ${{ github.ref == env.default-branch }} - uses: JamesIves/github-pages-deploy-action@v4 - with: - single-commit: true - branch: gh-pages - folder: target/doc \ No newline at end of file + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2166f29..0b757b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -15,8 +15,6 @@ use bitflags::bitflags; use linux_raw_sys::general::*; use spin::Mutex; -mod tests; - bitflags! { /// I/O events. #[derive(Debug, Clone, Copy)] diff --git a/src/tests.rs b/tests/tests.rs similarity index 63% rename from src/tests.rs rename to tests/tests.rs index 1b000f6..e2091cf 100644 --- a/src/tests.rs +++ b/tests/tests.rs @@ -4,37 +4,28 @@ extern crate std; #[cfg(test)] mod test { use super::*; - use crate::*; + use axpoll::PollSet; use std::sync::{ Arc, atomic::{AtomicUsize, Ordering}, }; - use std::task::{Context, RawWaker, RawWakerVTable, Waker}; + use std::task::{Context, Wake, Waker}; - fn make_waker(counter: Arc) -> Waker { - unsafe fn clone(ptr: *const ()) -> RawWaker { - let arc = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; - let arc2 = arc.clone(); - std::mem::forget(arc); - RawWaker::new(Arc::into_raw(arc2) as *const (), &VTABLE) - } - unsafe fn wake(ptr: *const ()) { - let arc = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; - arc.fetch_add(1, Ordering::SeqCst); - } - unsafe fn wake_by_ref(ptr: *const ()) { - let arc = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; - arc.fetch_add(1, Ordering::SeqCst); - std::mem::forget(arc); - } - unsafe fn drop_arc(ptr: *const ()) { - let _ = unsafe { Arc::::from_raw(ptr as *const AtomicUsize) }; + struct WrapArc(Arc); + + impl Wake for WrapArc { + fn wake(self: Arc) { + self.0.fetch_add(1, Ordering::SeqCst); } - static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop_arc); + fn wake_by_ref(self: &Arc) { + self.0.fetch_add(1, Ordering::SeqCst); + } + } - let raw = RawWaker::new(Arc::into_raw(counter.clone()) as *const (), &VTABLE); - unsafe { Waker::from_raw(raw) } + fn make_waker(counter: Arc) -> Waker { + let wrapped = Arc::new(WrapArc(counter.clone())); + Waker::from(wrapped) } #[test] From 683a7cb27951bf2a56cfc55438506b2faa344846 Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Tue, 25 Nov 2025 01:17:15 +0800 Subject: [PATCH 3/8] refactor make_waker --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d868a83..0662a06 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,8 @@ jobs: contents: read pages: write id-token: write + environment: + name: github-pages steps: - name: Deploy to GitHub Pages id: deployment From 642f9b9524a6772db2add6bbc09a883032a3659c Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Tue, 25 Nov 2025 19:51:52 +0800 Subject: [PATCH 4/8] format code --- .github/workflows/ci.yml | 34 +++------ tests/tests.rs | 148 +++++++++++++++++++++------------------ 2 files changed, 88 insertions(+), 94 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0662a06..434798e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,32 +3,18 @@ name: CI on: [push, pull_request] jobs: - ci: + check: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - rust-toolchain: [nightly] - targets: [x86_64-unknown-linux-gnu, x86_64-unknown-none, riscv64gc-unknown-none-elf, aarch64-unknown-none-softfloat] steps: - - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@nightly - with: - toolchain: ${{ matrix.rust-toolchain }} - components: rust-src, clippy, rustfmt - targets: ${{ matrix.targets }} - - name: Check rust version - run: rustc --version --verbose - - name: Check code format - run: cargo fmt --all -- --check - - name: Clippy - run: cargo clippy --target ${{ matrix.targets }} --all-features -- -A clippy::new_without_default - - name: Build - run: cargo build --target ${{ matrix.targets }} --all-features - - name: Unit test - if: ${{ matrix.targets == 'x86_64-unknown-linux-gnu' }} - run: cargo test --target ${{ matrix.targets }} -- --nocapture - + - uses: actions/checkout@v4 + - name: Setup Rust toolchain + run: | + rustup default nightly + rustup component add clippy + - name: Clippy + run: cargo clippy --all-features --all-targets -- -Dwarnings + - name: Test + run: cargo test --all-features doc: runs-on: ubuntu-latest steps: diff --git a/tests/tests.rs b/tests/tests.rs index e2091cf..da109ae 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,89 +1,97 @@ -#[cfg(test)] -extern crate std; - -#[cfg(test)] -mod test { - use super::*; - use axpoll::PollSet; - use std::sync::{ +use std::{ + sync::{ Arc, atomic::{AtomicUsize, Ordering}, - }; - use std::task::{Context, Wake, Waker}; + }, + task::{Context, Wake, Waker}, +}; - struct WrapArc(Arc); +use axpoll::PollSet; - impl Wake for WrapArc { - fn wake(self: Arc) { - self.0.fetch_add(1, Ordering::SeqCst); - } +struct Counter(Arc); - fn wake_by_ref(self: &Arc) { - self.0.fetch_add(1, Ordering::SeqCst); - } +impl Counter { + fn new() -> Self { + Self(Arc::new(AtomicUsize::new(0))) } - fn make_waker(counter: Arc) -> Waker { - let wrapped = Arc::new(WrapArc(counter.clone())); - Waker::from(wrapped) + fn count(&self) -> usize { + self.0.load(Ordering::SeqCst) } - #[test] - fn register_and_wake() { - let ps = PollSet::new(); - let counter = Arc::new(AtomicUsize::new(0)); - let w = make_waker(counter.clone()); - ps.register(&w); - assert_eq!(ps.wake(), 1); - assert_eq!(counter.load(Ordering::SeqCst), 1); + fn add(&self) { + self.0.fetch_add(1, Ordering::SeqCst); } +} - #[test] - fn empty_return() { - let ps = PollSet::new(); - assert_eq!(ps.wake(), 0); +impl Wake for Counter { + fn wake(self: Arc) { + self.add(); } - #[test] - fn full_capacity() { - let ps = PollSet::new(); - let counter = Arc::new(AtomicUsize::new(0)); - for _ in 0..64 { - let w = make_waker(counter.clone()); - let cx = Context::from_waker(&w); - ps.register(cx.waker()); - } - let woke = ps.wake(); - assert_eq!(woke, 64); - assert_eq!(counter.load(Ordering::SeqCst), 64); + fn wake_by_ref(self: &Arc) { + self.add(); } +} + +fn make_waker(counter: Arc) -> Waker { + let wrapped = Arc::new(Counter(counter.clone())); + Waker::from(wrapped) +} + +#[test] +fn register_and_wake() { + let ps = PollSet::new(); + let counter = Counter::new(); + let w = make_waker(counter.0.clone()); + ps.register(&w); + assert_eq!(ps.wake(), 1); + assert_eq!(counter.count(), 1); +} - #[test] - fn overwrite() { - let ps = PollSet::new(); - let counters = (0..65) - .map(|_| Arc::new(AtomicUsize::new(0))) - .collect::>>(); - for c in &counters { - let w = make_waker(c.clone()); - let cx = Context::from_waker(&w); - ps.register(cx.waker()); - } - assert_eq!(ps.wake(), 64); - let total: usize = counters.iter().map(|c| c.load(Ordering::SeqCst)).sum(); - assert_eq!(total, 65); +#[test] +fn empty_return() { + let ps = PollSet::new(); + assert_eq!(ps.wake(), 0); +} + +#[test] +fn full_capacity() { + let ps = PollSet::new(); + let counter = Counter::new(); + for _ in 0..64 { + let w = make_waker(counter.0.clone()); + let cx = Context::from_waker(&w); + ps.register(cx.waker()); + } + let woke = ps.wake(); + assert_eq!(woke, 64); + assert_eq!(counter.count(), 64); +} + +#[test] +fn overwrite() { + let ps = PollSet::new(); + let counters = (0..65).map(|_| Counter::new()).collect::>(); + for c in &counters { + let w = make_waker(c.0.clone()); + let cx = Context::from_waker(&w); + ps.register(cx.waker()); } + assert_eq!(ps.wake(), 64); + let total: usize = counters.iter().map(|c| c.count()).sum(); + assert_eq!(total, 65); +} - #[test] - fn drop_wakes() { - let ps = PollSet::new(); - let counters = Arc::new(AtomicUsize::new(0)); - for _ in 0..10 { - let w = make_waker(counters.clone()); - let cx = Context::from_waker(&w); - ps.register(cx.waker()); - } - drop(ps); - assert_eq!(counters.load(Ordering::SeqCst), 10); +#[test] +fn drop_wakes() { + let ps = PollSet::new(); + let counters = Counter::new(); + for _ in 0..10 { + let w = make_waker(counters.0.clone()); + let cx = Context::from_waker(&w); + ps.register(cx.waker()); } + drop(ps); + assert_eq!(counters.count(), 10); } From 3cbb3ee452a5b1382d019f2cafc2f1bd4db93b42 Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Tue, 25 Nov 2025 20:19:26 +0800 Subject: [PATCH 5/8] remove double Arc --- tests/tests.rs | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/tests/tests.rs b/tests/tests.rs index da109ae..57b316a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -8,11 +8,11 @@ use std::{ use axpoll::PollSet; -struct Counter(Arc); +struct Counter(AtomicUsize); impl Counter { - fn new() -> Self { - Self(Arc::new(AtomicUsize::new(0))) + fn new() -> Arc { + Arc::new(Self(AtomicUsize::new(0))) } fn count(&self) -> usize { @@ -34,16 +34,11 @@ impl Wake for Counter { } } -fn make_waker(counter: Arc) -> Waker { - let wrapped = Arc::new(Counter(counter.clone())); - Waker::from(wrapped) -} - #[test] fn register_and_wake() { let ps = PollSet::new(); let counter = Counter::new(); - let w = make_waker(counter.0.clone()); + let w = Waker::from(counter.clone()); ps.register(&w); assert_eq!(ps.wake(), 1); assert_eq!(counter.count(), 1); @@ -60,7 +55,7 @@ fn full_capacity() { let ps = PollSet::new(); let counter = Counter::new(); for _ in 0..64 { - let w = make_waker(counter.0.clone()); + let w = Waker::from(counter.clone()); let cx = Context::from_waker(&w); ps.register(cx.waker()); } @@ -74,7 +69,7 @@ fn overwrite() { let ps = PollSet::new(); let counters = (0..65).map(|_| Counter::new()).collect::>(); for c in &counters { - let w = make_waker(c.0.clone()); + let w = Waker::from(c.clone()); let cx = Context::from_waker(&w); ps.register(cx.waker()); } @@ -88,7 +83,7 @@ fn drop_wakes() { let ps = PollSet::new(); let counters = Counter::new(); for _ in 0..10 { - let w = make_waker(counters.0.clone()); + let w = Waker::from(counters.clone()); let cx = Context::from_waker(&w); ps.register(cx.waker()); } From 1123ef060564ad7edaeb8205bd60db39504ff50f Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Wed, 26 Nov 2025 01:08:18 +0800 Subject: [PATCH 6/8] test: add async test --- Cargo.lock | 139 +++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 3 ++ tests/tests.rs | 52 ++++++++++++++++++ 3 files changed, 194 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index acc0338..4322734 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "0.1.0" dependencies = [ "bitflags", "linux-raw-sys", + "rand", "spin", ] @@ -17,14 +18,152 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + [[package]] name = "linux-raw-sys" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom", +] + [[package]] name = "spin" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "zerocopy" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml index 2282781..63c9534 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,6 @@ linux-raw-sys = { version = "0.11", default-features = false, features = [ "general", ] } spin = { version = "0.10", default-features = false, features = ["spin_mutex"] } + +[dev-dependencies] +rand = "0.9" \ No newline at end of file diff --git a/tests/tests.rs b/tests/tests.rs index 57b316a..bcfc4f6 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,10 +2,14 @@ use std::{ sync::{ Arc, atomic::{AtomicUsize, Ordering}, + mpsc, }, task::{Context, Wake, Waker}, + thread, + time::Duration, }; +use rand::{Rng, rng}; use axpoll::PollSet; struct Counter(AtomicUsize); @@ -90,3 +94,51 @@ fn drop_wakes() { drop(ps); assert_eq!(counters.count(), 10); } +#[test] +fn concurrent_registers() { + let ps = Arc::new(PollSet::new()); + let (tx, rx) = mpsc::channel(); + let threads_n = 50usize; + let per_thread = 200usize; + let total = threads_n * per_thread; + let mut handles = Vec::new(); + + let ps1 = ps.clone(); + let wake_handle = thread::spawn(move || { + for _ in 0..(threads_n * 10) { + ps1.as_ref().wake(); + let mut rng = rng(); + let s = rng.random_range(0..3); + thread::sleep(Duration::from_millis(s)); + } + }); + for _ in 0..threads_n { + let tx = tx.clone(); + let ps = ps.clone(); + let handle = thread::spawn(move || { + let mut rng = rng(); + for _ in 0..per_thread { + let counter = Counter::new(); + let w = Waker::from(counter.clone()); + let cx = Context::from_waker(&w); + if rng.random_bool(0.1) { + thread::sleep(Duration::from_micros(rng.random_range(0..500))); + } + ps.register(cx.waker()); + tx.send(counter).unwrap(); + } + }); + handles.push(handle); + } + + drop(tx); + for h in handles { + h.join().unwrap(); + } + wake_handle.join().unwrap(); + let counters: Vec<_> = rx.into_iter().collect(); + ps.wake(); + let woke: usize = counters.iter().map(|c| c.count()).sum(); + assert_eq!(counters.len(), total); + assert_eq!(woke, total); +} From a3c45fb1ff2df02da481a656f8f1beac6b790180 Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Wed, 26 Nov 2025 01:17:09 +0800 Subject: [PATCH 7/8] fix: ci --- .github/workflows/ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 434798e..6b02fd1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,10 +7,9 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Setup Rust toolchain - run: | - rustup default nightly - rustup component add clippy + - uses: dtolnay/rust-toolchain@nightly + with: + components: clippy - name: Clippy run: cargo clippy --all-features --all-targets -- -Dwarnings - name: Test From d4b4eb962a0b724b5ddeaa1aa76470480e671f88 Mon Sep 17 00:00:00 2001 From: HoshimiP Date: Thu, 27 Nov 2025 20:55:04 +0800 Subject: [PATCH 8/8] test: Add async tests --- Cargo.lock | 188 +++++++++++++++++++++++++++++-------------------- Cargo.toml | 3 +- tests/async.rs | 102 +++++++++++++++++++++++++++ tests/tests.rs | 52 -------------- 4 files changed, 214 insertions(+), 131 deletions(-) create mode 100644 tests/async.rs diff --git a/Cargo.lock b/Cargo.lock index 4322734..b230bcd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,9 +7,10 @@ name = "axpoll" version = "0.1.0" dependencies = [ "bitflags", + "futures", "linux-raw-sys", - "rand", "spin", + "tokio", ] [[package]] @@ -19,97 +20,142 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] -name = "cfg-if" -version = "1.0.4" +name = "futures" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] [[package]] -name = "getrandom" -version = "0.3.4" +name = "futures-channel" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ - "cfg-if", - "libc", - "r-efi", - "wasip2", + "futures-core", + "futures-sink", ] [[package]] -name = "libc" -version = "0.2.177" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "linux-raw-sys" -version = "0.11.0" +name = "futures-executor" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ - "zerocopy", + "futures-core", + "futures-task", + "futures-util", ] [[package]] -name = "proc-macro2" -version = "1.0.103" +name = "futures-io" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" -dependencies = [ - "unicode-ident", -] +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] -name = "quote" -version = "1.0.42" +name = "futures-macro" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", + "quote", + "syn", ] [[package]] -name = "r-efi" -version = "5.3.0" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] -name = "rand" -version = "0.9.2" +name = "futures-task" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "rand_chacha", - "rand_core", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "rand_chacha" -version = "0.9.0" +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ - "ppv-lite86", - "rand_core", + "unicode-ident", ] [[package]] -name = "rand_core" -version = "0.9.3" +name = "quote" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ - "getrandom", + "proc-macro2", ] +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + [[package]] name = "spin" version = "0.10.0" @@ -128,42 +174,28 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "wasip2" -version = "1.0.1+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" -dependencies = [ - "wit-bindgen", -] - -[[package]] -name = "wit-bindgen" -version = "0.46.0" +name = "tokio" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" - -[[package]] -name = "zerocopy" -version = "0.8.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "zerocopy-derive", + "pin-project-lite", + "tokio-macros", ] [[package]] -name = "zerocopy-derive" -version = "0.8.30" +name = "tokio-macros" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" diff --git a/Cargo.toml b/Cargo.toml index 63c9534..6509be9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,4 +21,5 @@ linux-raw-sys = { version = "0.11", default-features = false, features = [ spin = { version = "0.10", default-features = false, features = ["spin_mutex"] } [dev-dependencies] -rand = "0.9" \ No newline at end of file +futures = "0.3" +tokio = { version = "1", features = ["rt-multi-thread", "macros", "time"] } \ No newline at end of file diff --git a/tests/async.rs b/tests/async.rs new file mode 100644 index 0000000..3d1cd5e --- /dev/null +++ b/tests/async.rs @@ -0,0 +1,102 @@ +use std::{ + future::Future, + pin::Pin, + sync::{ + Arc, + atomic::{AtomicBool, AtomicUsize, Ordering}, + }, + task::{Context, Poll}, + time::Duration, +}; + +use axpoll::PollSet; +use tokio::time; + +struct WaitFuture { + ps: Arc, + ready: Arc, +} + +impl Future for WaitFuture { + type Output = (); + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + if self.ready.load(Ordering::SeqCst) { + Poll::Ready(()) + } else { + self.ps.register(cx.waker()); + Poll::Pending + } + } +} + +impl WaitFuture { + fn new(ps: Arc, ready: Arc) -> Self { + Self { ps, ready } + } +} + +struct Counter(AtomicUsize); + +impl Counter { + fn new() -> Arc { + Arc::new(Self(AtomicUsize::new(0))) + } + + fn count(&self) -> usize { + self.0.load(Ordering::SeqCst) + } + + fn add(&self) { + self.0.fetch_add(1, Ordering::SeqCst); + } +} + +#[tokio::test] +async fn async_wake_single() { + let ps = Arc::new(PollSet::new()); + let ready = Arc::new(AtomicBool::new(false)); + + let f = WaitFuture::new(ps.clone(), ready.clone()); + + let handle = tokio::spawn(async move { + ready.clone().store(true, Ordering::SeqCst); + ps.clone().wake(); + }); + + f.await; + handle.await.unwrap(); +} + +#[tokio::test] +async fn async_wake_many() { + let ps = Arc::new(PollSet::new()); + let counter = Counter::new(); + + let mut flags = Vec::new(); + let mut handles = Vec::new(); + + for _ in 0..65 { + let flag = Arc::new(AtomicBool::new(false)); + let f = WaitFuture::new(ps.clone(), flag.clone()); + let counter = counter.clone(); + let h = tokio::spawn(async move { + f.await; + counter.add(); + }); + flags.push(flag); + handles.push(h); + } + + time::sleep(Duration::from_millis(20)).await; + + for f in &flags { + f.store(true, Ordering::SeqCst); + } + ps.wake(); + for h in handles { + h.await.unwrap(); + } + + assert_eq!(counter.count(), 65); +} diff --git a/tests/tests.rs b/tests/tests.rs index bcfc4f6..57b316a 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -2,14 +2,10 @@ use std::{ sync::{ Arc, atomic::{AtomicUsize, Ordering}, - mpsc, }, task::{Context, Wake, Waker}, - thread, - time::Duration, }; -use rand::{Rng, rng}; use axpoll::PollSet; struct Counter(AtomicUsize); @@ -94,51 +90,3 @@ fn drop_wakes() { drop(ps); assert_eq!(counters.count(), 10); } -#[test] -fn concurrent_registers() { - let ps = Arc::new(PollSet::new()); - let (tx, rx) = mpsc::channel(); - let threads_n = 50usize; - let per_thread = 200usize; - let total = threads_n * per_thread; - let mut handles = Vec::new(); - - let ps1 = ps.clone(); - let wake_handle = thread::spawn(move || { - for _ in 0..(threads_n * 10) { - ps1.as_ref().wake(); - let mut rng = rng(); - let s = rng.random_range(0..3); - thread::sleep(Duration::from_millis(s)); - } - }); - for _ in 0..threads_n { - let tx = tx.clone(); - let ps = ps.clone(); - let handle = thread::spawn(move || { - let mut rng = rng(); - for _ in 0..per_thread { - let counter = Counter::new(); - let w = Waker::from(counter.clone()); - let cx = Context::from_waker(&w); - if rng.random_bool(0.1) { - thread::sleep(Duration::from_micros(rng.random_range(0..500))); - } - ps.register(cx.waker()); - tx.send(counter).unwrap(); - } - }); - handles.push(handle); - } - - drop(tx); - for h in handles { - h.join().unwrap(); - } - wake_handle.join().unwrap(); - let counters: Vec<_> = rx.into_iter().collect(); - ps.wake(); - let woke: usize = counters.iter().map(|c| c.count()).sum(); - assert_eq!(counters.len(), total); - assert_eq!(woke, total); -}