async-io-2.3.3/.cargo_vcs_info.json0000644000000001360000000000100125630ustar { "git": { "sha1": "937ea849c089b3e05cea14641426d1083e5a4327" }, "path_in_vcs": "" }async-io-2.3.3/CHANGELOG.md000064400000000000000000000130701046102023000131650ustar 00000000000000# Version 2.3.3 - Fix nightly clippy warnings. (#191) # Version 2.3.2 - Fix usage of the wrong socket flags on AIX. (#187) # Version 2.3.1 - On Windows, call `WSAStartup` before any raw socket functions. (#183) # Version 2.3.0 - Add `Waitable`, which allows waiting for waitable handles on Windows. (#152) # Version 2.2.2 - Fix an `EINVAL` error that would occur when abstract sockets are used. (#176) # Version 2.2.1 - Remove dependency on `waker-fn`. (#165) - Update `windows-sys` to v0.52.0. (#173) # Version 2.2.0 - Bump `async-lock` and `futures-lite` to their latest version. (#170) # Version 2.1.0 - Implement `IoSafe` for `std::process::{ChildStdin, ChildStdout, ChildStderr}`. (#162) # Version 2.0.0 - **Breaking:** `Async::new()` now takes types that implement `AsFd`/`AsSocket` instead of `AsRawFd`/`AsRawSocket`, in order to implement I/O safety. (#142) - **Breaking:** `Async::get_mut()`, `Async::read_with_mut()` and `Async::write_with_mut()` are now `unsafe`. The underlying source is technically "borrowed" by the polling instance, so moving it out would be unsound. (#142) - Expose miscellaneous `kqueue` filters in the `os::kqueue` module. (#112) - Expose a way to get the underlying `Poller`'s file descriptor on Unix. (#125) - Add a new `Async::new_nonblocking` method to allow users to avoid duplicating an already nonblocking socket. (#159) - Remove the unused `fastrand` and `memchr` dependencies. (#131) - Use `tracing` instead of `log`. (#140) - Support ESP-IDF. (#144) - Optimize the `block_on` function to reduce allocation, leading to a slight performance improvement. (#149) # Version 1.13.0 - Use [`rustix`] instead of [`libc`]/[`windows-sys`] for system calls (#76) - Add a `will_fire` method to `Timer` to test if it will ever fire (#106) - Reduce syscalls in `Async::new` (#107) - Improve the drop ergonomics of `Readable` and `Writable` (#109) - Change the "`wepoll`" in documentation to "`IOCP`" (#116) [`rustix`]: https://crates.io/crates/rustix/ [`libc`]: https://crates.io/crates/libc/ [`windows-sys`]: https://crates.io/crates/windows-sys/ # Version 1.12.0 - Switch from `winapi` to `windows-sys` (#102) # Version 1.11.0 - Update `concurrent-queue` to v2. (#99) # Version 1.10.0 - Remove the dependency on the `once_cell` crate to restore the MSRV. (#95) # Version 1.9.0 - Fix panic on very large durations. (#87) - Add `Timer::never` (#87) # Version 1.8.0 - Implement I/O safety traits on Rust 1.63+ (#84) # Version 1.7.0 - Process timers set for exactly `now`. (#73) # 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-2.3.3/Cargo.lock0000644000000631660000000000100105520ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "async-channel" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" dependencies = [ "concurrent-queue", "event-listener 2.5.3", "futures-core", ] [[package]] name = "async-channel" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336d835910fab747186c56586562cb46f42809c2843ef3a84f47509009522838" dependencies = [ "concurrent-queue", "event-listener 3.0.1", "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] name = "async-io" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ "async-lock 3.0.0", "cfg-if", "concurrent-queue", "futures-io", "futures-lite 2.0.0", "parking", "polling", "rustix 0.38.19", "slab", "tracing", "windows-sys 0.52.0", ] [[package]] name = "async-io" version = "2.3.3" dependencies = [ "async-channel 2.0.0", "async-lock 3.0.0", "async-net", "blocking", "cfg-if", "concurrent-queue", "criterion", "futures-io", "futures-lite 2.0.0", "getrandom", "inotify", "parking", "polling", "rustix 0.38.19", "signal-hook", "slab", "tempfile", "timerfd", "tracing", "uds_windows", "windows-sys 0.52.0", ] [[package]] name = "async-lock" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" dependencies = [ "event-listener 2.5.3", ] [[package]] name = "async-lock" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45e900cdcd39bb94a14487d3f7ef92ca222162e6c7c3fe7cb3550ea75fb486ed" dependencies = [ "event-listener 3.0.1", "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-net" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" dependencies = [ "async-io 2.3.2", "blocking", "futures-lite 2.0.0", ] [[package]] name = "async-task" version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4eb2cdb97421e01129ccb49169d8279ed21e829929144f4a22a6e54ac549ca1" [[package]] name = "atomic-waker" version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atty" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ "hermit-abi 0.1.19", "libc", "winapi", ] [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "blocking" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" dependencies = [ "async-channel 1.9.0", "async-lock 2.8.0", "async-task", "fastrand", "futures-io", "futures-lite 1.13.0", "piper", "tracing", ] [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" [[package]] name = "ciborium-ll" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clap" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "bitflags 1.3.2", "clap_lex", "indexmap", "textwrap", ] [[package]] name = "clap_lex" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] [[package]] name = "concurrent-queue" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] [[package]] name = "criterion" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" dependencies = [ "anes", "atty", "cast", "ciborium", "clap", "criterion-plot", "itertools", "lazy_static", "num-traits", "oorandom", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools", ] [[package]] name = "crossbeam-utils" version = "0.8.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" dependencies = [ "cfg-if", ] [[package]] name = "either" version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "errno" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" dependencies = [ "libc", "windows-sys 0.48.0", ] [[package]] name = "event-listener" version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" [[package]] name = "event-listener" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cec0252c2afff729ee6f00e903d479fba81784c8e2bd77447673471fdfaea1" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] [[package]] name = "event-listener-strategy" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d96b852f1345da36d551b9473fa1e2b1eb5c5195585c6c018118bc92a8d91160" dependencies = [ "event-listener 3.0.1", "pin-project-lite", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "futures-core" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" [[package]] name = "futures-io" version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" [[package]] name = "futures-lite" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ "futures-core", "pin-project-lite", ] [[package]] name = "futures-lite" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c1155db57329dca6d018b61e76b1488ce9a2e5e44028cac420a5898f4fcef63" dependencies = [ "fastrand", "futures-core", "futures-io", "memchr", "parking", "pin-project-lite", "waker-fn", ] [[package]] name = "getrandom" version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "half" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hermit-abi" version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" dependencies = [ "libc", ] [[package]] name = "hermit-abi" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "indexmap" version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown", ] [[package]] name = "inotify" version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdd168d97690d0b8c412d6b6c10360277f4d7ee495c5d0d5d5fe0854923255cc" dependencies = [ "bitflags 1.3.2", "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 = "io-lifetimes" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ "hermit-abi 0.3.3", "libc", "windows-sys 0.48.0", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" [[package]] name = "linux-raw-sys" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "num-traits" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", ] [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "os_str_bytes" version = "6.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" [[package]] name = "parking" version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "piper" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", "fastrand", "futures-io", ] [[package]] name = "polling" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62a79e457c9898100b4298d57d69ec53d06f9a6ed352431ce5f377e082d2e846" dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", "rustix 0.38.19", "tracing", "windows-sys 0.48.0", ] [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags 1.3.2", ] [[package]] name = "regex" version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "rustix" version = "0.37.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4eb579851244c2c03e7c24f501c3432bed80b8f720af1d6e5b0e0f01555a035" dependencies = [ "bitflags 1.3.2", "errno", "io-lifetimes", "libc", "linux-raw-sys 0.3.8", "windows-sys 0.48.0", ] [[package]] name = "rustix" version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "745ecfa778e66b2b63c88a61cb36e0eea109e803b0b86bf9879fbc77c70e86ed" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys 0.4.10", "windows-sys 0.48.0", ] [[package]] name = "ryu" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e422a44e74ad4001bdc8eede9a4570ab52f71190e9c076d14369f38b9200537" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e48d1f918009ce3145511378cf68d613e3b3d9137d67272562080d68a2b32d5" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "signal-hook" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" dependencies = [ "libc", "signal-hook-registry", ] [[package]] name = "signal-hook-registry" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "syn" version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tempfile" version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix 0.38.19", "windows-sys 0.48.0", ] [[package]] name = "textwrap" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "timerfd" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d3fd47d83ad0b5c7be2e8db0b9d712901ef6ce5afbcc6f676761004f5104ea2" dependencies = [ "rustix 0.37.25", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "tracing" version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ "pin-project-lite", "tracing-core", ] [[package]] name = "tracing-core" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" [[package]] name = "uds_windows" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce65604324d3cce9b966701489fbd0cf318cb1f7bd9dd07ac9a4ee6fb791930d" dependencies = [ "tempfile", "winapi", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "waker-fn" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[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-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ "windows-targets 0.48.5", ] [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets 0.52.5", ] [[package]] name = "windows-targets" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ "windows_aarch64_gnullvm 0.48.5", "windows_aarch64_msvc 0.48.5", "windows_i686_gnu 0.48.5", "windows_i686_msvc 0.48.5", "windows_x86_64_gnu 0.48.5", "windows_x86_64_gnullvm 0.48.5", "windows_x86_64_msvc 0.48.5", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm 0.52.5", "windows_aarch64_msvc 0.52.5", "windows_i686_gnu 0.52.5", "windows_i686_gnullvm", "windows_i686_msvc 0.52.5", "windows_x86_64_gnu 0.52.5", "windows_x86_64_gnullvm 0.52.5", "windows_x86_64_msvc 0.52.5", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" async-io-2.3.3/Cargo.toml0000644000000045150000000000100105660ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" rust-version = "1.63" name = "async-io" version = "2.3.3" authors = ["Stjepan Glavina "] exclude = ["/.*"] description = "Async I/O and timers" readme = "README.md" keywords = [ "mio", "epoll", "kqueue", "iocp", ] categories = [ "asynchronous", "network-programming", "os", ] license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/async-io" [[bench]] name = "io" harness = false [[bench]] name = "timer" harness = false [dependencies.async-lock] version = "3.0.0" [dependencies.cfg-if] version = "1" [dependencies.concurrent-queue] version = "2.2.0" [dependencies.futures-io] version = "0.3.28" features = ["std"] default-features = false [dependencies.futures-lite] version = "2.0.0" default-features = false [dependencies.parking] version = "2.0.0" [dependencies.polling] version = "3.0.0" [dependencies.rustix] version = "0.38.2" features = [ "fs", "net", "std", ] default-features = false [dependencies.slab] version = "0.4.2" [dependencies.tracing] version = "0.1.37" default-features = false [dev-dependencies.async-channel] version = "2.0.0" [dev-dependencies.async-net] version = "2.0.0" [dev-dependencies.blocking] version = "1" [dev-dependencies.criterion] version = "0.4" features = ["cargo_bench_support"] default-features = false [dev-dependencies.getrandom] version = "0.2.7" [dev-dependencies.signal-hook] version = "0.3" [dev-dependencies.tempfile] version = "3" [target."cfg(target_os = \"linux\")".dev-dependencies.inotify] version = "0.10.1" default-features = false [target."cfg(target_os = \"linux\")".dev-dependencies.timerfd] version = "1" [target."cfg(windows)".dependencies.windows-sys] version = "0.52.0" features = ["Win32_Foundation"] [target."cfg(windows)".dev-dependencies.uds_windows] version = "1" [lints.rust.unexpected_cfgs] level = "warn" priority = 0 async-io-2.3.3/Cargo.toml.orig000064400000000000000000000031411046102023000142410ustar 00000000000000[package] name = "async-io" # When publishing a new version: # - Update CHANGELOG.md # - Create "v2.x.y" git tag version = "2.3.3" authors = ["Stjepan Glavina "] edition = "2021" rust-version = "1.63" description = "Async I/O and timers" license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/async-io" keywords = ["mio", "epoll", "kqueue", "iocp"] categories = ["asynchronous", "network-programming", "os"] exclude = ["/.*"] [lints.rust] unexpected_cfgs = { level = "warn", check-cfg = ['cfg(polling_test_poll_backend)'] } [[bench]] name = "io" harness = false [[bench]] name = "timer" harness = false [dependencies] async-lock = "3.0.0" cfg-if = "1" concurrent-queue = "2.2.0" futures-io = { version = "0.3.28", default-features = false, features = ["std"] } futures-lite = { version = "2.0.0", default-features = false } parking = "2.0.0" polling = "3.0.0" rustix = { version = "0.38.2", default-features = false, features = ["fs", "net", "std"] } slab = "0.4.2" tracing = { version = "0.1.37", default-features = false } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.52.0", features = ["Win32_Foundation"] } [dev-dependencies] async-channel = "2.0.0" async-net = "2.0.0" blocking = "1" criterion = { version = "0.4", default-features = false, features = ["cargo_bench_support"] } getrandom = "0.2.7" signal-hook = "0.3" tempfile = "3" [target.'cfg(target_os = "linux")'.dev-dependencies] inotify = { version = "0.10.1", default-features = false } timerfd = "1" [target.'cfg(windows)'.dev-dependencies] uds_windows = "1" [patch.crates-io] async-io = { path = "." } async-io-2.3.3/LICENSE-APACHE000064400000000000000000000251371046102023000133070ustar 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-2.3.3/LICENSE-MIT000064400000000000000000000017771046102023000130230ustar 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-2.3.3/README.md000064400000000000000000000053661046102023000126440ustar 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 [IOCP] 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 [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports [`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-2.3.3/benches/io.rs000064400000000000000000000126731046102023000137500ustar 00000000000000//! Benchmarks for a variety of I/O operations. use async_io::Async; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use futures_lite::{future, prelude::*}; use std::net::{Ipv4Addr, SocketAddr, TcpListener, TcpStream, UdpSocket}; /// Block on a future, either using the I/O driver or simple parking. fn block_on(fut: impl Future, drive: bool) -> R { if drive { async_io::block_on(fut) } else { future::block_on(fut) } } fn read_and_write(b: &mut Criterion) { const TCP_AMOUNT: usize = 1024 * 1024; let mut group = b.benchmark_group("read_and_write"); for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { // Benchmark the TCP streams. let init_reader_writer = || { let listener = TcpListener::bind("localhost:12345").unwrap(); let read_stream = TcpStream::connect("localhost:12345").unwrap(); let (write_stream, _) = listener.accept().unwrap(); let reader = Async::new(read_stream).unwrap(); let writer = Async::new(write_stream).unwrap(); (listener, reader, writer) }; group.bench_function(format!("TcpStream.{}", driver_name), move |b| { let (_listener, mut reader, mut writer) = init_reader_writer(); let mut buf = vec![0x42; TCP_AMOUNT]; b.iter(|| { let buf = &mut buf; block_on( async { black_box(writer.write_all(&*buf).await.ok()); black_box(reader.read_exact(buf).await.ok()); }, exec, ); }); }); #[cfg(unix)] { // Benchmark the Unix sockets. use std::os::unix::net::UnixStream; const UNIX_AMOUNT: usize = 1024; group.bench_function(format!("UnixStream.{}", driver_name), |b| { let (mut reader, mut writer) = Async::::pair().unwrap(); let mut buf = vec![0x42; UNIX_AMOUNT]; b.iter(|| { let buf = &mut buf; block_on( async { black_box(writer.write_all(&*buf).await.ok()); black_box(reader.read_exact(buf).await.ok()); }, exec, ); }); }); } } } fn connect_and_accept(c: &mut Criterion) { let mut group = c.benchmark_group("connect_and_accept"); for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { // Benchmark the TCP streams. group.bench_function(format!("TcpStream.{}", driver_name), move |b| { let socket_addr = SocketAddr::new("127.0.0.1".parse::().unwrap().into(), 12345); let listener = Async::::bind(socket_addr).unwrap(); b.iter(|| { block_on( async { let _reader = Async::::connect(socket_addr).await.ok(); black_box(listener.accept().await.ok()); drop(black_box(_reader)); }, exec, ); }); }); #[cfg(unix)] { // Benchmark the Unix sockets. use std::os::unix::net::{UnixListener, UnixStream}; let mut id = [0u8; 8]; getrandom::getrandom(&mut id).unwrap(); let id = u64::from_ne_bytes(id); let socket_addr = format!("/tmp/async-io-bench-{}.sock", id); let listener = Async::::bind(&socket_addr).unwrap(); group.bench_function(format!("UnixStream.{}", driver_name), |b| { b.iter(|| { block_on( async { let _reader = Async::::connect(&socket_addr).await.ok(); black_box(listener.accept().await.ok()); drop(black_box(_reader)); }, exec, ); }); }); drop(listener); } } } fn udp_send_recv(c: &mut Criterion) { const UDP_AMOUNT: usize = 1024; let mut group = c.benchmark_group("udp_send_recv"); // Create a pair of UDP sockets. let socket_addr = |port| SocketAddr::new("127.0.0.1".parse::().unwrap().into(), port); let socket_addr1 = socket_addr(12345); let socket_addr2 = socket_addr(12346); let reader = Async::::bind(socket_addr1).unwrap(); let writer = Async::::bind(socket_addr2).unwrap(); let mut buf = vec![0x42; UDP_AMOUNT]; for (driver_name, exec) in [("Undriven", false), ("Driven", true)] { group.bench_function(format!("UdpSocket.{}", driver_name), |b| { b.iter(|| { let buf = &mut buf; block_on( async { black_box(writer.send_to(&*buf, socket_addr1).await.ok()); black_box(reader.recv_from(buf).await.ok()); }, exec, ); }); }); } } criterion_group! { io_benchmarks, read_and_write, connect_and_accept, udp_send_recv } criterion_main!(io_benchmarks); async-io-2.3.3/benches/timer.rs000064400000000000000000000022541046102023000144530ustar 00000000000000//! Benchmarks for registering timers. use async_io::Timer; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use futures_lite::future; use std::time::Duration; /// Create a new `Timer` and poll it once to register it into the timer wheel. fn make_timer() -> Timer { let mut timer = Timer::after(Duration::from_secs(1)); future::block_on(future::poll_once(&mut timer)); timer } /// Benchmark the time it takes to register and deregister a timer. fn register_timer(c: &mut Criterion) { let mut group = c.benchmark_group("register_timer"); for prev_timer_count in [0, 1_000_000] { // Add timers to the timer wheel. let mut timers = Vec::new(); for _ in 0..prev_timer_count { timers.push(make_timer()); } // Benchmark registering a timer. group.bench_function( format!("register_timer.({} previous timers)", prev_timer_count), |b| { b.iter(|| { let timer = make_timer(); black_box(timer); }); }, ); } } criterion_group!(benches, register_timer); criterion_main!(benches); async-io-2.3.3/examples/kqueue-process.rs000064400000000000000000000023561046102023000165200ustar 00000000000000//! Uses the `async_io::os::kqueue` module to wait for a process to terminate. //! //! Run with: //! //! ``` //! cargo run --example kqueue-process //! ``` #[cfg(any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", ))] fn main() -> std::io::Result<()> { use std::process::Command; use async_io::os::kqueue::{Exit, Filter}; use futures_lite::future; future::block_on(async { // Spawn a process. let process = Command::new("sleep") .arg("3") .spawn() .expect("failed to spawn process"); // Wrap the process in an `Async` object that waits for it to exit. let process = Filter::new(Exit::new(process))?; // Wait for the process to exit. process.ready().await?; Ok(()) }) } #[cfg(not(any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", )))] fn main() { println!("This example only works for kqueue-enabled platforms."); } async-io-2.3.3/examples/linux-inotify.rs000064400000000000000000000034271046102023000163630ustar 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()?)?; // SAFETY: We do not move the inner file descriptor out. unsafe { inotify .get_mut() .watches() .add(".", 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 unsafe { 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-2.3.3/examples/linux-timerfd.rs000064400000000000000000000031661046102023000163340ustar 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 rustix::fd::BorrowedFd; use timerfd::{SetTimeFlags, TimerFd, TimerState}; /// 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| { // Safety: We assume `as_raw_fd()` returns a valid fd. When we // can depend on Rust >= 1.63, where `AsFd` is stabilized, and // when `TimerFd` implements it, we can remove this unsafe and // simplify this. let fd = unsafe { BorrowedFd::borrow_raw(t.as_raw_fd()) }; rustix::io::read(fd, &mut [0u8; 8]).map_err(io::Error::from) }) .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-2.3.3/examples/unix-signal.rs000064400000000000000000000015421046102023000157770ustar 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-2.3.3/examples/windows-command.rs000064400000000000000000000014361046102023000166510ustar 00000000000000//! Runs a command using waitable handles on Windows. //! //! Run with: //! //! ``` //! cargo run --example windows-command //! ``` #[cfg(windows)] fn main() -> std::io::Result<()> { use async_io::os::windows::Waitable; use std::process::Command; futures_lite::future::block_on(async { // Spawn a process. let process = Command::new("cmd") .args(["/C", "echo hello"]) .spawn() .expect("failed to spawn process"); // Wrap the process in an `Async` object that waits for it to exit. let process = Waitable::new(process)?; // Wait for the process to exit. process.ready().await?; Ok(()) }) } #[cfg(not(windows))] fn main() { println!("This example is only supported on Windows."); } async-io-2.3.3/examples/windows-uds.rs000064400000000000000000000066411046102023000160310ustar 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::ops::Deref; use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket}; use std::path::PathBuf; use async_io::Async; use blocking::Unblock; use futures_lite::{future, prelude::*}; use std::io; use tempfile::tempdir; // n.b.: notgull: uds_windows does not support I/O safety uet, hence the wrapper types struct UnixListener(uds_windows::UnixListener); impl From for UnixListener { fn from(ul: uds_windows::UnixListener) -> Self { Self(ul) } } impl Deref for UnixListener { type Target = uds_windows::UnixListener; fn deref(&self) -> &uds_windows::UnixListener { &self.0 } } impl AsSocket for UnixListener { fn as_socket(&self) -> BorrowedSocket<'_> { unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } struct UnixStream(uds_windows::UnixStream); impl From for UnixStream { fn from(ul: uds_windows::UnixStream) -> Self { Self(ul) } } impl Deref for UnixStream { type Target = uds_windows::UnixStream; fn deref(&self) -> &uds_windows::UnixStream { &self.0 } } impl AsSocket for UnixStream { fn as_socket(&self) -> BorrowedSocket<'_> { unsafe { BorrowedSocket::borrow_raw(self.as_raw_socket()) } } } impl io::Read for UnixStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { io::Read::read(&mut self.0, buf) } } impl io::Write for UnixStream { fn write(&mut self, buf: &[u8]) -> io::Result { io::Write::write(&mut self.0, buf) } fn flush(&mut self) -> io::Result<()> { io::Write::flush(&mut self.0) } } unsafe impl async_io::IoSafe for UnixStream {} async fn client(addr: PathBuf) -> io::Result<()> { // Connect to the address. let stream = Async::new(UnixStream::from(uds_windows::UnixStream::connect(addr)?))?; println!("Connected to {:?}", stream.get_ref().peer_addr()?); // Pipe the stream to stdout. let mut stdout = Unblock::new(std::io::stdout()); futures_lite::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::from(uds_windows::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(UnixStream::from(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-2.3.3/src/driver.rs000064400000000000000000000240071046102023000140060ustar 00000000000000use std::cell::{Cell, RefCell}; use std::future::Future; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::Arc; use std::task::Waker; use std::task::{Context, Poll}; use std::thread; use std::time::{Duration, Instant}; use async_lock::OnceCell; use futures_lite::pin; use parking::Parker; 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. fn unparker() -> &'static parking::Unparker { static UNPARKER: OnceCell = OnceCell::new(); UNPARKER.get_or_init_blocking(|| { 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() { let _ = unparker(); } /// The main loop for the "async-io" thread. fn main_loop(parker: parking::Parker) { let span = tracing::trace_span!("async_io::main_loop"); let _enter = span.enter(); // 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 { tracing::trace!("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); tracing::trace!("sleeping for {} us", delay_us); if parker.park_timeout(Duration::from_micros(*delay_us)) { tracing::trace!("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 { let span = tracing::trace_span!("async_io::block_on"); let _enter = span.enter(); // 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(); }); // Creates a parker and an associated waker that unparks it. fn parker_and_waker() -> (Parker, Waker, Arc) { // 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)); // Prepare the waker. let waker = BlockOnWaker::create(io_blocked.clone(), u); (p, waker, io_blocked) } thread_local! { // Cached parker and waker for efficiency. static CACHE: RefCell<(Parker, Waker, Arc)> = RefCell::new(parker_and_waker()); // Indicates that the current thread is polling I/O, but not necessarily blocked on it. static IO_POLLING: Cell = const { Cell::new(false) }; } struct BlockOnWaker { io_blocked: Arc, unparker: parking::Unparker, } impl BlockOnWaker { fn create(io_blocked: Arc, unparker: parking::Unparker) -> Waker { Waker::from(Arc::new(BlockOnWaker { io_blocked, unparker, })) } } impl std::task::Wake for BlockOnWaker { fn wake_by_ref(self: &Arc) { if self.unparker.unpark() { // Check if waking from another thread and if currently blocked on I/O. if !IO_POLLING.with(Cell::get) && self.io_blocked.load(Ordering::SeqCst) { Reactor::get().notify(); } } } fn wake(self: Arc) { self.wake_by_ref() } } CACHE.with(|cache| { // Try grabbing the cached parker and waker. let tmp_cached; let tmp_fresh; let (p, waker, io_blocked) = match cache.try_borrow_mut() { Ok(cache) => { // Use the cached parker and waker. tmp_cached = cache; &*tmp_cached } Err(_) => { // Looks like this is a recursive `block_on()` call. // Create a fresh parker and waker. tmp_fresh = parker_and_waker(); &tmp_fresh } }; pin!(future); let cx = &mut Context::from_waker(waker); loop { // Poll the future. if let Poll::Ready(t) = future.as_mut().poll(cx) { // Ensure the cached parker is reset to the unnotified state for future block_on calls, // in case this future called wake and then immediately returned Poll::Ready. p.park_timeout(Duration::from_secs(0)); tracing::trace!("completed"); return t; } // Check if a notification was received. if p.park_timeout(Duration::from_secs(0)) { tracing::trace!("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)) { tracing::trace!("notified"); break; } // Wait for I/O events. tracing::trace!("waiting on I/O"); reactor_lock.react(None).ok(); // Check if a notification has been received. if p.park_timeout(Duration::from_secs(0)) { tracing::trace!("notified"); break; } // Check if this thread been handling I/O events for a long time. if start.elapsed() > Duration::from_micros(500) { tracing::trace!("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. tracing::trace!("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-2.3.3/src/lib.rs000064400000000000000000002223751046102023000132710ustar 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 [IOCP] 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 //! [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports //! [`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)] #![doc( html_favicon_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" )] #![doc( html_logo_url = "https://raw.githubusercontent.com/smol-rs/smol/master/assets/images/logo_fullsize_transparent.png" )] 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::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}, os::unix::net::{SocketAddr as UnixSocketAddr, UnixDatagram, UnixListener, UnixStream}, path::Path, }; #[cfg(windows)] use std::os::windows::io::{AsRawSocket, AsSocket, BorrowedSocket, OwnedSocket, RawSocket}; use futures_io::{AsyncRead, AsyncWrite}; use futures_lite::stream::{self, Stream}; use futures_lite::{future, pin, ready}; use rustix::io as rio; use rustix::net as rn; use crate::reactor::{Reactor, Registration, Source}; mod driver; mod reactor; pub mod os; pub use driver::block_on; pub use reactor::{Readable, ReadableOwned, Writable, WritableOwned}; /// 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. /// /// # Precision /// /// There is a limit on the maximum precision that a `Timer` can provide. This limit is /// dependent on the current platform; for instance, on Windows, the maximum precision is /// about 16 milliseconds. Because of this limit, the timer may sleep for longer than the /// requested duration. It will never sleep for less. /// /// # 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(1)).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. /// /// If this timer is a blank timer, this value is None. If the timer /// must be set, this value contains the next instant at which the /// timer must fire. when: Option, /// The period. period: Duration, } impl Timer { /// Creates a timer that will never fire. /// /// # Examples /// /// This function may also be useful for creating a function with an optional timeout. /// /// ``` /// # futures_lite::future::block_on(async { /// use async_io::Timer; /// use futures_lite::prelude::*; /// use std::time::Duration; /// /// async fn run_with_timeout(timeout: Option) { /// let timer = timeout /// .map(|timeout| Timer::after(timeout)) /// .unwrap_or_else(Timer::never); /// /// run_lengthy_operation().or(timer).await; /// } /// # // Note that since a Timer as a Future returns an Instant, /// # // this function needs to return an Instant to be used /// # // in "or". /// # async fn run_lengthy_operation() -> std::time::Instant { /// # std::time::Instant::now() /// # } /// /// // Times out after 5 seconds. /// run_with_timeout(Some(Duration::from_secs(5))).await; /// // Does not time out. /// run_with_timeout(None).await; /// # }); /// ``` pub fn never() -> Timer { Timer { id_and_waker: None, when: None, period: Duration::MAX, } } /// 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 { Instant::now() .checked_add(duration) .map_or_else(Timer::never, Timer::at) } /// 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 { 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 { Instant::now() .checked_add(period) .map_or_else(Timer::never, |at| Timer::interval_at(at, 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: Some(start), period, } } /// Indicates whether or not this timer will ever fire. /// /// [`never()`] will never fire, and timers created with [`after()`] or [`at()`] will fire /// if the duration is not too large. /// /// [`never()`]: Timer::never() /// [`after()`]: Timer::after() /// [`at()`]: Timer::at() /// /// # Examples /// /// ``` /// # futures_lite::future::block_on(async { /// use async_io::Timer; /// use futures_lite::prelude::*; /// use std::time::Duration; /// /// // `never` will never fire. /// assert!(!Timer::never().will_fire()); /// /// // `after` will fire if the duration is not too large. /// assert!(Timer::after(Duration::from_secs(1)).will_fire()); /// assert!(!Timer::after(Duration::MAX).will_fire()); /// /// // However, once an `after` timer has fired, it will never fire again. /// let mut t = Timer::after(Duration::from_secs(1)); /// assert!(t.will_fire()); /// (&mut t).await; /// assert!(!t.will_fire()); /// /// // Interval timers will fire periodically. /// let mut t = Timer::interval(Duration::from_secs(1)); /// assert!(t.will_fire()); /// t.next().await; /// assert!(t.will_fire()); /// # }); /// ``` #[inline] pub fn will_fire(&self) -> bool { self.when.is_some() } /// 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) { match Instant::now().checked_add(duration) { Some(instant) => self.set_at(instant), None => { // Overflow to never going off. self.clear(); self.when = None; } } } /// 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) { self.clear(); // Update the timeout. self.when = Some(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(instant, 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) { match Instant::now().checked_add(period) { Some(instant) => self.set_interval_at(instant, period), None => { // Overflow to never going off. self.clear(); self.when = None; } } } /// 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) { self.clear(); self.when = Some(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(start, waker); } } /// Helper function to clear the current timer. fn clear(&mut self) { if let (Some(when), Some((id, _))) = (self.when, self.id_and_waker.as_ref()) { // Deregister the timer from the reactor. Reactor::get().remove_timer(when, *id); } } } impl Drop for Timer { fn drop(&mut self) { if let (Some(when), Some((id, _))) = (self.when, self.id_and_waker.take()) { // Deregister the timer from the reactor. Reactor::get().remove_timer(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(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); if let Some(ref mut when) = this.when { // Check if the timer has already fired. if Instant::now() >= *when { if let Some((id, _)) = this.id_and_waker.take() { // Deregister the timer from the reactor. Reactor::get().remove_timer(*when, id); } let result_time = *when; if let Some(next) = (*when).checked_add(this.period) { *when = next; // Register the timer in the reactor. let id = Reactor::get().insert_timer(next, cx.waker()); this.id_and_waker = Some((id, cx.waker().clone())); } else { this.when = None; } return Poll::Ready(Some(result_time)); } else { match &this.id_and_waker { None => { // Register the timer in the reactor. let id = Reactor::get().insert_timer(*when, cx.waker()); this.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(*when, *id); // Register the timer in the reactor with the new waker. let id = Reactor::get().insert_timer(*when, cx.waker()); this.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]/[IOCP], 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 /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports /// /// # 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). /// /// The most notable caveat is that it is unsafe to access the inner I/O source mutably /// using this primitive. Traits likes [`AsyncRead`] and [`AsyncWrite`] are not implemented by /// default unless it is guaranteed that the resource won't be invalidated by reading or writing. /// See the [`IoSafe`] trait for more information. /// /// [`async-net`]: https://github.com/smol-rs/async-net /// [`async-process`]: https://github.com/smol-rs/async-process /// [`AsyncRead`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html /// [`AsyncWrite`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html /// /// ### 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]/[IOCP]. /// /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement /// `AsSocket`. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports /// /// # 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> { // Put the file descriptor in non-blocking mode. set_nonblocking(io.as_fd())?; Self::new_nonblocking(io) } /// Creates an async I/O handle without setting it to non-blocking mode. /// /// This method will register the handle in [epoll]/[kqueue]/[event ports]/[IOCP]. /// /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement /// `AsSocket`. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports /// /// # Caveats /// /// The caller should ensure that the handle is set to non-blocking mode or that it is okay if /// it is not set. If not set to non-blocking mode, I/O operations may block the current thread /// and cause a deadlock in an asynchronous context. pub fn new_nonblocking(io: T) -> io::Result> { // SAFETY: It is impossible to drop the I/O source while it is registered through // this type. let registration = unsafe { Registration::new(io.as_fd()) }; Ok(Async { source: Reactor::get().insert_io(registration)?, io: Some(io), }) } } #[cfg(unix)] impl AsRawFd for Async { fn as_raw_fd(&self) -> RawFd { self.get_ref().as_raw_fd() } } #[cfg(unix)] impl AsFd for Async { fn as_fd(&self) -> BorrowedFd<'_> { self.get_ref().as_fd() } } #[cfg(unix)] impl> TryFrom for Async { type Error = io::Error; fn try_from(value: OwnedFd) -> Result { Async::new(value.into()) } } #[cfg(unix)] impl> TryFrom> for OwnedFd { type Error = io::Error; fn try_from(value: Async) -> Result { value.into_inner().map(Into::into) } } #[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]/[IOCP]. /// /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement /// `AsSocket`. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports /// /// # 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> { // Put the socket in non-blocking mode. set_nonblocking(io.as_socket())?; Self::new_nonblocking(io) } /// Creates an async I/O handle without setting it to non-blocking mode. /// /// This method will register the handle in [epoll]/[kqueue]/[event ports]/[IOCP]. /// /// On Unix systems, the handle must implement `AsFd`, while on Windows it must implement /// `AsSocket`. /// /// [epoll]: https://en.wikipedia.org/wiki/Epoll /// [kqueue]: https://en.wikipedia.org/wiki/Kqueue /// [event ports]: https://illumos.org/man/port_create /// [IOCP]: https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports /// /// # Caveats /// /// The caller should ensure that the handle is set to non-blocking mode or that it is okay if /// it is not set. If not set to non-blocking mode, I/O operations may block the current thread /// and cause a deadlock in an asynchronous context. pub fn new_nonblocking(io: T) -> io::Result> { // Create the registration. // // SAFETY: It is impossible to drop the I/O source while it is registered through // this type. let registration = unsafe { Registration::new(io.as_socket()) }; Ok(Async { source: Reactor::get().insert_io(registration)?, io: Some(io), }) } } #[cfg(windows)] impl AsRawSocket for Async { fn as_raw_socket(&self) -> RawSocket { self.get_ref().as_raw_socket() } } #[cfg(windows)] impl AsSocket for Async { fn as_socket(&self) -> BorrowedSocket<'_> { self.get_ref().as_socket() } } #[cfg(windows)] impl> TryFrom for Async { type Error = io::Error; fn try_from(value: OwnedSocket) -> Result { Async::new(value.into()) } } #[cfg(windows)] impl> TryFrom> for OwnedSocket { type Error = io::Error; fn try_from(value: Async) -> Result { value.into_inner().map(Into::into) } } 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. /// /// # Safety /// /// The underlying I/O source must not be dropped using this function. /// /// # 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 = unsafe { listener.get_mut() }; /// # std::io::Result::Ok(()) }); /// ``` pub unsafe 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. /// /// # Safety /// /// In the closure, the underlying I/O source must not be dropped. /// /// # 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) = unsafe { listener.read_with_mut(|l| l.accept()).await? }; /// # std::io::Result::Ok(()) }); /// ``` pub async unsafe 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. /// /// # Safety /// /// The closure receives a mutable reference to the I/O handle. In the closure, the underlying /// I/O source must not be dropped. /// /// # 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 = unsafe { socket.write_with_mut(|s| s.send(msg)).await? }; /// # std::io::Result::Ok(()) }); /// ``` pub async unsafe 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 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(); } } } /// Types whose I/O trait implementations do not drop the underlying I/O source. /// /// The resource contained inside of the [`Async`] cannot be invalidated. This invalidation can /// happen if the inner resource (the [`TcpStream`], [`UnixListener`] or other `T`) is moved out /// and dropped before the [`Async`]. Because of this, functions that grant mutable access to /// the inner type are unsafe, as there is no way to guarantee that the source won't be dropped /// and a dangling handle won't be left behind. /// /// Unfortunately this extends to implementations of [`Read`] and [`Write`]. Since methods on those /// traits take `&mut`, there is no guarantee that the implementor of those traits won't move the /// source out while the method is being run. /// /// This trait is an antidote to this predicament. By implementing this trait, the user pledges /// that using any I/O traits won't destroy the source. This way, [`Async`] can implement the /// `async` version of these I/O traits, like [`AsyncRead`] and [`AsyncWrite`]. /// /// # Safety /// /// Any I/O trait implementations for this type must not drop the underlying I/O source. Traits /// affected by this trait include [`Read`], [`Write`], [`Seek`] and [`BufRead`]. /// /// This trait is implemented by default on top of `libstd` types. In addition, it is implemented /// for immutable reference types, as it is impossible to invalidate any outstanding references /// while holding an immutable reference, even with interior mutability. As Rust's current pinning /// system relies on similar guarantees, I believe that this approach is robust. /// /// [`BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html /// [`Read`]: https://doc.rust-lang.org/std/io/trait.Read.html /// [`Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html /// [`Write`]: https://doc.rust-lang.org/std/io/trait.Write.html /// /// [`AsyncRead`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncRead.html /// [`AsyncWrite`]: https://docs.rs/futures-io/latest/futures_io/trait.AsyncWrite.html pub unsafe trait IoSafe {} /// Reference types can't be mutated. /// /// The worst thing that can happen is that external state is used to change what kind of pointer /// `as_fd()` returns. For instance: /// /// ``` /// # #[cfg(unix)] { /// use std::cell::Cell; /// use std::net::TcpStream; /// use std::os::unix::io::{AsFd, BorrowedFd}; /// /// struct Bar { /// flag: Cell, /// a: TcpStream, /// b: TcpStream /// } /// /// impl AsFd for Bar { /// fn as_fd(&self) -> BorrowedFd<'_> { /// if self.flag.replace(!self.flag.get()) { /// self.a.as_fd() /// } else { /// self.b.as_fd() /// } /// } /// } /// # } /// ``` /// /// We solve this problem by only calling `as_fd()` once to get the original source. Implementations /// like this are considered buggy (but not unsound) and are thus not really supported by `async-io`. unsafe impl IoSafe for &T {} // Can be implemented on top of libstd types. unsafe impl IoSafe for std::fs::File {} unsafe impl IoSafe for std::io::Stderr {} unsafe impl IoSafe for std::io::Stdin {} unsafe impl IoSafe for std::io::Stdout {} unsafe impl IoSafe for std::io::StderrLock<'_> {} unsafe impl IoSafe for std::io::StdinLock<'_> {} unsafe impl IoSafe for std::io::StdoutLock<'_> {} unsafe impl IoSafe for std::net::TcpStream {} unsafe impl IoSafe for std::process::ChildStdin {} unsafe impl IoSafe for std::process::ChildStdout {} unsafe impl IoSafe for std::process::ChildStderr {} #[cfg(unix)] unsafe impl IoSafe for std::os::unix::net::UnixStream {} unsafe impl IoSafe for std::io::BufReader {} unsafe impl IoSafe for std::io::BufWriter {} unsafe impl IoSafe for std::io::LineWriter {} unsafe impl IoSafe for &mut T {} unsafe impl IoSafe for Box {} unsafe impl IoSafe for std::borrow::Cow<'_, T> {} impl AsyncRead for Async { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { loop { match unsafe { (*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 unsafe { (*self).get_mut() }.read_vectored(bufs) { Err(err) if err.kind() == io::ErrorKind::WouldBlock => {} res => return Poll::Ready(res), } ready!(self.poll_readable(cx))?; } } } // Since this is through a reference, we can't mutate the inner I/O source. // Therefore this is safe! 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 unsafe { (*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 unsafe { (*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 unsafe { (*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> { // Figure out how to handle this address. let addr = addr.into(); let (domain, sock_addr) = match addr { SocketAddr::V4(v4) => (rn::AddressFamily::INET, rn::SocketAddrAny::V4(v4)), SocketAddr::V6(v6) => (rn::AddressFamily::INET6, rn::SocketAddrAny::V6(v6)), }; // Begin async connect. let socket = connect(sock_addr, domain, Some(rn::ipproto::TCP))?; // Use new_nonblocking because connect already sets socket to non-blocking mode. let stream = Async::new_nonblocking(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> { let address = convert_path_to_socket_address(path.as_ref())?; // Begin async connect. let socket = connect(address.into(), rn::AddressFamily::UNIX, None)?; // Use new_nonblocking because connect already sets socket to non-blocking mode. let stream = Async::new_nonblocking(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: rn::SocketAddrAny, domain: rn::AddressFamily, protocol: Option, ) -> io::Result { #[cfg(windows)] use rustix::fd::AsFd; setup_networking(); #[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" ))] let socket = rn::socket_with( domain, rn::SocketType::STREAM, rn::SocketFlags::CLOEXEC | rn::SocketFlags::NONBLOCK, 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" )))] let socket = { #[cfg(not(any( target_os = "aix", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "espidf", windows, )))] let flags = rn::SocketFlags::CLOEXEC; #[cfg(any( target_os = "aix", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "espidf", windows, ))] let flags = rn::SocketFlags::empty(); // Create the socket. let socket = rn::socket_with(domain, rn::SocketType::STREAM, flags, protocol)?; // Set cloexec if necessary. #[cfg(any( target_os = "aix", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", ))] rio::fcntl_setfd(&socket, rio::fcntl_getfd(&socket)? | rio::FdFlags::CLOEXEC)?; // Set non-blocking mode. set_nonblocking(socket.as_fd())?; socket }; // Set nosigpipe if necessary. #[cfg(any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd" ))] rn::sockopt::set_socket_nosigpipe(&socket, true)?; // Set the handle information to HANDLE_FLAG_INHERIT. #[cfg(windows)] unsafe { if windows_sys::Win32::Foundation::SetHandleInformation( socket.as_raw_socket() as _, windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, windows_sys::Win32::Foundation::HANDLE_FLAG_INHERIT, ) == 0 { return Err(io::Error::last_os_error()); } } #[allow(unreachable_patterns)] match rn::connect_any(&socket, &addr) { Ok(_) => {} #[cfg(unix)] Err(rio::Errno::INPROGRESS) => {} Err(rio::Errno::AGAIN) | Err(rio::Errno::WOULDBLOCK) => {} Err(err) => return Err(err.into()), } Ok(socket) } #[inline] fn setup_networking() { #[cfg(windows)] { // On Windows, we need to call WSAStartup before calling any networking code. // Make sure to call it at least once. static INIT: std::sync::Once = std::sync::Once::new(); INIT.call_once(|| { let _ = rustix::net::wsa_startup(); }); } } #[inline] fn set_nonblocking( #[cfg(unix)] fd: BorrowedFd<'_>, #[cfg(windows)] fd: BorrowedSocket<'_>, ) -> io::Result<()> { cfg_if::cfg_if! { // ioctl(FIONBIO) sets the flag atomically, but we use this only on Linux // for now, as with the standard library, because it seems to behave // differently depending on the platform. // https://github.com/rust-lang/rust/commit/efeb42be2837842d1beb47b51bb693c7474aba3d // https://github.com/libuv/libuv/blob/e9d91fccfc3e5ff772d5da90e1c4a24061198ca0/src/unix/poll.c#L78-L80 // https://github.com/tokio-rs/mio/commit/0db49f6d5caf54b12176821363d154384357e70a if #[cfg(any(windows, target_os = "linux"))] { rustix::io::ioctl_fionbio(fd, true)?; } else { let previous = rustix::fs::fcntl_getfl(fd)?; let new = previous | rustix::fs::OFlags::NONBLOCK; if new != previous { rustix::fs::fcntl_setfl(fd, new)?; } } } Ok(()) } /// Converts a `Path` to its socket address representation. /// /// This function is abstract socket-aware. #[cfg(unix)] #[inline] fn convert_path_to_socket_address(path: &Path) -> io::Result { // SocketAddrUnix::new() will throw EINVAL when a path with a zero in it is passed in. // However, some users expect to be able to pass in paths to abstract sockets, which // triggers this error as it has a zero in it. Therefore, if a path starts with a zero, // make it an abstract socket. #[cfg(any(target_os = "linux", target_os = "android"))] let address = { use std::os::unix::ffi::OsStrExt; let path = path.as_os_str(); match path.as_bytes().first() { Some(0) => rn::SocketAddrUnix::new_abstract_name(path.as_bytes().get(1..).unwrap())?, _ => rn::SocketAddrUnix::new(path)?, } }; // Only Linux and Android support abstract sockets. #[cfg(not(any(target_os = "linux", target_os = "android")))] let address = rn::SocketAddrUnix::new(path)?; Ok(address) } async-io-2.3.3/src/os/kqueue.rs000064400000000000000000000175431046102023000144420ustar 00000000000000//! Functionality that is only available for `kqueue`-based platforms. use __private::QueueableSealed; use crate::reactor::{Reactor, Readable, Registration}; use crate::Async; use std::future::Future; use std::io::{Error, Result}; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::pin::Pin; use std::process::Child; use std::task::{Context, Poll}; /// A wrapper around a queueable object that waits until it is ready. /// /// The underlying `kqueue` implementation can be used to poll for events besides file descriptor /// read/write readiness. This API makes these faculties available to the user. /// /// See the [`Queueable`] trait and its implementors for objects that currently support being registered /// into the reactor. #[derive(Debug)] pub struct Filter(Async); impl AsRef for Filter { fn as_ref(&self) -> &T { self.0.as_ref() } } impl AsMut for Filter { fn as_mut(&mut self) -> &mut T { self.get_mut() } } impl Filter { /// Create a new [`Filter`] around a [`Queueable`]. /// /// # Examples /// /// ```no_run /// use std::process::Command; /// use async_io::os::kqueue::{Exit, Filter}; /// /// // Create a new process to wait for. /// let mut child = Command::new("sleep").arg("5").spawn().unwrap(); /// /// // Wrap the process in an `Async` object that waits for it to exit. /// let process = Filter::new(Exit::new(child)).unwrap(); /// /// // Wait for the process to exit. /// # async_io::block_on(async { /// process.ready().await.unwrap(); /// # }); /// ``` pub fn new(mut filter: T) -> Result { Ok(Self(Async { source: Reactor::get().insert_io(filter.registration())?, io: Some(filter), })) } } impl AsRawFd for Filter { fn as_raw_fd(&self) -> RawFd { self.0.as_raw_fd() } } impl AsFd for Filter { fn as_fd(&self) -> BorrowedFd<'_> { self.0.as_fd() } } impl> TryFrom for Filter { type Error = Error; fn try_from(fd: OwnedFd) -> Result { Ok(Self(Async::try_from(fd)?)) } } impl> TryFrom> for OwnedFd { type Error = Error; fn try_from(filter: Filter) -> Result { filter.0.try_into() } } impl Filter { /// Gets a reference to the underlying [`Queueable`] object. /// /// # Examples /// /// ``` /// use async_io::os::kqueue::{Exit, Filter}; /// /// # futures_lite::future::block_on(async { /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); /// let process = Filter::new(Exit::new(child)).unwrap(); /// let inner = process.get_ref(); /// # }); /// ``` pub fn get_ref(&self) -> &T { self.0.get_ref() } /// Gets a mutable reference to the underlying [`Queueable`] object. /// /// Unlike in [`Async`], this method is safe to call, since dropping the [`Filter`] will /// not cause any undefined behavior. /// /// # Examples /// /// ``` /// use async_io::os::kqueue::{Exit, Filter}; /// /// # futures_lite::future::block_on(async { /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); /// let mut process = Filter::new(Exit::new(child)).unwrap(); /// let inner = process.get_mut(); /// # }); /// ``` pub fn get_mut(&mut self) -> &mut T { unsafe { self.0.get_mut() } } /// Unwraps the inner [`Queueable`] object. /// /// # Examples /// /// ``` /// use async_io::os::kqueue::{Exit, Filter}; /// /// # futures_lite::future::block_on(async { /// let child = std::process::Command::new("sleep").arg("5").spawn().unwrap(); /// let process = Filter::new(Exit::new(child)).unwrap(); /// let inner = process.into_inner().unwrap(); /// # }); /// ``` pub fn into_inner(self) -> Result { self.0.into_inner() } /// Waits until the [`Queueable`] object is ready. /// /// This method completes when the underlying [`Queueable`] object has completed. See the documentation /// for the [`Queueable`] object for more information. /// /// # Examples /// /// ```no_run /// use std::process::Command; /// use async_io::os::kqueue::{Exit, Filter}; /// /// # futures_lite::future::block_on(async { /// let child = Command::new("sleep").arg("5").spawn()?; /// let process = Filter::new(Exit::new(child))?; /// /// // Wait for the process to exit. /// process.ready().await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn ready(&self) -> Ready<'_, T> { Ready(self.0.readable()) } /// Polls the I/O handle for readiness. /// /// When this method returns [`Poll::Ready`], that means that the OS has delivered a notification /// that the underlying [`Queueable`] object is ready. See the documentation for the [`Queueable`] /// object for more information. /// /// # 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. /// /// # Examples /// /// ```no_run /// use std::process::Command; /// use async_io::os::kqueue::{Exit, Filter}; /// use futures_lite::future; /// /// # futures_lite::future::block_on(async { /// let child = Command::new("sleep").arg("5").spawn()?; /// let process = Filter::new(Exit::new(child))?; /// /// // Wait for the process to exit. /// future::poll_fn(|cx| process.poll_ready(cx)).await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { self.0.poll_readable(cx) } } /// Future for [`Filter::ready`]. #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] pub struct Ready<'a, T>(Readable<'a, T>); impl Future for Ready<'_, T> { type Output = Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx) } } /// Objects that can be registered into the reactor via a [`Async`](crate::Async). /// /// These objects represent other filters associated with the `kqueue` runtime aside from readability /// and writability. Rather than waiting on readable/writable, they wait on "readiness". This is /// typically used for signals and child process exits. pub trait Queueable: QueueableSealed {} /// An object representing a signal. /// /// When registered into [`Async`](crate::Async) via [`with_filter`](AsyncKqueueExt::with_filter), /// it will return a [`readable`](crate::Async::readable) event when the signal is received. #[derive(Debug, Copy, Clone, Eq, PartialEq, PartialOrd, Ord, Hash)] pub struct Signal(pub i32); impl QueueableSealed for Signal { fn registration(&mut self) -> Registration { Registration::Signal(*self) } } impl Queueable for Signal {} /// Wait for a child process to exit. /// /// When registered into [`Async`](crate::Async) via [`with_filter`](AsyncKqueueExt::with_filter), /// it will return a [`readable`](crate::Async::readable) event when the child process exits. #[derive(Debug)] pub struct Exit(Option); impl Exit { /// Create a new `Exit` object. pub fn new(child: Child) -> Self { Self(Some(child)) } } impl QueueableSealed for Exit { fn registration(&mut self) -> Registration { Registration::Process(self.0.take().expect("Cannot reregister child")) } } impl Queueable for Exit {} mod __private { use crate::reactor::Registration; #[doc(hidden)] pub trait QueueableSealed { /// Get a registration object for this filter. fn registration(&mut self) -> Registration; } } async-io-2.3.3/src/os/unix.rs000064400000000000000000000052671046102023000141260ustar 00000000000000//! Functionality that is only available for `unix` platforms. use std::os::unix::io::BorrowedFd; /// Get a file descriptor that can be used to wait for readiness in an external runtime. /// /// This file descriptor is equivalent to the one used by the underlying epoll/kqueue/event ports /// instance for polling. The intention is that this file descriptor can be registered into an /// external runtime (like [`calloop`] or [GLib]) so that `async-io` can be seamlessly polled /// alongside the other runtime. /// /// Not every backend used on `unix` has an associated file descriptor, however. While epoll, /// kqueue and event ports have a file descriptor as a backend, on some Unix systems `async-io` /// will use the `poll()` system call instead. Since there are no file descriptors intrinsically /// associated with `poll()`, this function will return `None`. /// /// There is presently no way to stop the "`async-io`" thread from being launched, so the reactor /// will still be continiously polled on that thread. This fact should be kept in mind by anyone /// looking to integrate `async-io` into another runtime using this function. /// /// It is possible to use this function to call raw system calls on the underlying event source. /// This is generally not recommended, since registered event sources may conflict with `async-io`'s /// existing scheme for managing sources. The behavior resulting from this is not specified, but /// will not result in undefined behavior. This could include panics, incorrect results, aborts, /// memory leaks, and non-termination. /// /// [`calloop`]: https://docs.rs/calloop /// [GLib]: https://en.wikipedia.org/wiki/GLib /// /// ## Example /// /// ``` /// #![cfg(unix)] /// /// use async_io::os::unix::reactor_fd; /// /// my_runtime::register(reactor_fd().unwrap()); /// # mod my_runtime { /// # use std::os::unix::io::BorrowedFd; /// # pub fn register(_: BorrowedFd<'_>) {} /// # } /// ``` pub fn reactor_fd() -> Option> { cfg_if::cfg_if! { if #[cfg(all( any( target_os = "linux", target_os = "android", target_os = "illumos", target_os = "solaris", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", ), not(polling_test_poll_backend), ))] { use std::os::unix::io::AsFd; Some(crate::Reactor::get().poller.as_fd()) } else { None } } } async-io-2.3.3/src/os/windows.rs000064400000000000000000000135401046102023000146260ustar 00000000000000//! Functionality that is only available on Windows. use crate::reactor::{Reactor, Readable, Registration}; use crate::Async; use std::future::Future; use std::io::{self, Result}; use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, OwnedHandle, RawHandle}; use std::pin::Pin; use std::task::{Context, Poll}; /// A waitable handle registered in the reactor. /// /// Some handles in Windows are “waitable”, which means that they emit a “readiness” signal after some event occurs. This function can be used to wait for such events to occur on a handle. This function can be used in addition to regular socket polling. /// /// Waitable objects include the following: /// /// - Console inputs /// - Waitable events /// - Mutexes /// - Processes /// - Semaphores /// - Threads /// - Timer /// /// This structure can be used to wait for any of these objects to become ready. /// /// ## Implementation /// /// The current implementation waits on the handle by registering it in the application-global /// Win32 threadpool. However, in the futur it may be possible to migrate to an implementation /// on Windows 10 that uses a mechanism similar to [`MsgWaitForMultipleObjectsEx`]. /// /// [`MsgWaitForMultipleObjectsEx`]: https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex /// /// ## Caveats /// /// Read the documentation for the [`Async`](crate::Async) type for more information regarding the /// abilities and caveats with using this type. #[derive(Debug)] pub struct Waitable(Async); impl AsRef for Waitable { fn as_ref(&self) -> &T { self.0.as_ref() } } impl Waitable { /// Create a new [`Waitable`] around a waitable handle. /// /// # Examples /// /// ```no_run /// use std::process::Command; /// use async_io::os::windows::Waitable; /// /// // Create a new process to wait for. /// let mut child = Command::new("sleep").arg("5").spawn().unwrap(); /// /// // Wrap the process in an `Async` object that waits for it to exit. /// let process = Waitable::new(child).unwrap(); /// /// // Wait for the process to exit. /// # async_io::block_on(async { /// process.ready().await.unwrap(); /// # }); /// ``` pub fn new(handle: T) -> Result { Ok(Self(Async { source: Reactor::get() .insert_io(unsafe { Registration::new_waitable(handle.as_handle()) })?, io: Some(handle), })) } } impl AsRawHandle for Waitable { fn as_raw_handle(&self) -> RawHandle { self.get_ref().as_raw_handle() } } impl AsHandle for Waitable { fn as_handle(&self) -> BorrowedHandle<'_> { self.get_ref().as_handle() } } impl> TryFrom for Waitable { type Error = io::Error; fn try_from(handle: OwnedHandle) -> Result { Self::new(handle.into()) } } impl> TryFrom> for OwnedHandle { type Error = io::Error; fn try_from(value: Waitable) -> std::result::Result { value.into_inner().map(|handle| handle.into()) } } impl Waitable { /// Get a reference to the inner handle. pub fn get_ref(&self) -> &T { self.0.get_ref() } /// Get a mutable reference to the inner handle. /// /// # Safety /// /// The underlying I/O source must not be dropped or moved out using this function. pub unsafe fn get_mut(&mut self) -> &mut T { self.0.get_mut() } /// Consumes the [`Waitable`], returning the inner handle. pub fn into_inner(self) -> Result { self.0.into_inner() } /// Waits until the [`Waitable`] object is ready. /// /// This method completes when the underlying [`Waitable`] object has completed. See the documentation /// for the [`Waitable`] object for more information. /// /// # Examples /// /// ```no_run /// use std::process::Command; /// use async_io::os::windows::Waitable; /// /// # futures_lite::future::block_on(async { /// let child = Command::new("sleep").arg("5").spawn()?; /// let process = Waitable::new(child)?; /// /// // Wait for the process to exit. /// process.ready().await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn ready(&self) -> Ready<'_, T> { Ready(self.0.readable()) } /// Polls the I/O handle for readiness. /// /// When this method returns [`Poll::Ready`], that means that the OS has delivered a notification /// that the underlying [`Waitable`] object is ready. See the documentation for the [`Waitable`] /// object for more information. /// /// # 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. /// /// # Examples /// /// ```no_run /// use std::process::Command; /// use async_io::os::windows::Waitable; /// use futures_lite::future; /// /// # futures_lite::future::block_on(async { /// let child = Command::new("sleep").arg("5").spawn()?; /// let process = Waitable::new(child)?; /// /// // Wait for the process to exit. /// future::poll_fn(|cx| process.poll_ready(cx)).await?; /// # std::io::Result::Ok(()) }); /// ``` pub fn poll_ready(&self, cx: &mut Context<'_>) -> Poll> { self.0.poll_readable(cx) } } /// Future for [`Filter::ready`]. #[must_use = "futures do nothing unless you `.await` or poll them"] #[derive(Debug)] pub struct Ready<'a, T>(Readable<'a, T>); impl Future for Ready<'_, T> { type Output = Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { Pin::new(&mut self.0).poll(cx) } } async-io-2.3.3/src/os.rs000064400000000000000000000005231046102023000131310ustar 00000000000000//! Platform-specific functionality. #[cfg(unix)] pub mod unix; #[cfg(any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", ))] pub mod kqueue; #[cfg(windows)] pub mod windows; async-io-2.3.3/src/reactor/kqueue.rs000064400000000000000000000067571046102023000154650ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 use crate::os::kqueue::Signal; use polling::os::kqueue::{PollerKqueueExt, Process, ProcessOps, Signal as PollSignal}; use polling::{Event, PollMode, Poller}; use std::fmt; use std::io::Result; use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; use std::process::Child; /// The raw registration into the reactor. /// /// This needs to be public, since it is technically exposed through the `QueueableSealed` trait. #[doc(hidden)] pub enum Registration { /// Raw file descriptor for readability/writability. /// /// /// # Invariant /// /// This describes a valid file descriptor that has not been `close`d. It will not be /// closed while this object is alive. Fd(RawFd), /// Raw signal number for signal delivery. Signal(Signal), /// Process for process termination. Process(Child), } impl fmt::Debug for Registration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Fd(raw) => fmt::Debug::fmt(raw, f), Self::Signal(signal) => fmt::Debug::fmt(signal, f), Self::Process(process) => fmt::Debug::fmt(process, f), } } } impl Registration { /// Add this file descriptor into the reactor. /// /// # Safety /// /// The provided file descriptor must be valid and not be closed while this object is alive. pub(crate) unsafe fn new(f: BorrowedFd<'_>) -> Self { Self::Fd(f.as_raw_fd()) } /// Registers the object into the reactor. #[inline] pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { match self { Self::Fd(raw) => { // SAFETY: This object's existence validates the invariants of Poller::add unsafe { poller.add(*raw, Event::none(token)) } } Self::Signal(signal) => { poller.add_filter(PollSignal(signal.0), token, PollMode::Oneshot) } Self::Process(process) => poller.add_filter( unsafe { Process::new(process, ProcessOps::Exit) }, token, PollMode::Oneshot, ), } } /// Re-registers the object into the reactor. #[inline] pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { match self { Self::Fd(raw) => { // SAFETY: self.raw is a valid file descriptor let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; poller.modify(fd, interest) } Self::Signal(signal) => { poller.modify_filter(PollSignal(signal.0), interest.key, PollMode::Oneshot) } Self::Process(process) => poller.modify_filter( unsafe { Process::new(process, ProcessOps::Exit) }, interest.key, PollMode::Oneshot, ), } } /// Deregisters the object from the reactor. #[inline] pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { match self { Self::Fd(raw) => { // SAFETY: self.raw is a valid file descriptor let fd = unsafe { BorrowedFd::borrow_raw(*raw) }; poller.delete(fd) } Self::Signal(signal) => poller.delete_filter(PollSignal(signal.0)), Self::Process(process) => { poller.delete_filter(unsafe { Process::new(process, ProcessOps::Exit) }) } } } } async-io-2.3.3/src/reactor/unix.rs000064400000000000000000000034031046102023000151320ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 use polling::{Event, Poller}; use std::fmt; use std::io::Result; use std::os::unix::io::{AsRawFd, BorrowedFd, RawFd}; /// The raw registration into the reactor. #[doc(hidden)] pub struct Registration { /// Raw file descriptor on Unix. /// /// # Invariant /// /// This describes a valid file descriptor that has not been `close`d. It will not be /// closed while this object is alive. raw: RawFd, } impl fmt::Debug for Registration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&self.raw, f) } } impl Registration { /// Add this file descriptor into the reactor. /// /// # Safety /// /// The provided file descriptor must be valid and not be closed while this object is alive. pub(crate) unsafe fn new(f: BorrowedFd<'_>) -> Self { Self { raw: f.as_raw_fd() } } /// Registers the object into the reactor. #[inline] pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { // SAFETY: This object's existence validates the invariants of Poller::add unsafe { poller.add(self.raw, Event::none(token)) } } /// Re-registers the object into the reactor. #[inline] pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { // SAFETY: self.raw is a valid file descriptor let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; poller.modify(fd, interest) } /// Deregisters the object from the reactor. #[inline] pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { // SAFETY: self.raw is a valid file descriptor let fd = unsafe { BorrowedFd::borrow_raw(self.raw) }; poller.delete(fd) } } async-io-2.3.3/src/reactor/windows.rs000064400000000000000000000063471046102023000156530ustar 00000000000000// SPDX-License-Identifier: MIT OR Apache-2.0 use polling::os::iocp::PollerIocpExt; use polling::{Event, PollMode, Poller}; use std::fmt; use std::io::Result; use std::os::windows::io::{ AsRawHandle, AsRawSocket, BorrowedHandle, BorrowedSocket, RawHandle, RawSocket, }; /// The raw registration into the reactor. #[doc(hidden)] pub enum Registration { /// Raw socket handle on Windows. /// /// # Invariant /// /// This describes a valid socket that has not been `close`d. It will not be /// closed while this object is alive. Socket(RawSocket), /// Waitable handle for Windows. /// /// # Invariant /// /// This describes a valid waitable handle that has not been `close`d. It will not be /// closed while this object is alive. Handle(RawHandle), } unsafe impl Send for Registration {} unsafe impl Sync for Registration {} impl fmt::Debug for Registration { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Socket(raw) => fmt::Debug::fmt(raw, f), Self::Handle(handle) => fmt::Debug::fmt(handle, f), } } } impl Registration { /// Add this file descriptor into the reactor. /// /// # Safety /// /// The provided file descriptor must be valid and not be closed while this object is alive. pub(crate) unsafe fn new(f: BorrowedSocket<'_>) -> Self { Self::Socket(f.as_raw_socket()) } /// Create a new [`Registration`] around a waitable handle. /// /// # Safety /// /// The provided handle must be valid and not be closed while this object is alive. pub(crate) unsafe fn new_waitable(f: BorrowedHandle<'_>) -> Self { Self::Handle(f.as_raw_handle()) } /// Registers the object into the reactor. #[inline] pub(crate) fn add(&self, poller: &Poller, token: usize) -> Result<()> { // SAFETY: This object's existence validates the invariants of Poller::add unsafe { match self { Self::Socket(raw) => poller.add(*raw, Event::none(token)), Self::Handle(handle) => { poller.add_waitable(*handle, Event::none(token), PollMode::Oneshot) } } } } /// Re-registers the object into the reactor. #[inline] pub(crate) fn modify(&self, poller: &Poller, interest: Event) -> Result<()> { // SAFETY: self.raw is a valid file descriptor match self { Self::Socket(raw) => { poller.modify(unsafe { BorrowedSocket::borrow_raw(*raw) }, interest) } Self::Handle(handle) => poller.modify_waitable( unsafe { BorrowedHandle::borrow_raw(*handle) }, interest, PollMode::Oneshot, ), } } /// Deregisters the object from the reactor. #[inline] pub(crate) fn delete(&self, poller: &Poller) -> Result<()> { // SAFETY: self.raw is a valid file descriptor match self { Self::Socket(raw) => poller.delete(unsafe { BorrowedSocket::borrow_raw(*raw) }), Self::Handle(handle) => { poller.remove_waitable(unsafe { BorrowedHandle::borrow_raw(*handle) }) } } } } async-io-2.3.3/src/reactor.rs000064400000000000000000000542001046102023000141500ustar 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; 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 async_lock::OnceCell; use concurrent_queue::ConcurrentQueue; use futures_lite::ready; use polling::{Event, Events, Poller}; use slab::Slab; // Choose the proper implementation of `Registration` based on the target platform. cfg_if::cfg_if! { if #[cfg(windows)] { mod windows; pub use windows::Registration; } else if #[cfg(any( target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd", target_os = "dragonfly", ))] { mod kqueue; pub use kqueue::Registration; } else if #[cfg(unix)] { mod unix; pub use unix::Registration; } else { compile_error!("unsupported platform"); } } #[cfg(not(target_os = "espidf"))] const TIMER_QUEUE_SIZE: usize = 1000; /// ESP-IDF - being an embedded OS - does not need so many timers /// and this saves ~ 20K RAM which is a lot for an MCU with RAM < 400K #[cfg(target_os = "espidf")] const TIMER_QUEUE_SIZE: usize = 100; 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/IOCP. /// /// This is where I/O is polled, producing I/O events. pub(crate) 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: OnceCell = OnceCell::new(); REACTOR.get_or_init_blocking(|| { 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(Events::new()), timers: Mutex::new(BTreeMap::new()), timer_ops: ConcurrentQueue::bounded(TIMER_QUEUE_SIZE), } }) } /// 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, raw: Registration) -> 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 { registration: raw, key, state: Default::default(), }); sources.insert(source.clone()); source }; // Register the file descriptor. if let Err(err) = source.registration.add(&self.poller, 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); source.registration.delete(&self.poller) } /// 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 span = tracing::trace_span!("process_timers"); let _enter = span.enter(); let mut timers = self.timers.lock().unwrap(); self.process_timer_ops(&mut timers); let now = Instant::now(); // Split timers into ready and pending timers. // // Careful to split just *after* `now`, so that a timer set for exactly `now` is considered // ready. let pending = timers.split_off(&(now + Duration::from_nanos(1), 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. tracing::trace!("{} 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. self.timer_ops .try_iter() .take(self.timer_ops.capacity().unwrap()) .for_each(|op| match op { TimerOp::Insert(when, id, waker) => { timers.insert((when, id), waker); } TimerOp::Remove(when, id) => { timers.remove(&(when, id)); } }); } } /// A lock on the reactor. pub(crate) struct ReactorLock<'a> { reactor: &'a Reactor, events: MutexGuard<'a, Events>, } impl ReactorLock<'_> { /// Processes new events, blocking until the first event or the timeout. pub(crate) fn react(&mut self, timeout: Option) -> io::Result<()> { let span = tracing::trace_span!("react"); let _enter = span.enter(); 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. This 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() { // Create the event that we are interested in. let event = { let mut event = Event::none(source.key); event.readable = !state[READ].is_empty(); event.writable = !state[WRITE].is_empty(); event }; // Register interest in this event. source.registration.modify(&self.reactor.poller, event)?; } } } 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. tracing::trace!("{} 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 { /// This source's registration into the reactor. registration: Registration, /// 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 { // Create the event that we are interested in. let event = { let mut event = Event::none(self.key); event.readable = !state[READ].is_empty(); event.writable = !state[WRITE].is_empty(); event }; // Register interest in it. self.registration.modify(&Reactor::get().poller, event)?; } 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, _capture: PhantomData, } } } /// 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))?; tracing::trace!(fd = ?self.0.handle.source.registration, "readable"); 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))?; tracing::trace!(fd = ?self.0.handle.source.registration, "readable_owned"); 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))?; tracing::trace!(fd = ?self.0.handle.source.registration, "writable"); 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))?; tracing::trace!(fd = ?self.0.handle.source.registration, "writable_owned"); 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, _capture: PhantomData T>, } 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, .. } = &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); *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 { // Create the event that we are interested in. let event = { let mut event = Event::none(handle.borrow().source.key); event.readable = !state[READ].is_empty(); event.writable = !state[WRITE].is_empty(); event }; // Indicate that we are interested in this event. handle .borrow() .source .registration .modify(&Reactor::get().poller, event)?; } Poll::Pending } } impl>, T> Drop for Ready { fn drop(&mut self) { // Remove our waker when dropped. if let Some(key) = self.index { let mut state = self.handle.borrow().source.state.lock().unwrap(); let wakers = &mut state[self.dir].wakers; if wakers.contains(key) { wakers.remove(key); } } } } async-io-2.3.3/tests/async.rs000064400000000000000000000316561046102023000142130ustar 00000000000000use 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(()) }) } // This test is broken for now on OpenBSD: https://github.com/rust-lang/rust/issues/116523 #[cfg(all(unix, not(target_os = "openbsd")))] #[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(()) }) } // prevent source from unregistering by trying to register it twice #[test] fn duplicate_socket_insert() -> io::Result<()> { future::block_on(async { let listener = Async::::bind(([127, 0, 0, 1], 0))?; let addr = listener.as_ref().local_addr()?; // attempt to register twice assert!(Async::new(&listener).is_err(), "fails upon second insert"); // Read and Write to confirm socket did not deregister on duplication attempt // Write to stream_w let mut stream_w = Async::::connect(addr).await?; stream_w.write(LOREM_IPSUM).await?; stream_w.get_ref().shutdown(Shutdown::Write)?; // Read from stream_r let mut stream_r = listener.accept().await?.0; let mut buffer = vec![0; LOREM_IPSUM.len()]; stream_r.read_exact(&mut buffer).await?; assert_eq!(buffer, LOREM_IPSUM); Ok(()) }) } #[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn abstract_socket() -> io::Result<()> { use std::ffi::OsStr; #[cfg(target_os = "android")] use std::os::android::net::SocketAddrExt; #[cfg(target_os = "linux")] use std::os::linux::net::SocketAddrExt; use std::os::unix::ffi::OsStrExt; use std::os::unix::net::{SocketAddr, UnixListener, UnixStream}; future::block_on(async { // Bind a listener to a socket. let path = OsStr::from_bytes(b"\0smolabstract"); let addr = SocketAddr::from_abstract_name(b"smolabstract")?; let listener = Async::new(UnixListener::bind_addr(&addr)?)?; // Future that connects to the listener. let connector = async { // Connect to the socket. let mut stream = Async::::connect(path).await?; // Write some bytes to the stream. stream.write_all(LOREM_IPSUM).await?; // Read some bytes from the stream. let mut buf = vec![0; LOREM_IPSUM.len()]; stream.read_exact(&mut buf).await?; assert_eq!(buf.as_slice(), LOREM_IPSUM); io::Result::Ok(()) }; // Future that drives the listener. let driver = async { // Wait for a new connection. let (mut stream, _) = listener.accept().await?; // Read some bytes from the stream. let mut buf = vec![0; LOREM_IPSUM.len()]; stream.read_exact(&mut buf).await?; assert_eq!(buf.as_slice(), LOREM_IPSUM); // Write some bytes to the stream. stream.write_all(LOREM_IPSUM).await?; io::Result::Ok(()) }; // Run both in parallel. future::try_zip(connector, driver).await?; Ok(()) }) } async-io-2.3.3/tests/block_on.rs000064400000000000000000000112041046102023000146470ustar 00000000000000use async_io::block_on; use std::{ future::Future, pin::Pin, task::{Context, Poll, Waker}, time::{Duration, Instant}, }; #[test] fn doesnt_poll_after_ready() { #[derive(Default)] struct Bomb { returned_ready: bool, } impl Future for Bomb { type Output = (); fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll { if self.returned_ready { panic!("Future was polled again after returning Poll::Ready"); } else { self.returned_ready = true; Poll::Ready(()) } } } block_on(Bomb::default()) } #[test] fn recursive_wakers_are_different() { struct Outer; impl Future for Outer { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let outer_waker = cx.waker(); block_on(Inner { outer_waker }); Poll::Ready(()) } } struct Inner<'a> { pub outer_waker: &'a Waker, } impl Future for Inner<'_> { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let inner_waker = cx.waker(); assert!(!inner_waker.will_wake(self.outer_waker)); Poll::Ready(()) } } block_on(Outer); } #[test] fn inner_cannot_wake_outer() { #[derive(Default)] struct Outer { elapsed: Option, } impl Future for Outer { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(elapsed) = self.elapsed { assert!(elapsed.elapsed() >= Duration::from_secs(1)); Poll::Ready(()) } else { let outer_waker = cx.waker().clone(); block_on(Inner); std::thread::spawn(|| { std::thread::sleep(Duration::from_secs(1)); outer_waker.wake(); }); self.elapsed = Some(Instant::now()); Poll::Pending } } } struct Inner; impl Future for Inner { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let inner_waker = cx.waker(); inner_waker.wake_by_ref(); Poll::Ready(()) } } block_on(Outer::default()); } #[test] fn outer_cannot_wake_inner() { struct Outer; impl Future for Outer { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let outer_waker = cx.waker(); outer_waker.wake_by_ref(); block_on(Inner::default()); Poll::Ready(()) } } #[derive(Default)] struct Inner { elapsed: Option, } impl Future for Inner { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(elapsed) = self.elapsed { assert!(elapsed.elapsed() >= Duration::from_secs(1)); Poll::Ready(()) } else { let inner_waker = cx.waker().clone(); std::thread::spawn(|| { std::thread::sleep(Duration::from_secs(1)); inner_waker.wake(); }); self.elapsed = Some(Instant::now()); Poll::Pending } } } block_on(Outer); } #[test] fn first_cannot_wake_second() { struct First; impl Future for First { type Output = (); fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { let first_waker = cx.waker(); first_waker.wake_by_ref(); Poll::Ready(()) } } #[derive(Default)] struct Second { elapsed: Option, } impl Future for Second { type Output = (); fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { if let Some(elapsed) = self.elapsed { assert!(elapsed.elapsed() >= Duration::from_secs(1)); Poll::Ready(()) } else { let second_waker = cx.waker().clone(); std::thread::spawn(|| { std::thread::sleep(Duration::from_secs(1)); second_waker.wake(); }); self.elapsed = Some(Instant::now()); Poll::Pending } } } block_on(First); block_on(Second::default()); } async-io-2.3.3/tests/issue_182.rs000064400000000000000000000012301046102023000146010ustar 00000000000000//! https://github.com/smol-rs/async-io/issues/182 use async_io::Async; use std::net::{TcpStream, ToSocketAddrs}; #[test] fn networking_initialized() { let address = match ToSocketAddrs::to_socket_addrs(&("google.com", 80)) { Ok(mut addrs) => addrs.next().unwrap(), Err(err) => { eprintln!("Got error {err} when looking up google.com, exiting test early."); return; } }; // Make sure we can access the host normally. if TcpStream::connect(address).is_err() { return; } async_io::block_on(async move { let _ = Async::::connect(address).await.unwrap(); }); } async-io-2.3.3/tests/timer.rs000064400000000000000000000051051046102023000142040ustar 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)); }); }