async-io-1.6.0/.cargo_vcs_info.json0000644000000001120000000000100125540ustar { "git": { "sha1": "36ab1d28a9341ffe92dd2265d70c42990c6754b8" } } async-io-1.6.0/.github/workflows/build-and-test.yaml000064400000000000000000000041510072674642500205000ustar 00000000000000name: Build and test on: push: branches: - master pull_request: jobs: build_and_test: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, windows-latest, macos-latest] rust: [nightly, beta, stable] steps: - uses: actions/checkout@v2 - 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 }} - 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 test # Copied from: https://github.com/rust-lang/stacker/pull/19/files windows_gnu: runs-on: windows-latest strategy: matrix: rust: [nightly] target: - x86_64-pc-windows-gnu steps: - uses: actions/checkout@v1 - 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 }} - run: rustup target add ${{ matrix.target }} # https://github.com/rust-lang/rust/issues/49078 - name: Fix windows-gnu rust-mingw run : | for i in crt2.o dllcrt2.o libmingwex.a libmsvcrt.a ; do cp -f "/C/ProgramData/Chocolatey/lib/mingw/tools/install/mingw64/x86_64-w64-mingw32/lib/$i" "`rustc --print sysroot`/lib/rustlib/x86_64-pc-windows-gnu/lib" done shell: bash - run: cargo build --target ${{ matrix.target }} --all --all-features --all-targets - run: cargo test --target ${{ matrix.target }} msrv: runs-on: ubuntu-latest strategy: matrix: rust: [1.46.0] steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }} - run: cargo build async-io-1.6.0/.github/workflows/cross.yaml000064400000000000000000000016770072674642500170270ustar 00000000000000name: Cross compile on: push: branches: - master pull_request: jobs: cross: name: Cross compile runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest] steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update stable - name: Install cross run: cargo install cross - name: Android if: startsWith(matrix.os, 'ubuntu') run: cross test --target arm-linux-androideabi - name: NetBSD if: startsWith(matrix.os, 'ubuntu') run: cross build --target x86_64-unknown-netbsd - name: FreeBSD if: startsWith(matrix.os, 'ubuntu') run: cross build --target x86_64-unknown-freebsd - name: iOS if: startsWith(matrix.os, 'macos') run: cross build --target aarch64-apple-ios # - name: illumos # if: startsWith(matrix.os, 'ubuntu') # run: cross build --target x86_64-unknown-illumos async-io-1.6.0/.github/workflows/lint.yaml000064400000000000000000000005610072674642500166330ustar 00000000000000name: Lint on: push: branches: - master pull_request: jobs: clippy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install Rust run: rustup update stable - uses: actions-rs/clippy-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} args: --all-features -- -W clippy::all async-io-1.6.0/.github/workflows/security.yaml000064400000000000000000000004240072674642500175320ustar 00000000000000name: Security audit on: push: branches: - master pull_request: jobs: security_audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/audit-check@v1 with: token: ${{ secrets.GITHUB_TOKEN }} async-io-1.6.0/.gitignore000064400000000000000000000000230072674642500133650ustar 00000000000000/target Cargo.lock async-io-1.6.0/CHANGELOG.md000064400000000000000000000061110072674642500132120ustar 00000000000000# Version 1.6.0 - Add `Readable` and `Writable` futures. (#64, #66) - Add `Async::{readable_owned, writable_owned}`. (#66) # Version 1.5.0 [YANKED] - Add `Readable` and `Writable` futures. (#64) # Version 1.4.1 - Remove dependency on deprecated `vec-arena`. (#60) # Version 1.4.0 - Implement `AsRef` and `AsMut` for `Async`. (#44) - Remove dependency on deprecated `nb-connect`. (#55) # Version 1.3.1 - Lower MSRV to 1.41.0 # Version 1.3.0 - Add `Timer::interval()` and `Timer::set_interval()`. - Add `Timer::interval_at()` and `Timer::set_interval_at()`. - Implement `Stream` for `Timer`. # Version 1.2.0 - Add `Async::poll_readable()` and `Async::poll_writable()`. # Version 1.1.10 - Update `futures-lite`. # Version 1.1.9 - Only require `libc` on Unix platforms. # Version 1.1.8 - Re-enable `async-net` dependency and fix CI. # Version 1.1.7 - Update `polling` to v2.0.0 # Version 1.1.6 - Remove randomized yielding everywhere. # Version 1.1.5 - Remove randomized yielding in write operations. # Version 1.1.4 - Implement proper cancelation for `readable()` and `writable()`. # Version 1.1.3 - Improve docs. # Version 1.1.2 - Add `nb-connect` dependency. - Remove `wepoll-sys-stjepang` dependency. # Version 1.1.1 - Remove `socket2` dependency. # Version 1.1.0 - Add `TryFrom` conversion impls for `Async`. # Version 1.0.2 - Don't box `T` in `Async`. - `Async::incoming()` doesn't return `Unpin` streams anymore. # Version 1.0.1 - Update dependencies. # Version 1.0.0 - Stabilize. # Version 0.2.7 - Replace `log::debug!` with `log::trace!`. # Version 0.2.6 - Add logging. # Version 0.2.5 - On Linux, fail fast if `writable()` succeeds after connecting to `UnixStream`, but the connection is not really established. # Version 0.2.4 - Prevent threads in `async_io::block_on()` from hogging the reactor forever. # Version 0.2.3 - Performance optimizations in `block_on()`. # Version 0.2.2 - Add probabilistic yielding to improve fairness. # Version 0.2.1 - Update readme. # Version 0.2.0 - Replace `parking` module with `block_on()`. - Fix a bug in `Async::::connect()`. # Version 0.1.11 - Bug fix: clear events list before polling. # Version 0.1.10 - Simpler implementation of the `parking` module. - Extracted raw bindings to epoll/kqueue/wepoll into the `polling` crate. # Version 0.1.9 - Update dependencies. - More documentation. # Version 0.1.8 - Tweak the async-io to poll I/O less aggressively. # Version 0.1.7 - Tweak the async-io thread to use less CPU. - More examples. # Version 0.1.6 - Add `Timer::reset()`. - Add third party licenses. - Code cleanup. # Version 0.1.5 - Make `Parker` and `Unparker` unwind-safe. # Version 0.1.4 - Initialize the reactor in `Parker::new()`. # Version 0.1.3 - Always use the last waker given to `Timer`. - Shutdown the socket in `AsyncWrite::poll_close()`. - Reduce the number of dependencies. # Version 0.1.2 - Shutdown the write side of the socket in `AsyncWrite::poll_close()`. - Code and dependency cleanup. - Always use the last waker when polling a timer. # Version 0.1.1 - Initial version async-io-1.6.0/Cargo.lock0000644000000315740000000000100105470ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "async-channel" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" dependencies = [ "concurrent-queue", "event-listener", "futures-core", ] [[package]] name = "async-io" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bbfd5cf2794b1e908ea8457e6c45f8f8f1f6ec5f74617bf4662623f47503c3b" dependencies = [ "concurrent-queue", "fastrand", "futures-lite", "libc", "log", "once_cell", "parking", "polling", "slab", "socket2", "waker-fn", "winapi", ] [[package]] name = "async-io" version = "1.6.0" dependencies = [ "async-channel", "async-net", "blocking", "concurrent-queue", "futures-lite", "inotify", "libc", "log", "nix", "once_cell", "parking", "polling", "signal-hook", "slab", "socket2", "tempfile", "timerfd", "uds_windows", "waker-fn", "winapi", ] [[package]] name = "async-net" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69b0a74e7f70af3c8cf1aa539edbd044795706659ac52b78a71dc1a205ecefdf" dependencies = [ "async-io 1.4.1", "blocking", "fastrand", "futures-lite", ] [[package]] name = "async-task" version = "4.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" [[package]] name = "atomic-waker" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" [[package]] name = "autocfg" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "blocking" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" dependencies = [ "async-channel", "async-task", "atomic-waker", "fastrand", "futures-lite", "once_cell", ] [[package]] name = "cache-padded" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" [[package]] name = "cc" version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a72c244c1ff497a746a7e1fb3d14bd08420ecda70c8f25c7112f2781652d787" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ "cache-padded", ] [[package]] name = "event-listener" version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" [[package]] name = "fastrand" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77b705829d1e87f762c2df6da140b26af5839e1033aa84aa5f56bb688e4e1bdb" dependencies = [ "instant", ] [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" [[package]] name = "futures-core" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0402f765d8a89a26043b889b26ce3c4679d268fa6bb22cd7c6aad98340e179d1" [[package]] name = "futures-io" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acc499defb3b348f8d8f3f66415835a9131856ff7714bf10dadfc4ec4bdb29a1" [[package]] name = "futures-lite" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" dependencies = [ "fastrand", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "getrandom" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "inotify" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b031475cb1b103ee221afb806a23d35e0570bf7271d7588762ceba8127ed43b3" dependencies = [ "bitflags", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" dependencies = [ "libc", ] [[package]] name = "instant" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" dependencies = [ "cfg-if", ] [[package]] name = "libc" version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6" [[package]] name = "log" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if", ] [[package]] name = "memchr" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" [[package]] name = "memoffset" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" dependencies = [ "autocfg", ] [[package]] name = "nix" version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c3728fec49d363a50a8828a190b379a446cc5cf085c06259bbbeb34447e4ec7" dependencies = [ "bitflags", "cc", "cfg-if", "libc", "memoffset", ] [[package]] name = "once_cell" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" [[package]] name = "parking" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" [[package]] name = "pin-project-lite" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" [[package]] name = "polling" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" dependencies = [ "cfg-if", "libc", "log", "wepoll-ffi", "winapi", ] [[package]] name = "ppv-lite86" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" [[package]] name = "rand" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" dependencies = [ "fuchsia-cprng", "libc", "rand_core 0.3.1", "rdrand", "winapi", ] [[package]] name = "rand" version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" dependencies = [ "libc", "rand_chacha", "rand_core 0.6.3", "rand_hc", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core 0.6.3", ] [[package]] name = "rand_core" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" dependencies = [ "rand_core 0.4.2", ] [[package]] name = "rand_core" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" [[package]] name = "rand_core" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ "getrandom", ] [[package]] name = "rand_hc" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" dependencies = [ "rand_core 0.6.3", ] [[package]] name = "rdrand" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" dependencies = [ "rand_core 0.3.1", ] [[package]] name = "redox_syscall" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee" dependencies = [ "bitflags", ] [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[package]] name = "signal-hook" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "470c5a6397076fae0094aaf06a08e6ba6f37acb77d3b1b91ea92b4d6c8650c39" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f173ac3d1a7e3b28003f40de0b5ce7fe2710f9b9dc3fc38664cebee46b3b6527" [[package]] name = "socket2" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e3dfc207c526015c632472a77be09cf1b6e46866581aecae5cc38fb4235dea2" dependencies = [ "libc", "winapi", ] [[package]] name = "tempdir" version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" dependencies = [ "rand 0.4.6", "remove_dir_all", ] [[package]] name = "tempfile" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" dependencies = [ "cfg-if", "libc", "rand 0.8.4", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] name = "timerfd" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb53e6628675d73224925201a9a41f01c8d31108fdccb983975a1c1449dfc91" dependencies = [ "libc", ] [[package]] name = "uds_windows" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "486992108df0fe0160680af1941fe856c521be931d5a5ecccefe0de86dc47e4a" dependencies = [ "tempdir", "winapi", ] [[package]] name = "waker-fn" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "wepoll-ffi" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" dependencies = [ "cc", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" async-io-1.6.0/Cargo.toml0000644000000040470000000000100105650ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "async-io" version = "1.6.0" authors = ["Stjepan Glavina "] description = "Async I/O and timers" homepage = "https://github.com/smol-rs/async-io" documentation = "https://docs.rs/async-io" keywords = ["mio", "epoll", "kqueue", "iocp", "wepoll"] categories = ["asynchronous", "network-programming", "os"] license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/async-io" [dependencies.concurrent-queue] version = "1.2.2" [dependencies.futures-lite] version = "1.11.0" [dependencies.log] version = "0.4.11" [dependencies.once_cell] version = "1.4.1" [dependencies.parking] version = "2.0.0" [dependencies.polling] version = "2.0.0" [dependencies.slab] version = "0.4.2" [dependencies.socket2] version = "0.4.0" features = ["all"] [dependencies.waker-fn] version = "1.1.0" [dev-dependencies.async-channel] version = "1.4.2" [dev-dependencies.async-net] version = "1.3.0" [dev-dependencies.blocking] version = "1.0.0" [dev-dependencies.signal-hook] version = "0.3" [dev-dependencies.tempfile] version = "3.1.0" [target."cfg(target_os = \"linux\")".dev-dependencies.inotify] version = "0.9" default-features = false [target."cfg(target_os = \"linux\")".dev-dependencies.nix] version = "0.21" [target."cfg(target_os = \"linux\")".dev-dependencies.timerfd] version = "1.1.1" [target."cfg(unix)".dependencies.libc] version = "0.2.77" [target."cfg(windows)".dependencies.winapi] version = "0.3.9" features = ["winsock2"] [target."cfg(windows)".dev-dependencies.uds_windows] version = "1" async-io-1.6.0/Cargo.toml.orig000064400000000000000000000023000072674642500142640ustar 00000000000000[package] name = "async-io" # When publishing a new version: # - Update CHANGELOG.md # - Create "v1.x.y" git tag version = "1.6.0" authors = ["Stjepan Glavina "] edition = "2018" description = "Async I/O and timers" license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/async-io" homepage = "https://github.com/smol-rs/async-io" documentation = "https://docs.rs/async-io" keywords = ["mio", "epoll", "kqueue", "iocp", "wepoll"] categories = ["asynchronous", "network-programming", "os"] [dependencies] concurrent-queue = "1.2.2" futures-lite = "1.11.0" log = "0.4.11" once_cell = "1.4.1" parking = "2.0.0" polling = "2.0.0" slab = "0.4.2" socket2 = { version = "0.4.0", features = ["all"] } waker-fn = "1.1.0" [target."cfg(unix)".dependencies] libc = "0.2.77" [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.9", features = ["winsock2"] } [dev-dependencies] async-channel = "1.4.2" async-net = "1.3.0" blocking = "1.0.0" signal-hook = "0.3" tempfile = "3.1.0" [target.'cfg(target_os = "linux")'.dev-dependencies] inotify = { version = "0.9", default-features = false } nix = "0.21" timerfd = "1.1.1" [target.'cfg(windows)'.dev-dependencies] uds_windows = "1" async-io-1.6.0/LICENSE-APACHE000064400000000000000000000251370072674642500133360ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. async-io-1.6.0/LICENSE-MIT000064400000000000000000000017770072674642500130520ustar 00000000000000Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. async-io-1.6.0/README.md000064400000000000000000000053250072674642500126660ustar 00000000000000# async-io [![Build](https://github.com/smol-rs/async-io/workflows/Build%20and%20test/badge.svg)]( https://github.com/smol-rs/async-io/actions) [![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( https://github.com/smol-rs/async-io) [![Cargo](https://img.shields.io/crates/v/async-io.svg)]( https://crates.io/crates/async-io) [![Documentation](https://docs.rs/async-io/badge.svg)]( https://docs.rs/async-io) Async I/O and timers. This crate provides two tools: * `Async`, an adapter for standard networking types (and [many other] types) to use in async programs. * `Timer`, a future that expires at a point in time. For concrete async networking types built on top of this crate, see [`async-net`]. [many other]: https://github.com/smol-rs/async-io/tree/master/examples [`async-net`]: https://docs.rs/async-net ## Implementation The first time `Async` or `Timer` is used, a thread named "async-io" will be spawned. The purpose of this thread is to wait for I/O events reported by the operating system, and then wake appropriate futures blocked on I/O or timers when they can be resumed. To wait for the next I/O event, the "async-io" thread uses [epoll] on Linux/Android/illumos, [kqueue] on macOS/iOS/BSD, [event ports] on illumos/Solaris, and [wepoll] on Windows. That functionality is provided by the [`polling`] crate. However, note that you can also process I/O events and wake futures on any thread using the `block_on()` function. The "async-io" thread is therefore just a fallback mechanism processing I/O events in case no other threads are. [epoll]: https://en.wikipedia.org/wiki/Epoll [kqueue]: https://en.wikipedia.org/wiki/Kqueue [event ports]: https://illumos.org/man/port_create [wepoll]: https://github.com/piscisaureus/wepoll [`polling`]: https://docs.rs/polling ## Examples Connect to `example.com:80`, or time out after 10 seconds. ```rust use async_io::{Async, Timer}; use futures_lite::{future::FutureExt, io}; use std::net::{TcpStream, ToSocketAddrs}; use std::time::Duration; let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); let stream = Async::::connect(addr).or(async { Timer::after(Duration::from_secs(10)).await; Err(io::ErrorKind::TimedOut.into()) }) .await?; ``` ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. #### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. async-io-1.6.0/examples/linux-inotify.rs000064400000000000000000000031640072674642500164100ustar 00000000000000//! Uses the `inotify` crate to watch for changes in the current directory. //! //! Run with: //! //! ``` //! cargo run --example linux-inotify //! ``` #[cfg(target_os = "linux")] fn main() -> std::io::Result<()> { use std::ffi::OsString; use std::io; use async_io::Async; use futures_lite::future; use inotify::{EventMask, Inotify, WatchMask}; type Event = (OsString, EventMask); /// Reads some events without blocking. /// /// If there are no events, an [`io::ErrorKind::WouldBlock`] error is returned. fn read_op(inotify: &mut Inotify) -> io::Result> { let mut buffer = [0; 1024]; let events = inotify .read_events(&mut buffer)? .filter_map(|ev| ev.name.map(|name| (name.to_owned(), ev.mask))) .collect::>(); if events.is_empty() { Err(io::ErrorKind::WouldBlock.into()) } else { Ok(events) } } future::block_on(async { // Watch events in the current directory. let mut inotify = Async::new(Inotify::init()?)?; inotify.get_mut().add_watch(".", WatchMask::ALL_EVENTS)?; println!("Watching for filesystem events in the current directory..."); println!("Try opening a file to trigger some events."); println!(); // Wait for events in a loop and print them on the screen. loop { for event in inotify.read_with_mut(read_op).await? { println!("{:?}", event); } } }) } #[cfg(not(target_os = "linux"))] fn main() { println!("This example works only on Linux!"); } async-io-1.6.0/examples/linux-timerfd.rs000064400000000000000000000027470072674642500163670ustar 00000000000000//! Uses the `timerfd` crate to sleep using an OS timer. //! //! Run with: //! //! ``` //! cargo run --example linux-timerfd //! ``` #[cfg(target_os = "linux")] fn main() -> std::io::Result<()> { use std::io; use std::os::unix::io::AsRawFd; use std::time::{Duration, Instant}; use async_io::Async; use futures_lite::future; use timerfd::{SetTimeFlags, TimerFd, TimerState}; /// Converts a [`nix::Error`] into [`std::io::Error`]. fn io_err(err: nix::Error) -> io::Error { match err { nix::Error::Sys(code) => code.into(), err => io::Error::new(io::ErrorKind::Other, Box::new(err)), } } /// Sleeps using an OS timer. async fn sleep(dur: Duration) -> io::Result<()> { // Create an OS timer. let mut timer = TimerFd::new()?; timer.set_state(TimerState::Oneshot(dur), SetTimeFlags::Default); // When the OS timer fires, a 64-bit integer can be read from it. Async::new(timer)? .read_with(|t| nix::unistd::read(t.as_raw_fd(), &mut [0u8; 8]).map_err(io_err)) .await?; Ok(()) } future::block_on(async { let start = Instant::now(); println!("Sleeping..."); // Sleep for a second using an OS timer. sleep(Duration::from_secs(1)).await?; println!("Woke up after {:?}", start.elapsed()); Ok(()) }) } #[cfg(not(target_os = "linux"))] fn main() { println!("This example works only on Linux!"); } async-io-1.6.0/examples/unix-signal.rs000064400000000000000000000015420072674642500160260ustar 00000000000000//! Uses the `signal-hook` crate to catch the Ctrl-C signal. //! //! Run with: //! //! ``` //! cargo run --example unix-signal //! ``` #[cfg(unix)] fn main() -> std::io::Result<()> { use std::os::unix::{io::AsRawFd, net::UnixStream}; use async_io::Async; use futures_lite::{future, prelude::*}; future::block_on(async { // Create a Unix stream that receives a byte on each signal occurrence. let (a, mut b) = Async::::pair()?; signal_hook::low_level::pipe::register_raw(signal_hook::consts::SIGINT, a.as_raw_fd())?; println!("Waiting for Ctrl-C..."); // Receive a byte that indicates the Ctrl-C signal occurred. b.read_exact(&mut [0]).await?; println!("Done!"); Ok(()) }) } #[cfg(not(unix))] fn main() { println!("This example works only on Unix systems!"); } async-io-1.6.0/examples/windows-uds.rs000064400000000000000000000031150072674642500160510ustar 00000000000000//! Uses the `uds_windows` crate to simulate Unix sockets on Windows. //! //! Run with: //! //! ``` //! cargo run --example windows-uds //! ``` #[cfg(windows)] fn main() -> std::io::Result<()> { use std::path::PathBuf; use async_io::Async; use blocking::Unblock; use futures_lite::{future, io, prelude::*}; use tempfile::tempdir; use uds_windows::{UnixListener, UnixStream}; async fn client(addr: PathBuf) -> io::Result<()> { // Connect to the address. let stream = Async::new(UnixStream::connect(addr)?)?; println!("Connected to {:?}", stream.get_ref().peer_addr()?); // Pipe the stream to stdout. let mut stdout = Unblock::new(std::io::stdout()); io::copy(&stream, &mut stdout).await?; Ok(()) } let dir = tempdir()?; let path = dir.path().join("socket"); future::block_on(async { // Create a listener. let listener = Async::new(UnixListener::bind(&path)?)?; println!("Listening on {:?}", listener.get_ref().local_addr()?); future::try_zip( async { // Accept the client. let (stream, _) = listener.read_with(|l| l.accept()).await?; println!("Accepted a client"); // Send a message, drop the stream, and wait for the client. Async::new(stream)?.write_all(b"Hello!\n").await?; Ok(()) }, client(path), ) .await?; Ok(()) }) } #[cfg(not(windows))] fn main() { println!("This example works only on Windows!"); } async-io-1.6.0/src/driver.rs000064400000000000000000000174600072674642500140420ustar 00000000000000use std::cell::Cell; use std::future::Future; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; use std::thread; use std::time::{Duration, Instant}; use futures_lite::pin; use once_cell::sync::Lazy; use waker_fn::waker_fn; use crate::reactor::Reactor; /// Number of currently active `block_on()` invocations. static BLOCK_ON_COUNT: AtomicUsize = AtomicUsize::new(0); /// Unparker for the "async-io" thread. static UNPARKER: Lazy = Lazy::new(|| { let (parker, unparker) = parking::pair(); // Spawn a helper thread driving the reactor. // // Note that this thread is not exactly necessary, it's only here to help push things // forward if there are no `Parker`s around or if `Parker`s are just idling and never // parking. thread::Builder::new() .name("async-io".to_string()) .spawn(move || main_loop(parker)) .expect("cannot spawn async-io thread"); unparker }); /// Initializes the "async-io" thread. pub(crate) fn init() { Lazy::force(&UNPARKER); } /// The main loop for the "async-io" thread. fn main_loop(parker: parking::Parker) { // The last observed reactor tick. let mut last_tick = 0; // Number of sleeps since this thread has called `react()`. let mut sleeps = 0u64; loop { let tick = Reactor::get().ticker(); if last_tick == tick { let reactor_lock = if sleeps >= 10 { // If no new ticks have occurred for a while, stop sleeping and spinning in // this loop and just block on the reactor lock. Some(Reactor::get().lock()) } else { Reactor::get().try_lock() }; if let Some(mut reactor_lock) = reactor_lock { log::trace!("main_loop: waiting on I/O"); reactor_lock.react(None).ok(); last_tick = Reactor::get().ticker(); sleeps = 0; } } else { last_tick = tick; } if BLOCK_ON_COUNT.load(Ordering::SeqCst) > 0 { // Exponential backoff from 50us to 10ms. let delay_us = [50, 75, 100, 250, 500, 750, 1000, 2500, 5000] .get(sleeps as usize) .unwrap_or(&10_000); log::trace!("main_loop: sleeping for {} us", delay_us); if parker.park_timeout(Duration::from_micros(*delay_us)) { log::trace!("main_loop: notified"); // If notified before timeout, reset the last tick and the sleep counter. last_tick = Reactor::get().ticker(); sleeps = 0; } else { sleeps += 1; } } } } /// Blocks the current thread on a future, processing I/O events when idle. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use std::time::Duration; /// /// async_io::block_on(async { /// // This timer will likely be processed by the current /// // thread rather than the fallback "async-io" thread. /// Timer::after(Duration::from_millis(1)).await; /// }); /// ``` pub fn block_on(future: impl Future) -> T { log::trace!("block_on()"); // Increment `BLOCK_ON_COUNT` so that the "async-io" thread becomes less aggressive. BLOCK_ON_COUNT.fetch_add(1, Ordering::SeqCst); // Make sure to decrement `BLOCK_ON_COUNT` at the end and wake the "async-io" thread. let _guard = CallOnDrop(|| { BLOCK_ON_COUNT.fetch_sub(1, Ordering::SeqCst); UNPARKER.unpark(); }); // Parker and unparker for notifying the current thread. let (p, u) = parking::pair(); // This boolean is set to `true` when the current thread is blocked on I/O. let io_blocked = Arc::new(AtomicBool::new(false)); thread_local! { // Indicates that the current thread is polling I/O, but not necessarily blocked on it. static IO_POLLING: Cell = Cell::new(false); } // Prepare the waker. let waker = waker_fn({ let io_blocked = io_blocked.clone(); move || { if u.unpark() { // Check if waking from another thread and if currently blocked on I/O. if !IO_POLLING.with(Cell::get) && io_blocked.load(Ordering::SeqCst) { Reactor::get().notify(); } } } }); let cx = &mut Context::from_waker(&waker); pin!(future); loop { // Poll the future. if let Poll::Ready(t) = future.as_mut().poll(cx) { log::trace!("block_on: completed"); return t; } // Check if a notification was received. if p.park_timeout(Duration::from_secs(0)) { log::trace!("block_on: notified"); // Try grabbing a lock on the reactor to process I/O events. if let Some(mut reactor_lock) = Reactor::get().try_lock() { // First let wakers know this parker is processing I/O events. IO_POLLING.with(|io| io.set(true)); let _guard = CallOnDrop(|| { IO_POLLING.with(|io| io.set(false)); }); // Process available I/O events. reactor_lock.react(Some(Duration::from_secs(0))).ok(); } continue; } // Try grabbing a lock on the reactor to wait on I/O. if let Some(mut reactor_lock) = Reactor::get().try_lock() { // Record the instant at which the lock was grabbed. let start = Instant::now(); loop { // First let wakers know this parker is blocked on I/O. IO_POLLING.with(|io| io.set(true)); io_blocked.store(true, Ordering::SeqCst); let _guard = CallOnDrop(|| { IO_POLLING.with(|io| io.set(false)); io_blocked.store(false, Ordering::SeqCst); }); // 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)) { log::trace!("block_on: notified"); break; } // Wait for I/O events. log::trace!("block_on: waiting on I/O"); reactor_lock.react(None).ok(); // Check if a notification has been received. if p.park_timeout(Duration::from_secs(0)) { log::trace!("block_on: notified"); break; } // Check if this thread been handling I/O events for a long time. if start.elapsed() > Duration::from_micros(500) { log::trace!("block_on: stops hogging the reactor"); // This thread is clearly processing I/O events for some other threads // because it didn't get a notification yet. It's best to stop hogging the // reactor and give other threads a chance to process I/O events for // themselves. drop(reactor_lock); // Unpark the "async-io" thread in case no other thread is ready to start // processing I/O events. This way we prevent a potential latency spike. UNPARKER.unpark(); // Wait for a notification. p.park(); break; } } } else { // Wait for an actual notification. log::trace!("block_on: sleep until notification"); p.park(); } } } /// Runs a closure when dropped. struct CallOnDrop(F); impl Drop for CallOnDrop { fn drop(&mut self) { (self.0)(); } } async-io-1.6.0/src/lib.rs000064400000000000000000001620660072674642500133200ustar 00000000000000//! Async I/O and timers. //! //! This crate provides two tools: //! //! * [`Async`], an adapter for standard networking types (and [many other] types) to use in //! async programs. //! * [`Timer`], a future or stream that emits timed events. //! //! For concrete async networking types built on top of this crate, see [`async-net`]. //! //! [many other]: https://github.com/smol-rs/async-io/tree/master/examples //! [`async-net`]: https://docs.rs/async-net //! //! # Implementation //! //! The first time [`Async`] or [`Timer`] is used, a thread named "async-io" will be spawned. //! The purpose of this thread is to wait for I/O events reported by the operating system, and then //! wake appropriate futures blocked on I/O or timers when they can be resumed. //! //! To wait for the next I/O event, the "async-io" thread uses [epoll] on Linux/Android/illumos, //! [kqueue] on macOS/iOS/BSD, [event ports] on illumos/Solaris, and [wepoll] on Windows. That //! functionality is provided by the [`polling`] crate. //! //! However, note that you can also process I/O events and wake futures on any thread using the //! [`block_on()`] function. The "async-io" thread is therefore just a fallback mechanism //! processing I/O events in case no other threads are. //! //! [epoll]: https://en.wikipedia.org/wiki/Epoll //! [kqueue]: https://en.wikipedia.org/wiki/Kqueue //! [event ports]: https://illumos.org/man/port_create //! [wepoll]: https://github.com/piscisaureus/wepoll //! [`polling`]: https://docs.rs/polling //! //! # Examples //! //! Connect to `example.com:80`, or time out after 10 seconds. //! //! ``` //! use async_io::{Async, Timer}; //! use futures_lite::{future::FutureExt, io}; //! //! use std::net::{TcpStream, ToSocketAddrs}; //! use std::time::Duration; //! //! # futures_lite::future::block_on(async { //! let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); //! //! let stream = Async::::connect(addr).or(async { //! Timer::after(Duration::from_secs(10)).await; //! Err(io::ErrorKind::TimedOut.into()) //! }) //! .await?; //! # std::io::Result::Ok(()) }); //! ``` #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] use std::convert::TryFrom; use std::future::Future; use std::io::{self, IoSlice, IoSliceMut, Read, Write}; use std::net::{SocketAddr, TcpListener, TcpStream, UdpSocket}; use std::pin::Pin; use std::sync::Arc; use std::task::{Context, Poll, Waker}; use std::time::{Duration, Instant}; #[cfg(unix)] use std::{ os::unix::io::{AsRawFd, RawFd}, os::unix::net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream}, path::Path, }; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, RawSocket}; use futures_lite::io::{AsyncRead, AsyncWrite}; use futures_lite::stream::{self, Stream}; use futures_lite::{future, pin, ready}; use socket2::{Domain, Protocol, SockAddr, Socket, Type}; use crate::reactor::{Reactor, Source}; mod driver; mod reactor; pub use driver::block_on; pub use reactor::{Readable, ReadableOwned, Writable, WritableOwned}; /// Use `Duration::MAX` once `duration_constants` are stabilized. fn duration_max() -> Duration { Duration::new(std::u64::MAX, 1_000_000_000 - 1) } /// A future or stream that emits timed events. /// /// Timers are futures that output a single [`Instant`] when they fire. /// /// Timers are also streams that can output [`Instant`]s periodically. /// /// # Examples /// /// Sleep for 1 second: /// /// ``` /// use async_io::Timer; /// use std::time::Duration; /// /// # futures_lite::future::block_on(async { /// Timer::after(Duration::from_secs(1)).await; /// # }); /// ``` /// /// Timeout after 1 second: /// /// ``` /// use async_io::Timer; /// use futures_lite::FutureExt; /// use std::time::Duration; /// /// # futures_lite::future::block_on(async { /// let addrs = async_net::resolve("google.com:80") /// .or(async { /// Timer::after(Duration::from_secs(10)).await; /// Err(std::io::ErrorKind::TimedOut.into()) /// }) /// .await?; /// # std::io::Result::Ok(()) }); /// ``` #[derive(Debug)] pub struct Timer { /// This timer's ID and last waker that polled it. /// /// When this field is set to `None`, this timer is not registered in the reactor. id_and_waker: Option<(usize, Waker)>, /// The next instant at which this timer fires. when: Instant, /// The period. period: Duration, } impl Timer { /// Creates a timer that emits an event once after the given duration of time. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use std::time::Duration; /// /// # futures_lite::future::block_on(async { /// Timer::after(Duration::from_secs(1)).await; /// # }); /// ``` pub fn after(duration: Duration) -> Timer { Timer::at(Instant::now() + duration) } /// Creates a timer that emits an event once at the given time instant. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use std::time::{Duration, Instant}; /// /// # futures_lite::future::block_on(async { /// let now = Instant::now(); /// let when = now + Duration::from_secs(1); /// Timer::at(when).await; /// # }); /// ``` pub fn at(instant: Instant) -> Timer { // Use Duration::MAX once duration_constants are stabilized. Timer::interval_at(instant, duration_max()) } /// Creates a timer that emits events periodically. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use futures_lite::StreamExt; /// use std::time::{Duration, Instant}; /// /// # futures_lite::future::block_on(async { /// let period = Duration::from_secs(1); /// Timer::interval(period).next().await; /// # }); /// ``` pub fn interval(period: Duration) -> Timer { Timer::interval_at(Instant::now() + period, period) } /// Creates a timer that emits events periodically, starting at `start`. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use futures_lite::StreamExt; /// use std::time::{Duration, Instant}; /// /// # futures_lite::future::block_on(async { /// let start = Instant::now(); /// let period = Duration::from_secs(1); /// Timer::interval_at(start, period).next().await; /// # }); /// ``` pub fn interval_at(start: Instant, period: Duration) -> Timer { Timer { id_and_waker: None, when: start, period, } } /// Sets the timer to emit an en event once after the given duration of time. /// /// Note that resetting a timer is different from creating a new timer because /// [`set_after()`][`Timer::set_after()`] does not remove the waker associated with the task /// that is polling the timer. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use std::time::Duration; /// /// # futures_lite::future::block_on(async { /// let mut t = Timer::after(Duration::from_secs(1)); /// t.set_after(Duration::from_millis(100)); /// # }); /// ``` pub fn set_after(&mut self, duration: Duration) { self.set_at(Instant::now() + duration); } /// Sets the timer to emit an event once at the given time instant. /// /// Note that resetting a timer is different from creating a new timer because /// [`set_at()`][`Timer::set_at()`] does not remove the waker associated with the task /// that is polling the timer. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use std::time::{Duration, Instant}; /// /// # futures_lite::future::block_on(async { /// let mut t = Timer::after(Duration::from_secs(1)); /// /// let now = Instant::now(); /// let when = now + Duration::from_secs(1); /// t.set_at(when); /// # }); /// ``` pub fn set_at(&mut self, instant: Instant) { if let Some((id, _)) = self.id_and_waker.as_ref() { // Deregister the timer from the reactor. Reactor::get().remove_timer(self.when, *id); } // Update the timeout. self.when = instant; if let Some((id, waker)) = self.id_and_waker.as_mut() { // Re-register the timer with the new timeout. *id = Reactor::get().insert_timer(self.when, waker); } } /// Sets the timer to emit events periodically. /// /// Note that resetting a timer is different from creating a new timer because /// [`set_interval()`][`Timer::set_interval()`] does not remove the waker associated with the /// task that is polling the timer. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use futures_lite::StreamExt; /// use std::time::{Duration, Instant}; /// /// # futures_lite::future::block_on(async { /// let mut t = Timer::after(Duration::from_secs(1)); /// /// let period = Duration::from_secs(2); /// t.set_interval(period); /// # }); /// ``` pub fn set_interval(&mut self, period: Duration) { self.set_interval_at(Instant::now() + period, period); } /// Sets the timer to emit events periodically, starting at `start`. /// /// Note that resetting a timer is different from creating a new timer because /// [`set_interval_at()`][`Timer::set_interval_at()`] does not remove the waker associated with /// the task that is polling the timer. /// /// # Examples /// /// ``` /// use async_io::Timer; /// use futures_lite::StreamExt; /// use std::time::{Duration, Instant}; /// /// # futures_lite::future::block_on(async { /// let mut t = Timer::after(Duration::from_secs(1)); /// /// let start = Instant::now(); /// let period = Duration::from_secs(2); /// t.set_interval_at(start, period); /// # }); /// ``` pub fn set_interval_at(&mut self, start: Instant, period: Duration) { if let Some((id, _)) = self.id_and_waker.as_ref() { // Deregister the timer from the reactor. Reactor::get().remove_timer(self.when, *id); } self.when = start; self.period = period; if let Some((id, waker)) = self.id_and_waker.as_mut() { // Re-register the timer with the new timeout. *id = Reactor::get().insert_timer(self.when, waker); } } } impl Drop for Timer { fn drop(&mut self) { if let Some((id, _)) = self.id_and_waker.take() { // Deregister the timer from the reactor. Reactor::get().remove_timer(self.when, id); } } } impl Future for Timer { type Output = Instant; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { match self.poll_next(cx) { Poll::Ready(Some(when)) => Poll::Ready(when), Poll::Pending => Poll::Pending, Poll::Ready(None) => unreachable!(), } } } impl Stream for Timer { type Item = Instant; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { // Check if the timer has already fired. if Instant::now() >= self.when { if let Some((id, _)) = self.id_and_waker.take() { // Deregister the timer from the reactor. Reactor::get().remove_timer(self.when, id); } let when = self.when; if let Some(next) = when.checked_add(self.period) { self.when = next; // Register the timer in the reactor. let id = Reactor::get().insert_timer(self.when, cx.waker()); self.id_and_waker = Some((id, cx.waker().clone())); } return Poll::Ready(Some(when)); } else { match &self.id_and_waker { None => { // Register the timer in the reactor. let id = Reactor::get().insert_timer(self.when, cx.waker()); self.id_and_waker = Some((id, cx.waker().clone())); } Some((id, w)) if !w.will_wake(cx.waker()) => { // Deregister the timer from the reactor to remove the old waker. Reactor::get().remove_timer(self.when, *id); // Register the timer in the reactor with the new waker. let id = Reactor::get().insert_timer(self.when, cx.waker()); self.id_and_waker = Some((id, cx.waker().clone())); } Some(_) => {} } } Poll::Pending } } /// Async adapter for I/O types. /// /// This type puts an I/O handle into non-blocking mode, registers it in /// [epoll]/[kqueue]/[event ports]/[wepoll], and then provides an async interface for it. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [wepoll]: https://github.com/piscisaureus/wepoll /// /// # Caveats /// /// [`Async`] is a low-level primitive, and as such it comes with some caveats. /// /// For higher-level primitives built on top of [`Async`], look into [`async-net`] or /// [`async-process`] (on Unix). /// /// [`async-net`]: https://github.com/smol-rs/async-net /// [`async-process`]: https://github.com/smol-rs/async-process /// /// ### Supported types /// /// [`Async`] supports all networking types, as well as some OS-specific file descriptors like /// [timerfd] and [inotify]. /// /// However, do not use [`Async`] with types like [`File`][`std::fs::File`], /// [`Stdin`][`std::io::Stdin`], [`Stdout`][`std::io::Stdout`], or [`Stderr`][`std::io::Stderr`] /// because all operating systems have issues with them when put in non-blocking mode. /// /// [timerfd]: https://github.com/smol-rs/async-io/blob/master/examples/linux-timerfd.rs /// [inotify]: https://github.com/smol-rs/async-io/blob/master/examples/linux-inotify.rs /// /// ### Concurrent I/O /// /// Note that [`&Async`][`Async`] implements [`AsyncRead`] and [`AsyncWrite`] if `&T` /// implements those traits, which means tasks can concurrently read and write using shared /// references. /// /// But there is a catch: only one task can read a time, and only one task can write at a time. It /// is okay to have two tasks where one is reading and the other is writing at the same time, but /// it is not okay to have two tasks reading at the same time or writing at the same time. If you /// try to do that, conflicting tasks will just keep waking each other in turn, thus wasting CPU /// time. /// /// Besides [`AsyncRead`] and [`AsyncWrite`], this caveat also applies to /// [`poll_readable()`][`Async::poll_readable()`] and /// [`poll_writable()`][`Async::poll_writable()`]. /// /// However, any number of tasks can be concurrently calling other methods like /// [`readable()`][`Async::readable()`] or [`read_with()`][`Async::read_with()`]. /// /// ### Closing /// /// Closing the write side of [`Async`] with [`close()`][`futures_lite::AsyncWriteExt::close()`] /// simply flushes. If you want to shutdown a TCP or Unix socket, use /// [`Shutdown`][`std::net::Shutdown`]. /// /// # Examples /// /// Connect to a server and echo incoming messages back to the server: /// /// ```no_run /// use async_io::Async; /// use futures_lite::io; /// use std::net::TcpStream; /// /// # futures_lite::future::block_on(async { /// // Connect to a local server. /// let stream = Async::::connect(([127, 0, 0, 1], 8000)).await?; /// /// // Echo all messages from the read side of the stream into the write side. /// io::copy(&stream, &stream).await?; /// # std::io::Result::Ok(()) }); /// ``` /// /// You can use either predefined async methods or wrap blocking I/O operations in /// [`Async::read_with()`], [`Async::read_with_mut()`], [`Async::write_with()`], and /// [`Async::write_with_mut()`]: /// /// ```no_run /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; /// /// // These two lines are equivalent: /// let (stream, addr) = listener.accept().await?; /// let (stream, addr) = listener.read_with(|inner| inner.accept()).await?; /// # std::io::Result::Ok(()) }); /// ``` #[derive(Debug)] pub struct Async { /// A source registered in the reactor. source: Arc, /// The inner I/O handle. io: Option, } impl Unpin for Async {} #[cfg(unix)] impl Async { /// Creates an async I/O handle. /// /// This method will put the handle in non-blocking mode and register it in /// [epoll]/[kqueue]/[event ports]/[wepoll]. /// /// On Unix systems, the handle must implement `AsRawFd`, while on Windows it must implement /// `AsRawSocket`. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [wepoll]: https://github.com/piscisaureus/wepoll /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::{SocketAddr, TcpListener}; /// /// # futures_lite::future::block_on(async { /// let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?; /// let listener = Async::new(listener)?; /// # std::io::Result::Ok(()) }); /// ``` pub fn new(io: T) -> io::Result> { let fd = io.as_raw_fd(); // Put the file descriptor in non-blocking mode. unsafe { let mut res = libc::fcntl(fd, libc::F_GETFL); if res != -1 { res = libc::fcntl(fd, libc::F_SETFL, res | libc::O_NONBLOCK); } if res == -1 { return Err(io::Error::last_os_error()); } } Ok(Async { source: Reactor::get().insert_io(fd)?, io: Some(io), }) } } #[cfg(unix)] impl AsRawFd for Async { fn as_raw_fd(&self) -> RawFd { self.source.raw } } #[cfg(windows)] impl Async { /// Creates an async I/O handle. /// /// This method will put the handle in non-blocking mode and register it in /// [epoll]/[kqueue]/[event ports]/[wepoll]. /// /// On Unix systems, the handle must implement `AsRawFd`, while on Windows it must implement /// `AsRawSocket`. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [wepoll]: https://github.com/piscisaureus/wepoll /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::{SocketAddr, TcpListener}; /// /// # futures_lite::future::block_on(async { /// let listener = TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], 0)))?; /// let listener = Async::new(listener)?; /// # std::io::Result::Ok(()) }); /// ``` pub fn new(io: T) -> io::Result> { let sock = io.as_raw_socket(); // Put the socket in non-blocking mode. unsafe { use winapi::ctypes; use winapi::um::winsock2; let mut nonblocking = true as ctypes::c_ulong; let res = winsock2::ioctlsocket( sock as winsock2::SOCKET, winsock2::FIONBIO, &mut nonblocking, ); if res != 0 { return Err(io::Error::last_os_error()); } } Ok(Async { source: Reactor::get().insert_io(sock)?, io: Some(io), }) } } #[cfg(windows)] impl AsRawSocket for Async { fn as_raw_socket(&self) -> RawSocket { self.source.raw } } impl Async { /// Gets a reference to the inner I/O handle. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; /// let inner = listener.get_ref(); /// # std::io::Result::Ok(()) }); /// ``` pub fn get_ref(&self) -> &T { self.io.as_ref().unwrap() } /// Gets a mutable reference to the inner I/O handle. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; /// let inner = listener.get_mut(); /// # std::io::Result::Ok(()) }); /// ``` pub fn get_mut(&mut self) -> &mut T { self.io.as_mut().unwrap() } /// Unwraps the inner I/O handle. /// /// This method will **not** put the I/O handle back into blocking mode. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; /// let inner = listener.into_inner()?; /// /// // Put the listener back into blocking mode. /// inner.set_nonblocking(false)?; /// # std::io::Result::Ok(()) }); /// ``` pub fn into_inner(mut self) -> io::Result { let io = self.io.take().unwrap(); Reactor::get().remove_io(&self.source)?; Ok(io) } /// Waits until the I/O handle is readable. /// /// This method completes when a read operation on this I/O handle wouldn't block. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; /// /// // Wait until a client can be accepted. /// listener.readable().await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn readable(&self) -> Readable<'_, T> { Source::readable(self) } /// Waits until the I/O handle is readable. /// /// This method completes when a read operation on this I/O handle wouldn't block. pub fn readable_owned(self: Arc) -> ReadableOwned { Source::readable_owned(self) } /// Waits until the I/O handle is writable. /// /// This method completes when a write operation on this I/O handle wouldn't block. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::{TcpStream, ToSocketAddrs}; /// /// # futures_lite::future::block_on(async { /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); /// let stream = Async::::connect(addr).await?; /// /// // Wait until the stream is writable. /// stream.writable().await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn writable(&self) -> Writable<'_, T> { Source::writable(self) } /// Waits until the I/O handle is writable. /// /// This method completes when a write operation on this I/O handle wouldn't block. pub fn writable_owned(self: Arc) -> WritableOwned { Source::writable_owned(self) } /// Polls the I/O handle for readability. /// /// When this method returns [`Poll::Ready`], that means the OS has delivered an event /// indicating readability since the last time this task has called the method and received /// [`Poll::Pending`]. /// /// # Caveats /// /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks /// will just keep waking each other in turn, thus wasting CPU time. /// /// Note that the [`AsyncRead`] implementation for [`Async`] also uses this method. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use futures_lite::future; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; /// /// // Wait until a client can be accepted. /// future::poll_fn(|cx| listener.poll_readable(cx)).await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { self.source.poll_readable(cx) } /// Polls the I/O handle for writability. /// /// When this method returns [`Poll::Ready`], that means the OS has delivered an event /// indicating writability since the last time this task has called the method and received /// [`Poll::Pending`]. /// /// # Caveats /// /// Two different tasks should not call this method concurrently. Otherwise, conflicting tasks /// will just keep waking each other in turn, thus wasting CPU time. /// /// Note that the [`AsyncWrite`] implementation for [`Async`] also uses this method. /// /// # Examples /// /// ``` /// use async_io::Async; /// use futures_lite::future; /// use std::net::{TcpStream, ToSocketAddrs}; /// /// # futures_lite::future::block_on(async { /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); /// let stream = Async::::connect(addr).await?; /// /// // Wait until the stream is writable. /// future::poll_fn(|cx| stream.poll_writable(cx)).await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { self.source.poll_writable(cx) } /// Performs a read operation asynchronously. /// /// The I/O handle is registered in the reactor and put in non-blocking mode. This method /// invokes the `op` closure in a loop until it succeeds or returns an error other than /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS /// sends a notification that the I/O handle is readable. /// /// The closure receives a shared reference to the I/O handle. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; /// /// // Accept a new client asynchronously. /// let (stream, addr) = listener.read_with(|l| l.accept()).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn read_with(&self, op: impl FnMut(&T) -> io::Result) -> io::Result { let mut op = op; loop { match op(self.get_ref()) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return res, } optimistic(self.readable()).await?; } } /// Performs a read operation asynchronously. /// /// The I/O handle is registered in the reactor and put in non-blocking mode. This method /// invokes the `op` closure in a loop until it succeeds or returns an error other than /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS /// sends a notification that the I/O handle is readable. /// /// The closure receives a mutable reference to the I/O handle. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let mut listener = Async::::bind(([127, 0, 0, 1], 0))?; /// /// // Accept a new client asynchronously. /// let (stream, addr) = listener.read_with_mut(|l| l.accept()).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn read_with_mut( &mut self, op: impl FnMut(&mut T) -> io::Result, ) -> io::Result { let mut op = op; loop { match op(self.get_mut()) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return res, } optimistic(self.readable()).await?; } } /// Performs a write operation asynchronously. /// /// The I/O handle is registered in the reactor and put in non-blocking mode. This method /// invokes the `op` closure in a loop until it succeeds or returns an error other than /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS /// sends a notification that the I/O handle is writable. /// /// The closure receives a shared reference to the I/O handle. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// socket.get_ref().connect("127.0.0.1:9000")?; /// /// let msg = b"hello"; /// let len = socket.write_with(|s| s.send(msg)).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn write_with(&self, op: impl FnMut(&T) -> io::Result) -> io::Result { let mut op = op; loop { match op(self.get_ref()) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return res, } optimistic(self.writable()).await?; } } /// Performs a write operation asynchronously. /// /// The I/O handle is registered in the reactor and put in non-blocking mode. This method /// invokes the `op` closure in a loop until it succeeds or returns an error other than /// [`io::ErrorKind::WouldBlock`]. In between iterations of the loop, it waits until the OS /// sends a notification that the I/O handle is writable. /// /// The closure receives a mutable reference to the I/O handle. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let mut socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// socket.get_ref().connect("127.0.0.1:9000")?; /// /// let msg = b"hello"; /// let len = socket.write_with_mut(|s| s.send(msg)).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn write_with_mut( &mut self, op: impl FnMut(&mut T) -> io::Result, ) -> io::Result { let mut op = op; loop { match op(self.get_mut()) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return res, } optimistic(self.writable()).await?; } } } impl AsRef for Async { fn as_ref(&self) -> &T { self.get_ref() } } impl AsMut for Async { fn as_mut(&mut self) -> &mut T { self.get_mut() } } impl Drop for Async { fn drop(&mut self) { if self.io.is_some() { // Deregister and ignore errors because destructors should not panic. Reactor::get().remove_io(&self.source).ok(); // Drop the I/O handle to close it. self.io.take(); } } } impl AsyncRead for Async { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { loop { match (&mut *self).get_mut().read(buf) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_readable(cx))?; } } fn poll_read_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { loop { match (&mut *self).get_mut().read_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_readable(cx))?; } } } impl AsyncRead for &Async where for<'a> &'a T: Read, { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { loop { match (&*self).get_ref().read(buf) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_readable(cx))?; } } fn poll_read_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { loop { match (&*self).get_ref().read_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_readable(cx))?; } } } impl AsyncWrite for Async { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { loop { match (&mut *self).get_mut().write(buf) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_writable(cx))?; } } fn poll_write_vectored( mut self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { loop { match (&mut *self).get_mut().write_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_writable(cx))?; } } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match (&mut *self).get_mut().flush() { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_writable(cx))?; } } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush(cx) } } impl AsyncWrite for &Async where for<'a> &'a T: Write, { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { loop { match (&*self).get_ref().write(buf) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_writable(cx))?; } } fn poll_write_vectored( self: Pin<&mut Self>, cx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { loop { match (&*self).get_ref().write_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_writable(cx))?; } } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { loop { match (&*self).get_ref().flush() { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_writable(cx))?; } } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { self.poll_flush(cx) } } impl Async { /// Creates a TCP listener bound to the specified address. /// /// Binding with port number 0 will request an available port from the OS. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 0))?; /// println!("Listening on {}", listener.get_ref().local_addr()?); /// # std::io::Result::Ok(()) }); /// ``` pub fn bind>(addr: A) -> io::Result> { let addr = addr.into(); Async::new(TcpListener::bind(addr)?) } /// Accepts a new incoming TCP connection. /// /// When a connection is established, it will be returned as a TCP stream together with its /// remote address. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 8000))?; /// let (stream, addr) = listener.accept().await?; /// println!("Accepted client: {}", addr); /// # std::io::Result::Ok(()) }); /// ``` pub async fn accept(&self) -> io::Result<(Async, SocketAddr)> { let (stream, addr) = self.read_with(|io| io.accept()).await?; Ok((Async::new(stream)?, addr)) } /// Returns a stream of incoming TCP connections. /// /// The stream is infinite, i.e. it never stops with a [`None`]. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use futures_lite::{pin, stream::StreamExt}; /// use std::net::TcpListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind(([127, 0, 0, 1], 8000))?; /// let incoming = listener.incoming(); /// pin!(incoming); /// /// while let Some(stream) = incoming.next().await { /// let stream = stream?; /// println!("Accepted client: {}", stream.get_ref().peer_addr()?); /// } /// # std::io::Result::Ok(()) }); /// ``` pub fn incoming(&self) -> impl Stream>> + Send + '_ { stream::unfold(self, |listener| async move { let res = listener.accept().await.map(|(stream, _)| stream); Some((res, listener)) }) } } impl TryFrom for Async { type Error = io::Error; fn try_from(listener: std::net::TcpListener) -> io::Result { Async::new(listener) } } impl Async { /// Creates a TCP connection to the specified address. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::{TcpStream, ToSocketAddrs}; /// /// # futures_lite::future::block_on(async { /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); /// let stream = Async::::connect(addr).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn connect>(addr: A) -> io::Result> { // Begin async connect. let addr = addr.into(); let domain = Domain::for_address(addr); let socket = connect(addr.into(), domain, Some(Protocol::TCP))?; let stream = Async::new(TcpStream::from(socket))?; // The stream becomes writable when connected. stream.writable().await?; // Check if there was an error while connecting. match stream.get_ref().take_error()? { None => Ok(stream), Some(err) => Err(err), } } /// Reads data from the stream without removing it from the buffer. /// /// Returns the number of bytes read. Successive calls of this method read the same data. /// /// # Examples /// /// ``` /// use async_io::Async; /// use futures_lite::{io::AsyncWriteExt, stream::StreamExt}; /// use std::net::{TcpStream, ToSocketAddrs}; /// /// # futures_lite::future::block_on(async { /// let addr = "example.com:80".to_socket_addrs()?.next().unwrap(); /// let mut stream = Async::::connect(addr).await?; /// /// stream /// .write_all(b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n") /// .await?; /// /// let mut buf = [0u8; 1024]; /// let len = stream.peek(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn peek(&self, buf: &mut [u8]) -> io::Result { self.read_with(|io| io.peek(buf)).await } } impl TryFrom for Async { type Error = io::Error; fn try_from(stream: std::net::TcpStream) -> io::Result { Async::new(stream) } } impl Async { /// Creates a UDP socket bound to the specified address. /// /// Binding with port number 0 will request an available port from the OS. /// /// # Examples /// /// ``` /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 0))?; /// println!("Bound to {}", socket.get_ref().local_addr()?); /// # std::io::Result::Ok(()) }); /// ``` pub fn bind>(addr: A) -> io::Result> { let addr = addr.into(); Async::new(UdpSocket::bind(addr)?) } /// Receives a single datagram message. /// /// Returns the number of bytes read and the address the message came from. /// /// This method must be called with a valid byte slice of sufficient size to hold the message. /// If the message is too long to fit, excess bytes may get discarded. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// /// let mut buf = [0u8; 1024]; /// let (len, addr) = socket.recv_from(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { self.read_with(|io| io.recv_from(buf)).await } /// Receives a single datagram message without removing it from the queue. /// /// Returns the number of bytes read and the address the message came from. /// /// This method must be called with a valid byte slice of sufficient size to hold the message. /// If the message is too long to fit, excess bytes may get discarded. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// /// let mut buf = [0u8; 1024]; /// let (len, addr) = socket.peek_from(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn peek_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { self.read_with(|io| io.peek_from(buf)).await } /// Sends data to the specified address. /// /// Returns the number of bytes writen. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 0))?; /// let addr = socket.get_ref().local_addr()?; /// /// let msg = b"hello"; /// let len = socket.send_to(msg, addr).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn send_to>(&self, buf: &[u8], addr: A) -> io::Result { let addr = addr.into(); self.write_with(|io| io.send_to(buf, addr)).await } /// Receives a single datagram message from the connected peer. /// /// Returns the number of bytes read. /// /// This method must be called with a valid byte slice of sufficient size to hold the message. /// If the message is too long to fit, excess bytes may get discarded. /// /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. /// This method will fail if the socket is not connected. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// socket.get_ref().connect("127.0.0.1:9000")?; /// /// let mut buf = [0u8; 1024]; /// let len = socket.recv(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { self.read_with(|io| io.recv(buf)).await } /// Receives a single datagram message from the connected peer without removing it from the /// queue. /// /// Returns the number of bytes read and the address the message came from. /// /// This method must be called with a valid byte slice of sufficient size to hold the message. /// If the message is too long to fit, excess bytes may get discarded. /// /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. /// This method will fail if the socket is not connected. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// socket.get_ref().connect("127.0.0.1:9000")?; /// /// let mut buf = [0u8; 1024]; /// let len = socket.peek(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn peek(&self, buf: &mut [u8]) -> io::Result { self.read_with(|io| io.peek(buf)).await } /// Sends data to the connected peer. /// /// Returns the number of bytes written. /// /// The [`connect`][`UdpSocket::connect()`] method connects this socket to a remote address. /// This method will fail if the socket is not connected. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::net::UdpSocket; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind(([127, 0, 0, 1], 8000))?; /// socket.get_ref().connect("127.0.0.1:9000")?; /// /// let msg = b"hello"; /// let len = socket.send(msg).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { self.write_with(|io| io.send(buf)).await } } impl TryFrom for Async { type Error = io::Error; fn try_from(socket: std::net::UdpSocket) -> io::Result { Async::new(socket) } } #[cfg(unix)] impl Async { /// Creates a UDS listener bound to the specified path. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind("/tmp/socket")?; /// println!("Listening on {:?}", listener.get_ref().local_addr()?); /// # std::io::Result::Ok(()) }); /// ``` pub fn bind>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); Async::new(UnixListener::bind(path)?) } /// Accepts a new incoming UDS stream connection. /// /// When a connection is established, it will be returned as a stream together with its remote /// address. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind("/tmp/socket")?; /// let (stream, addr) = listener.accept().await?; /// println!("Accepted client: {:?}", addr); /// # std::io::Result::Ok(()) }); /// ``` pub async fn accept(&self) -> io::Result<(Async, UnixSocketAddr)> { let (stream, addr) = self.read_with(|io| io.accept()).await?; Ok((Async::new(stream)?, addr)) } /// Returns a stream of incoming UDS connections. /// /// The stream is infinite, i.e. it never stops with a [`None`] item. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use futures_lite::{pin, stream::StreamExt}; /// use std::os::unix::net::UnixListener; /// /// # futures_lite::future::block_on(async { /// let listener = Async::::bind("/tmp/socket")?; /// let incoming = listener.incoming(); /// pin!(incoming); /// /// while let Some(stream) = incoming.next().await { /// let stream = stream?; /// println!("Accepted client: {:?}", stream.get_ref().peer_addr()?); /// } /// # std::io::Result::Ok(()) }); /// ``` pub fn incoming(&self) -> impl Stream>> + Send + '_ { stream::unfold(self, |listener| async move { let res = listener.accept().await.map(|(stream, _)| stream); Some((res, listener)) }) } } #[cfg(unix)] impl TryFrom for Async { type Error = io::Error; fn try_from(listener: std::os::unix::net::UnixListener) -> io::Result { Async::new(listener) } } #[cfg(unix)] impl Async { /// Creates a UDS stream connected to the specified path. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixStream; /// /// # futures_lite::future::block_on(async { /// let stream = Async::::connect("/tmp/socket").await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn connect>(path: P) -> io::Result> { // Begin async connect. let socket = connect(SockAddr::unix(path)?, Domain::UNIX, None)?; let stream = Async::new(UnixStream::from(socket))?; // The stream becomes writable when connected. stream.writable().await?; // On Linux, it appears the socket may become writable even when connecting fails, so we // must do an extra check here and see if the peer address is retrievable. stream.get_ref().peer_addr()?; Ok(stream) } /// Creates an unnamed pair of connected UDS stream sockets. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixStream; /// /// # futures_lite::future::block_on(async { /// let (stream1, stream2) = Async::::pair()?; /// # std::io::Result::Ok(()) }); /// ``` pub fn pair() -> io::Result<(Async, Async)> { let (stream1, stream2) = UnixStream::pair()?; Ok((Async::new(stream1)?, Async::new(stream2)?)) } } #[cfg(unix)] impl TryFrom for Async { type Error = io::Error; fn try_from(stream: std::os::unix::net::UnixStream) -> io::Result { Async::new(stream) } } #[cfg(unix)] impl Async { /// Creates a UDS datagram socket bound to the specified path. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind("/tmp/socket")?; /// # std::io::Result::Ok(()) }); /// ``` pub fn bind>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); Async::new(UnixDatagram::bind(path)?) } /// Creates a UDS datagram socket not bound to any address. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::unbound()?; /// # std::io::Result::Ok(()) }); /// ``` pub fn unbound() -> io::Result> { Async::new(UnixDatagram::unbound()?) } /// Creates an unnamed pair of connected Unix datagram sockets. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let (socket1, socket2) = Async::::pair()?; /// # std::io::Result::Ok(()) }); /// ``` pub fn pair() -> io::Result<(Async, Async)> { let (socket1, socket2) = UnixDatagram::pair()?; Ok((Async::new(socket1)?, Async::new(socket2)?)) } /// Receives data from the socket. /// /// Returns the number of bytes read and the address the message came from. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind("/tmp/socket")?; /// /// let mut buf = [0u8; 1024]; /// let (len, addr) = socket.recv_from(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, UnixSocketAddr)> { self.read_with(|io| io.recv_from(buf)).await } /// Sends data to the specified address. /// /// Returns the number of bytes written. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::unbound()?; /// /// let msg = b"hello"; /// let addr = "/tmp/socket"; /// let len = socket.send_to(msg, addr).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { self.write_with(|io| io.send_to(buf, &path)).await } /// Receives data from the connected peer. /// /// Returns the number of bytes read and the address the message came from. /// /// The [`connect`][`UnixDatagram::connect()`] method connects this socket to a remote address. /// This method will fail if the socket is not connected. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind("/tmp/socket1")?; /// socket.get_ref().connect("/tmp/socket2")?; /// /// let mut buf = [0u8; 1024]; /// let len = socket.recv(&mut buf).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { self.read_with(|io| io.recv(buf)).await } /// Sends data to the connected peer. /// /// Returns the number of bytes written. /// /// The [`connect`][`UnixDatagram::connect()`] method connects this socket to a remote address. /// This method will fail if the socket is not connected. /// /// # Examples /// /// ```no_run /// use async_io::Async; /// use std::os::unix::net::UnixDatagram; /// /// # futures_lite::future::block_on(async { /// let socket = Async::::bind("/tmp/socket1")?; /// socket.get_ref().connect("/tmp/socket2")?; /// /// let msg = b"hello"; /// let len = socket.send(msg).await?; /// # std::io::Result::Ok(()) }); /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { self.write_with(|io| io.send(buf)).await } } #[cfg(unix)] impl TryFrom for Async { type Error = io::Error; fn try_from(socket: std::os::unix::net::UnixDatagram) -> io::Result { Async::new(socket) } } /// Polls a future once, waits for a wakeup, and then optimistically assumes the future is ready. async fn optimistic(fut: impl Future>) -> io::Result<()> { let mut polled = false; pin!(fut); future::poll_fn(|cx| { if !polled { polled = true; fut.as_mut().poll(cx) } else { Poll::Ready(Ok(())) } }) .await } fn connect(addr: SockAddr, domain: Domain, protocol: Option) -> io::Result { let sock_type = Type::STREAM; #[cfg(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd" ))] // If we can, set nonblocking at socket creation for unix let sock_type = sock_type.nonblocking(); // This automatically handles cloexec on unix, no_inherit on windows and nosigpipe on macos let socket = Socket::new(domain, sock_type, protocol)?; #[cfg(not(any( target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "fuchsia", target_os = "illumos", target_os = "linux", target_os = "netbsd", target_os = "openbsd" )))] // If the current platform doesn't support nonblocking at creation, enable it after creation socket.set_nonblocking(true)?; match socket.connect(&addr) { Ok(_) => {} #[cfg(unix)] Err(err) if err.raw_os_error() == Some(libc::EINPROGRESS) => {} Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} Err(err) => return Err(err), } Ok(socket) } async-io-1.6.0/src/reactor.rs000064400000000000000000000515000072674642500141770ustar 00000000000000use std::borrow::Borrow; use std::collections::BTreeMap; use std::fmt; use std::future::Future; use std::io; use std::marker::PhantomData; use std::mem; #[cfg(unix)] use std::os::unix::io::RawFd; #[cfg(windows)] use std::os::windows::io::RawSocket; use std::panic; use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, MutexGuard}; use std::task::{Context, Poll, Waker}; use std::time::{Duration, Instant}; use concurrent_queue::ConcurrentQueue; use futures_lite::ready; use once_cell::sync::Lazy; use polling::{Event, Poller}; use slab::Slab; const READ: usize = 0; const WRITE: usize = 1; /// The reactor. /// /// There is only one global instance of this type, accessible by [`Reactor::get()`]. pub(crate) struct Reactor { /// Portable bindings to epoll/kqueue/event ports/wepoll. /// /// This is where I/O is polled, producing I/O events. poller: Poller, /// Ticker bumped before polling. /// /// This is useful for checking what is the current "round" of `ReactorLock::react()` when /// synchronizing things in `Source::readable()` and `Source::writable()`. Both of those /// methods must make sure they don't receive stale I/O events - they only accept events from a /// fresh "round" of `ReactorLock::react()`. ticker: AtomicUsize, /// Registered sources. sources: Mutex>>, /// Temporary storage for I/O events when polling the reactor. /// /// Holding a lock on this event list implies the exclusive right to poll I/O. events: Mutex>, /// An ordered map of registered timers. /// /// Timers are in the order in which they fire. The `usize` in this type is a timer ID used to /// distinguish timers that fire at the same time. The `Waker` represents the task awaiting the /// timer. timers: Mutex>, /// A queue of timer operations (insert and remove). /// /// When inserting or removing a timer, we don't process it immediately - we just push it into /// this queue. Timers actually get processed when the queue fills up or the reactor is polled. timer_ops: ConcurrentQueue, } impl Reactor { /// Returns a reference to the reactor. pub(crate) fn get() -> &'static Reactor { static REACTOR: Lazy = Lazy::new(|| { crate::driver::init(); Reactor { poller: Poller::new().expect("cannot initialize I/O event notification"), ticker: AtomicUsize::new(0), sources: Mutex::new(Slab::new()), events: Mutex::new(Vec::new()), timers: Mutex::new(BTreeMap::new()), timer_ops: ConcurrentQueue::bounded(1000), } }); &REACTOR } /// Returns the current ticker. pub(crate) fn ticker(&self) -> usize { self.ticker.load(Ordering::SeqCst) } /// Registers an I/O source in the reactor. pub(crate) fn insert_io( &self, #[cfg(unix)] raw: RawFd, #[cfg(windows)] raw: RawSocket, ) -> io::Result> { // Create an I/O source for this file descriptor. let source = { let mut sources = self.sources.lock().unwrap(); let key = sources.vacant_entry().key(); let source = Arc::new(Source { raw, key, state: Default::default(), }); sources.insert(source.clone()); source }; // Register the file descriptor. if let Err(err) = self.poller.add(raw, Event::none(source.key)) { let mut sources = self.sources.lock().unwrap(); sources.remove(source.key); return Err(err); } Ok(source) } /// Deregisters an I/O source from the reactor. pub(crate) fn remove_io(&self, source: &Source) -> io::Result<()> { let mut sources = self.sources.lock().unwrap(); sources.remove(source.key); self.poller.delete(source.raw) } /// Registers a timer in the reactor. /// /// Returns the inserted timer's ID. pub(crate) fn insert_timer(&self, when: Instant, waker: &Waker) -> usize { // Generate a new timer ID. static ID_GENERATOR: AtomicUsize = AtomicUsize::new(1); let id = ID_GENERATOR.fetch_add(1, Ordering::Relaxed); // Push an insert operation. while self .timer_ops .push(TimerOp::Insert(when, id, waker.clone())) .is_err() { // If the queue is full, drain it and try again. let mut timers = self.timers.lock().unwrap(); self.process_timer_ops(&mut timers); } // Notify that a timer has been inserted. self.notify(); id } /// Deregisters a timer from the reactor. pub(crate) fn remove_timer(&self, when: Instant, id: usize) { // Push a remove operation. while self.timer_ops.push(TimerOp::Remove(when, id)).is_err() { // If the queue is full, drain it and try again. let mut timers = self.timers.lock().unwrap(); self.process_timer_ops(&mut timers); } } /// Notifies the thread blocked on the reactor. pub(crate) fn notify(&self) { self.poller.notify().expect("failed to notify reactor"); } /// Locks the reactor, potentially blocking if the lock is held by another thread. pub(crate) fn lock(&self) -> ReactorLock<'_> { let reactor = self; let events = self.events.lock().unwrap(); ReactorLock { reactor, events } } /// Attempts to lock the reactor. pub(crate) fn try_lock(&self) -> Option> { self.events.try_lock().ok().map(|events| { let reactor = self; ReactorLock { reactor, events } }) } /// Processes ready timers and extends the list of wakers to wake. /// /// Returns the duration until the next timer before this method was called. fn process_timers(&self, wakers: &mut Vec) -> Option { let mut timers = self.timers.lock().unwrap(); self.process_timer_ops(&mut timers); let now = Instant::now(); // Split timers into ready and pending timers. let pending = timers.split_off(&(now, 0)); let ready = mem::replace(&mut *timers, pending); // Calculate the duration until the next event. let dur = if ready.is_empty() { // Duration until the next timer. timers .keys() .next() .map(|(when, _)| when.saturating_duration_since(now)) } else { // Timers are about to fire right now. Some(Duration::from_secs(0)) }; // Drop the lock before waking. drop(timers); // Add wakers to the list. log::trace!("process_timers: {} ready wakers", ready.len()); for (_, waker) in ready { wakers.push(waker); } dur } /// Processes queued timer operations. fn process_timer_ops(&self, timers: &mut MutexGuard<'_, BTreeMap<(Instant, usize), Waker>>) { // Process only as much as fits into the queue, or else this loop could in theory run // forever. for _ in 0..self.timer_ops.capacity().unwrap() { match self.timer_ops.pop() { Ok(TimerOp::Insert(when, id, waker)) => { timers.insert((when, id), waker); } Ok(TimerOp::Remove(when, id)) => { timers.remove(&(when, id)); } Err(_) => break, } } } } /// A lock on the reactor. pub(crate) struct ReactorLock<'a> { reactor: &'a Reactor, events: MutexGuard<'a, Vec>, } impl ReactorLock<'_> { /// Processes new events, blocking until the first event or the timeout. pub(crate) fn react(&mut self, timeout: Option) -> io::Result<()> { let mut wakers = Vec::new(); // Process ready timers. let next_timer = self.reactor.process_timers(&mut wakers); // compute the timeout for blocking on I/O events. let timeout = match (next_timer, timeout) { (None, None) => None, (Some(t), None) | (None, Some(t)) => Some(t), (Some(a), Some(b)) => Some(a.min(b)), }; // Bump the ticker before polling I/O. let tick = self .reactor .ticker .fetch_add(1, Ordering::SeqCst) .wrapping_add(1); self.events.clear(); // Block on I/O events. let res = match self.reactor.poller.wait(&mut self.events, timeout) { // No I/O events occurred. Ok(0) => { if timeout != Some(Duration::from_secs(0)) { // The non-zero timeout was hit so fire ready timers. self.reactor.process_timers(&mut wakers); } Ok(()) } // At least one I/O event occurred. Ok(_) => { // Iterate over sources in the event list. let sources = self.reactor.sources.lock().unwrap(); for ev in self.events.iter() { // Check if there is a source in the table with this key. if let Some(source) = sources.get(ev.key) { let mut state = source.state.lock().unwrap(); // Collect wakers if a writability event was emitted. for &(dir, emitted) in &[(WRITE, ev.writable), (READ, ev.readable)] { if emitted { state[dir].tick = tick; state[dir].drain_into(&mut wakers); } } // Re-register if there are still writers or readers. The can happen if // e.g. we were previously interested in both readability and writability, // but only one of them was emitted. if !state[READ].is_empty() || !state[WRITE].is_empty() { self.reactor.poller.modify( source.raw, Event { key: source.key, readable: !state[READ].is_empty(), writable: !state[WRITE].is_empty(), }, )?; } } } Ok(()) } // The syscall was interrupted. Err(err) if err.kind() == io::ErrorKind::Interrupted => Ok(()), // An actual error occureed. Err(err) => Err(err), }; // Wake up ready tasks. log::trace!("react: {} ready wakers", wakers.len()); for waker in wakers { // Don't let a panicking waker blow everything up. panic::catch_unwind(|| waker.wake()).ok(); } res } } /// A single timer operation. enum TimerOp { Insert(Instant, usize, Waker), Remove(Instant, usize), } /// A registered source of I/O events. #[derive(Debug)] pub(crate) struct Source { /// Raw file descriptor on Unix platforms. #[cfg(unix)] pub(crate) raw: RawFd, /// Raw socket handle on Windows. #[cfg(windows)] pub(crate) raw: RawSocket, /// The key of this source obtained during registration. key: usize, /// Inner state with registered wakers. state: Mutex<[Direction; 2]>, } /// A read or write direction. #[derive(Debug, Default)] struct Direction { /// Last reactor tick that delivered an event. tick: usize, /// Ticks remembered by `Async::poll_readable()` or `Async::poll_writable()`. ticks: Option<(usize, usize)>, /// Waker stored by `Async::poll_readable()` or `Async::poll_writable()`. waker: Option, /// Wakers of tasks waiting for the next event. /// /// Registered by `Async::readable()` and `Async::writable()`. wakers: Slab>, } impl Direction { /// Returns `true` if there are no wakers interested in this direction. fn is_empty(&self) -> bool { self.waker.is_none() && self.wakers.iter().all(|(_, opt)| opt.is_none()) } /// Moves all wakers into a `Vec`. fn drain_into(&mut self, dst: &mut Vec) { if let Some(w) = self.waker.take() { dst.push(w); } for (_, opt) in self.wakers.iter_mut() { if let Some(w) = opt.take() { dst.push(w); } } } } impl Source { /// Polls the I/O source for readability. pub(crate) fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { self.poll_ready(READ, cx) } /// Polls the I/O source for writability. pub(crate) fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { self.poll_ready(WRITE, cx) } /// Registers a waker from `poll_readable()` or `poll_writable()`. /// /// If a different waker is already registered, it gets replaced and woken. fn poll_ready(&self, dir: usize, cx: &mut Context<'_>) -> Poll> { let mut state = self.state.lock().unwrap(); // Check if the reactor has delivered an event. if let Some((a, b)) = state[dir].ticks { // If `state[dir].tick` has changed to a value other than the old reactor tick, // that means a newer reactor tick has delivered an event. if state[dir].tick != a && state[dir].tick != b { state[dir].ticks = None; return Poll::Ready(Ok(())); } } let was_empty = state[dir].is_empty(); // Register the current task's waker. if let Some(w) = state[dir].waker.take() { if w.will_wake(cx.waker()) { state[dir].waker = Some(w); return Poll::Pending; } // Wake the previous waker because it's going to get replaced. panic::catch_unwind(|| w.wake()).ok(); } state[dir].waker = Some(cx.waker().clone()); state[dir].ticks = Some((Reactor::get().ticker(), state[dir].tick)); // Update interest in this I/O handle. if was_empty { Reactor::get().poller.modify( self.raw, Event { key: self.key, readable: !state[READ].is_empty(), writable: !state[WRITE].is_empty(), }, )?; } Poll::Pending } /// Waits until the I/O source is readable. pub(crate) fn readable(handle: &crate::Async) -> Readable<'_, T> { Readable(Self::ready(handle, READ)) } /// Waits until the I/O source is readable. pub(crate) fn readable_owned(handle: Arc>) -> ReadableOwned { ReadableOwned(Self::ready(handle, READ)) } /// Waits until the I/O source is writable. pub(crate) fn writable(handle: &crate::Async) -> Writable<'_, T> { Writable(Self::ready(handle, WRITE)) } /// Waits until the I/O source is writable. pub(crate) fn writable_owned(handle: Arc>) -> WritableOwned { WritableOwned(Self::ready(handle, WRITE)) } /// Waits until the I/O source is readable or writable. fn ready> + Clone, T>(handle: H, dir: usize) -> Ready { Ready { handle, dir, ticks: None, index: None, _guard: None, } } } /// Future for [`Async::readable`](crate::Async::readable). #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Readable<'a, T>(Ready<&'a crate::Async, T>); impl Future for Readable<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; log::trace!("readable: fd={}", self.0.handle.source.raw); Poll::Ready(Ok(())) } } impl fmt::Debug for Readable<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Readable").finish() } } /// Future for [`Async::readable_owned`](crate::Async::readable_owned). #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct ReadableOwned(Ready>, T>); impl Future for ReadableOwned { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; log::trace!("readable_owned: fd={}", self.0.handle.source.raw); Poll::Ready(Ok(())) } } impl fmt::Debug for ReadableOwned { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ReadableOwned").finish() } } /// Future for [`Async::writable`](crate::Async::writable). #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct Writable<'a, T>(Ready<&'a crate::Async, T>); impl Future for Writable<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; log::trace!("writable: fd={}", self.0.handle.source.raw); Poll::Ready(Ok(())) } } impl fmt::Debug for Writable<'_, T> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Writable").finish() } } /// Future for [`Async::writable_owned`](crate::Async::writable_owned). #[must_use = "futures do nothing unless you `.await` or poll them"] pub struct WritableOwned(Ready>, T>); impl Future for WritableOwned { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(Pin::new(&mut self.0).poll(cx))?; log::trace!("writable_owned: fd={}", self.0.handle.source.raw); Poll::Ready(Ok(())) } } impl fmt::Debug for WritableOwned { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("WritableOwned").finish() } } struct Ready>, T> { handle: H, dir: usize, ticks: Option<(usize, usize)>, index: Option, _guard: Option>, } impl>, T> Unpin for Ready {} impl> + Clone, T> Future for Ready { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let Self { ref handle, dir, ticks, index, _guard, .. } = &mut *self; let mut state = handle.borrow().source.state.lock().unwrap(); // Check if the reactor has delivered an event. if let Some((a, b)) = *ticks { // If `state[dir].tick` has changed to a value other than the old reactor tick, // that means a newer reactor tick has delivered an event. if state[*dir].tick != a && state[*dir].tick != b { return Poll::Ready(Ok(())); } } let was_empty = state[*dir].is_empty(); // Register the current task's waker. let i = match *index { Some(i) => i, None => { let i = state[*dir].wakers.insert(None); *_guard = Some(RemoveOnDrop { handle: handle.clone(), dir: *dir, key: i, _marker: PhantomData, }); *index = Some(i); *ticks = Some((Reactor::get().ticker(), state[*dir].tick)); i } }; state[*dir].wakers[i] = Some(cx.waker().clone()); // Update interest in this I/O handle. if was_empty { Reactor::get().poller.modify( handle.borrow().source.raw, Event { key: handle.borrow().source.key, readable: !state[READ].is_empty(), writable: !state[WRITE].is_empty(), }, )?; } Poll::Pending } } /// Remove waker when dropped. struct RemoveOnDrop>, T> { handle: H, dir: usize, key: usize, _marker: PhantomData T>, } impl>, T> Drop for RemoveOnDrop { fn drop(&mut self) { let mut state = self.handle.borrow().source.state.lock().unwrap(); let wakers = &mut state[self.dir].wakers; if wakers.contains(self.key) { wakers.remove(self.key); } } } async-io-1.6.0/tests/async.rs000064400000000000000000000242030072674642500142300ustar 00000000000000use std::future::Future; use std::io; use std::net::{Shutdown, TcpListener, TcpStream, UdpSocket}; #[cfg(unix)] use std::os::unix::net::{UnixDatagram, UnixListener, UnixStream}; use std::sync::Arc; use std::thread; use std::time::Duration; use async_io::{Async, Timer}; use futures_lite::{future, prelude::*}; #[cfg(unix)] use tempfile::tempdir; const LOREM_IPSUM: &[u8] = b" Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec pretium ante erat, vitae sodales mi varius quis. Etiam vestibulum lorem vel urna tempor, eu fermentum odio aliquam. Aliquam consequat urna vitae ipsum pulvinar, in blandit purus eleifend. "; fn spawn( f: impl Future + Send + 'static, ) -> impl Future + Send + 'static { let (s, r) = async_channel::bounded(1); thread::spawn(move || { future::block_on(async { s.send(f.await).await.ok(); }) }); Box::pin(async move { r.recv().await.unwrap() }) } #[test] fn tcp_connect() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let addr = listener.get_ref().local_addr()?; let task = spawn(async move { listener.accept().await }); let stream2 = Async::::connect(addr).await?; let stream1 = task.await?.0; assert_eq!( stream1.get_ref().peer_addr()?, stream2.get_ref().local_addr()?, ); assert_eq!( stream2.get_ref().peer_addr()?, stream1.get_ref().local_addr()?, ); // Now that the listener is closed, connect should fail. let err = Async::::connect(addr).await.unwrap_err(); assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused); Ok(()) }) } #[test] fn tcp_peek_read() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let addr = listener.get_ref().local_addr()?; let mut stream = Async::::connect(addr).await?; stream.write_all(LOREM_IPSUM).await?; let mut buf = [0; 1024]; let mut incoming = Box::pin(listener.incoming()); let mut stream = incoming.next().await.unwrap()?; let n = stream.peek(&mut buf).await?; assert_eq!(&buf[..n], LOREM_IPSUM); let n = stream.read(&mut buf).await?; assert_eq!(&buf[..n], LOREM_IPSUM); Ok(()) }) } #[test] fn tcp_reader_hangup() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let addr = listener.get_ref().local_addr()?; let task = spawn(async move { listener.accept().await }); let mut stream2 = Async::::connect(addr).await?; let stream1 = task.await?.0; let task = spawn(async move { Timer::after(Duration::from_secs(1)).await; drop(stream1); }); while stream2.write_all(LOREM_IPSUM).await.is_ok() {} task.await; Ok(()) }) } #[test] fn tcp_writer_hangup() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let addr = listener.get_ref().local_addr()?; let task = spawn(async move { listener.accept().await }); let mut stream2 = Async::::connect(addr).await?; let stream1 = task.await?.0; let task = spawn(async move { Timer::after(Duration::from_secs(1)).await; drop(stream1); }); let mut v = vec![]; stream2.read_to_end(&mut v).await?; assert!(v.is_empty()); task.await; Ok(()) }) } #[test] fn udp_send_recv() -> io::Result<()> { future::block_on(async { let socket1 = Async::::bind(([127, 0, 0, 1], 0))?; let socket2 = Async::::bind(([127, 0, 0, 1], 0))?; socket1.get_ref().connect(socket2.get_ref().local_addr()?)?; let mut buf = [0u8; 1024]; socket1.send(LOREM_IPSUM).await?; let n = socket2.peek(&mut buf).await?; assert_eq!(&buf[..n], LOREM_IPSUM); let n = socket2.recv(&mut buf).await?; assert_eq!(&buf[..n], LOREM_IPSUM); socket2 .send_to(LOREM_IPSUM, socket1.get_ref().local_addr()?) .await?; let n = socket1.peek_from(&mut buf).await?.0; assert_eq!(&buf[..n], LOREM_IPSUM); let n = socket1.recv_from(&mut buf).await?.0; assert_eq!(&buf[..n], LOREM_IPSUM); Ok(()) }) } #[cfg(unix)] #[test] fn udp_connect() -> io::Result<()> { future::block_on(async { let dir = tempdir()?; let path = dir.path().join("socket"); let listener = Async::::bind(&path)?; let mut stream = Async::::connect(&path).await?; stream.write_all(LOREM_IPSUM).await?; let mut buf = [0; 1024]; let mut incoming = Box::pin(listener.incoming()); let mut stream = incoming.next().await.unwrap()?; let n = stream.read(&mut buf).await?; assert_eq!(&buf[..n], LOREM_IPSUM); Ok(()) }) } #[cfg(unix)] #[test] fn uds_connect() -> io::Result<()> { future::block_on(async { let dir = tempdir()?; let path = dir.path().join("socket"); let listener = Async::::bind(&path)?; let addr = listener.get_ref().local_addr()?; let task = spawn(async move { listener.accept().await }); let stream2 = Async::::connect(addr.as_pathname().unwrap()).await?; let stream1 = task.await?.0; assert_eq!( stream1.get_ref().peer_addr()?.as_pathname(), stream2.get_ref().local_addr()?.as_pathname(), ); assert_eq!( stream2.get_ref().peer_addr()?.as_pathname(), stream1.get_ref().local_addr()?.as_pathname(), ); // Now that the listener is closed, connect should fail. let err = Async::::connect(addr.as_pathname().unwrap()) .await .unwrap_err(); assert_eq!(err.kind(), io::ErrorKind::ConnectionRefused); Ok(()) }) } #[cfg(unix)] #[test] fn uds_send_recv() -> io::Result<()> { future::block_on(async { let (socket1, socket2) = Async::::pair()?; socket1.send(LOREM_IPSUM).await?; let mut buf = [0; 1024]; let n = socket2.recv(&mut buf).await?; assert_eq!(&buf[..n], LOREM_IPSUM); Ok(()) }) } #[cfg(unix)] #[test] fn uds_send_to_recv_from() -> io::Result<()> { future::block_on(async { let dir = tempdir()?; let path = dir.path().join("socket"); let socket1 = Async::::bind(&path)?; let socket2 = Async::::unbound()?; socket2.send_to(LOREM_IPSUM, &path).await?; let mut buf = [0; 1024]; let n = socket1.recv_from(&mut buf).await?.0; assert_eq!(&buf[..n], LOREM_IPSUM); Ok(()) }) } #[cfg(unix)] #[test] fn uds_reader_hangup() -> io::Result<()> { future::block_on(async { let (socket1, mut socket2) = Async::::pair()?; let task = spawn(async move { Timer::after(Duration::from_secs(1)).await; drop(socket1); }); while socket2.write_all(LOREM_IPSUM).await.is_ok() {} task.await; Ok(()) }) } #[cfg(unix)] #[test] fn uds_writer_hangup() -> io::Result<()> { future::block_on(async { let (socket1, mut socket2) = Async::::pair()?; let task = spawn(async move { Timer::after(Duration::from_secs(1)).await; drop(socket1); }); let mut v = vec![]; socket2.read_to_end(&mut v).await?; assert!(v.is_empty()); task.await; Ok(()) }) } // Test that we correctly re-register interests after we've previously been // interested in both readable and writable events and then we get only one of // those (we need to re-register interest on the other). #[test] fn tcp_duplex() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let stream1 = Arc::new(Async::::connect(listener.get_ref().local_addr()?).await?); let stream2 = Arc::new(listener.accept().await?.0); async fn do_read(s: Arc>) -> io::Result<()> { let mut buf = vec![0u8; 4096]; loop { let len = (&*s).read(&mut buf).await?; if len == 0 { return Ok(()); } } } async fn do_write(s: Arc>) -> io::Result<()> { let buf = vec![0u8; 4096]; for _ in 0..4096 { (&*s).write_all(&buf).await?; } s.get_ref().shutdown(Shutdown::Write)?; Ok(()) } // Read from and write to stream1. let r1 = spawn(do_read(stream1.clone())); let w1 = spawn(do_write(stream1)); // Sleep a bit, so that reading and writing are both blocked. Timer::after(Duration::from_millis(5)).await; // Start reading stream2, make stream1 writable. let r2 = spawn(do_read(stream2.clone())); // Finish writing to stream1. w1.await?; r2.await?; // Start writing to stream2, make stream1 readable. let w2 = spawn(do_write(stream2)); // Will r1 be correctly woken? r1.await?; w2.await?; Ok(()) }) } #[test] fn shutdown() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let addr = listener.get_ref().local_addr()?; let ((mut reader, _), writer) = future::try_zip(listener.accept(), Async::::connect(addr)).await?; // The writer must be closed in order for `read_to_end()` to finish. let mut buf = Vec::new(); future::try_zip(reader.read_to_end(&mut buf), async { writer.get_ref().shutdown(Shutdown::Write) }) .await?; Ok(()) }) } async-io-1.6.0/tests/timer.rs000064400000000000000000000051050072674642500142330ustar 00000000000000use std::future::Future; use std::pin::Pin; use std::sync::{Arc, Mutex}; use std::thread; use std::time::{Duration, Instant}; use async_io::Timer; use futures_lite::{future, FutureExt, StreamExt}; fn spawn( f: impl Future + Send + 'static, ) -> impl Future + Send + 'static { let (s, r) = async_channel::bounded(1); thread::spawn(move || { future::block_on(async { s.send(f.await).await.ok(); }) }); Box::pin(async move { r.recv().await.unwrap() }) } #[test] fn smoke() { future::block_on(async { let start = Instant::now(); Timer::after(Duration::from_secs(1)).await; assert!(start.elapsed() >= Duration::from_secs(1)); }); } #[test] fn interval() { future::block_on(async { let period = Duration::from_secs(1); let jitter = Duration::from_millis(500); let start = Instant::now(); let mut timer = Timer::interval(period); timer.next().await; let elapsed = start.elapsed(); assert!(elapsed >= period && elapsed - period < jitter); timer.next().await; let elapsed = start.elapsed(); assert!(elapsed >= period * 2 && elapsed - period * 2 < jitter); }); } #[test] fn poll_across_tasks() { future::block_on(async { let start = Instant::now(); let (sender, receiver) = async_channel::bounded(1); let task1 = spawn(async move { let mut timer = Timer::after(Duration::from_secs(1)); async { (&mut timer).await; panic!("timer should not be ready") } .or(async {}) .await; sender.send(timer).await.ok(); }); let task2 = spawn(async move { let timer = receiver.recv().await.unwrap(); timer.await; }); task1.await; task2.await; assert!(start.elapsed() >= Duration::from_secs(1)); }); } #[test] fn set() { future::block_on(async { let start = Instant::now(); let timer = Arc::new(Mutex::new(Timer::after(Duration::from_secs(10)))); thread::spawn({ let timer = timer.clone(); move || { thread::sleep(Duration::from_secs(1)); timer.lock().unwrap().set_after(Duration::from_secs(2)); } }); future::poll_fn(|cx| Pin::new(&mut *timer.lock().unwrap()).poll(cx)).await; assert!(start.elapsed() >= Duration::from_secs(2)); assert!(start.elapsed() < Duration::from_secs(10)); }); }