polling-3.4.0/.cargo_vcs_info.json0000644000000001360000000000100125040ustar { "git": { "sha1": "ac7fbcae3116974e5e134c6dcb05a5c2484e9bc5" }, "path_in_vcs": "" }polling-3.4.0/CHANGELOG.md000064400000000000000000000115551046102023000131140ustar 00000000000000# Version 3.4.0 - Add the ability to identify whether socket connection has failed. (#185) - On BSD, add the ability to wait on a process by its PID. Previously, it was only possible to wait on a process by a `Child` object. (#180) - On ESP-IDF, annotate `eventfd` initialization failures with a message indicating the source of those failures. (#186) # Version 3.3.2 - When AFD fails to initialize, the resulting error now references the underlying system error. (#174) # Version 3.3.1 - Bump `windows-sys` to v0.52.0. (#169) # Version 3.3.0 - Automatically restarts polling when `ErrorKind::Interrupted` is returned, rather than relying on the user to handle it. (#164) - Fix bad link in documentation for `Poller::wait()`. (#163) # Version 3.2.0 - The `kqueue` backend previously allowed the following operations that other backends forbid. Now these operations result in an error: (#153) - Inserting a source that was already inserted. - Modifying/deleting a source that was not already inserted. - Add support for Haiku OS. (#154) # Version 3.1.0 - Add an `Event::new()` constructor to simplify creating `Event`s. (#149) # Version 3.0.0 - Replace `libc` in all backends with the `rustix` crate (#108). - Use `tracing` instead of `log` for logging (#119). - **Breaking:** Rework the API to use I/O safety. Note that this makes several previously safe functions unsafe. (#123) - Add support for the ESP-IDF platform. (#128) - **Breaking:** Make `Event` partially opaque, and create a new `Events` struct for holding events. (#133) - Add support for running `polling` in Linux containers without `eventfd` available. (#134) - Specify the behavior when registered in multiple `Poller`s. (#136) - **Breaking:** Use `c_int` from the standard library in `polling::os::kqueue` instead of defining our own. (#143) - **Breaking:** Remove the useless `std` feature. (#147) # Version 2.8.0 - Add functionality for posting events to the IOCP. (#101) # Version 2.7.0 - Add edge/oneshot combination mode. (#96) - Update windows-sys requirement from 0.45 to 0.48. (#103) # Version 2.6.0 - Add level and edge triggered modes to the poller (#59) - Support tvOS and watchOS (#60) - Prevent large timeouts from causing panics on certain backends (#71) - For certain BSDs, use `EVFILT_USER` to wake up the poller instead of a pipe (#73) - For Solaris/illumos, use `port_send` to wake up the poller instead of a pipe (#74) - Update `windows_sys` from 0.42 to 0.45 (#80) - Expose other `kqueue` filter types (#83) - Replace the Windows backend with a hand-written version, rather than bringing in a C dependency (#88) # Version 2.5.2 - Update use of `libc::timespec` to prepare for future libc version (#55) - Update use of `libc::kevent` to prepare for future libc version (#56) - Add error message for Wepoll (#54) # Version 2.5.1 - Fix the build error with MSRV on Windows # Version 2.5.0 - Switch from `winapi` to `windows-sys` (#47) # Version 2.4.0 - Fix the build error on illumos and Solaris (#43) - Bump MSRV to 1.47 (#40) - Optimize `Poller` internal representation (#40) # Version 2.3.0 - Implement `AsRawFd` for `Poller` on most Unix systems (#39) - Implement `AsRawHandle` for `Poller` on Windows (#39) - Implement I/O safety traits on Rust 1.63+ (#39) # Version 2.2.0 - Support VxWorks, Fuchsia and other Unix systems by using poll. (#26) # Version 2.1.0 - Switch from `wepoll-sys` to `wepoll-ffi`. # Version 2.0.3 - Update `cfg-if` dependency to 1. # Version 2.0.2 - Replace manual pointer conversion with `as_ptr()` and `as_mut_ptr()`. # Version 2.0.1 - Minor docs improvements. # Version 2.0.0 - Add `Event` argument to `Poller::insert()`. - Don't put fd/socket in non-blocking mode upon insertion. - Rename `insert()`/`interest()`/`remove()` to `add()`/`modify()`/`delete()`. - Replace `wepoll-sys-stjepang` with an `wepoll-sys`. # Version 1.1.0 - Add "std" cargo feature. # Version 1.0.3 - Remove `libc` dependency on Windows. # Version 1.0.2 - Bump MSRV to 1.40.0 - Replace the `epoll_create1` hack with a cleaner solution. - Pass timeout to `epoll_wait` to support systems without `timerfd`. # Version 1.0.1 - Fix a typo in the readme. # Version 1.0.0 - Stabilize. # Version 0.1.9 - Fix compilation on x86_64-unknown-linux-gnux32 # Version 0.1.8 - Replace `log::debug!` with `log::trace!`. # Version 0.1.7 - Specify oneshot mode in epoll/wepoll at insert. # Version 0.1.6 - Add logging. # Version 0.1.5 - Fix a bug where epoll would block when the timeout is set to zero. - More tests. # Version 0.1.4 - Optimize notifications. - Fix a bug in timeouts on Windows where it would trigger too early. - Support sub-nanosecond precision on Linux/Android. # Version 0.1.3 - Improve error handling around event ports fcntl # Version 0.1.2 - Add support for event ports (illumos and Solaris) # Version 0.1.1 - Improve documentation - Fix a bug in `Event::none()`. # Version 0.1.0 - Initial version polling-3.4.0/Cargo.lock0000644000000176170000000000100104730ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "concurrent-queue" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" dependencies = [ "crossbeam-utils", ] [[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 = "easy-parallel" version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2afbb9b0aef60e4f0d2b18129b6c0dff035a6f7dbbd17c2f38c1432102ee223c" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys 0.52.0", ] [[package]] name = "fastrand" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "libc" version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "pin-project-lite" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "polling" version = "3.4.0" dependencies = [ "cfg-if", "concurrent-queue", "easy-parallel", "fastrand", "libc", "pin-project-lite", "rustix", "signal-hook", "socket2", "tracing", "windows-sys 0.52.0", ] [[package]] name = "rustix" version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys 0.52.0", ] [[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 = "socket2" version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" dependencies = [ "libc", "windows-sys 0.48.0", ] [[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 = "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.0", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" dependencies = [ "windows_aarch64_gnullvm 0.52.0", "windows_aarch64_msvc 0.52.0", "windows_i686_gnu 0.52.0", "windows_i686_msvc 0.52.0", "windows_x86_64_gnu 0.52.0", "windows_x86_64_gnullvm 0.52.0", "windows_x86_64_msvc 0.52.0", ] [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[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.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" polling-3.4.0/Cargo.toml0000644000000042020000000000100105000ustar # 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 = "polling" version = "3.4.0" authors = [ "Stjepan Glavina ", "John Nunley ", ] exclude = ["/.*"] description = "Portable interface to epoll, kqueue, event ports, and IOCP" 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/polling" [package.metadata.docs.rs] rustdoc-args = [ "--cfg", "docsrs", ] [dependencies.cfg-if] version = "1" [dependencies.tracing] version = "0.1.37" default-features = false [dev-dependencies.easy-parallel] version = "3.1.0" [dev-dependencies.fastrand] version = "2.0.0" [dev-dependencies.socket2] version = "0.5.5" [target."cfg(any(unix, target_os = \"fuchsia\", target_os = \"vxworks\"))".dependencies.rustix] version = "0.38.8" features = [ "event", "fs", "pipe", "process", "std", "time", ] default-features = false [target."cfg(unix)".dev-dependencies.libc] version = "0.2" [target."cfg(unix)".dev-dependencies.signal-hook] version = "0.3.17" [target."cfg(windows)".dependencies.concurrent-queue] version = "2.2.0" [target."cfg(windows)".dependencies.pin-project-lite] version = "0.2.9" [target."cfg(windows)".dependencies.windows-sys] version = "0.52" features = [ "Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_LibraryLoader", "Win32_System_Threading", "Win32_System_WindowsProgramming", ] polling-3.4.0/Cargo.toml.orig000064400000000000000000000027421046102023000141700ustar 00000000000000[package] name = "polling" # When publishing a new version: # - Update CHANGELOG.md # - Create "v3.x.y" git tag version = "3.4.0" authors = ["Stjepan Glavina ", "John Nunley "] edition = "2021" rust-version = "1.63" description = "Portable interface to epoll, kqueue, event ports, and IOCP" license = "Apache-2.0 OR MIT" repository = "https://github.com/smol-rs/polling" keywords = ["mio", "epoll", "kqueue", "iocp"] categories = ["asynchronous", "network-programming", "os"] exclude = ["/.*"] [package.metadata.docs.rs] rustdoc-args = ["--cfg", "docsrs"] [dependencies] cfg-if = "1" tracing = { version = "0.1.37", default-features = false } [target.'cfg(any(unix, target_os = "fuchsia", target_os = "vxworks"))'.dependencies] rustix = { version = "0.38.8", features = ["event", "fs", "pipe", "process", "std", "time"], default-features = false } [target.'cfg(windows)'.dependencies] concurrent-queue = "2.2.0" pin-project-lite = "0.2.9" [target.'cfg(windows)'.dependencies.windows-sys] version = "0.52" features = [ "Wdk_Foundation", "Wdk_Storage_FileSystem", "Win32_Foundation", "Win32_Networking_WinSock", "Win32_Security", "Win32_Storage_FileSystem", "Win32_System_IO", "Win32_System_LibraryLoader", "Win32_System_Threading", "Win32_System_WindowsProgramming", ] [dev-dependencies] easy-parallel = "3.1.0" fastrand = "2.0.0" socket2 = "0.5.5" [target.'cfg(unix)'.dev-dependencies] libc = "0.2" signal-hook = "0.3.17" polling-3.4.0/Cross.toml000064400000000000000000000003021046102023000132550ustar 00000000000000[target.arm-linux-androideabi] # Workaround https://github.com/cross-rs/cross/issues/1128 / https://github.com/rust-lang/rust/issues/103673 image = "ghcr.io/cross-rs/arm-linux-androideabi:edge" polling-3.4.0/LICENSE-APACHE000064400000000000000000000251371046102023000132300ustar 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. polling-3.4.0/LICENSE-MIT000064400000000000000000000017771046102023000127440ustar 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. polling-3.4.0/README.md000064400000000000000000000047331046102023000125620ustar 00000000000000# polling [![Build](https://github.com/smol-rs/polling/actions/workflows/ci.yml/badge.svg)]( https://github.com/smol-rs/polling/actions) [![License](https://img.shields.io/badge/license-Apache--2.0_OR_MIT-blue.svg)]( https://github.com/smol-rs/polling) [![Cargo](https://img.shields.io/crates/v/polling.svg)]( https://crates.io/crates/polling) [![Documentation](https://docs.rs/polling/badge.svg)]( https://docs.rs/polling) Portable interface to epoll, kqueue, event ports, and IOCP. Supported platforms: - [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android - [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD - [event ports](https://illumos.org/man/port_create): illumos, Solaris - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, other Unix systems - [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+) Polling is done in oneshot mode, which means interest in I/O events needs to be reset after an event is delivered if we're interested in the next event of the same kind. Only one thread can be waiting for I/O events at a time. ## Examples ```rust,no_run use polling::{Event, Poller}; use std::net::TcpListener; // Create a TCP listener. let socket = TcpListener::bind("127.0.0.1:8000")?; socket.set_nonblocking(true)?; let key = 7; // Arbitrary key identifying the socket. // Create a poller and register interest in readability on the socket. let poller = Poller::new()?; poller.add(&socket, Event::readable(key))?; // The event loop. let mut events = Vec::new(); loop { // Wait for at least one I/O event. events.clear(); poller.wait(&mut events, None)?; for ev in &events { if ev.key == key { // Perform a non-blocking accept operation. socket.accept()?; // Set interest in the next readability event. poller.modify(&socket, Event::readable(key))?; } } } ``` ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/license/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. polling-3.4.0/examples/tcp_client.rs000064400000000000000000000015741046102023000156130ustar 00000000000000use std::{io, net}; use polling::Event; use socket2::Type; fn main() -> io::Result<()> { let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; let poller = polling::Poller::new()?; unsafe { poller.add(&socket, Event::new(0, true, true))?; } let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); socket.set_nonblocking(true)?; let _ = socket.connect(&addr.into()); let mut events = polling::Events::new(); events.clear(); poller.wait(&mut events, None)?; let event = events.iter().next(); let event = match event { Some(event) => event, None => { println!("no event"); return Ok(()); } }; println!("event: {:?}", event); if event.is_connect_failed().unwrap_or_default() { println!("connect failed"); } Ok(()) } polling-3.4.0/examples/two-listeners.rs000064400000000000000000000022131046102023000162750ustar 00000000000000use std::io; use std::net::TcpListener; use polling::{Event, Events, Poller}; fn main() -> io::Result<()> { let l1 = TcpListener::bind("127.0.0.1:8001")?; let l2 = TcpListener::bind("127.0.0.1:8002")?; l1.set_nonblocking(true)?; l2.set_nonblocking(true)?; let poller = Poller::new()?; unsafe { poller.add(&l1, Event::readable(1))?; poller.add(&l2, Event::readable(2))?; } println!("You can connect to the server using `nc`:"); println!(" $ nc 127.0.0.1 8001"); println!(" $ nc 127.0.0.1 8002"); let mut events = Events::new(); loop { events.clear(); poller.wait(&mut events, None)?; for ev in events.iter() { match ev.key { 1 => { println!("Accept on l1"); l1.accept()?; poller.modify(&l1, Event::readable(1))?; } 2 => { println!("Accept on l2"); l2.accept()?; poller.modify(&l2, Event::readable(2))?; } _ => unreachable!(), } } } } polling-3.4.0/examples/wait-signal.rs000064400000000000000000000034551046102023000157060ustar 00000000000000#[cfg(all( 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", ), not(polling_test_poll_backend), ))] mod example { use polling::os::kqueue::{PollerKqueueExt, Signal}; use polling::{Events, PollMode, Poller}; pub(super) fn main2() { // Create a poller. let poller = Poller::new().unwrap(); // Register SIGINT in the poller. let sigint = Signal(rustix::process::Signal::Int as _); poller.add_filter(sigint, 1, PollMode::Oneshot).unwrap(); let mut events = Events::new(); println!("Press Ctrl+C to exit..."); // Wait for events. poller.wait(&mut events, None).unwrap(); // Process events. let ev = events.iter().next().unwrap(); match ev.key { 1 => { println!("SIGINT received"); } _ => unreachable!(), } } } #[cfg(all( 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", ), not(polling_test_poll_backend), ))] fn main() { example::main2(); } #[cfg(not(all( 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", ), not(polling_test_poll_backend), )))] fn main() { eprintln!("This example is only supported on kqueue-based platforms."); } polling-3.4.0/src/epoll.rs000064400000000000000000000320461046102023000135510ustar 00000000000000//! Bindings to epoll (Linux, Android). use std::io; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::time::Duration; use rustix::event::{epoll, eventfd, EventfdFlags}; use rustix::fd::OwnedFd; use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags}; use rustix::pipe::{pipe, pipe_with, PipeFlags}; use rustix::time::{ timerfd_create, timerfd_settime, Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags, Timespec, }; use crate::{Event, PollMode}; /// Interface to epoll. #[derive(Debug)] pub struct Poller { /// File descriptor for the epoll instance. epoll_fd: OwnedFd, /// Notifier used to wake up epoll. notifier: Notifier, /// File descriptor for the timerfd that produces timeouts. timer_fd: Option, } impl Poller { /// Creates a new poller. pub fn new() -> io::Result { // Create an epoll instance. // // Use `epoll_create1` with `EPOLL_CLOEXEC`. let epoll_fd = epoll::create(epoll::CreateFlags::CLOEXEC)?; // Set up notifier and timerfd. let notifier = Notifier::new()?; let timer_fd = timerfd_create( TimerfdClockId::Monotonic, TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK, ) .ok(); let poller = Poller { epoll_fd, notifier, timer_fd, }; unsafe { if let Some(ref timer_fd) = poller.timer_fd { poller.add( timer_fd.as_raw_fd(), Event::none(crate::NOTIFY_KEY), PollMode::Oneshot, )?; } poller.add( poller.notifier.as_fd().as_raw_fd(), Event::readable(crate::NOTIFY_KEY), PollMode::Oneshot, )?; } tracing::trace!( epoll_fd = ?poller.epoll_fd.as_raw_fd(), notifier = ?poller.notifier, timer_fd = ?poller.timer_fd, "new", ); Ok(poller) } /// Whether this poller supports level-triggered events. pub fn supports_level(&self) -> bool { true } /// Whether the poller supports edge-triggered events. pub fn supports_edge(&self) -> bool { true } /// Adds a new file descriptor. /// /// # Safety /// /// The `fd` must be a valid file descriptor. The usual condition of remaining registered in /// the `Poller` doesn't apply to `epoll`. pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { let span = tracing::trace_span!( "add", epoll_fd = ?self.epoll_fd.as_raw_fd(), ?fd, ?ev, ); let _enter = span.enter(); epoll::add( &self.epoll_fd, unsafe { rustix::fd::BorrowedFd::borrow_raw(fd) }, epoll::EventData::new_u64(ev.key as u64), epoll_flags(&ev, mode) | ev.extra.flags, )?; Ok(()) } /// Modifies an existing file descriptor. pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { let span = tracing::trace_span!( "modify", epoll_fd = ?self.epoll_fd.as_raw_fd(), ?fd, ?ev, ); let _enter = span.enter(); epoll::modify( &self.epoll_fd, fd, epoll::EventData::new_u64(ev.key as u64), epoll_flags(&ev, mode) | ev.extra.flags, )?; Ok(()) } /// Deletes a file descriptor. pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { let span = tracing::trace_span!( "delete", epoll_fd = ?self.epoll_fd.as_raw_fd(), ?fd, ); let _enter = span.enter(); epoll::delete(&self.epoll_fd, fd)?; Ok(()) } /// Waits for I/O events with an optional timeout. #[allow(clippy::needless_update)] pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result<()> { let span = tracing::trace_span!( "wait", epoll_fd = ?self.epoll_fd.as_raw_fd(), ?timeout, ); let _enter = span.enter(); if let Some(ref timer_fd) = self.timer_fd { // Configure the timeout using timerfd. let new_val = Itimerspec { it_interval: TS_ZERO, it_value: match timeout { None => TS_ZERO, Some(t) => { let mut ts = TS_ZERO; ts.tv_sec = t.as_secs() as _; ts.tv_nsec = t.subsec_nanos() as _; ts } }, ..unsafe { std::mem::zeroed() } }; timerfd_settime(timer_fd, TimerfdTimerFlags::empty(), &new_val)?; // Set interest in timerfd. self.modify( timer_fd.as_fd(), Event::readable(crate::NOTIFY_KEY), PollMode::Oneshot, )?; } // Timeout in milliseconds for epoll. let timeout_ms = match (&self.timer_fd, timeout) { (_, Some(t)) if t == Duration::from_secs(0) => 0, (None, Some(t)) => { // Round up to a whole millisecond. let mut ms = t.as_millis().try_into().unwrap_or(std::i32::MAX); if Duration::from_millis(ms as u64) < t { ms = ms.saturating_add(1); } ms } _ => -1, }; // Wait for I/O events. epoll::wait(&self.epoll_fd, &mut events.list, timeout_ms)?; tracing::trace!( epoll_fd = ?self.epoll_fd.as_raw_fd(), res = ?events.list.len(), "new events", ); // Clear the notification (if received) and re-register interest in it. self.notifier.clear(); self.modify( self.notifier.as_fd(), Event::readable(crate::NOTIFY_KEY), PollMode::Oneshot, )?; Ok(()) } /// Sends a notification to wake up the current or next `wait()` call. pub fn notify(&self) -> io::Result<()> { let span = tracing::trace_span!( "notify", epoll_fd = ?self.epoll_fd.as_raw_fd(), notifier = ?self.notifier, ); let _enter = span.enter(); self.notifier.notify(); Ok(()) } } impl AsRawFd for Poller { fn as_raw_fd(&self) -> RawFd { self.epoll_fd.as_raw_fd() } } impl AsFd for Poller { fn as_fd(&self) -> BorrowedFd<'_> { self.epoll_fd.as_fd() } } impl Drop for Poller { fn drop(&mut self) { let span = tracing::trace_span!( "drop", epoll_fd = ?self.epoll_fd.as_raw_fd(), notifier = ?self.notifier, timer_fd = ?self.timer_fd ); let _enter = span.enter(); if let Some(timer_fd) = self.timer_fd.take() { let _ = self.delete(timer_fd.as_fd()); } let _ = self.delete(self.notifier.as_fd()); } } /// `timespec` value that equals zero. const TS_ZERO: Timespec = unsafe { std::mem::transmute([0u8; std::mem::size_of::()]) }; /// Get the EPOLL flags for the interest. fn epoll_flags(interest: &Event, mode: PollMode) -> epoll::EventFlags { let mut flags = match mode { PollMode::Oneshot => epoll::EventFlags::ONESHOT, PollMode::Level => epoll::EventFlags::empty(), PollMode::Edge => epoll::EventFlags::ET, PollMode::EdgeOneshot => epoll::EventFlags::ET | epoll::EventFlags::ONESHOT, }; if interest.readable { flags |= read_flags(); } if interest.writable { flags |= write_flags(); } flags } /// Epoll flags for all possible readability events. fn read_flags() -> epoll::EventFlags { use epoll::EventFlags as Epoll; Epoll::IN | Epoll::HUP | Epoll::ERR | Epoll::PRI } /// Epoll flags for all possible writability events. fn write_flags() -> epoll::EventFlags { use epoll::EventFlags as Epoll; Epoll::OUT | Epoll::HUP | Epoll::ERR } /// A list of reported I/O events. pub struct Events { list: epoll::EventVec, } unsafe impl Send for Events {} impl Events { /// Creates an empty list. pub fn with_capacity(cap: usize) -> Events { Events { list: epoll::EventVec::with_capacity(cap), } } /// Iterates over I/O events. pub fn iter(&self) -> impl Iterator + '_ { self.list.iter().map(|ev| { let flags = ev.flags; Event { key: ev.data.u64() as usize, readable: flags.intersects(read_flags()), writable: flags.intersects(write_flags()), extra: EventExtra { flags }, } }) } /// Clear the list. pub fn clear(&mut self) { self.list.clear(); } /// Get the capacity of the list. pub fn capacity(&self) -> usize { self.list.capacity() } } /// Extra information about this event. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct EventExtra { flags: epoll::EventFlags, } impl EventExtra { /// Create an empty version of the data. #[inline] pub const fn empty() -> EventExtra { EventExtra { flags: epoll::EventFlags::empty(), } } /// Add the interrupt flag to this event. #[inline] pub fn set_hup(&mut self, active: bool) { self.flags.set(epoll::EventFlags::HUP, active); } /// Add the priority flag to this event. #[inline] pub fn set_pri(&mut self, active: bool) { self.flags.set(epoll::EventFlags::PRI, active); } /// Tell if the interrupt flag is set. #[inline] pub fn is_hup(&self) -> bool { self.flags.contains(epoll::EventFlags::HUP) } /// Tell if the priority flag is set. #[inline] pub fn is_pri(&self) -> bool { self.flags.contains(epoll::EventFlags::PRI) } #[inline] pub fn is_connect_failed(&self) -> Option { Some( self.flags.contains(epoll::EventFlags::ERR) || self.flags.contains(epoll::EventFlags::HUP), ) } } /// The notifier for Linux. /// /// Certain container runtimes do not expose eventfd to the client, as it relies on the host and /// can be used to "escape" the container under certain conditions. Gramine is the prime example, /// see [here](gramine). In this case, fall back to using a pipe. /// /// [gramine]: https://gramine.readthedocs.io/en/stable/manifest-syntax.html#allowing-eventfd #[derive(Debug)] enum Notifier { /// The primary notifier, using eventfd. EventFd(OwnedFd), /// The fallback notifier, using a pipe. Pipe { /// The read end of the pipe. read_pipe: OwnedFd, /// The write end of the pipe. write_pipe: OwnedFd, }, } impl Notifier { /// Create a new notifier. fn new() -> io::Result { // Skip eventfd for testing if necessary. if !cfg!(polling_test_epoll_pipe) { // Try to create an eventfd. match eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK) { Ok(fd) => { tracing::trace!("created eventfd for notifier"); return Ok(Notifier::EventFd(fd)); } Err(err) => { tracing::warn!( "eventfd() failed with error ({}), falling back to pipe", err ); } } } let (read, write) = pipe_with(PipeFlags::CLOEXEC).or_else(|_| { let (read, write) = pipe()?; fcntl_setfd(&read, fcntl_getfd(&read)? | FdFlags::CLOEXEC)?; fcntl_setfd(&write, fcntl_getfd(&write)? | FdFlags::CLOEXEC)?; io::Result::Ok((read, write)) })?; fcntl_setfl(&read, fcntl_getfl(&read)? | OFlags::NONBLOCK)?; Ok(Notifier::Pipe { read_pipe: read, write_pipe: write, }) } /// The file descriptor to register in the poller. fn as_fd(&self) -> BorrowedFd<'_> { match self { Notifier::EventFd(fd) => fd.as_fd(), Notifier::Pipe { read_pipe: read, .. } => read.as_fd(), } } /// Notify the poller. fn notify(&self) { match self { Self::EventFd(fd) => { let buf: [u8; 8] = 1u64.to_ne_bytes(); let _ = write(fd, &buf); } Self::Pipe { write_pipe, .. } => { write(write_pipe, &[0; 1]).ok(); } } } /// Clear the notification. fn clear(&self) { match self { Self::EventFd(fd) => { let mut buf = [0u8; 8]; let _ = read(fd, &mut buf); } Self::Pipe { read_pipe, .. } => while read(read_pipe, &mut [0u8; 1024]).is_ok() {}, } } } polling-3.4.0/src/iocp/afd.rs000064400000000000000000000447301046102023000141250ustar 00000000000000//! Safe wrapper around \Device\Afd use super::port::{Completion, CompletionHandle}; use std::cell::UnsafeCell; use std::fmt; use std::io; use std::marker::{PhantomData, PhantomPinned}; use std::mem::{self, size_of, transmute, MaybeUninit}; use std::ops; use std::os::windows::prelude::{AsRawHandle, RawHandle, RawSocket}; use std::pin::Pin; use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Once; use windows_sys::Wdk::Foundation::OBJECT_ATTRIBUTES; use windows_sys::Wdk::Storage::FileSystem::FILE_OPEN; use windows_sys::Win32::Foundation::{ CloseHandle, HANDLE, HMODULE, NTSTATUS, STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS, UNICODE_STRING, }; use windows_sys::Win32::Networking::WinSock::{ WSAIoctl, SIO_BASE_HANDLE, SIO_BSP_HANDLE_POLL, SOCKET_ERROR, }; use windows_sys::Win32::Storage::FileSystem::{FILE_SHARE_READ, FILE_SHARE_WRITE, SYNCHRONIZE}; use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress}; use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; #[derive(Default)] #[repr(C)] pub(super) struct AfdPollInfo { /// The timeout for this poll. timeout: i64, /// The number of handles being polled. handle_count: u32, /// Whether or not this poll is exclusive for this handle. exclusive: u32, /// The handles to poll. handles: [AfdPollHandleInfo; 1], } #[derive(Default)] #[repr(C)] struct AfdPollHandleInfo { /// The handle to poll. handle: HANDLE, /// The events to poll for. events: AfdPollMask, /// The status of the poll. status: NTSTATUS, } impl AfdPollInfo { pub(super) fn handle_count(&self) -> u32 { self.handle_count } pub(super) fn events(&self) -> AfdPollMask { self.handles[0].events } } #[derive(Default, Copy, Clone, PartialEq, Eq)] #[repr(transparent)] pub(super) struct AfdPollMask(u32); impl AfdPollMask { pub(crate) const RECEIVE: AfdPollMask = AfdPollMask(0x001); pub(crate) const RECEIVE_EXPEDITED: AfdPollMask = AfdPollMask(0x002); pub(crate) const SEND: AfdPollMask = AfdPollMask(0x004); pub(crate) const DISCONNECT: AfdPollMask = AfdPollMask(0x008); pub(crate) const ABORT: AfdPollMask = AfdPollMask(0x010); pub(crate) const LOCAL_CLOSE: AfdPollMask = AfdPollMask(0x020); pub(crate) const ACCEPT: AfdPollMask = AfdPollMask(0x080); pub(crate) const CONNECT_FAIL: AfdPollMask = AfdPollMask(0x100); /// Creates an empty mask. pub(crate) const fn empty() -> AfdPollMask { AfdPollMask(0) } /// Checks if this mask contains the other mask. pub(crate) fn intersects(self, other: AfdPollMask) -> bool { (self.0 & other.0) != 0 } /// Sets a flag. pub(crate) fn set(&mut self, other: AfdPollMask, value: bool) { if value { *self |= other; } else { self.0 &= !other.0; } } } impl fmt::Debug for AfdPollMask { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { const FLAGS: &[(&str, AfdPollMask)] = &[ ("RECEIVE", AfdPollMask::RECEIVE), ("RECEIVE_EXPEDITED", AfdPollMask::RECEIVE_EXPEDITED), ("SEND", AfdPollMask::SEND), ("DISCONNECT", AfdPollMask::DISCONNECT), ("ABORT", AfdPollMask::ABORT), ("LOCAL_CLOSE", AfdPollMask::LOCAL_CLOSE), ("ACCEPT", AfdPollMask::ACCEPT), ("CONNECT_FAIL", AfdPollMask::CONNECT_FAIL), ]; let mut first = true; for (name, value) in FLAGS { if self.intersects(*value) { if !first { write!(f, " | ")?; } first = false; write!(f, "{}", name)?; } } Ok(()) } } impl ops::BitOr for AfdPollMask { type Output = Self; fn bitor(self, rhs: Self) -> Self { AfdPollMask(self.0 | rhs.0) } } impl ops::BitOrAssign for AfdPollMask { fn bitor_assign(&mut self, rhs: Self) { self.0 |= rhs.0; } } impl ops::BitAnd for AfdPollMask { type Output = Self; fn bitand(self, rhs: Self) -> Self { AfdPollMask(self.0 & rhs.0) } } impl ops::BitAndAssign for AfdPollMask { fn bitand_assign(&mut self, rhs: Self) { self.0 &= rhs.0; } } pub(super) trait HasAfdInfo { fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell>; } macro_rules! define_ntdll_import { ( $( $(#[$attr:meta])* fn $name:ident($($arg:ident: $arg_ty:ty),*) -> $ret:ty; )* ) => { /// Imported functions from ntdll.dll. #[allow(non_snake_case)] pub(super) struct NtdllImports { $( $(#[$attr])* $name: unsafe extern "system" fn($($arg_ty),*) -> $ret, )* } #[allow(non_snake_case)] impl NtdllImports { unsafe fn load(ntdll: HMODULE) -> io::Result { $( let $name = { const NAME: &str = concat!(stringify!($name), "\0"); let addr = GetProcAddress(ntdll, NAME.as_ptr() as *const _); let addr = match addr { Some(addr) => addr, None => { tracing::error!("Failed to load ntdll function {}", NAME); return Err(io::Error::last_os_error()); }, }; transmute::<_, unsafe extern "system" fn($($arg_ty),*) -> $ret>(addr) }; )* Ok(Self { $( $name, )* }) } $( $(#[$attr])* unsafe fn $name(&self, $($arg: $arg_ty),*) -> $ret { (self.$name)($($arg),*) } )* } }; } define_ntdll_import! { /// Cancels an ongoing I/O operation. fn NtCancelIoFileEx( FileHandle: HANDLE, IoRequestToCancel: *mut IO_STATUS_BLOCK, IoStatusBlock: *mut IO_STATUS_BLOCK ) -> NTSTATUS; /// Opens or creates a file handle. #[allow(clippy::too_many_arguments)] fn NtCreateFile( FileHandle: *mut HANDLE, DesiredAccess: u32, ObjectAttributes: *mut OBJECT_ATTRIBUTES, IoStatusBlock: *mut IO_STATUS_BLOCK, AllocationSize: *mut i64, FileAttributes: u32, ShareAccess: u32, CreateDisposition: u32, CreateOptions: u32, EaBuffer: *mut (), EaLength: u32 ) -> NTSTATUS; /// Runs an I/O control on a file handle. /// /// Practically equivalent to `ioctl`. #[allow(clippy::too_many_arguments)] fn NtDeviceIoControlFile( FileHandle: HANDLE, Event: HANDLE, ApcRoutine: *mut (), ApcContext: *mut (), IoStatusBlock: *mut IO_STATUS_BLOCK, IoControlCode: u32, InputBuffer: *mut (), InputBufferLength: u32, OutputBuffer: *mut (), OutputBufferLength: u32 ) -> NTSTATUS; /// Converts `NTSTATUS` to a DOS error code. fn RtlNtStatusToDosError( Status: NTSTATUS ) -> u32; } impl NtdllImports { fn get() -> io::Result<&'static Self> { macro_rules! s { ($e:expr) => {{ $e as u16 }}; } // ntdll.dll static NTDLL_NAME: &[u16] = &[ s!('n'), s!('t'), s!('d'), s!('l'), s!('l'), s!('.'), s!('d'), s!('l'), s!('l'), s!('\0'), ]; static NTDLL_IMPORTS: OnceCell> = OnceCell::new(); NTDLL_IMPORTS .get_or_init(|| unsafe { let ntdll = GetModuleHandleW(NTDLL_NAME.as_ptr() as *const _); if ntdll == 0 { tracing::error!("Failed to load ntdll.dll"); return Err(io::Error::last_os_error()); } NtdllImports::load(ntdll) }) .as_ref() .map_err(|e| io::Error::from(e.kind())) } pub(super) fn force_load() -> io::Result<()> { Self::get()?; Ok(()) } } /// The handle to the AFD device. pub(super) struct Afd { /// The handle to the AFD device. handle: HANDLE, /// We own `T`. _marker: PhantomData, } impl fmt::Debug for Afd { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct WriteAsHex(HANDLE); impl fmt::Debug for WriteAsHex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:010x}", self.0) } } f.debug_struct("Afd") .field("handle", &WriteAsHex(self.handle)) .finish() } } impl Drop for Afd { fn drop(&mut self) { unsafe { CloseHandle(self.handle); } } } impl AsRawHandle for Afd { fn as_raw_handle(&self) -> RawHandle { self.handle as _ } } impl Afd where T::Completion: AsIoStatusBlock + HasAfdInfo, { /// Create a new AFD device. pub(super) fn new() -> io::Result { macro_rules! s { ($e:expr) => { ($e) as u16 }; } /// \Device\Afd\Smol const AFD_NAME: &[u16] = &[ s!('\\'), s!('D'), s!('e'), s!('v'), s!('i'), s!('c'), s!('e'), s!('\\'), s!('A'), s!('f'), s!('d'), s!('\\'), s!('S'), s!('m'), s!('o'), s!('l'), s!('\0'), ]; // Set up device attributes. let mut device_name = UNICODE_STRING { Length: mem::size_of_val(AFD_NAME) as u16, MaximumLength: mem::size_of_val(AFD_NAME) as u16, Buffer: AFD_NAME.as_ptr() as *mut _, }; let mut device_attributes = OBJECT_ATTRIBUTES { Length: size_of::() as u32, RootDirectory: 0, ObjectName: &mut device_name, Attributes: 0, SecurityDescriptor: ptr::null_mut(), SecurityQualityOfService: ptr::null_mut(), }; let mut handle = MaybeUninit::::uninit(); let mut iosb = MaybeUninit::::zeroed(); let ntdll = NtdllImports::get()?; let result = unsafe { ntdll.NtCreateFile( handle.as_mut_ptr(), SYNCHRONIZE, &mut device_attributes, iosb.as_mut_ptr(), ptr::null_mut(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, FILE_OPEN, 0, ptr::null_mut(), 0, ) }; if result != STATUS_SUCCESS { let real_code = unsafe { ntdll.RtlNtStatusToDosError(result) }; return Err(io::Error::from_raw_os_error(real_code as i32)); } let handle = unsafe { handle.assume_init() }; Ok(Self { handle, _marker: PhantomData, }) } /// Begin polling with the provided handle. pub(super) fn poll( &self, packet: T, base_socket: RawSocket, afd_events: AfdPollMask, ) -> io::Result<()> { const IOCTL_AFD_POLL: u32 = 0x00012024; // Lock the packet. if !packet.get().try_lock() { return Err(io::Error::new( io::ErrorKind::WouldBlock, "packet is already in use", )); } // Set up the AFD poll info. let poll_info = unsafe { let poll_info = Pin::into_inner_unchecked(packet.get().afd_info()).get(); // Initialize the AFD poll info. (*poll_info).exclusive = false.into(); (*poll_info).handle_count = 1; (*poll_info).timeout = std::i64::MAX; (*poll_info).handles[0].handle = base_socket as HANDLE; (*poll_info).handles[0].status = 0; (*poll_info).handles[0].events = afd_events; poll_info }; let iosb = T::into_ptr(packet).cast::(); // Set Status to pending unsafe { (*iosb).Anonymous.Status = STATUS_PENDING; } let ntdll = NtdllImports::get()?; let result = unsafe { ntdll.NtDeviceIoControlFile( self.handle, 0, ptr::null_mut(), iosb.cast(), iosb.cast(), IOCTL_AFD_POLL, poll_info.cast(), size_of::() as u32, poll_info.cast(), size_of::() as u32, ) }; match result { STATUS_SUCCESS => Ok(()), STATUS_PENDING => Err(io::ErrorKind::WouldBlock.into()), status => { let real_code = unsafe { ntdll.RtlNtStatusToDosError(status) }; Err(io::Error::from_raw_os_error(real_code as i32)) } } } /// Cancel an ongoing poll operation. /// /// # Safety /// /// The poll operation must currently be in progress for this AFD. pub(super) unsafe fn cancel(&self, packet: &T) -> io::Result<()> { let ntdll = NtdllImports::get()?; let result = { // First, check if the packet is still in use. let iosb = packet.as_ptr().cast::(); if (*iosb).Anonymous.Status != STATUS_PENDING { return Ok(()); } // Cancel the packet. let mut cancel_iosb = MaybeUninit::::zeroed(); ntdll.NtCancelIoFileEx(self.handle, iosb, cancel_iosb.as_mut_ptr()) }; if result == STATUS_SUCCESS || result == STATUS_NOT_FOUND { Ok(()) } else { let real_code = ntdll.RtlNtStatusToDosError(result); Err(io::Error::from_raw_os_error(real_code as i32)) } } } /// A one-time initialization cell. struct OnceCell { /// The value. value: UnsafeCell>, /// The one-time initialization. once: Once, } unsafe impl Send for OnceCell {} unsafe impl Sync for OnceCell {} impl OnceCell { /// Creates a new `OnceCell`. pub const fn new() -> Self { OnceCell { value: UnsafeCell::new(MaybeUninit::uninit()), once: Once::new(), } } /// Gets the value or initializes it. pub fn get_or_init(&self, f: F) -> &T where F: FnOnce() -> T, { self.once.call_once(|| unsafe { let value = f(); *self.value.get() = MaybeUninit::new(value); }); unsafe { &*self.value.get().cast() } } } pin_project_lite::pin_project! { /// An I/O status block paired with some auxillary data. #[repr(C)] pub(super) struct IoStatusBlock { // The I/O status block. iosb: UnsafeCell, // Whether or not the block is in use. in_use: AtomicBool, // The auxillary data. #[pin] data: T, // This block is not allowed to move. #[pin] _marker: PhantomPinned, } } impl fmt::Debug for IoStatusBlock { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("IoStatusBlock") .field("iosb", &"..") .field("in_use", &self.in_use) .field("data", &self.data) .finish() } } unsafe impl Send for IoStatusBlock {} unsafe impl Sync for IoStatusBlock {} impl From for IoStatusBlock { fn from(data: T) -> Self { Self { iosb: UnsafeCell::new(unsafe { std::mem::zeroed() }), in_use: AtomicBool::new(false), data, _marker: PhantomPinned, } } } impl IoStatusBlock { pub(super) fn iosb(self: Pin<&Self>) -> &UnsafeCell { self.project_ref().iosb } pub(super) fn data(self: Pin<&Self>) -> Pin<&T> { self.project_ref().data } } impl HasAfdInfo for IoStatusBlock { fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell> { self.project_ref().data.afd_info() } } /// Can be transmuted to an I/O status block. /// /// # Safety /// /// A pointer to `T` must be able to be converted to a pointer to `IO_STATUS_BLOCK` /// without any issues. pub(super) unsafe trait AsIoStatusBlock {} unsafe impl AsIoStatusBlock for IoStatusBlock {} unsafe impl Completion for IoStatusBlock { fn try_lock(self: Pin<&Self>) -> bool { !self.in_use.swap(true, Ordering::SeqCst) } unsafe fn unlock(self: Pin<&Self>) { self.in_use.store(false, Ordering::SeqCst); } } /// Get the base socket associated with a socket. pub(super) fn base_socket(sock: RawSocket) -> io::Result { // First, try the SIO_BASE_HANDLE ioctl. let result = unsafe { try_socket_ioctl(sock, SIO_BASE_HANDLE) }; match result { Ok(sock) => return Ok(sock), Err(e) if e.kind() == io::ErrorKind::InvalidInput => return Err(e), Err(_) => {} } // Some poorly coded LSPs may not handle SIO_BASE_HANDLE properly, but in some cases may // handle SIO_BSP_HANDLE_POLL better. Try that. let result = unsafe { try_socket_ioctl(sock, SIO_BSP_HANDLE_POLL)? }; if result == sock { return Err(io::Error::from(io::ErrorKind::InvalidInput)); } // Try `SIO_BASE_HANDLE` again, in case the LSP fixed itself. unsafe { try_socket_ioctl(result, SIO_BASE_HANDLE) } } /// Run an IOCTL on a socket and return a socket. /// /// # Safety /// /// The `ioctl` parameter must be a valid I/O control that returns a valid socket. unsafe fn try_socket_ioctl(sock: RawSocket, ioctl: u32) -> io::Result { let mut out = MaybeUninit::::uninit(); let mut bytes = 0u32; let result = WSAIoctl( sock as _, ioctl, ptr::null_mut(), 0, out.as_mut_ptr().cast(), size_of::() as u32, &mut bytes, ptr::null_mut(), None, ); if result == SOCKET_ERROR { return Err(io::Error::last_os_error()); } Ok(out.assume_init()) } polling-3.4.0/src/iocp/mod.rs000064400000000000000000001265501046102023000141530ustar 00000000000000//! Bindings to Windows I/O Completion Ports. //! //! I/O Completion Ports is a completion-based API rather than a polling-based API, like //! epoll or kqueue. Therefore, we have to adapt the IOCP API to the crate's API. //! //! WinSock is powered by the Auxillary Function Driver (AFD) subsystem, which can be //! accessed directly by using unstable `ntdll` functions. AFD exposes features that are not //! available through the normal WinSock interface, such as IOCTL_AFD_POLL. This function is //! similar to the exposed `WSAPoll` method. However, once the targeted socket is "ready", //! a completion packet is queued to an I/O completion port. //! //! We take advantage of IOCTL_AFD_POLL to "translate" this crate's polling-based API //! to the one Windows expects. When a device is added to the `Poller`, an IOCTL_AFD_POLL //! operation is started and queued to the IOCP. To modify a currently registered device //! (e.g. with `modify()` or `delete()`), the ongoing POLL is cancelled and then restarted //! with new parameters. Whn the POLL eventually completes, the packet is posted to the IOCP. //! From here it's a simple matter of using `GetQueuedCompletionStatusEx` to read the packets //! from the IOCP and react accordingly. Notifying the poller is trivial, because we can //! simply post a packet to the IOCP to wake it up. //! //! The main disadvantage of this strategy is that it relies on unstable Windows APIs. //! However, as `libuv` (the backing I/O library for Node.JS) relies on the same unstable //! AFD strategy, it is unlikely to be broken without plenty of advanced warning. //! //! Previously, this crate used the `wepoll` library for polling. `wepoll` uses a similar //! AFD-based strategy for polling. mod afd; mod port; use afd::{base_socket, Afd, AfdPollInfo, AfdPollMask, HasAfdInfo, IoStatusBlock}; use port::{IoCompletionPort, OverlappedEntry}; use windows_sys::Win32::Foundation::{ BOOLEAN, ERROR_INVALID_HANDLE, ERROR_IO_PENDING, STATUS_CANCELLED, }; use windows_sys::Win32::System::Threading::{ RegisterWaitForSingleObject, UnregisterWait, INFINITE, WT_EXECUTELONGFUNCTION, WT_EXECUTEONLYONCE, }; use crate::{Event, PollMode}; use concurrent_queue::ConcurrentQueue; use pin_project_lite::pin_project; use std::cell::UnsafeCell; use std::collections::hash_map::{Entry, HashMap}; use std::ffi::c_void; use std::fmt; use std::io; use std::marker::PhantomPinned; use std::mem::{forget, MaybeUninit}; use std::os::windows::io::{ AsHandle, AsRawHandle, AsRawSocket, BorrowedHandle, BorrowedSocket, RawHandle, RawSocket, }; use std::pin::Pin; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak}; use std::time::{Duration, Instant}; /// Macro to lock and ignore lock poisoning. macro_rules! lock { ($lock_result:expr) => {{ $lock_result.unwrap_or_else(|e| e.into_inner()) }}; } /// Interface to I/O completion ports. #[derive(Debug)] pub(super) struct Poller { /// The I/O completion port. port: Arc>, /// List of currently active AFD instances. /// /// AFD acts as the actual source of the socket events. It's essentially running `WSAPoll` on /// the sockets and then posting the events to the IOCP. /// /// AFD instances can be keyed to an unlimited number of sockets. However, each AFD instance /// polls their sockets linearly. Therefore, it is best to limit the number of sockets each AFD /// instance is responsible for. The limit of 32 is chosen because that's what `wepoll` uses. /// /// Weak references are kept here so that the AFD handle is automatically dropped when the last /// associated socket is dropped. afd: Mutex>>>, /// The state of the sources registered with this poller. /// /// Each source is keyed by its raw socket ID. sources: RwLock>, /// The state of the waitable handles registered with this poller. waitables: RwLock>, /// Sockets with pending updates. /// /// This list contains packets with sockets that need to have their AFD state adjusted by /// calling the `update()` function on them. It's best to queue up packets as they need to /// be updated and then run all of the updates before we start waiting on the IOCP, rather than /// updating them as we come. If we're waiting on the IOCP updates should be run immediately. pending_updates: ConcurrentQueue, /// Are we currently polling? /// /// This indicates whether or not we are blocking on the IOCP, and is used to determine /// whether pending updates should be run immediately or queued. polling: AtomicBool, /// The packet used to notify the poller. /// /// This is a special-case packet that is used to wake up the poller when it is waiting. notifier: Packet, } unsafe impl Send for Poller {} unsafe impl Sync for Poller {} impl Poller { /// Creates a new poller. pub(super) fn new() -> io::Result { // Make sure AFD is able to be used. if let Err(e) = afd::NtdllImports::force_load() { return Err(io::Error::new( io::ErrorKind::Unsupported, AfdError::new("failed to initialize unstable Windows functions", e), )); } // Create and destroy a single AFD to test if we support it. Afd::::new().map_err(|e| { io::Error::new( io::ErrorKind::Unsupported, AfdError::new("failed to initialize \\Device\\Afd", e), ) })?; let port = IoCompletionPort::new(0)?; tracing::trace!(handle = ?port, "new"); Ok(Poller { port: Arc::new(port), afd: Mutex::new(vec![]), sources: RwLock::new(HashMap::new()), waitables: RwLock::new(HashMap::new()), pending_updates: ConcurrentQueue::bounded(1024), polling: AtomicBool::new(false), notifier: Arc::pin( PacketInner::Wakeup { _pinned: PhantomPinned, } .into(), ), }) } /// Whether this poller supports level-triggered events. pub(super) fn supports_level(&self) -> bool { true } /// Whether this poller supports edge-triggered events. pub(super) fn supports_edge(&self) -> bool { false } /// Add a new source to the poller. /// /// # Safety /// /// The socket must be a valid socket and must last until it is deleted. pub(super) unsafe fn add( &self, socket: RawSocket, interest: Event, mode: PollMode, ) -> io::Result<()> { let span = tracing::trace_span!( "add", handle = ?self.port, sock = ?socket, ev = ?interest, ); let _enter = span.enter(); // We don't support edge-triggered events. if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "edge-triggered events are not supported", )); } // Create a new packet. let socket_state = { // Create a new socket state and assign an AFD handle to it. let state = SocketState { socket, base_socket: base_socket(socket)?, interest, interest_error: true, afd: self.afd_handle()?, mode, waiting_on_delete: false, status: SocketStatus::Idle, }; // We wrap this socket state in a Packet so the IOCP can use it. Arc::pin(IoStatusBlock::from(PacketInner::Socket { packet: UnsafeCell::new(AfdPollInfo::default()), socket: Mutex::new(state), })) }; // Keep track of the source in the poller. { let mut sources = lock!(self.sources.write()); match sources.entry(socket) { Entry::Vacant(v) => { v.insert(Pin::>::clone(&socket_state)); } Entry::Occupied(_) => { return Err(io::Error::from(io::ErrorKind::AlreadyExists)); } } } // Update the packet. self.update_packet(socket_state) } /// Update a source in the poller. pub(super) fn modify( &self, socket: BorrowedSocket<'_>, interest: Event, mode: PollMode, ) -> io::Result<()> { let span = tracing::trace_span!( "modify", handle = ?self.port, sock = ?socket, ev = ?interest, ); let _enter = span.enter(); // We don't support edge-triggered events. if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "edge-triggered events are not supported", )); } // Get a reference to the source. let source = { let sources = lock!(self.sources.read()); sources .get(&socket.as_raw_socket()) .cloned() .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? }; // Set the new event. if source.as_ref().set_events(interest, mode) { // The packet needs to be updated. self.update_packet(source)?; } Ok(()) } /// Delete a source from the poller. pub(super) fn delete(&self, socket: BorrowedSocket<'_>) -> io::Result<()> { let span = tracing::trace_span!( "remove", handle = ?self.port, sock = ?socket, ); let _enter = span.enter(); // Remove the source from our associative map. let source = { let mut sources = lock!(self.sources.write()); match sources.remove(&socket.as_raw_socket()) { Some(s) => s, None => { // If the source has already been removed, then we can just return. return Ok(()); } } }; // Indicate to the source that it is being deleted. // This cancels any ongoing AFD_IOCTL_POLL operations. source.begin_delete() } /// Add a new waitable to the poller. pub(super) fn add_waitable( &self, handle: RawHandle, interest: Event, mode: PollMode, ) -> io::Result<()> { tracing::trace!( "add_waitable: handle={:?}, waitable={:p}, ev={:?}", self.port, handle, interest ); // We don't support edge-triggered events. if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "edge-triggered events are not supported", )); } // Create a new packet. let handle_state = { let state = WaitableState { handle, port: Arc::downgrade(&self.port), interest, mode, status: WaitableStatus::Idle, }; Arc::pin(IoStatusBlock::from(PacketInner::Waitable { handle: Mutex::new(state), })) }; // Keep track of the source in the poller. { let mut sources = lock!(self.waitables.write()); match sources.entry(handle) { Entry::Vacant(v) => { v.insert(Pin::>::clone(&handle_state)); } Entry::Occupied(_) => { return Err(io::Error::from(io::ErrorKind::AlreadyExists)); } } } // Update the packet. self.update_packet(handle_state) } /// Update a waitable in the poller. pub(crate) fn modify_waitable( &self, waitable: RawHandle, interest: Event, mode: PollMode, ) -> io::Result<()> { tracing::trace!( "modify_waitable: handle={:?}, waitable={:p}, ev={:?}", self.port, waitable, interest ); // We don't support edge-triggered events. if matches!(mode, PollMode::Edge | PollMode::EdgeOneshot) { return Err(io::Error::new( io::ErrorKind::InvalidInput, "edge-triggered events are not supported", )); } // Get a reference to the source. let source = { let sources = lock!(self.waitables.read()); sources .get(&waitable) .cloned() .ok_or_else(|| io::Error::from(io::ErrorKind::NotFound))? }; // Set the new event. if source.as_ref().set_events(interest, mode) { self.update_packet(source)?; } Ok(()) } /// Delete a waitable from the poller. pub(super) fn remove_waitable(&self, waitable: RawHandle) -> io::Result<()> { tracing::trace!("remove: handle={:?}, waitable={:p}", self.port, waitable); // Get a reference to the source. let source = { let mut sources = lock!(self.waitables.write()); match sources.remove(&waitable) { Some(s) => s, None => { // If the source has already been removed, then we can just return. return Ok(()); } } }; // Indicate to the source that it is being deleted. // This cancels any ongoing AFD_IOCTL_POLL operations. source.begin_delete() } /// Wait for events. pub(super) fn wait(&self, events: &mut Events, timeout: Option) -> io::Result<()> { let span = tracing::trace_span!( "wait", handle = ?self.port, ?timeout, ); let _enter = span.enter(); // Make sure we have a consistent timeout. let deadline = timeout.and_then(|timeout| Instant::now().checked_add(timeout)); let mut notified = false; events.packets.clear(); loop { let mut new_events = 0; // Indicate that we are now polling. let was_polling = self.polling.swap(true, Ordering::SeqCst); debug_assert!(!was_polling); // Even if we panic, we want to make sure we indicate that polling has stopped. let guard = CallOnDrop(|| { let was_polling = self.polling.swap(false, Ordering::SeqCst); debug_assert!(was_polling); }); // Process every entry in the queue before we start polling. self.drain_update_queue(false)?; // Get the time to wait for. let timeout = deadline.map(|t| t.saturating_duration_since(Instant::now())); // Wait for I/O events. let len = self.port.wait(&mut events.completions, timeout)?; tracing::trace!( handle = ?self.port, res = ?len, "new events"); // We are no longer polling. drop(guard); // Process all of the events. for entry in events.completions.drain(..) { let packet = entry.into_packet(); // Feed the event into the packet. match packet.feed_event(self)? { FeedEventResult::NoEvent => {} FeedEventResult::Event(event) => { events.packets.push(event); new_events += 1; } FeedEventResult::Notified => { notified = true; } } } // Break if there was a notification or at least one event, or if deadline is reached. let timeout_is_empty = timeout.map_or(false, |t| t.as_secs() == 0 && t.subsec_nanos() == 0); if notified || new_events > 0 || timeout_is_empty { break; } tracing::trace!("wait: no events found, re-entering polling loop"); } Ok(()) } /// Notify this poller. pub(super) fn notify(&self) -> io::Result<()> { // Push the notify packet into the IOCP. self.port.post(0, 0, self.notifier.clone()) } /// Push an IOCP packet into the queue. pub(super) fn post(&self, packet: CompletionPacket) -> io::Result<()> { self.port.post(0, 0, packet.0) } /// Run an update on a packet. fn update_packet(&self, mut packet: Packet) -> io::Result<()> { loop { // If we are currently polling, we need to update the packet immediately. if self.polling.load(Ordering::Acquire) { packet.update()?; return Ok(()); } // Try to queue the update. match self.pending_updates.push(packet) { Ok(()) => return Ok(()), Err(p) => packet = p.into_inner(), } // If we failed to queue the update, we need to drain the queue first. self.drain_update_queue(true)?; // Loop back and try again. } } /// Drain the update queue. fn drain_update_queue(&self, limit: bool) -> io::Result<()> { // Determine how many packets to process. let max = if limit { // Only drain the queue's capacity, since this could in theory run forever. self.pending_updates.capacity().unwrap() } else { // Less of a concern if we're draining the queue prior to a poll operation. std::usize::MAX }; self.pending_updates .try_iter() .take(max) .try_for_each(|packet| packet.update()) } /// Get a handle to the AFD reference. /// /// This finds an AFD handle with less than 32 associated sockets, or creates a new one if /// one does not exist. fn afd_handle(&self) -> io::Result>> { const AFD_MAX_SIZE: usize = 32; // Crawl the list and see if there are any existing AFD instances that we can use. // While we're here, remove any unused AFD pointers. let mut afd_handles = lock!(self.afd.lock()); let mut i = 0; while i < afd_handles.len() { // Get the reference count of the AFD instance. let refcount = Weak::strong_count(&afd_handles[i]); match refcount { 0 => { // Prune the AFD pointer if it has no references. afd_handles.swap_remove(i); } refcount if refcount >= AFD_MAX_SIZE => { // Skip this one, since it is already at the maximum size. i += 1; } _ => { // We can use this AFD instance. match afd_handles[i].upgrade() { Some(afd) => return Ok(afd), None => { // The last socket dropped the AFD before we could acquire it. // Prune the AFD pointer and continue. afd_handles.swap_remove(i); } } } } } // No available handles, create a new AFD instance. let afd = Arc::new(Afd::new()?); // Register the AFD instance with the I/O completion port. self.port.register(&*afd, true)?; // Insert a weak pointer to the AFD instance into the list for other sockets. afd_handles.push(Arc::downgrade(&afd)); Ok(afd) } } impl AsRawHandle for Poller { fn as_raw_handle(&self) -> RawHandle { self.port.as_raw_handle() } } impl AsHandle for Poller { fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } } } /// The container for events. pub(super) struct Events { /// List of IOCP packets. packets: Vec, /// Buffer for completion packets. completions: Vec>, } unsafe impl Send for Events {} impl Events { /// Creates an empty list of events. pub fn with_capacity(cap: usize) -> Events { Events { packets: Vec::with_capacity(cap), completions: Vec::with_capacity(cap), } } /// Iterate over I/O events. pub fn iter(&self) -> impl Iterator + '_ { self.packets.iter().copied() } /// Clear the list of events. pub fn clear(&mut self) { self.packets.clear(); } /// The capacity of the list of events. pub fn capacity(&self) -> usize { self.packets.capacity() } } /// Extra information about an event. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct EventExtra { /// Flags associated with this event. flags: AfdPollMask, } impl EventExtra { /// Create a new, empty version of this struct. #[inline] pub const fn empty() -> EventExtra { EventExtra { flags: AfdPollMask::empty(), } } /// Is this a HUP event? #[inline] pub fn is_hup(&self) -> bool { self.flags.intersects(AfdPollMask::ABORT) } /// Is this a PRI event? #[inline] pub fn is_pri(&self) -> bool { self.flags.intersects(AfdPollMask::RECEIVE_EXPEDITED) } /// Set up a listener for HUP events. #[inline] pub fn set_hup(&mut self, active: bool) { self.flags.set(AfdPollMask::ABORT, active); } /// Set up a listener for PRI events. #[inline] pub fn set_pri(&mut self, active: bool) { self.flags.set(AfdPollMask::RECEIVE_EXPEDITED, active); } /// Check if TCP connect failed. #[inline] pub fn is_connect_failed(&self) -> Option { Some(self.flags.intersects(AfdPollMask::CONNECT_FAIL)) } } /// A packet used to wake up the poller with an event. #[derive(Debug, Clone)] pub struct CompletionPacket(Packet); impl CompletionPacket { /// Create a new completion packet with a custom event. pub fn new(event: Event) -> Self { Self(Arc::pin(IoStatusBlock::from(PacketInner::Custom { event }))) } /// Get the event associated with this packet. pub fn event(&self) -> &Event { let data = self.0.as_ref().data().project_ref(); match data { PacketInnerProj::Custom { event } => event, _ => unreachable!(), } } } /// The type of our completion packet. /// /// It needs to be pinned, since it contains data that is expected by IOCP not to be moved. type Packet = Pin>; type PacketUnwrapped = IoStatusBlock; pin_project! { /// The inner type of the packet. #[project_ref = PacketInnerProj] #[project = PacketInnerProjMut] enum PacketInner { // A packet for a socket. Socket { // The AFD packet state. #[pin] packet: UnsafeCell, // The socket state. socket: Mutex }, /// A packet for a waitable handle. Waitable { handle: Mutex }, /// A custom event sent by the user. Custom { event: Event, }, // A packet used to wake up the poller. Wakeup { #[pin] _pinned: PhantomPinned }, } } unsafe impl Send for PacketInner {} unsafe impl Sync for PacketInner {} impl fmt::Debug for PacketInner { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Wakeup { .. } => f.write_str("Wakeup { .. }"), Self::Custom { event } => f.debug_struct("Custom").field("event", event).finish(), Self::Socket { socket, .. } => f .debug_struct("Socket") .field("packet", &"..") .field("socket", socket) .finish(), Self::Waitable { handle } => { f.debug_struct("Waitable").field("handle", handle).finish() } } } } impl HasAfdInfo for PacketInner { fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell> { match self.project_ref() { PacketInnerProj::Socket { packet, .. } => packet, _ => unreachable!(), } } } impl PacketUnwrapped { /// Set the new events that this socket is waiting on. /// /// Returns `true` if we need to be updated. fn set_events(self: Pin<&Self>, interest: Event, mode: PollMode) -> bool { match self.data().project_ref() { PacketInnerProj::Socket { socket, .. } => { let mut socket = lock!(socket.lock()); socket.interest = interest; socket.mode = mode; socket.interest_error = true; // If there was a change, indicate that we need an update. match socket.status { SocketStatus::Polling { flags } => { let our_flags = event_to_afd_mask(socket.interest, socket.interest_error); our_flags != flags } _ => true, } } PacketInnerProj::Waitable { handle } => { let mut handle = lock!(handle.lock()); // Set the new interest. handle.interest = interest; handle.mode = mode; // Update if there is no ongoing wait. handle.status.is_idle() } _ => true, } } /// Update the socket and install the new status in AFD. /// /// This function does one of the following: /// /// - Nothing, if the packet is waiting on being dropped anyways. /// - Cancels the ongoing poll, if we want to poll for different events than we are currently /// polling for. /// - Starts a new AFD_POLL operation, if we are not currently polling. fn update(self: Pin>) -> io::Result<()> { let mut socket = match self.as_ref().data().project_ref() { PacketInnerProj::Socket { socket, .. } => lock!(socket.lock()), PacketInnerProj::Waitable { handle } => { let mut handle = lock!(handle.lock()); // If there is no interests, or if we have been cancelled, we don't need to update. if !handle.interest.readable && !handle.interest.writable { return Ok(()); } // If we are idle, we need to update. if !handle.status.is_idle() { return Ok(()); } // Start a new wait. let packet = self.clone(); let wait_handle = WaitHandle::new( handle.handle, move || { let mut handle = match packet.as_ref().data().project_ref() { PacketInnerProj::Waitable { handle } => lock!(handle.lock()), _ => unreachable!(), }; // Try to get the IOCP. let iocp = match handle.port.upgrade() { Some(iocp) => iocp, None => return, }; // Set us back into the idle state. handle.status = WaitableStatus::Idle; // Push this packet. drop(handle); if let Err(e) = iocp.post(0, 0, packet) { tracing::error!("failed to post completion packet: {}", e); } }, None, false, )?; // Set the new status. handle.status = WaitableStatus::Waiting(wait_handle); return Ok(()); } _ => return Err(io::Error::new(io::ErrorKind::Other, "invalid socket state")), }; // If we are waiting on a delete, just return, dropping the packet. if socket.waiting_on_delete { return Ok(()); } // Check the current status. match socket.status { SocketStatus::Polling { flags } => { // If we need to poll for events aside from what we are currently polling, we need // to update the packet. Cancel the ongoing poll. let our_flags = event_to_afd_mask(socket.interest, socket.interest_error); if our_flags != flags { return self.cancel(socket); } // All events that we are currently waiting on are accounted for. Ok(()) } SocketStatus::Cancelled => { // The ongoing operation was cancelled, and we're still waiting for it to return. // For now, wait until the top-level loop calls feed_event(). Ok(()) } SocketStatus::Idle => { // Start a new poll. let mask = event_to_afd_mask(socket.interest, socket.interest_error); let result = socket.afd.poll(self.clone(), socket.base_socket, mask); match result { Ok(()) => {} Err(err) if err.raw_os_error() == Some(ERROR_IO_PENDING as i32) || err.kind() == io::ErrorKind::WouldBlock => { // The operation is pending. } Err(err) if err.raw_os_error() == Some(ERROR_INVALID_HANDLE as i32) => { // The socket was closed. We need to delete it. // This should happen after we drop it here. } Err(err) => return Err(err), } // We are now polling for the current events. socket.status = SocketStatus::Polling { flags: mask }; Ok(()) } } } /// This socket state was notified; see if we need to update it. /// /// This indicates that this packet was indicated as "ready" by the IOCP and needs to be /// processed. fn feed_event(self: Pin>, poller: &Poller) -> io::Result { let inner = self.as_ref().data().project_ref(); let (afd_info, socket) = match inner { PacketInnerProj::Socket { packet, socket } => (packet, socket), PacketInnerProj::Custom { event } => { // This is a custom event. return Ok(FeedEventResult::Event(*event)); } PacketInnerProj::Wakeup { .. } => { // The poller was notified. return Ok(FeedEventResult::Notified); } PacketInnerProj::Waitable { handle } => { let mut handle = lock!(handle.lock()); let event = handle.interest; // Clear the events if we are in one-shot mode. if matches!(handle.mode, PollMode::Oneshot) { handle.interest = Event::none(handle.interest.key); } // Submit for an update. drop(handle); poller.update_packet(self)?; return Ok(FeedEventResult::Event(event)); } }; let mut socket_state = lock!(socket.lock()); let mut event = Event::none(socket_state.interest.key); // Put ourselves into the idle state. socket_state.status = SocketStatus::Idle; // If we are waiting to be deleted, just return and let the drop handler do their thing. if socket_state.waiting_on_delete { return Ok(FeedEventResult::NoEvent); } unsafe { // SAFETY: The packet is not in transit. let iosb = &mut *self.as_ref().iosb().get(); // Check the status. match iosb.Anonymous.Status { STATUS_CANCELLED => { // Poll request was cancelled. } status if status < 0 => { // There was an error, so we signal both ends. event.readable = true; event.writable = true; } _ => { // Check in on the AFD data. let afd_data = &*afd_info.get(); // There was at least one event. if afd_data.handle_count() >= 1 { let events = afd_data.events(); // If we closed the socket, remove it from being polled. if events.intersects(AfdPollMask::LOCAL_CLOSE) { let source = lock!(poller.sources.write()) .remove(&socket_state.socket) .unwrap(); return source.begin_delete().map(|()| FeedEventResult::NoEvent); } // Report socket-related events. let (readable, writable) = afd_mask_to_event(events); event.readable = readable; event.writable = writable; event.extra.flags = events; } } } } // Filter out events that the user didn't ask for. event.readable &= socket_state.interest.readable; event.writable &= socket_state.interest.writable; // If this event doesn't have anything that interests us, don't return or // update the oneshot state. let return_value = if event.readable || event.writable || event .extra .flags .intersects(socket_state.interest.extra.flags) { // If we are in oneshot mode, remove the interest. if matches!(socket_state.mode, PollMode::Oneshot) { socket_state.interest = Event::none(socket_state.interest.key); socket_state.interest_error = false; } FeedEventResult::Event(event) } else { FeedEventResult::NoEvent }; // Put ourselves in the update queue. drop(socket_state); poller.update_packet(self)?; // Return the event. Ok(return_value) } /// Begin deleting this socket. fn begin_delete(self: Pin>) -> io::Result<()> { // If we aren't already being deleted, start deleting. let mut socket = match self.as_ref().data().project_ref() { PacketInnerProj::Socket { socket, .. } => lock!(socket.lock()), PacketInnerProj::Waitable { handle } => { let mut handle = lock!(handle.lock()); // Set the status to be cancelled. This drops the wait handle and prevents // any further updates. handle.status = WaitableStatus::Cancelled; return Ok(()); } _ => panic!("can't delete packet that doesn't belong to a socket"), }; if !socket.waiting_on_delete { socket.waiting_on_delete = true; if matches!(socket.status, SocketStatus::Polling { .. }) { // Cancel the ongoing poll. self.cancel(socket)?; } } // Either drop it now or wait for it to be dropped later. Ok(()) } fn cancel(self: &Pin>, mut socket: MutexGuard<'_, SocketState>) -> io::Result<()> { assert!(matches!(socket.status, SocketStatus::Polling { .. })); // Send the cancel request. unsafe { socket.afd.cancel(self)?; } // Move state to cancelled. socket.status = SocketStatus::Cancelled; Ok(()) } } /// Per-socket state. #[derive(Debug)] struct SocketState { /// The raw socket handle. socket: RawSocket, /// The base socket handle. base_socket: RawSocket, /// The event that this socket is currently waiting on. interest: Event, /// Whether to listen for error events. interest_error: bool, /// The current poll mode. mode: PollMode, /// The AFD instance that this socket is registered with. afd: Arc>, /// Whether this socket is waiting to be deleted. waiting_on_delete: bool, /// The current status of the socket. status: SocketStatus, } /// The mode that a socket can be in. #[derive(Debug, Copy, Clone, PartialEq, Eq)] enum SocketStatus { /// We are currently not polling. Idle, /// We are currently polling these events. Polling { /// The flags we are currently polling for. flags: AfdPollMask, }, /// The last poll operation was cancelled, and we're waiting for it to /// complete. Cancelled, } /// Per-waitable handle state. #[derive(Debug)] struct WaitableState { /// The handle that this state is for. handle: RawHandle, /// The IO completion port that this handle is registered with. port: Weak>, /// The event that this handle will report. interest: Event, /// The current poll mode. mode: PollMode, /// The status of this waitable. status: WaitableStatus, } #[derive(Debug)] enum WaitableStatus { /// We are not polling. Idle, /// We are waiting on this handle to become signaled. Waiting(#[allow(dead_code)] WaitHandle), /// This handle has been cancelled. Cancelled, } impl WaitableStatus { fn is_idle(&self) -> bool { matches!(self, WaitableStatus::Idle) } } /// The result of calling `feed_event`. #[derive(Debug)] enum FeedEventResult { /// No event was yielded. NoEvent, /// An event was yielded. Event(Event), /// The poller has been notified. Notified, } /// A handle for an ongoing wait operation. #[derive(Debug)] struct WaitHandle(RawHandle); impl Drop for WaitHandle { fn drop(&mut self) { unsafe { UnregisterWait(self.0 as _); } } } impl WaitHandle { /// Wait for a waitable handle to become signaled. fn new( handle: RawHandle, callback: F, timeout: Option, long_wait: bool, ) -> io::Result where F: FnOnce() + Send + Sync + 'static, { // Make sure a panic in the callback doesn't propagate to the OS. struct AbortOnDrop; impl Drop for AbortOnDrop { fn drop(&mut self) { std::process::abort(); } } unsafe extern "system" fn wait_callback( context: *mut c_void, _timer_fired: BOOLEAN, ) { let _guard = AbortOnDrop; let callback = Box::from_raw(context as *mut F); callback(); // We executed without panicking, so don't abort. forget(_guard); } let mut wait_handle = MaybeUninit::::uninit(); let mut flags = WT_EXECUTEONLYONCE; if long_wait { flags |= WT_EXECUTELONGFUNCTION; } let res = unsafe { RegisterWaitForSingleObject( wait_handle.as_mut_ptr().cast::<_>(), handle as _, Some(wait_callback::), Box::into_raw(Box::new(callback)) as _, timeout.map_or(INFINITE, dur2timeout), flags, ) }; if res == 0 { return Err(io::Error::last_os_error()); } let wait_handle = unsafe { wait_handle.assume_init() }; Ok(Self(wait_handle)) } } /// Translate an event to the mask expected by AFD. #[inline] fn event_to_afd_mask(event: Event, error: bool) -> afd::AfdPollMask { event_properties_to_afd_mask(event.readable, event.writable, error) | event.extra.flags } /// Translate an event to the mask expected by AFD. #[inline] fn event_properties_to_afd_mask(readable: bool, writable: bool, error: bool) -> afd::AfdPollMask { use afd::AfdPollMask as AfdPoll; let mut mask = AfdPoll::empty(); if error || readable || writable { mask |= AfdPoll::ABORT | AfdPoll::CONNECT_FAIL; } if readable { mask |= AfdPoll::RECEIVE | AfdPoll::ACCEPT | AfdPoll::DISCONNECT | AfdPoll::RECEIVE_EXPEDITED; } if writable { mask |= AfdPoll::SEND; } mask } /// Convert the mask reported by AFD to an event. #[inline] fn afd_mask_to_event(mask: afd::AfdPollMask) -> (bool, bool) { use afd::AfdPollMask as AfdPoll; let mut readable = false; let mut writable = false; if mask.intersects( AfdPoll::RECEIVE | AfdPoll::ACCEPT | AfdPoll::DISCONNECT | AfdPoll::RECEIVE_EXPEDITED, ) { readable = true; } if mask.intersects(AfdPoll::SEND) { writable = true; } if mask.intersects(AfdPoll::ABORT | AfdPoll::CONNECT_FAIL) { readable = true; writable = true; } (readable, writable) } // Implementation taken from https://github.com/rust-lang/rust/blob/db5476571d9b27c862b95c1e64764b0ac8980e23/src/libstd/sys/windows/mod.rs fn dur2timeout(dur: Duration) -> u32 { // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the // timeouts in windows APIs are typically u32 milliseconds. To translate, we // have two pieces to take care of: // // * Nanosecond precision is rounded up // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE // (never time out). dur.as_secs() .checked_mul(1000) .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000)) .and_then(|ms| { if dur.subsec_nanos() % 1_000_000 > 0 { ms.checked_add(1) } else { Some(ms) } }) .and_then(|x| u32::try_from(x).ok()) .unwrap_or(INFINITE) } /// An error type that wraps around failing to open AFD. struct AfdError { /// String description of what happened. description: &'static str, /// The underlying system error. system: io::Error, } impl AfdError { #[inline] fn new(description: &'static str, system: io::Error) -> Self { Self { description, system, } } } impl fmt::Debug for AfdError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AfdError") .field("description", &self.description) .field("system", &self.system) .field("note", &"probably caused by old Windows or Wine") .finish() } } impl fmt::Display for AfdError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}: {}\nThis error is usually caused by running on old Windows or Wine", self.description, &self.system ) } } impl std::error::Error for AfdError { #[inline] fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { Some(&self.system) } } struct CallOnDrop(F); impl Drop for CallOnDrop { fn drop(&mut self) { (self.0)(); } } polling-3.4.0/src/iocp/port.rs000064400000000000000000000205161046102023000143530ustar 00000000000000//! A safe wrapper around the Windows I/O API. use super::dur2timeout; use std::fmt; use std::io; use std::marker::PhantomData; use std::mem::MaybeUninit; use std::ops::Deref; use std::os::windows::io::{AsRawHandle, RawHandle}; use std::pin::Pin; use std::sync::Arc; use std::time::Duration; use windows_sys::Win32::Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE}; use windows_sys::Win32::Storage::FileSystem::SetFileCompletionNotificationModes; use windows_sys::Win32::System::Threading::INFINITE; use windows_sys::Win32::System::WindowsProgramming::FILE_SKIP_SET_EVENT_ON_HANDLE; use windows_sys::Win32::System::IO::{ CreateIoCompletionPort, GetQueuedCompletionStatusEx, PostQueuedCompletionStatus, OVERLAPPED, OVERLAPPED_ENTRY, }; /// A completion block which can be used with I/O completion ports. /// /// # Safety /// /// This must be a valid completion block. pub(super) unsafe trait Completion { /// Signal to the completion block that we are about to start an operation. fn try_lock(self: Pin<&Self>) -> bool; /// Unlock the completion block. unsafe fn unlock(self: Pin<&Self>); } /// The pointer to a completion block. /// /// # Safety /// /// This must be a valid completion block. pub(super) unsafe trait CompletionHandle: Deref + Sized { /// Type of the completion block. type Completion: Completion; /// Get a pointer to the completion block. /// /// The pointer is pinned since the underlying object should not be moved /// after creation. This prevents it from being invalidated while it's /// used in an overlapped operation. fn get(&self) -> Pin<&Self::Completion>; /// Convert this block into a pointer that can be passed as `*mut OVERLAPPED`. fn into_ptr(this: Self) -> *mut OVERLAPPED; /// Convert a pointer that was passed as `*mut OVERLAPPED` into a pointer to this block. /// /// # Safety /// /// This must be a valid pointer to a completion block. unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self; /// Convert to a pointer without losing ownership. fn as_ptr(&self) -> *mut OVERLAPPED; } unsafe impl<'a, T: Completion> CompletionHandle for Pin<&'a T> { type Completion = T; fn get(&self) -> Pin<&Self::Completion> { *self } fn into_ptr(this: Self) -> *mut OVERLAPPED { unsafe { Pin::into_inner_unchecked(this) as *const T as *mut OVERLAPPED } } unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self { Pin::new_unchecked(&*(ptr as *const T)) } fn as_ptr(&self) -> *mut OVERLAPPED { self.get_ref() as *const T as *mut OVERLAPPED } } unsafe impl CompletionHandle for Pin> { type Completion = T; fn get(&self) -> Pin<&Self::Completion> { self.as_ref() } fn into_ptr(this: Self) -> *mut OVERLAPPED { unsafe { Arc::into_raw(Pin::into_inner_unchecked(this)) as *const T as *mut OVERLAPPED } } unsafe fn from_ptr(ptr: *mut OVERLAPPED) -> Self { Pin::new_unchecked(Arc::from_raw(ptr as *const T)) } fn as_ptr(&self) -> *mut OVERLAPPED { self.as_ref().get_ref() as *const T as *mut OVERLAPPED } } /// A handle to the I/O completion port. pub(super) struct IoCompletionPort { /// The underlying handle. handle: HANDLE, /// We own the status block. _marker: PhantomData, } impl Drop for IoCompletionPort { fn drop(&mut self) { unsafe { CloseHandle(self.handle); } } } impl AsRawHandle for IoCompletionPort { fn as_raw_handle(&self) -> RawHandle { self.handle as _ } } impl fmt::Debug for IoCompletionPort { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { struct WriteAsHex(HANDLE); impl fmt::Debug for WriteAsHex { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:010x}", self.0) } } f.debug_struct("IoCompletionPort") .field("handle", &WriteAsHex(self.handle)) .finish() } } impl IoCompletionPort { /// Create a new I/O completion port. pub(super) fn new(threads: usize) -> io::Result { let handle = unsafe { CreateIoCompletionPort( INVALID_HANDLE_VALUE, 0, 0, threads.try_into().expect("too many threads"), ) }; if handle == 0 { Err(io::Error::last_os_error()) } else { Ok(Self { handle, _marker: PhantomData, }) } } /// Register a handle with this I/O completion port. pub(super) fn register( &self, handle: &impl AsRawHandle, // TODO change to AsHandle skip_set_event_on_handle: bool, ) -> io::Result<()> { let handle = handle.as_raw_handle(); let result = unsafe { CreateIoCompletionPort(handle as _, self.handle, handle as usize, 0) }; if result == 0 { return Err(io::Error::last_os_error()); } if skip_set_event_on_handle { // Set the skip event on handle. let result = unsafe { SetFileCompletionNotificationModes(handle as _, FILE_SKIP_SET_EVENT_ON_HANDLE as _) }; if result == 0 { return Err(io::Error::last_os_error()); } } Ok(()) } /// Post a completion packet to this port. pub(super) fn post(&self, bytes_transferred: usize, id: usize, packet: T) -> io::Result<()> { let result = unsafe { PostQueuedCompletionStatus( self.handle, bytes_transferred .try_into() .expect("too many bytes transferred"), id, T::into_ptr(packet), ) }; if result == 0 { Err(io::Error::last_os_error()) } else { Ok(()) } } /// Wait for completion packets to arrive. pub(super) fn wait( &self, packets: &mut Vec>, timeout: Option, ) -> io::Result { // Drop the current packets. packets.clear(); let mut count = MaybeUninit::::uninit(); let timeout = timeout.map_or(INFINITE, dur2timeout); let result = unsafe { GetQueuedCompletionStatusEx( self.handle, packets.as_mut_ptr() as _, packets.capacity().try_into().expect("too many packets"), count.as_mut_ptr(), timeout, 0, ) }; if result == 0 { let io_error = io::Error::last_os_error(); if io_error.kind() == io::ErrorKind::TimedOut { Ok(0) } else { Err(io_error) } } else { let count = unsafe { count.assume_init() }; unsafe { packets.set_len(count as _); } Ok(count as _) } } } /// An `OVERLAPPED_ENTRY` resulting from an I/O completion port. #[repr(transparent)] pub(super) struct OverlappedEntry { /// The underlying entry. entry: OVERLAPPED_ENTRY, /// We own the status block. _marker: PhantomData, } impl fmt::Debug for OverlappedEntry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("OverlappedEntry { .. }") } } impl OverlappedEntry { /// Convert into the completion packet. pub(super) fn into_packet(self) -> T { let packet = unsafe { self.packet() }; std::mem::forget(self); packet } /// Get the packet reference that this entry refers to. /// /// # Safety /// /// This function should only be called once, since it moves /// out the `T` from the `OVERLAPPED_ENTRY`. unsafe fn packet(&self) -> T { let packet = T::from_ptr(self.entry.lpOverlapped); packet.get().unlock(); packet } } impl Drop for OverlappedEntry { fn drop(&mut self) { drop(unsafe { self.packet() }); } } struct CallOnDrop(F); impl Drop for CallOnDrop { fn drop(&mut self) { (self.0)(); } } polling-3.4.0/src/kqueue.rs000064400000000000000000000376751046102023000137520ustar 00000000000000//! Bindings to kqueue (macOS, iOS, tvOS, watchOS, FreeBSD, NetBSD, OpenBSD, DragonFly BSD). use std::collections::HashSet; use std::io; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use std::sync::RwLock; use std::time::Duration; use rustix::event::kqueue; use rustix::io::{fcntl_setfd, Errno, FdFlags}; use crate::{Event, PollMode}; /// Interface to kqueue. #[derive(Debug)] pub struct Poller { /// File descriptor for the kqueue instance. kqueue_fd: OwnedFd, /// List of sources currently registered in this poller. /// /// This is used to make sure the same source is not registered twice. sources: RwLock>, /// Notification pipe for waking up the poller. /// /// On platforms that support `EVFILT_USER`, this uses that to wake up the poller. Otherwise, it /// uses a pipe. notify: notify::Notify, } /// Identifier for a source. #[doc(hidden)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub enum SourceId { /// Registered file descriptor. Fd(RawFd), /// Signal. Signal(std::os::raw::c_int), /// Process ID. Pid(rustix::process::Pid), /// Timer ID. Timer(usize), } impl Poller { /// Creates a new poller. pub fn new() -> io::Result { // Create a kqueue instance. let kqueue_fd = kqueue::kqueue()?; fcntl_setfd(&kqueue_fd, FdFlags::CLOEXEC)?; let poller = Poller { kqueue_fd, sources: RwLock::new(HashSet::new()), notify: notify::Notify::new()?, }; // Register the notification pipe. poller.notify.register(&poller)?; tracing::trace!( kqueue_fd = ?poller.kqueue_fd.as_raw_fd(), "new" ); Ok(poller) } /// Whether this poller supports level-triggered events. pub fn supports_level(&self) -> bool { true } /// Whether this poller supports edge-triggered events. pub fn supports_edge(&self) -> bool { true } /// Adds a new file descriptor. /// /// # Safety /// /// The file descriptor must be valid and it must last until it is deleted. pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { self.add_source(SourceId::Fd(fd))?; // File descriptors don't need to be added explicitly, so just modify the interest. self.modify(BorrowedFd::borrow_raw(fd), ev, mode) } /// Modifies an existing file descriptor. pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { let span = if !self.notify.has_fd(fd) { let span = tracing::trace_span!( "add", kqueue_fd = ?self.kqueue_fd.as_raw_fd(), ?fd, ?ev, ); Some(span) } else { None }; let _enter = span.as_ref().map(|s| s.enter()); self.has_source(SourceId::Fd(fd.as_raw_fd()))?; let mode_flags = mode_to_flags(mode); let read_flags = if ev.readable { kqueue::EventFlags::ADD | mode_flags } else { kqueue::EventFlags::DELETE }; let write_flags = if ev.writable { kqueue::EventFlags::ADD | mode_flags } else { kqueue::EventFlags::DELETE }; // A list of changes for kqueue. let changelist = [ kqueue::Event::new( kqueue::EventFilter::Read(fd.as_raw_fd()), read_flags | kqueue::EventFlags::RECEIPT, ev.key as _, ), kqueue::Event::new( kqueue::EventFilter::Write(fd.as_raw_fd()), write_flags | kqueue::EventFlags::RECEIPT, ev.key as _, ), ]; // Apply changes. self.submit_changes(changelist) } /// Submit one or more changes to the kernel queue and check to see if they succeeded. pub(crate) fn submit_changes(&self, changelist: A) -> io::Result<()> where A: Copy + AsRef<[kqueue::Event]> + AsMut<[kqueue::Event]>, { let mut eventlist = Vec::with_capacity(changelist.as_ref().len()); // Apply changes. { let changelist = changelist.as_ref(); unsafe { kqueue::kevent(&self.kqueue_fd, changelist, &mut eventlist, None)?; } } // Check for errors. for &ev in &eventlist { let data = ev.data(); // Explanation for ignoring EPIPE: https://github.com/tokio-rs/mio/issues/582 if (ev.flags().contains(kqueue::EventFlags::ERROR)) && data != 0 && data != Errno::NOENT.raw_os_error() as _ && data != Errno::PIPE.raw_os_error() as _ { return Err(io::Error::from_raw_os_error(data as _)); } } Ok(()) } /// Add a source to the sources set. #[inline] pub(crate) fn add_source(&self, source: SourceId) -> io::Result<()> { if self .sources .write() .unwrap_or_else(|e| e.into_inner()) .insert(source) { Ok(()) } else { Err(io::Error::from(io::ErrorKind::AlreadyExists)) } } /// Tell if a source is currently inside the set. #[inline] pub(crate) fn has_source(&self, source: SourceId) -> io::Result<()> { if self .sources .read() .unwrap_or_else(|e| e.into_inner()) .contains(&source) { Ok(()) } else { Err(io::Error::from(io::ErrorKind::NotFound)) } } /// Remove a source from the sources set. #[inline] pub(crate) fn remove_source(&self, source: SourceId) -> io::Result<()> { if self .sources .write() .unwrap_or_else(|e| e.into_inner()) .remove(&source) { Ok(()) } else { Err(io::Error::from(io::ErrorKind::NotFound)) } } /// Deletes a file descriptor. pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { // Simply delete interest in the file descriptor. self.modify(fd, Event::none(0), PollMode::Oneshot)?; self.remove_source(SourceId::Fd(fd.as_raw_fd())) } /// Waits for I/O events with an optional timeout. pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result<()> { let span = tracing::trace_span!( "wait", kqueue_fd = ?self.kqueue_fd.as_raw_fd(), ?timeout, ); let _enter = span.enter(); // Wait for I/O events. let changelist = []; let eventlist = &mut events.list; let res = unsafe { kqueue::kevent(&self.kqueue_fd, &changelist, eventlist, timeout)? }; tracing::trace!( kqueue_fd = ?self.kqueue_fd.as_raw_fd(), ?res, "new events", ); // Clear the notification (if received) and re-register interest in it. self.notify.reregister(self)?; Ok(()) } /// Sends a notification to wake up the current or next `wait()` call. pub fn notify(&self) -> io::Result<()> { let span = tracing::trace_span!( "notify", kqueue_fd = ?self.kqueue_fd.as_raw_fd(), ); let _enter = span.enter(); self.notify.notify(self).ok(); Ok(()) } } impl AsRawFd for Poller { fn as_raw_fd(&self) -> RawFd { self.kqueue_fd.as_raw_fd() } } impl AsFd for Poller { fn as_fd(&self) -> BorrowedFd<'_> { self.kqueue_fd.as_fd() } } impl Drop for Poller { fn drop(&mut self) { let span = tracing::trace_span!( "drop", kqueue_fd = ?self.kqueue_fd.as_raw_fd(), ); let _enter = span.enter(); let _ = self.notify.deregister(self); } } /// A list of reported I/O events. pub struct Events { list: Vec, } unsafe impl Send for Events {} impl Events { /// Creates an empty list. pub fn with_capacity(cap: usize) -> Events { Events { list: Vec::with_capacity(cap), } } /// Iterates over I/O events. pub fn iter(&self) -> impl Iterator + '_ { // On some platforms, closing the read end of a pipe wakes up writers, but the // event is reported as EVFILT_READ with the EV_EOF flag. // // https://github.com/golang/go/commit/23aad448b1e3f7c3b4ba2af90120bde91ac865b4 self.list.iter().map(|ev| Event { key: ev.udata() as usize, readable: matches!( ev.filter(), kqueue::EventFilter::Read(..) | kqueue::EventFilter::Vnode { .. } | kqueue::EventFilter::Proc { .. } | kqueue::EventFilter::Signal { .. } | kqueue::EventFilter::Timer { .. } ), writable: matches!(ev.filter(), kqueue::EventFilter::Write(..)) || (matches!(ev.filter(), kqueue::EventFilter::Read(..)) && (ev.flags().intersects(kqueue::EventFlags::EOF))), extra: EventExtra, }) } /// Clears the list. pub fn clear(&mut self) { self.list.clear(); } /// Get the capacity of the list. pub fn capacity(&self) -> usize { self.list.capacity() } } /// Extra information associated with an event. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct EventExtra; impl EventExtra { /// Create a new, empty version of this struct. #[inline] pub const fn empty() -> EventExtra { EventExtra } /// Set the interrupt flag. #[inline] pub fn set_hup(&mut self, _value: bool) { // No-op. } /// Set the priority flag. #[inline] pub fn set_pri(&mut self, _value: bool) { // No-op. } /// Is the interrupt flag set? #[inline] pub fn is_hup(&self) -> bool { false } /// Is the priority flag set? #[inline] pub fn is_pri(&self) -> bool { false } #[inline] pub fn is_connect_failed(&self) -> Option { None } } pub(crate) fn mode_to_flags(mode: PollMode) -> kqueue::EventFlags { use kqueue::EventFlags as EV; match mode { PollMode::Oneshot => EV::ONESHOT, PollMode::Level => EV::empty(), PollMode::Edge => EV::CLEAR, PollMode::EdgeOneshot => EV::ONESHOT | EV::CLEAR, } } #[cfg(any( target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", ))] mod notify { use super::Poller; use rustix::event::kqueue; use std::io; use std::os::unix::io::BorrowedFd; /// A notification pipe. /// /// This implementation uses `EVFILT_USER` to avoid allocating a pipe. #[derive(Debug)] pub(super) struct Notify; impl Notify { /// Creates a new notification pipe. pub(super) fn new() -> io::Result { Ok(Self) } /// Registers this notification pipe in the `Poller`. pub(super) fn register(&self, poller: &Poller) -> io::Result<()> { // Register an EVFILT_USER event. poller.submit_changes([kqueue::Event::new( kqueue::EventFilter::User { ident: 0, flags: kqueue::UserFlags::empty(), user_flags: kqueue::UserDefinedFlags::new(0), }, kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT | kqueue::EventFlags::CLEAR, crate::NOTIFY_KEY as _, )]) } /// Reregister this notification pipe in the `Poller`. pub(super) fn reregister(&self, _poller: &Poller) -> io::Result<()> { // We don't need to do anything, it's already registered as EV_CLEAR. Ok(()) } /// Notifies the `Poller`. pub(super) fn notify(&self, poller: &Poller) -> io::Result<()> { // Trigger the EVFILT_USER event. poller.submit_changes([kqueue::Event::new( kqueue::EventFilter::User { ident: 0, flags: kqueue::UserFlags::TRIGGER, user_flags: kqueue::UserDefinedFlags::new(0), }, kqueue::EventFlags::ADD | kqueue::EventFlags::RECEIPT, crate::NOTIFY_KEY as _, )])?; Ok(()) } /// Deregisters this notification pipe from the `Poller`. pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> { // Deregister the EVFILT_USER event. poller.submit_changes([kqueue::Event::new( kqueue::EventFilter::User { ident: 0, flags: kqueue::UserFlags::empty(), user_flags: kqueue::UserDefinedFlags::new(0), }, kqueue::EventFlags::DELETE | kqueue::EventFlags::RECEIPT, crate::NOTIFY_KEY as _, )]) } /// Whether this raw file descriptor is associated with this pipe. pub(super) fn has_fd(&self, _fd: BorrowedFd<'_>) -> bool { false } } } #[cfg(not(any( target_os = "freebsd", target_os = "dragonfly", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", )))] mod notify { use super::Poller; use crate::{Event, PollMode, NOTIFY_KEY}; use std::io::{self, prelude::*}; use std::os::unix::{ io::{AsFd, AsRawFd, BorrowedFd}, net::UnixStream, }; /// A notification pipe. /// /// This implementation uses a pipe to send notifications. #[derive(Debug)] pub(super) struct Notify { /// The read end of the pipe. read_stream: UnixStream, /// The write end of the pipe. write_stream: UnixStream, } impl Notify { /// Creates a new notification pipe. pub(super) fn new() -> io::Result { let (read_stream, write_stream) = UnixStream::pair()?; read_stream.set_nonblocking(true)?; write_stream.set_nonblocking(true)?; Ok(Self { read_stream, write_stream, }) } /// Registers this notification pipe in the `Poller`. pub(super) fn register(&self, poller: &Poller) -> io::Result<()> { // Register the read end of this pipe. unsafe { poller.add( self.read_stream.as_raw_fd(), Event::readable(NOTIFY_KEY), PollMode::Oneshot, ) } } /// Reregister this notification pipe in the `Poller`. pub(super) fn reregister(&self, poller: &Poller) -> io::Result<()> { // Clear out the notification. while (&self.read_stream).read(&mut [0; 64]).is_ok() {} // Reregister the read end of this pipe. poller.modify( self.read_stream.as_fd(), Event::readable(NOTIFY_KEY), PollMode::Oneshot, ) } /// Notifies the `Poller`. #[allow(clippy::unused_io_amount)] pub(super) fn notify(&self, _poller: &Poller) -> io::Result<()> { // Write to the write end of the pipe (&self.write_stream).write(&[1])?; Ok(()) } /// Deregisters this notification pipe from the `Poller`. pub(super) fn deregister(&self, poller: &Poller) -> io::Result<()> { // Deregister the read end of the pipe. poller.delete(self.read_stream.as_fd()) } /// Whether this raw file descriptor is associated with this pipe. pub(super) fn has_fd(&self, fd: BorrowedFd<'_>) -> bool { self.read_stream.as_raw_fd() == fd.as_raw_fd() } } } polling-3.4.0/src/lib.rs000064400000000000000000001021211046102023000131740ustar 00000000000000//! Portable interface to epoll, kqueue, event ports, and IOCP. //! //! Supported platforms: //! - [epoll](https://en.wikipedia.org/wiki/Epoll): Linux, Android //! - [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, FreeBSD, NetBSD, OpenBSD, //! DragonFly BSD //! - [event ports](https://illumos.org/man/port_create): illumos, Solaris //! - [poll](https://en.wikipedia.org/wiki/Poll_(Unix)): VxWorks, Fuchsia, other Unix systems //! - [IOCP](https://learn.microsoft.com/en-us/windows/win32/fileio/i-o-completion-ports): Windows, Wine (version 7.13+) //! //! By default, polling is done in oneshot mode, which means interest in I/O events needs to //! be re-enabled after an event is delivered if we're interested in the next event of the same //! kind. However, level and edge triggered modes are also available for certain operating //! systems. See the documentation of the [`PollMode`] type for more information. //! //! Only one thread can be waiting for I/O events at a time. //! //! # Examples //! //! ```no_run //! use polling::{Event, Events, Poller}; //! use std::net::TcpListener; //! //! // Create a TCP listener. //! let socket = TcpListener::bind("127.0.0.1:8000")?; //! socket.set_nonblocking(true)?; //! let key = 7; // Arbitrary key identifying the socket. //! //! // Create a poller and register interest in readability on the socket. //! let poller = Poller::new()?; //! unsafe { //! poller.add(&socket, Event::readable(key))?; //! } //! //! // The event loop. //! let mut events = Events::new(); //! loop { //! // Wait for at least one I/O event. //! events.clear(); //! poller.wait(&mut events, None)?; //! //! for ev in events.iter() { //! if ev.key == key { //! // Perform a non-blocking accept operation. //! socket.accept()?; //! // Set interest in the next readability event. //! poller.modify(&socket, Event::readable(key))?; //! } //! } //! } //! //! poller.delete(&socket)?; //! # std::io::Result::Ok(()) //! ``` #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![allow(clippy::useless_conversion, clippy::unnecessary_cast, unused_unsafe)] #![cfg_attr(docsrs, feature(doc_cfg))] #![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::cell::Cell; use std::fmt; use std::io; use std::marker::PhantomData; use std::num::NonZeroUsize; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use std::time::{Duration, Instant}; use cfg_if::cfg_if; cfg_if! { // Note: This cfg is intended to make it easy for polling developers to test // the backend that uses poll, and is not a public API. if #[cfg(polling_test_poll_backend)] { mod poll; use poll as sys; } else if #[cfg(any(target_os = "linux", target_os = "android"))] { mod epoll; use epoll as sys; } else if #[cfg(any( target_os = "illumos", target_os = "solaris", ))] { mod port; use port as sys; } 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; use kqueue as sys; } else if #[cfg(any( target_os = "vxworks", target_os = "fuchsia", target_os = "horizon", unix, ))] { mod poll; use poll as sys; } else if #[cfg(target_os = "windows")] { mod iocp; use iocp as sys; } else { compile_error!("polling does not support this target OS"); } } pub mod os; /// Key associated with notifications. const NOTIFY_KEY: usize = usize::MAX; /// Indicates that a file descriptor or socket can read or write without blocking. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Event { /// Key identifying the file descriptor or socket. pub key: usize, /// Can it do a read operation without blocking? pub readable: bool, /// Can it do a write operation without blocking? pub writable: bool, /// System-specific event data. extra: sys::EventExtra, } /// The mode in which the poller waits for I/O events. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum PollMode { /// Poll in oneshot mode. /// /// In this mode, the poller will only deliver one event per file descriptor or socket. /// Once an event has been delivered, interest in the event needs to be re-enabled /// by calling `Poller::modify` or `Poller::add`. /// /// This is the default mode. Oneshot, /// Poll in level-triggered mode. /// /// Once an event has been delivered, polling will continue to deliver that event /// until interest in the event is disabled by calling `Poller::modify` or `Poller::delete`. /// /// Not all operating system support this mode. Trying to register a file descriptor with /// this mode in an unsupported operating system will raise an error. You can check if /// the operating system supports this mode by calling `Poller::supports_level`. Level, /// Poll in edge-triggered mode. /// /// Once an event has been delivered, polling will not deliver that event again unless /// a new event occurs. /// /// Not all operating system support this mode. Trying to register a file descriptor with /// this mode in an unsupported operating system will raise an error. You can check if /// the operating system supports this mode by calling `Poller::supports_edge`. Edge, /// Poll in both edge-triggered and oneshot mode. /// /// This mode is similar to the `Oneshot` mode, but it will only deliver one event per new /// event. /// /// Not all operating system support this mode. Trying to register a file descriptor with /// this mode in an unsupported operating system will raise an error. You can check if /// the operating system supports this mode by calling `Poller::supports_edge`. EdgeOneshot, } impl Event { /// Create a new event. pub const fn new(key: usize, readable: bool, writable: bool) -> Event { Event { key, readable, writable, extra: sys::EventExtra::empty(), } } /// All kinds of events (readable and writable). /// /// Equivalent to: `Event::new(key, true, true)` #[inline] pub const fn all(key: usize) -> Event { Event::new(key, true, true) } /// Only the readable event. /// /// Equivalent to: `Event::new(key, true, false)` #[inline] pub const fn readable(key: usize) -> Event { Event::new(key, true, false) } /// Only the writable event. /// /// Equivalent to: `Event::new(key, false, true)` #[inline] pub const fn writable(key: usize) -> Event { Event::new(key, false, true) } /// No events. /// /// Equivalent to: `Event::new(key, false, false)` #[inline] pub const fn none(key: usize) -> Event { Event::new(key, false, false) } /// Add interruption events to this interest. /// /// This usually indicates that the file descriptor or socket has been closed. It corresponds /// to the `EPOLLHUP` and `POLLHUP` events. /// /// Interruption events are only supported on the following platforms: /// /// - `epoll` /// - `poll` /// - IOCP /// - Event Ports /// /// On other platforms, this function is a no-op. #[inline] pub fn set_interrupt(&mut self, active: bool) { self.extra.set_hup(active); } /// Add interruption events to this interest. /// /// This usually indicates that the file descriptor or socket has been closed. It corresponds /// to the `EPOLLHUP` and `POLLHUP` events. /// /// Interruption events are only supported on the following platforms: /// /// - `epoll` /// - `poll` /// - IOCP /// - Event Ports /// /// On other platforms, this function is a no-op. #[inline] pub fn with_interrupt(mut self) -> Self { self.set_interrupt(true); self } /// Add priority events to this interest. /// /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and /// `POLLPRI` events. /// /// Priority events are only supported on the following platforms: /// /// - `epoll` /// - `poll` /// - IOCP /// - Event Ports /// /// On other platforms, this function is a no-op. #[inline] pub fn set_priority(&mut self, active: bool) { self.extra.set_pri(active); } /// Add priority events to this interest. /// /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and /// `POLLPRI` events. /// /// Priority events are only supported on the following platforms: /// /// - `epoll` /// - `poll` /// - IOCP /// - Event Ports /// /// On other platforms, this function is a no-op. #[inline] pub fn with_priority(mut self) -> Self { self.set_priority(true); self } /// Tell if this event is the result of an interrupt notification. /// /// This usually indicates that the file descriptor or socket has been closed. It corresponds /// to the `EPOLLHUP` and `POLLHUP` events. /// /// Interruption events are only supported on the following platforms: /// /// - `epoll` /// - `poll` /// - IOCP /// - Event Ports /// /// On other platforms, this always returns `false`. #[inline] pub fn is_interrupt(&self) -> bool { self.extra.is_hup() } /// Tell if this event is the result of a priority notification. /// /// This indicates that there is urgent data to read. It corresponds to the `EPOLLPRI` and /// `POLLPRI` events. /// /// Priority events are only supported on the following platforms: /// /// - `epoll` /// - `poll` /// - IOCP /// - Event Ports /// /// On other platforms, this always returns `false`. #[inline] pub fn is_priority(&self) -> bool { self.extra.is_pri() } /// Tells if this event is the result of a connection failure. /// /// This function checks if a TCP connection has failed. It corresponds to the `EPOLLERR` or `EPOLLHUP` event in Linux /// and `CONNECT_FAILED` event in Windows IOCP. /// /// # Examples /// /// ``` /// use std::{io, net}; /// // Assuming polling and socket2 are included as dependencies in Cargo.toml /// use polling::Event; /// use socket2::Type; /// /// fn main() -> io::Result<()> { /// let socket = socket2::Socket::new(socket2::Domain::IPV4, Type::STREAM, None)?; /// let poller = polling::Poller::new()?; /// unsafe { /// poller.add(&socket, Event::new(0, true, true))?; /// } /// let addr = net::SocketAddr::new(net::Ipv4Addr::LOCALHOST.into(), 8080); /// socket.set_nonblocking(true)?; /// let _ = socket.connect(&addr.into()); /// /// let mut events = polling::Events::new(); /// /// events.clear(); /// poller.wait(&mut events, None)?; /// /// let event = events.iter().next(); /// /// let event = match event { /// Some(event) => event, /// None => { /// println!("no event"); /// return Ok(()); /// }, /// }; /// /// println!("event: {:?}", event); /// if event /// .is_connect_failed() /// .unwrap_or_default() /// { /// println!("connect failed"); /// } /// /// Ok(()) /// } /// ``` /// /// # Returns /// /// Returns `Some(true)` if the connection has failed, `Some(false)` if the connection has not failed, /// or `None` if the platform does not support detecting this condition. #[inline] pub fn is_connect_failed(&self) -> Option { self.extra.is_connect_failed() } /// Remove any extra information from this event. #[inline] pub fn clear_extra(&mut self) { self.extra = sys::EventExtra::empty(); } /// Get a version of this event with no extra information. /// /// This is useful for comparing events with `==`. #[inline] pub fn with_no_extra(mut self) -> Self { self.clear_extra(); self } } /// Waits for I/O events. pub struct Poller { poller: sys::Poller, lock: Mutex<()>, notified: AtomicBool, } impl Poller { /// Creates a new poller. /// /// # Examples /// /// ``` /// use polling::Poller; /// /// let poller = Poller::new()?; /// # std::io::Result::Ok(()) /// ``` pub fn new() -> io::Result { Ok(Poller { poller: sys::Poller::new()?, lock: Mutex::new(()), notified: AtomicBool::new(false), }) } /// Tell whether or not this `Poller` supports level-triggered polling. pub fn supports_level(&self) -> bool { self.poller.supports_level() } /// Tell whether or not this `Poller` supports edge-triggered polling. pub fn supports_edge(&self) -> bool { self.poller.supports_edge() } /// Adds a file descriptor or socket to the poller. /// /// A file descriptor or socket is considered readable or writable when a read or write /// operation on it would not block. This doesn't mean the read or write operation will /// succeed, it only means the operation will return immediately. /// /// If interest is set in both readability and writability, the two kinds of events might be /// delivered either separately or together. /// /// For example, interest in `Event { key: 7, readable: true, writable: true }` might result in /// a single [`Event`] of the same form, or in two separate [`Event`]s: /// - `Event { key: 7, readable: true, writable: false }` /// - `Event { key: 7, readable: false, writable: true }` /// /// Note that interest in I/O events needs to be re-enabled using /// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in /// the next event of the same kind. /// /// It is possible to register interest in the same file descriptor or socket using multiple /// separate [`Poller`] instances. When the event is delivered, one or more [`Poller`]s are /// notified with that event. The exact number of [`Poller`]s notified depends on the /// underlying platform. When registering multiple sources into one event, the user should /// be careful to accommodate for events lost to other pollers. /// /// One may also register one source into other, non-`polling` event loops, like GLib's /// context. While the plumbing will vary from platform to platform, in general the [`Poller`] /// will act as if the source was registered with another [`Poller`], with the same caveats /// as above. /// /// # Safety /// /// The source must be [`delete()`]d from this `Poller` before it is dropped. /// /// [`delete()`]: Poller::delete /// /// # Errors /// /// This method returns an error in the following situations: /// /// * If `key` equals `usize::MAX` because that key is reserved for internal use. /// * If an error is returned by the syscall. /// /// # Examples /// /// Set interest in all events: /// /// ```no_run /// use polling::{Event, Poller}; /// /// let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// source.set_nonblocking(true)?; /// let key = 7; /// /// let poller = Poller::new()?; /// unsafe { /// poller.add(&source, Event::all(key))?; /// } /// poller.delete(&source)?; /// # std::io::Result::Ok(()) /// ``` pub unsafe fn add(&self, source: impl AsRawSource, interest: Event) -> io::Result<()> { self.add_with_mode(source, interest, PollMode::Oneshot) } /// Adds a file descriptor or socket to the poller in the specified mode. /// /// This is identical to the `add()` function, but allows specifying the /// polling mode to use for this socket. /// /// # Safety /// /// The source must be [`delete()`]d from this `Poller` before it is dropped. /// /// [`delete()`]: Poller::delete /// /// # Errors /// /// If the operating system does not support the specified mode, this function /// will return an error. pub unsafe fn add_with_mode( &self, source: impl AsRawSource, interest: Event, mode: PollMode, ) -> io::Result<()> { if interest.key == NOTIFY_KEY { return Err(io::Error::new( io::ErrorKind::InvalidInput, "the key is not allowed to be `usize::MAX`", )); } self.poller.add(source.raw(), interest, mode) } /// Modifies the interest in a file descriptor or socket. /// /// This method has the same behavior as [`add()`][`Poller::add()`] except it modifies the /// interest of a previously added file descriptor or socket. /// /// To use this method with a file descriptor or socket, you must first add it using /// [`add()`][`Poller::add()`]. /// /// Note that interest in I/O events needs to be re-enabled using /// [`modify()`][`Poller::modify()`] again after an event is delivered if we're interested in /// the next event of the same kind. /// /// # Errors /// /// This method returns an error in the following situations: /// /// * If `key` equals `usize::MAX` because that key is reserved for internal use. /// * If an error is returned by the syscall. /// /// # Examples /// /// To enable interest in all events: /// /// ```no_run /// # use polling::{Event, Poller}; /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # let key = 7; /// # let poller = Poller::new()?; /// # unsafe { poller.add(&source, Event::none(key))?; } /// poller.modify(&source, Event::all(key))?; /// # std::io::Result::Ok(()) /// ``` /// /// To enable interest in readable events and disable interest in writable events: /// /// ```no_run /// # use polling::{Event, Poller}; /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # let key = 7; /// # let poller = Poller::new()?; /// # unsafe { poller.add(&source, Event::none(key))?; } /// poller.modify(&source, Event::readable(key))?; /// # poller.delete(&source)?; /// # std::io::Result::Ok(()) /// ``` /// /// To disable interest in readable events and enable interest in writable events: /// /// ```no_run /// # use polling::{Event, Poller}; /// # let poller = Poller::new()?; /// # let key = 7; /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # unsafe { poller.add(&source, Event::none(key))? }; /// poller.modify(&source, Event::writable(key))?; /// # poller.delete(&source)?; /// # std::io::Result::Ok(()) /// ``` /// /// To disable interest in all events: /// /// ```no_run /// # use polling::{Event, Poller}; /// # let source = std::net::TcpListener::bind("127.0.0.1:0")?; /// # let key = 7; /// # let poller = Poller::new()?; /// # unsafe { poller.add(&source, Event::none(key))?; } /// poller.modify(&source, Event::none(key))?; /// # poller.delete(&source)?; /// # std::io::Result::Ok(()) /// ``` pub fn modify(&self, source: impl AsSource, interest: Event) -> io::Result<()> { self.modify_with_mode(source, interest, PollMode::Oneshot) } /// Modifies interest in a file descriptor or socket to the poller, but with the specified /// mode. /// /// This is identical to the `modify()` function, but allows specifying the polling mode /// to use for this socket. /// /// # Performance Notes /// /// This function can be used to change a source from one polling mode to another. However, /// on some platforms, this switch can cause delays in the delivery of events. /// /// # Errors /// /// If the operating system does not support the specified mode, this function will return /// an error. pub fn modify_with_mode( &self, source: impl AsSource, interest: Event, mode: PollMode, ) -> io::Result<()> { if interest.key == NOTIFY_KEY { return Err(io::Error::new( io::ErrorKind::InvalidInput, "the key is not allowed to be `usize::MAX`", )); } self.poller.modify(source.source(), interest, mode) } /// Removes a file descriptor or socket from the poller. /// /// Unlike [`add()`][`Poller::add()`], this method only removes the file descriptor or /// socket from the poller without putting it back into blocking mode. /// /// # Examples /// /// ``` /// use polling::{Event, Poller}; /// use std::net::TcpListener; /// /// let socket = TcpListener::bind("127.0.0.1:0")?; /// socket.set_nonblocking(true)?; /// let key = 7; /// /// let poller = Poller::new()?; /// unsafe { poller.add(&socket, Event::all(key))?; } /// poller.delete(&socket)?; /// # std::io::Result::Ok(()) /// ``` pub fn delete(&self, source: impl AsSource) -> io::Result<()> { self.poller.delete(source.source()) } /// Waits for at least one I/O event and returns the number of new events. /// /// New events will be appended to `events`. If necessary, make sure to clear the /// [`Events`][Events::clear()] before calling [`wait()`][`Poller::wait()`]! /// /// This method will return with no new events if a notification is delivered by the /// [`notify()`] method, or the timeout is reached. Sometimes it may even return with no events /// spuriously. /// /// Only one thread can wait on I/O. If another thread is already in [`wait()`], concurrent /// calls to this method will return immediately with no new events. /// /// If the operating system is ready to deliver a large number of events at once, this method /// may decide to deliver them in smaller batches. /// /// [`notify()`]: `Poller::notify()` /// [`wait()`]: `Poller::wait()` /// /// # Examples /// /// ``` /// use polling::{Event, Events, Poller}; /// use std::net::TcpListener; /// use std::time::Duration; /// /// let socket = TcpListener::bind("127.0.0.1:0")?; /// socket.set_nonblocking(true)?; /// let key = 7; /// /// let poller = Poller::new()?; /// unsafe { /// poller.add(&socket, Event::all(key))?; /// } /// /// let mut events = Events::new(); /// let n = poller.wait(&mut events, Some(Duration::from_secs(1)))?; /// poller.delete(&socket)?; /// # std::io::Result::Ok(()) /// ``` pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result { let span = tracing::trace_span!("Poller::wait", ?timeout); let _enter = span.enter(); if let Ok(_lock) = self.lock.try_lock() { let deadline = timeout.and_then(|timeout| Instant::now().checked_add(timeout)); loop { // Figure out how long to wait for. let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now())); // Wait for I/O events. if let Err(e) = self.poller.wait(&mut events.events, timeout) { // If the wait was interrupted by a signal, clear events and try again. if e.kind() == io::ErrorKind::Interrupted { events.clear(); continue; } else { return Err(e); } } // Clear the notification, if any. self.notified.swap(false, Ordering::SeqCst); // Indicate number of events. return Ok(events.len()); } } else { tracing::trace!("wait: skipping because another thread is already waiting on I/O"); Ok(0) } } /// Wakes up the current or the following invocation of [`wait()`]. /// /// If no thread is calling [`wait()`] right now, this method will cause the following call /// to wake up immediately. /// /// [`wait()`]: `Poller::wait()` /// /// # Examples /// /// ``` /// use polling::{Events, Poller}; /// /// let poller = Poller::new()?; /// /// // Notify the poller. /// poller.notify()?; /// /// let mut events = Events::new(); /// poller.wait(&mut events, None)?; // wakes up immediately /// assert!(events.is_empty()); /// # std::io::Result::Ok(()) /// ``` pub fn notify(&self) -> io::Result<()> { let span = tracing::trace_span!("Poller::notify"); let _enter = span.enter(); if self .notified .compare_exchange(false, true, Ordering::SeqCst, Ordering::SeqCst) .is_ok() { self.poller.notify()?; } Ok(()) } } /// A container for I/O events. pub struct Events { events: sys::Events, /// This is intended to be used from &mut, thread locally, so we should make it !Sync /// for consistency with the rest of the API. _not_sync: PhantomData>, } impl Default for Events { #[inline] fn default() -> Self { Self::new() } } impl Events { /// Create a new container for events, using the default capacity. /// /// The default capacity is 1024. /// /// # Examples /// /// ``` /// use polling::Events; /// /// let events = Events::new(); /// ``` #[inline] pub fn new() -> Self { // ESP-IDF has a low amount of RAM, so we use a smaller default capacity. #[cfg(target_os = "espidf")] const DEFAULT_CAPACITY: usize = 32; #[cfg(not(target_os = "espidf"))] const DEFAULT_CAPACITY: usize = 1024; Self::with_capacity(NonZeroUsize::new(DEFAULT_CAPACITY).unwrap()) } /// Create a new container with the provided capacity. /// /// # Examples /// /// ``` /// use polling::Events; /// use std::num::NonZeroUsize; /// /// let capacity = NonZeroUsize::new(1024).unwrap(); /// let events = Events::with_capacity(capacity); /// ``` #[inline] pub fn with_capacity(capacity: NonZeroUsize) -> Self { Self { events: sys::Events::with_capacity(capacity.get()), _not_sync: PhantomData, } } /// Create a new iterator over I/O events. /// /// This returns all of the events in the container, excluding the notification event. /// /// # Examples /// /// ``` /// use polling::{Event, Events, Poller}; /// use std::time::Duration; /// /// # fn main() -> std::io::Result<()> { /// let poller = Poller::new()?; /// let mut events = Events::new(); /// /// poller.wait(&mut events, Some(Duration::from_secs(0)))?; /// assert!(events.iter().next().is_none()); /// # Ok(()) } /// ``` #[inline] pub fn iter(&self) -> impl Iterator + '_ { self.events.iter().filter(|ev| ev.key != NOTIFY_KEY) } /// Delete all of the events in the container. /// /// # Examples /// /// ```no_run /// use polling::{Event, Events, Poller}; /// /// # fn main() -> std::io::Result<()> { /// let poller = Poller::new()?; /// let mut events = Events::new(); /// /// /* register some sources */ /// /// poller.wait(&mut events, None)?; /// /// events.clear(); /// # Ok(()) } /// ``` #[inline] pub fn clear(&mut self) { self.events.clear(); } /// Returns the number of events in the container. /// /// # Examples /// /// ``` /// use polling::Events; /// /// let events = Events::new(); /// assert_eq!(events.len(), 0); /// ``` #[inline] pub fn len(&self) -> usize { self.iter().count() } /// Returns `true` if the container contains no events. /// /// # Examples /// /// ``` /// use polling::Events; /// /// let events = Events::new(); /// assert!(events.is_empty()); /// ``` #[inline] pub fn is_empty(&self) -> bool { self.len() == 0 } /// Get the total capacity of the list. /// /// # Examples /// /// ``` /// use polling::Events; /// use std::num::NonZeroUsize; /// /// let cap = NonZeroUsize::new(10).unwrap(); /// let events = Events::with_capacity(std::num::NonZeroUsize::new(10).unwrap()); /// assert_eq!(events.capacity(), cap); /// ``` #[inline] pub fn capacity(&self) -> NonZeroUsize { NonZeroUsize::new(self.events.capacity()).unwrap() } } impl fmt::Debug for Events { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Events { .. }") } } #[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), ))] #[cfg_attr( docsrs, doc(cfg(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", ))) )] mod raw_fd_impl { use crate::Poller; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; impl AsRawFd for Poller { fn as_raw_fd(&self) -> RawFd { self.poller.as_raw_fd() } } impl AsFd for Poller { fn as_fd(&self) -> BorrowedFd<'_> { self.poller.as_fd() } } } #[cfg(windows)] #[cfg_attr(docsrs, doc(cfg(windows)))] mod raw_handle_impl { use crate::Poller; use std::os::windows::io::{AsHandle, AsRawHandle, BorrowedHandle, RawHandle}; impl AsRawHandle for Poller { fn as_raw_handle(&self) -> RawHandle { self.poller.as_raw_handle() } } impl AsHandle for Poller { fn as_handle(&self) -> BorrowedHandle<'_> { self.poller.as_handle() } } } impl fmt::Debug for Poller { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.poller.fmt(f) } } cfg_if! { if #[cfg(unix)] { use std::os::unix::io::{AsRawFd, RawFd, AsFd, BorrowedFd}; /// A resource with a raw file descriptor. pub trait AsRawSource { /// Returns the raw file descriptor. fn raw(&self) -> RawFd; } impl AsRawSource for &T { fn raw(&self) -> RawFd { self.as_raw_fd() } } impl AsRawSource for RawFd { fn raw(&self) -> RawFd { *self } } /// A resource with a borrowed file descriptor. pub trait AsSource: AsFd { /// Returns the borrowed file descriptor. fn source(&self) -> BorrowedFd<'_> { self.as_fd() } } impl AsSource for T {} } else if #[cfg(windows)] { use std::os::windows::io::{AsRawSocket, RawSocket, AsSocket, BorrowedSocket}; /// A resource with a raw socket. pub trait AsRawSource { /// Returns the raw socket. fn raw(&self) -> RawSocket; } impl AsRawSource for &T { fn raw(&self) -> RawSocket { self.as_raw_socket() } } impl AsRawSource for RawSocket { fn raw(&self) -> RawSocket { *self } } /// A resource with a borrowed socket. pub trait AsSource: AsSocket { /// Returns the borrowed socket. fn source(&self) -> BorrowedSocket<'_> { self.as_socket() } } impl AsSource for T {} } } #[allow(unused)] fn unsupported_error(err: impl Into) -> io::Error { io::Error::new(io::ErrorKind::Unsupported, err.into()) } fn _assert_send_and_sync() { fn assert_send() {} fn assert_sync() {} assert_send::(); assert_sync::(); assert_send::(); assert_sync::(); assert_send::(); // Events can be !Sync } polling-3.4.0/src/os/iocp.rs000064400000000000000000000165561046102023000140210ustar 00000000000000//! Functionality that is only availale for IOCP-based platforms. pub use crate::sys::CompletionPacket; use super::__private::PollerSealed; use crate::{Event, PollMode, Poller}; use std::io; use std::os::windows::io::{AsRawHandle, RawHandle}; use std::os::windows::prelude::{AsHandle, BorrowedHandle}; /// Extension trait for the [`Poller`] type that provides functionality specific to IOCP-based /// platforms. /// /// [`Poller`]: crate::Poller pub trait PollerIocpExt: PollerSealed { /// Post a new [`Event`] to the poller. /// /// # Examples /// /// ```rust /// use polling::{Poller, Event, Events}; /// use polling::os::iocp::{CompletionPacket, PollerIocpExt}; /// /// use std::thread; /// use std::sync::Arc; /// use std::time::Duration; /// /// # fn main() -> std::io::Result<()> { /// // Spawn a thread to wake us up after 100ms. /// let poller = Arc::new(Poller::new()?); /// thread::spawn({ /// let poller = poller.clone(); /// move || { /// let packet = CompletionPacket::new(Event::readable(0)); /// thread::sleep(Duration::from_millis(100)); /// poller.post(packet).unwrap(); /// } /// }); /// /// // Wait for the event. /// let mut events = Events::new(); /// poller.wait(&mut events, None)?; /// /// assert_eq!(events.len(), 1); /// # Ok(()) } /// ``` fn post(&self, packet: CompletionPacket) -> io::Result<()>; /// Add a waitable handle to this poller. /// /// 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 /// /// Once the object has been signalled, the poller will emit the `interest` event. /// /// # Safety /// /// The added handle must not be dropped before it is deleted. /// /// # Examples /// /// ```no_run /// use polling::{Poller, Event, Events, PollMode}; /// use polling::os::iocp::PollerIocpExt; /// /// use std::process::Command; /// /// // Spawn a new process. /// let mut child = Command::new("echo") /// .arg("Hello, world!") /// .spawn() /// .unwrap(); /// /// // Create a new poller. /// let poller = Poller::new().unwrap(); /// /// // Add the child process to the poller. /// unsafe { /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); /// } /// /// // Wait for the child process to exit. /// let mut events = Events::new(); /// poller.wait(&mut events, None).unwrap(); /// /// assert_eq!(events.len(), 1); /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); /// ``` unsafe fn add_waitable( &self, handle: impl AsRawWaitable, interest: Event, mode: PollMode, ) -> io::Result<()>; /// Modify an existing waitable handle. /// /// This function can be used to change the emitted event and/or mode of an existing waitable /// handle. The handle must have been previously added to the poller using [`add_waitable`]. /// /// [`add_waitable`]: Self::add_waitable /// /// # Examples /// /// ```no_run /// use polling::{Poller, Event, Events, PollMode}; /// use polling::os::iocp::PollerIocpExt; /// /// use std::process::Command; /// /// // Spawn a new process. /// let mut child = Command::new("echo") /// .arg("Hello, world!") /// .spawn() /// .unwrap(); /// /// // Create a new poller. /// let poller = Poller::new().unwrap(); /// /// // Add the child process to the poller. /// unsafe { /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); /// } /// /// // Wait for the child process to exit. /// let mut events = Events::new(); /// poller.wait(&mut events, None).unwrap(); /// /// assert_eq!(events.len(), 1); /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); /// /// // Modify the waitable handle. /// poller.modify_waitable(&child, Event::readable(0), PollMode::Oneshot).unwrap(); /// ``` fn modify_waitable( &self, handle: impl AsWaitable, interest: Event, mode: PollMode, ) -> io::Result<()>; /// Remove a waitable handle from this poller. /// /// This function can be used to remove a waitable handle from the poller. The handle must /// have been previously added to the poller using [`add_waitable`]. /// /// [`add_waitable`]: Self::add_waitable /// /// # Examples /// /// ```no_run /// use polling::{Poller, Event, Events, PollMode}; /// use polling::os::iocp::PollerIocpExt; /// /// use std::process::Command; /// /// // Spawn a new process. /// let mut child = Command::new("echo") /// .arg("Hello, world!") /// .spawn() /// .unwrap(); /// /// // Create a new poller. /// let poller = Poller::new().unwrap(); /// /// // Add the child process to the poller. /// unsafe { /// poller.add_waitable(&child, Event::all(0), PollMode::Oneshot).unwrap(); /// } /// /// // Wait for the child process to exit. /// let mut events = Events::new(); /// poller.wait(&mut events, None).unwrap(); /// /// assert_eq!(events.len(), 1); /// assert_eq!(events.iter().next().unwrap(), Event::all(0)); /// /// // Remove the waitable handle. /// poller.remove_waitable(&child).unwrap(); /// ``` fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()>; } impl PollerIocpExt for Poller { fn post(&self, packet: CompletionPacket) -> io::Result<()> { self.poller.post(packet) } unsafe fn add_waitable( &self, handle: impl AsRawWaitable, event: Event, mode: PollMode, ) -> io::Result<()> { self.poller .add_waitable(handle.as_raw_handle(), event, mode) } fn modify_waitable( &self, handle: impl AsWaitable, interest: Event, mode: PollMode, ) -> io::Result<()> { self.poller .modify_waitable(handle.as_waitable().as_raw_handle(), interest, mode) } fn remove_waitable(&self, handle: impl AsWaitable) -> io::Result<()> { self.poller .remove_waitable(handle.as_waitable().as_raw_handle()) } } /// A type that represents a waitable handle. pub trait AsRawWaitable { /// Returns the raw handle of this waitable. fn as_raw_handle(&self) -> RawHandle; } impl AsRawWaitable for RawHandle { fn as_raw_handle(&self) -> RawHandle { *self } } impl AsRawWaitable for &T { fn as_raw_handle(&self) -> RawHandle { AsRawHandle::as_raw_handle(*self) } } /// A type that represents a waitable handle. pub trait AsWaitable: AsHandle { /// Returns the raw handle of this waitable. fn as_waitable(&self) -> BorrowedHandle<'_> { self.as_handle() } } impl AsWaitable for T {} polling-3.4.0/src/os/kqueue.rs000064400000000000000000000221551046102023000143560ustar 00000000000000//! Functionality that is only available for `kqueue`-based platforms. use crate::sys::{mode_to_flags, SourceId}; use crate::{PollMode, Poller}; use std::io; use std::marker::PhantomData; use std::process::Child; use std::time::Duration; use rustix::event::kqueue; use super::__private::PollerSealed; use __private::FilterSealed; // TODO(notgull): We should also have EVFILT_AIO, EVFILT_VNODE and EVFILT_USER. However, the current // API makes it difficult to effectively express events from these filters. At the next breaking // change, we should change `Event` to be a struct with private fields, and encode additional // information in there. /// Functionality that is only available for `kqueue`-based platforms. /// /// `kqueue` is able to monitor much more than just read/write readiness on file descriptors. Using /// this extension trait, you can monitor for signals, process exits, and more. See the implementors /// of the [`Filter`] trait for more information. pub trait PollerKqueueExt: PollerSealed { /// Add a filter to the poller. /// /// This is similar to [`add`][Poller::add], but it allows you to specify a filter instead of /// a socket. See the implementors of the [`Filter`] trait for more information. /// /// # Examples /// /// ```no_run /// use polling::{Events, Poller, PollMode}; /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; /// /// let poller = Poller::new().unwrap(); /// /// // Register the SIGINT signal. /// poller.add_filter(Signal(rustix::process::Signal::Int as _), 0, PollMode::Oneshot).unwrap(); /// /// // Wait for the signal. /// let mut events = Events::new(); /// poller.wait(&mut events, None).unwrap(); /// # let _ = events; /// ``` fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; /// Modify a filter in the poller. /// /// This is similar to [`modify`][Poller::modify], but it allows you to specify a filter /// instead of a socket. See the implementors of the [`Filter`] trait for more information. /// /// # Examples /// /// ```no_run /// use polling::{Events, Poller, PollMode}; /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; /// /// let poller = Poller::new().unwrap(); /// /// // Register the SIGINT signal. /// poller.add_filter(Signal(rustix::process::Signal::Int as _), 0, PollMode::Oneshot).unwrap(); /// /// // Re-register with a different key. /// poller.modify_filter(Signal(rustix::process::Signal::Int as _), 1, PollMode::Oneshot).unwrap(); /// /// // Wait for the signal. /// let mut events = Events::new(); /// poller.wait(&mut events, None).unwrap(); /// # let _ = events; /// ``` fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()>; /// Remove a filter from the poller. /// /// This is used to remove filters that were previously added with /// [`add_filter`](PollerKqueueExt::add_filter). /// /// # Examples /// /// ```no_run /// use polling::{Poller, PollMode}; /// use polling::os::kqueue::{Filter, PollerKqueueExt, Signal}; /// /// let poller = Poller::new().unwrap(); /// /// // Register the SIGINT signal. /// poller.add_filter(Signal(rustix::process::Signal::Int as _), 0, PollMode::Oneshot).unwrap(); /// /// // Remove the filter. /// poller.delete_filter(Signal(rustix::process::Signal::Int as _)).unwrap(); /// ``` fn delete_filter(&self, filter: F) -> io::Result<()>; } impl PollerKqueueExt for Poller { #[inline(always)] fn add_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { // No difference between adding and modifying in kqueue. self.poller.add_source(filter.source_id())?; self.modify_filter(filter, key, mode) } fn modify_filter(&self, filter: F, key: usize, mode: PollMode) -> io::Result<()> { self.poller.has_source(filter.source_id())?; // Convert the filter into a kevent. let event = filter.filter(kqueue::EventFlags::ADD | mode_to_flags(mode), key); // Modify the filter. self.poller.submit_changes([event]) } fn delete_filter(&self, filter: F) -> io::Result<()> { // Convert the filter into a kevent. let event = filter.filter(kqueue::EventFlags::DELETE, 0); // Delete the filter. self.poller.submit_changes([event])?; self.poller.remove_source(filter.source_id()) } } /// A filter that can be registered into a `kqueue`. pub trait Filter: FilterSealed {} unsafe impl FilterSealed for &T { #[inline(always)] fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { (**self).filter(flags, key) } #[inline(always)] fn source_id(&self) -> SourceId { (**self).source_id() } } impl Filter for &T {} /// Monitor this signal number. /// /// No matter what `PollMode` is specified, this filter will always be /// oneshot-only. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Signal(pub std::os::raw::c_int); unsafe impl FilterSealed for Signal { #[inline(always)] fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { kqueue::Event::new( kqueue::EventFilter::Signal { signal: rustix::process::Signal::from_raw(self.0).expect("invalid signal number"), times: 0, }, flags | kqueue::EventFlags::RECEIPT, key as _, ) } #[inline(always)] fn source_id(&self) -> SourceId { SourceId::Signal(self.0) } } impl Filter for Signal {} /// Monitor a child process. #[derive(Debug)] pub struct Process<'a> { /// The process ID to monitor. pid: rustix::process::Pid, /// The operation to monitor. ops: ProcessOps, /// Lifetime of the underlying process. _lt: PhantomData<&'a Child>, } /// The operations that a monitored process can perform. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum ProcessOps { /// The process exited. Exit, /// The process was forked. Fork, /// The process executed a new process. Exec, } impl<'a> Process<'a> { /// Monitor a child process. /// /// # Safety /// /// Once registered into the `Poller`, the `Child` object must outlive this filter's /// registration into the poller. pub unsafe fn new(child: &'a Child, ops: ProcessOps) -> Self { Self { pid: rustix::process::Pid::from_child(child), ops, _lt: PhantomData, } } /// Create a `Process` from a PID. /// /// # Safety /// /// The PID must be tied to an actual child process. pub unsafe fn from_pid(pid: std::num::NonZeroI32, ops: ProcessOps) -> Self { Self { pid: unsafe { rustix::process::Pid::from_raw_unchecked(pid.get()) }, ops, _lt: PhantomData, } } } unsafe impl FilterSealed for Process<'_> { #[inline(always)] fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { let events = match self.ops { ProcessOps::Exit => kqueue::ProcessEvents::EXIT, ProcessOps::Fork => kqueue::ProcessEvents::FORK, ProcessOps::Exec => kqueue::ProcessEvents::EXEC, }; kqueue::Event::new( kqueue::EventFilter::Proc { // SAFETY: We know that the PID is nonzero. pid: self.pid, flags: events, }, flags | kqueue::EventFlags::RECEIPT, key as _, ) } #[inline(always)] fn source_id(&self) -> SourceId { // SAFETY: We know that the PID is nonzero SourceId::Pid(self.pid) } } impl Filter for Process<'_> {} /// Wait for a timeout to expire. /// /// Modifying the timeout after it has been added to the poller will reset it. #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Timer { /// Identifier for the timer. pub id: usize, /// The timeout to wait for. pub timeout: Duration, } unsafe impl FilterSealed for Timer { fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event { kqueue::Event::new( kqueue::EventFilter::Timer { ident: self.id as _, timer: Some(self.timeout), }, flags | kqueue::EventFlags::RECEIPT, key as _, ) } #[inline(always)] fn source_id(&self) -> SourceId { SourceId::Timer(self.id) } } impl Filter for Timer {} mod __private { use crate::sys::SourceId; use rustix::event::kqueue; #[doc(hidden)] pub unsafe trait FilterSealed { /// Get the filter for the given event. /// /// This filter's flags must have `EV_RECEIPT`. fn filter(&self, flags: kqueue::EventFlags, key: usize) -> kqueue::Event; /// Get the source ID for this source. fn source_id(&self) -> SourceId; } } polling-3.4.0/src/os.rs000064400000000000000000000010061046102023000130470ustar 00000000000000//! Platform-specific functionality. #[cfg(all( 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", ), not(polling_test_poll_backend), ))] pub mod kqueue; #[cfg(target_os = "windows")] pub mod iocp; mod __private { #[doc(hidden)] pub trait PollerSealed {} impl PollerSealed for crate::Poller {} } polling-3.4.0/src/poll.rs000064400000000000000000000522551046102023000134100ustar 00000000000000//! Bindings to poll (VxWorks, Fuchsia, other Unix systems). use std::collections::HashMap; use std::io; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; use std::sync::{Condvar, Mutex}; use std::time::{Duration, Instant}; use rustix::event::{poll, PollFd, PollFlags}; use rustix::fd::{AsFd, AsRawFd, BorrowedFd}; // std::os::unix doesn't exist on Fuchsia type RawFd = std::os::raw::c_int; use crate::{Event, PollMode}; /// Interface to poll. #[derive(Debug)] pub struct Poller { /// File descriptors to poll. fds: Mutex, /// Notification pipe for waking up the poller. /// /// On all platforms except ESP IDF, the `pipe` syscall is used. /// On ESP IDF, the `eventfd` syscall is used instead. notify: notify::Notify, /// The number of operations (`add`, `modify` or `delete`) that are currently waiting on the /// mutex to become free. When this is nonzero, `wait` must be suspended until it reaches zero /// again. waiting_operations: AtomicUsize, /// Whether `wait` has been notified by the user. notified: AtomicBool, /// The condition variable that gets notified when `waiting_operations` reaches zero or /// `notified` becomes true. /// /// This is used with the `fds` mutex. operations_complete: Condvar, } /// The file descriptors to poll in a `Poller`. #[derive(Debug)] struct Fds { /// The list of `pollfds` taken by poll. /// /// The first file descriptor is always present and is used to notify the poller. It is also /// stored in `notify_read`. poll_fds: Vec>, /// The map of each file descriptor to data associated with it. This does not include the file /// descriptors `notify_read` or `notify_write`. fd_data: HashMap, } /// Data associated with a file descriptor in a poller. #[derive(Debug)] struct FdData { /// The index into `poll_fds` this file descriptor is. poll_fds_index: usize, /// The key of the `Event` associated with this file descriptor. key: usize, /// Whether to remove this file descriptor from the poller on the next call to `wait`. remove: bool, } impl Poller { /// Creates a new poller. pub fn new() -> io::Result { let notify = notify::Notify::new()?; tracing::trace!(?notify, "new"); Ok(Self { fds: Mutex::new(Fds { poll_fds: vec![PollFd::from_borrowed_fd( // SAFETY: `notify.fd()` will remain valid until we drop `self`. unsafe { BorrowedFd::borrow_raw(notify.fd().as_raw_fd()) }, notify.poll_flags(), )], fd_data: HashMap::new(), }), notify, waiting_operations: AtomicUsize::new(0), operations_complete: Condvar::new(), notified: AtomicBool::new(false), }) } /// Whether this poller supports level-triggered events. pub fn supports_level(&self) -> bool { true } /// Whether the poller supports edge-triggered events. pub fn supports_edge(&self) -> bool { false } /// Adds a new file descriptor. pub fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { if self.notify.has_fd(fd) { return Err(io::Error::from(io::ErrorKind::InvalidInput)); } let span = tracing::trace_span!( "add", notify_read = ?self.notify.fd().as_raw_fd(), ?fd, ?ev, ); let _enter = span.enter(); self.modify_fds(|fds| { if fds.fd_data.contains_key(&fd) { return Err(io::Error::from(io::ErrorKind::AlreadyExists)); } let poll_fds_index = fds.poll_fds.len(); fds.fd_data.insert( fd, FdData { poll_fds_index, key: ev.key, remove: cvt_mode_as_remove(mode)?, }, ); fds.poll_fds.push(PollFd::from_borrowed_fd( // SAFETY: Until we have I/O safety, assume that `fd` is valid forever. unsafe { BorrowedFd::borrow_raw(fd) }, poll_events(ev), )); Ok(()) }) } /// Modifies an existing file descriptor. pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { if self.notify.has_fd(fd.as_raw_fd()) { return Err(io::Error::from(io::ErrorKind::InvalidInput)); } let span = tracing::trace_span!( "modify", notify_read = ?self.notify.fd().as_raw_fd(), ?fd, ?ev, ); let _enter = span.enter(); self.modify_fds(|fds| { let data = fds .fd_data .get_mut(&fd.as_raw_fd()) .ok_or(io::ErrorKind::NotFound)?; data.key = ev.key; let poll_fds_index = data.poll_fds_index; // SAFETY: This is essentially transmuting a `PollFd<'a>` to a `PollFd<'static>`, which // only works if it's removed in time with `delete()`. fds.poll_fds[poll_fds_index] = PollFd::from_borrowed_fd( unsafe { BorrowedFd::borrow_raw(fd.as_raw_fd()) }, poll_events(ev), ); data.remove = cvt_mode_as_remove(mode)?; Ok(()) }) } /// Deletes a file descriptor. pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { if self.notify.has_fd(fd.as_raw_fd()) { return Err(io::Error::from(io::ErrorKind::InvalidInput)); } let span = tracing::trace_span!( "delete", notify_read = ?self.notify.fd().as_raw_fd(), ?fd, ); let _enter = span.enter(); self.modify_fds(|fds| { let data = fds .fd_data .remove(&fd.as_raw_fd()) .ok_or(io::ErrorKind::NotFound)?; fds.poll_fds.swap_remove(data.poll_fds_index); if let Some(swapped_pollfd) = fds.poll_fds.get(data.poll_fds_index) { fds.fd_data .get_mut(&swapped_pollfd.as_fd().as_raw_fd()) .unwrap() .poll_fds_index = data.poll_fds_index; } Ok(()) }) } /// Waits for I/O events with an optional timeout. pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result<()> { let span = tracing::trace_span!( "wait", notify_read = ?self.notify.fd().as_raw_fd(), ?timeout, ); let _enter = span.enter(); let deadline = timeout.and_then(|t| Instant::now().checked_add(t)); events.inner.clear(); let mut fds = self.fds.lock().unwrap(); loop { // Complete all current operations. loop { if self.notified.swap(false, Ordering::SeqCst) { // `notify` will have sent a notification in case we were polling. We weren't, // so remove it. return self.notify.pop_notification(); } else if self.waiting_operations.load(Ordering::SeqCst) == 0 { break; } fds = self.operations_complete.wait(fds).unwrap(); } // Convert the timeout to milliseconds. let timeout_ms = deadline .map(|deadline| { let timeout = deadline.saturating_duration_since(Instant::now()); // Round up to a whole millisecond. let mut ms = timeout.as_millis().try_into().unwrap_or(std::u64::MAX); if Duration::from_millis(ms) < timeout { ms = ms.saturating_add(1); } ms.try_into().unwrap_or(std::i32::MAX) }) .unwrap_or(-1); // Perform the poll. let num_events = poll(&mut fds.poll_fds, timeout_ms)?; let notified = !fds.poll_fds[0].revents().is_empty(); let num_fd_events = if notified { num_events - 1 } else { num_events }; tracing::trace!(?num_events, ?notified, ?num_fd_events, "new events",); // Read all notifications. if notified { self.notify.pop_all_notifications()?; } // If the only event that occurred during polling was notification and it wasn't to // exit, another thread is trying to perform an operation on the fds. Continue the // loop. if !self.notified.swap(false, Ordering::SeqCst) && num_fd_events == 0 && notified { continue; } // Store the events if there were any. if num_fd_events > 0 { let fds = &mut *fds; events.inner.reserve(num_fd_events); for fd_data in fds.fd_data.values_mut() { let poll_fd = &mut fds.poll_fds[fd_data.poll_fds_index]; if !poll_fd.revents().is_empty() { // Store event let revents = poll_fd.revents(); events.inner.push(Event { key: fd_data.key, readable: revents.intersects(read_events()), writable: revents.intersects(write_events()), extra: EventExtra { flags: revents }, }); // Remove interest if necessary if fd_data.remove { *poll_fd = PollFd::from_borrowed_fd( unsafe { BorrowedFd::borrow_raw(poll_fd.as_fd().as_raw_fd()) }, PollFlags::empty(), ); } if events.inner.len() == num_fd_events { break; } } } } break; } Ok(()) } /// Sends a notification to wake up the current or next `wait()` call. pub fn notify(&self) -> io::Result<()> { let span = tracing::trace_span!( "notify", notify_read = ?self.notify.fd().as_raw_fd(), ); let _enter = span.enter(); if !self.notified.swap(true, Ordering::SeqCst) { self.notify.notify()?; self.operations_complete.notify_one(); } Ok(()) } /// Perform a modification on `fds`, interrupting the current caller of `wait` if it's running. fn modify_fds(&self, f: impl FnOnce(&mut Fds) -> io::Result<()>) -> io::Result<()> { self.waiting_operations.fetch_add(1, Ordering::SeqCst); // Wake up the current caller of `wait` if there is one. let sent_notification = self.notify.notify().is_ok(); let mut fds = self.fds.lock().unwrap(); // If there was no caller of `wait` our notification was not removed from the pipe. if sent_notification { let _ = self.notify.pop_notification(); } let res = f(&mut fds); if self.waiting_operations.fetch_sub(1, Ordering::SeqCst) == 1 { self.operations_complete.notify_one(); } res } } /// Get the input poll events for the given event. fn poll_events(ev: Event) -> PollFlags { (if ev.readable { PollFlags::IN | PollFlags::PRI } else { PollFlags::empty() }) | (if ev.writable { PollFlags::OUT | PollFlags::WRBAND } else { PollFlags::empty() }) } /// Returned poll events for reading. fn read_events() -> PollFlags { PollFlags::IN | PollFlags::PRI | PollFlags::HUP | PollFlags::ERR } /// Returned poll events for writing. fn write_events() -> PollFlags { PollFlags::OUT | PollFlags::WRBAND | PollFlags::HUP | PollFlags::ERR } /// A list of reported I/O events. pub struct Events { inner: Vec, } impl Events { /// Creates an empty list. pub fn with_capacity(cap: usize) -> Events { Self { inner: Vec::with_capacity(cap), } } /// Iterates over I/O events. pub fn iter(&self) -> impl Iterator + '_ { self.inner.iter().copied() } /// Clear the list. pub fn clear(&mut self) { self.inner.clear(); } /// Get the capacity of the list. pub fn capacity(&self) -> usize { self.inner.capacity() } } /// Extra information associated with an event. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct EventExtra { /// Flags associated with this event. flags: PollFlags, } impl EventExtra { /// Creates an empty set of extra information. #[inline] pub const fn empty() -> Self { Self { flags: PollFlags::empty(), } } /// Set the interrupt flag. #[inline] pub fn set_hup(&mut self, value: bool) { self.flags.set(PollFlags::HUP, value); } /// Set the priority flag. #[inline] pub fn set_pri(&mut self, value: bool) { self.flags.set(PollFlags::PRI, value); } /// Is this an interrupt event? #[inline] pub fn is_hup(&self) -> bool { self.flags.contains(PollFlags::HUP) } /// Is this a priority event? #[inline] pub fn is_pri(&self) -> bool { self.flags.contains(PollFlags::PRI) } #[inline] pub fn is_connect_failed(&self) -> Option { Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP)) } } fn cvt_mode_as_remove(mode: PollMode) -> io::Result { match mode { PollMode::Oneshot => Ok(true), PollMode::Level => Ok(false), _ => Err(crate::unsupported_error( "edge-triggered I/O events are not supported in poll()", )), } } #[cfg(not(target_os = "espidf"))] mod notify { use std::io; use rustix::event::PollFlags; use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use rustix::fs::{fcntl_getfl, fcntl_setfl, OFlags}; use rustix::io::{fcntl_getfd, fcntl_setfd, read, write, FdFlags}; #[cfg(not(target_os = "haiku"))] use rustix::pipe::pipe_with; use rustix::pipe::{pipe, PipeFlags}; /// A notification pipe. /// /// This implementation uses a pipe to send notifications. #[derive(Debug)] pub(super) struct Notify { /// The file descriptor of the read half of the notify pipe. This is also stored as the first /// file descriptor in `fds.poll_fds`. read_pipe: OwnedFd, /// The file descriptor of the write half of the notify pipe. /// /// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the /// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs /// to occur (in which case `Poller::waiting_operations` would have been incremented). write_pipe: OwnedFd, } impl Notify { /// Creates a new notification pipe. pub(super) fn new() -> io::Result { let fallback_pipe = |_| { let (read_pipe, write_pipe) = pipe()?; fcntl_setfd(&read_pipe, fcntl_getfd(&read_pipe)? | FdFlags::CLOEXEC)?; fcntl_setfd(&write_pipe, fcntl_getfd(&write_pipe)? | FdFlags::CLOEXEC)?; io::Result::Ok((read_pipe, write_pipe)) }; #[cfg(not(target_os = "haiku"))] let (read_pipe, write_pipe) = pipe_with(PipeFlags::CLOEXEC).or_else(fallback_pipe)?; #[cfg(target_os = "haiku")] let (read_pipe, write_pipe) = fallback_pipe(PipeFlags::CLOEXEC)?; // Put the reading side into non-blocking mode. fcntl_setfl(&read_pipe, fcntl_getfl(&read_pipe)? | OFlags::NONBLOCK)?; Ok(Self { read_pipe, write_pipe, }) } /// Provides the file handle of the read half of the notify pipe that needs to be registered by the `Poller`. pub(super) fn fd(&self) -> BorrowedFd<'_> { self.read_pipe.as_fd() } /// Provides the poll flags to be used when registering the read half of the botify pipe with the `Poller`. pub(super) fn poll_flags(&self) -> PollFlags { PollFlags::RDNORM } /// Notifies the `Poller` instance via the write half of the notify pipe. pub(super) fn notify(&self) -> Result<(), io::Error> { write(&self.write_pipe, &[0; 1])?; Ok(()) } /// Pops a notification (if any) from the pipe. pub(super) fn pop_notification(&self) -> Result<(), io::Error> { read(&self.read_pipe, &mut [0; 1])?; Ok(()) } /// Pops all notifications from the pipe. pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> { while read(&self.read_pipe, &mut [0; 64]).is_ok() {} Ok(()) } /// Whether this raw file descriptor is associated with this notifier. pub(super) fn has_fd(&self, fd: RawFd) -> bool { self.read_pipe.as_raw_fd() == fd || self.write_pipe.as_raw_fd() == fd } } } #[cfg(target_os = "espidf")] mod notify { use std::io; use std::mem; use rustix::event::PollFlags; use rustix::event::{eventfd, EventfdFlags}; use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd}; use rustix::io::{read, write}; /// A notification pipe. /// /// This implementation uses ther `eventfd` syscall to send notifications. #[derive(Debug)] pub(super) struct Notify { /// The file descriptor of the eventfd object. This is also stored as the first /// file descriptor in `fds.poll_fds`. /// /// Data is written to this to wake up the current instance of `Poller::wait`, which can occur when the /// user notifies it (in which case `Poller::notified` would have been set) or when an operation needs /// to occur (in which case `Poller::waiting_operations` would have been incremented). event_fd: OwnedFd, } impl Notify { /// Creates a new notification pipe. pub(super) fn new() -> io::Result { // Note that the eventfd() implementation in ESP-IDF deviates from the specification in the following ways: // 1) The file descriptor is always in a non-blocking mode, as if EFD_NONBLOCK was passed as a flag; // passing EFD_NONBLOCK or calling fcntl(.., F_GETFL/F_SETFL) on the eventfd() file descriptor is not supported // 2) It always returns the counter value, even if it is 0. This is contrary to the specification which mandates // that it should instead fail with EAGAIN // // (1) is not a problem for us, as we want the eventfd() file descriptor to be in a non-blocking mode anyway // (2) is also not a problem, as long as we don't try to read the counter value in an endless loop when we detect being notified let flags = EventfdFlags::empty(); let event_fd = eventfd(0, flags).map_err(|err| { match err { rustix::io::Errno::PERM => { // EPERM can happen if the eventfd isn't initialized yet. // Tell the user to call esp_vfs_eventfd_register. io::Error::new( io::ErrorKind::PermissionDenied, "failed to initialize eventfd for polling, try calling `esp_vfs_eventfd_register`" ) }, err => io::Error::from(err), } })?; Ok(Self { event_fd }) } /// Provides the eventfd file handle that needs to be registered by the `Poller`. pub(super) fn fd(&self) -> BorrowedFd<'_> { self.event_fd.as_fd() } /// Provides the eventfd file handle poll flags to be used when registering it with the `Poller`. pub(super) fn poll_flags(&self) -> PollFlags { PollFlags::IN } /// Notifies the `Poller` instance via the eventfd file descriptor. pub(super) fn notify(&self) -> Result<(), io::Error> { write(&self.event_fd, &1u64.to_ne_bytes())?; Ok(()) } /// Pops a notification (if any) from the eventfd file descriptor. pub(super) fn pop_notification(&self) -> Result<(), io::Error> { read(&self.event_fd, &mut [0; mem::size_of::()])?; Ok(()) } /// Pops all notifications from the eventfd file descriptor. /// Since the eventfd object accumulates all writes in a single 64 bit value, /// this operation is - in fact - equivalent to `pop_notification`. pub(super) fn pop_all_notifications(&self) -> Result<(), io::Error> { let _ = self.pop_notification(); Ok(()) } /// Whether this raw file descriptor is associated with this notifier. pub(super) fn has_fd(&self, fd: RawFd) -> bool { self.event_fd.as_raw_fd() == fd } } } polling-3.4.0/src/port.rs000064400000000000000000000153371046102023000134260ustar 00000000000000//! Bindings to event port (illumos, Solaris). use std::io; use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd}; use std::time::Duration; use rustix::event::{port, PollFlags}; use rustix::fd::OwnedFd; use rustix::io::{fcntl_getfd, fcntl_setfd, FdFlags}; use crate::{Event, PollMode}; /// Interface to event ports. #[derive(Debug)] pub struct Poller { /// File descriptor for the port instance. port_fd: OwnedFd, } impl Poller { /// Creates a new poller. pub fn new() -> io::Result { let port_fd = port::port_create()?; let flags = fcntl_getfd(&port_fd)?; fcntl_setfd(&port_fd, flags | FdFlags::CLOEXEC)?; tracing::trace!( port_fd = ?port_fd.as_raw_fd(), "new", ); Ok(Poller { port_fd }) } /// Whether this poller supports level-triggered events. pub fn supports_level(&self) -> bool { false } /// Whether this poller supports edge-triggered events. pub fn supports_edge(&self) -> bool { false } /// Adds a file descriptor. /// /// # Safety /// /// The `fd` must be a valid file descriptor and it must last until it is deleted. pub unsafe fn add(&self, fd: RawFd, ev: Event, mode: PollMode) -> io::Result<()> { // File descriptors don't need to be added explicitly, so just modify the interest. self.modify(BorrowedFd::borrow_raw(fd), ev, mode) } /// Modifies an existing file descriptor. pub fn modify(&self, fd: BorrowedFd<'_>, ev: Event, mode: PollMode) -> io::Result<()> { let span = tracing::trace_span!( "modify", port_fd = ?self.port_fd.as_raw_fd(), ?fd, ?ev, ); let _enter = span.enter(); let mut flags = PollFlags::empty(); if ev.readable { flags |= read_flags(); } if ev.writable { flags |= write_flags(); } if mode != PollMode::Oneshot { return Err(crate::unsupported_error( "this kind of event is not supported with event ports", )); } unsafe { port::port_associate_fd(&self.port_fd, fd, flags, ev.key as _)?; } Ok(()) } /// Deletes a file descriptor. pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> { let span = tracing::trace_span!( "delete", port_fd = ?self.port_fd.as_raw_fd(), ?fd, ); let _enter = span.enter(); let result = unsafe { port::port_dissociate_fd(&self.port_fd, fd) }; if let Err(e) = result { match e { rustix::io::Errno::NOENT => return Ok(()), _ => return Err(e.into()), } } Ok(()) } /// Waits for I/O events with an optional timeout. pub fn wait(&self, events: &mut Events, timeout: Option) -> io::Result<()> { let span = tracing::trace_span!( "wait", port_fd = ?self.port_fd.as_raw_fd(), ?timeout, ); let _enter = span.enter(); // Wait for I/O events. let res = port::port_getn(&self.port_fd, &mut events.list, 1, timeout); tracing::trace!( port_fd = ?self.port_fd, res = ?events.list.len(), "new events" ); // Event ports sets the return value to -1 and returns ETIME on timer expire. The number of // returned events is stored in nget, but in our case it should always be 0 since we set // nget to 1 initially. if let Err(e) = res { match e { rustix::io::Errno::TIME => {} _ => return Err(e.into()), } } Ok(()) } /// Sends a notification to wake up the current or next `wait()` call. pub fn notify(&self) -> io::Result<()> { const PORT_SOURCE_USER: i32 = 3; let span = tracing::trace_span!( "notify", port_fd = ?self.port_fd.as_raw_fd(), ); let _enter = span.enter(); // Use port_send to send a notification to the port. port::port_send(&self.port_fd, PORT_SOURCE_USER, crate::NOTIFY_KEY as _)?; Ok(()) } } impl AsRawFd for Poller { fn as_raw_fd(&self) -> RawFd { self.port_fd.as_raw_fd() } } impl AsFd for Poller { fn as_fd(&self) -> BorrowedFd<'_> { self.port_fd.as_fd() } } /// Poll flags for all possible readability events. fn read_flags() -> PollFlags { PollFlags::IN | PollFlags::HUP | PollFlags::ERR | PollFlags::PRI } /// Poll flags for all possible writability events. fn write_flags() -> PollFlags { PollFlags::OUT | PollFlags::HUP | PollFlags::ERR } /// A list of reported I/O events. pub struct Events { list: Vec, } unsafe impl Send for Events {} impl Events { /// Creates an empty list. pub fn with_capacity(cap: usize) -> Events { Events { list: Vec::with_capacity(cap), } } /// Iterates over I/O events. pub fn iter(&self) -> impl Iterator + '_ { self.list.iter().map(|ev| { let flags = PollFlags::from_bits_truncate(ev.events() as _); Event { key: ev.userdata() as usize, readable: flags.intersects(read_flags()), writable: flags.intersects(write_flags()), extra: EventExtra { flags }, } }) } /// Clear the list. pub fn clear(&mut self) { self.list.clear(); } /// Get the capacity of the list. pub fn capacity(&self) -> usize { self.list.capacity() } } /// Extra information associated with an event. #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct EventExtra { /// Flags associated with this event. flags: PollFlags, } impl EventExtra { /// Create a new, empty version of this struct. #[inline] pub const fn empty() -> EventExtra { EventExtra { flags: PollFlags::empty(), } } /// Set the interrupt flag. #[inline] pub fn set_hup(&mut self, value: bool) { self.flags.set(PollFlags::HUP, value); } /// Set the priority flag. #[inline] pub fn set_pri(&mut self, value: bool) { self.flags.set(PollFlags::PRI, value); } /// Is this an interrupt event? #[inline] pub fn is_hup(&self) -> bool { self.flags.contains(PollFlags::HUP) } /// Is this a priority event? #[inline] pub fn is_pri(&self) -> bool { self.flags.contains(PollFlags::PRI) } #[inline] pub fn is_connect_failed(&self) -> Option { Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP)) } } polling-3.4.0/tests/concurrent_modification.rs000064400000000000000000000065361046102023000177250ustar 00000000000000use std::io::{self, Write}; use std::net::{TcpListener, TcpStream}; use std::thread; use std::time::Duration; use easy_parallel::Parallel; use polling::{Event, Events, Poller}; #[test] fn concurrent_add() -> io::Result<()> { let (reader, mut writer) = tcp_pair()?; let poller = Poller::new()?; let mut events = Events::new(); let result = Parallel::new() .add(|| { poller.wait(&mut events, None)?; Ok(()) }) .add(|| { thread::sleep(Duration::from_millis(100)); unsafe { poller.add(&reader, Event::readable(0))?; } writer.write_all(&[1])?; Ok(()) }) .run() .into_iter() .collect::>(); poller.delete(&reader)?; result?; assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(0) ); Ok(()) } #[test] fn concurrent_modify() -> io::Result<()> { let (reader, mut writer) = tcp_pair()?; let poller = Poller::new()?; unsafe { poller.add(&reader, Event::none(0))?; } let mut events = Events::new(); Parallel::new() .add(|| { poller.wait(&mut events, Some(Duration::from_secs(10)))?; Ok(()) }) .add(|| { thread::sleep(Duration::from_millis(100)); poller.modify(&reader, Event::readable(0))?; writer.write_all(&[1])?; Ok(()) }) .run() .into_iter() .collect::>()?; assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(0) ); Ok(()) } #[cfg(unix)] #[test] fn concurrent_interruption() -> io::Result<()> { struct MakeItSend(T); unsafe impl Send for MakeItSend {} let (reader, _writer) = tcp_pair()?; let poller = Poller::new()?; unsafe { poller.add(&reader, Event::none(0))?; } let mut events = Events::new(); let events_borrow = &mut events; let (sender, receiver) = std::sync::mpsc::channel(); Parallel::new() .add(move || { // Register a signal handler so that the syscall is actually interrupted. A signal that // is ignored by default does not cause an interrupted syscall. signal_hook::flag::register(signal_hook::consts::signal::SIGURG, Default::default())?; // Signal to the other thread how to send a signal to us sender .send(MakeItSend(unsafe { libc::pthread_self() })) .unwrap(); poller.wait(events_borrow, Some(Duration::from_secs(1)))?; Ok(()) }) .add(move || { let MakeItSend(target_thread) = receiver.recv().unwrap(); thread::sleep(Duration::from_millis(100)); assert_eq!(0, unsafe { libc::pthread_kill(target_thread, libc::SIGURG) }); Ok(()) }) .run() .into_iter() .collect::>()?; assert_eq!(events.len(), 0); Ok(()) } fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { let listener = TcpListener::bind("127.0.0.1:0")?; let a = TcpStream::connect(listener.local_addr()?)?; let (b, _) = listener.accept()?; Ok((a, b)) } polling-3.4.0/tests/io.rs000064400000000000000000000042271046102023000134200ustar 00000000000000use polling::{Event, Events, Poller}; use std::io::{self, Write}; use std::net::{TcpListener, TcpStream}; use std::sync::Arc; use std::time::Duration; #[test] fn basic_io() { let poller = Poller::new().unwrap(); let (read, mut write) = tcp_pair().unwrap(); unsafe { poller.add(&read, Event::readable(1)).unwrap(); } // Nothing should be available at first. let mut events = Events::new(); assert_eq!( poller .wait(&mut events, Some(Duration::from_secs(0))) .unwrap(), 0 ); assert!(events.is_empty()); // After a write, the event should be available now. write.write_all(&[1]).unwrap(); assert_eq!( poller .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); poller.delete(&read).unwrap(); } #[test] fn insert_twice() { #[cfg(unix)] use std::os::unix::io::AsRawFd; #[cfg(windows)] use std::os::windows::io::AsRawSocket; let (read, mut write) = tcp_pair().unwrap(); let read = Arc::new(read); let poller = Poller::new().unwrap(); unsafe { #[cfg(unix)] let read = read.as_raw_fd(); #[cfg(windows)] let read = read.as_raw_socket(); poller.add(read, Event::readable(1)).unwrap(); assert_eq!( poller.add(read, Event::readable(1)).unwrap_err().kind(), io::ErrorKind::AlreadyExists ); } write.write_all(&[1]).unwrap(); let mut events = Events::new(); assert_eq!( poller .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); poller.delete(&read).unwrap(); } fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { let listener = TcpListener::bind("127.0.0.1:0")?; let a = TcpStream::connect(listener.local_addr()?)?; let (b, _) = listener.accept()?; Ok((a, b)) } polling-3.4.0/tests/many_connections.rs000064400000000000000000000036711046102023000163610ustar 00000000000000//! Tests to ensure more than 32 connections can be polled at once. // Doesn't work on OpenBSD. #![cfg(not(target_os = "openbsd"))] use std::io::{self, prelude::*}; use std::net::{TcpListener, TcpStream}; use std::time::Duration; use polling::Events; #[test] fn many_connections() { // Create 100 connections. let mut connections = Vec::new(); for i in 0..100 { let (reader, writer) = tcp_pair().unwrap(); connections.push((i, reader, writer)); } // Create a poller and add all the connections. let poller = polling::Poller::new().unwrap(); for (i, reader, _) in connections.iter() { unsafe { poller.add(reader, polling::Event::readable(*i)).unwrap(); } } let mut events = Events::new(); while !connections.is_empty() { // Choose a random connection to write to. let i = fastrand::usize(..connections.len()); let (id, mut reader, mut writer) = connections.remove(i); // Write a byte to the connection. writer.write_all(&[1]).unwrap(); // Wait for the connection to become readable. poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); // Check that the connection is readable. let current_events = events.iter().collect::>(); assert_eq!(current_events.len(), 1, "events: {:?}", current_events); assert_eq!( current_events[0].with_no_extra(), polling::Event::readable(id) ); // Read the byte from the connection. let mut buf = [0]; reader.read_exact(&mut buf).unwrap(); assert_eq!(buf, [1]); poller.delete(&reader).unwrap(); events.clear(); } } fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { let listener = TcpListener::bind("127.0.0.1:0")?; let a = TcpStream::connect(listener.local_addr()?)?; let (b, _) = listener.accept()?; Ok((a, b)) } polling-3.4.0/tests/multiple_pollers.rs000064400000000000000000000220131046102023000163750ustar 00000000000000//! Test registering one source into multiple pollers. use polling::{Event, Events, PollMode, Poller}; use std::io::{self, prelude::*}; use std::net::{TcpListener, TcpStream}; use std::time::Duration; #[test] fn level_triggered() { let poller1 = Poller::new().unwrap(); let poller2 = Poller::new().unwrap(); let mut events = Events::new(); if !poller1.supports_level() || !poller2.supports_level() { return; } // Register the source into both pollers. let (mut reader, mut writer) = tcp_pair().unwrap(); unsafe { poller1 .add_with_mode(&reader, Event::readable(1), PollMode::Level) .unwrap(); poller2 .add_with_mode(&reader, Event::readable(2), PollMode::Level) .unwrap(); } // Neither poller should have any events. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); // Write to the source. writer.write_all(&[1]).unwrap(); // At least one poller should have an event. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); events.clear(); // poller2 should have zero or one events. match poller2.wait(&mut events, Some(Duration::from_secs(1))) { Ok(1) => { assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(2) ); } Ok(0) => assert!(events.is_empty()), _ => panic!("unexpected error"), } // Writing more data should cause the same event. writer.write_all(&[1]).unwrap(); events.clear(); assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); // poller2 should have zero or one events. events.clear(); match poller2.wait(&mut events, Some(Duration::from_secs(1))) { Ok(1) => { assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(2) ); } Ok(0) => assert!(events.is_empty()), _ => panic!("unexpected error"), } // Read from the source. reader.read_exact(&mut [0; 2]).unwrap(); // Both pollers should not have any events. events.clear(); assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); // Dereference the pollers. poller1.delete(&reader).unwrap(); poller2.delete(&reader).unwrap(); } #[test] fn edge_triggered() { let poller1 = Poller::new().unwrap(); let poller2 = Poller::new().unwrap(); let mut events = Events::new(); if !poller1.supports_edge() || !poller2.supports_edge() { return; } // Register the source into both pollers. let (mut reader, mut writer) = tcp_pair().unwrap(); unsafe { poller1 .add_with_mode(&reader, Event::readable(1), PollMode::Edge) .unwrap(); poller2 .add_with_mode(&reader, Event::readable(2), PollMode::Edge) .unwrap(); } // Neither poller should have any events. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); // Write to the source. writer.write_all(&[1]).unwrap(); // Both pollers should have an event. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); events.clear(); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(2) ); // Writing to the poller again should cause an event. writer.write_all(&[1]).unwrap(); // Both pollers should have one event. events.clear(); assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); events.clear(); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 1 ); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(2) ); // Read from the source. reader.read_exact(&mut [0; 2]).unwrap(); // Both pollers should not have any events. events.clear(); assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); // Dereference the pollers. poller1.delete(&reader).unwrap(); poller2.delete(&reader).unwrap(); } #[test] fn oneshot_triggered() { let poller1 = Poller::new().unwrap(); let poller2 = Poller::new().unwrap(); let mut events = Events::new(); // Register the source into both pollers. let (mut reader, mut writer) = tcp_pair().unwrap(); unsafe { poller1 .add_with_mode(&reader, Event::readable(1), PollMode::Oneshot) .unwrap(); poller2 .add_with_mode(&reader, Event::readable(2), PollMode::Oneshot) .unwrap(); } // Neither poller should have any events. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); // Write to the source. writer.write_all(&[1]).unwrap(); // Sources should have either one or no events. match poller1.wait(&mut events, Some(Duration::from_secs(1))) { Ok(1) => { assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); } Ok(0) => assert!(events.is_empty()), _ => panic!("unexpected error"), } events.clear(); match poller2.wait(&mut events, Some(Duration::from_secs(1))) { Ok(1) => { assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(2) ); } Ok(0) => assert!(events.is_empty()), _ => panic!("unexpected error"), } events.clear(); // Writing more data should not cause an event. writer.write_all(&[1]).unwrap(); // Sources should have no events. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); // Read from the source. reader.read_exact(&mut [0; 2]).unwrap(); // Sources should have no events. assert_eq!( poller1 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); assert_eq!( poller2 .wait(&mut events, Some(Duration::from_secs(1))) .unwrap(), 0 ); assert!(events.is_empty()); } fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { let listener = TcpListener::bind("127.0.0.1:0")?; let a = TcpStream::connect(listener.local_addr()?)?; let (b, _) = listener.accept()?; Ok((a, b)) } polling-3.4.0/tests/notify.rs000064400000000000000000000013701046102023000143150ustar 00000000000000use std::io; use std::thread; use std::time::Duration; use easy_parallel::Parallel; use polling::Events; use polling::Poller; #[test] fn simple() -> io::Result<()> { let poller = Poller::new()?; let mut events = Events::new(); for _ in 0..10 { poller.notify()?; poller.wait(&mut events, None)?; assert!(events.is_empty()); } Ok(()) } #[test] fn concurrent() -> io::Result<()> { let poller = Poller::new()?; let mut events = Events::new(); for _ in 0..2 { Parallel::new() .add(|| { thread::sleep(Duration::from_secs(0)); poller.notify().unwrap(); }) .finish(|| poller.wait(&mut events, None).unwrap()); } Ok(()) } polling-3.4.0/tests/other_modes.rs000064400000000000000000000177311046102023000153250ustar 00000000000000//! Tests for level triggered and edge triggered mode. #![allow(clippy::unused_io_amount)] use std::io::{self, prelude::*}; use std::net::{TcpListener, TcpStream}; use std::time::Duration; use polling::{Event, Events, PollMode, Poller}; #[test] fn level_triggered() { // Create our streams. let (mut reader, mut writer) = tcp_pair().unwrap(); let reader_token = 1; // Create our poller and register our streams. let poller = Poller::new().unwrap(); if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Level) } .is_err() { // Only panic if we're on a platform that should support level mode. cfg_if::cfg_if! { if #[cfg(any(target_os = "solaris", target_os = "illumos"))] { return; } else { panic!("Level mode should be supported on this platform"); } } } // Write some data to the writer. let data = [1, 2, 3, 4, 5]; writer.write_all(&data).unwrap(); // A "readable" notification should be delivered. let mut events = Events::new(); poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); // If we read some of the data, the notification should still be available. reader.read_exact(&mut [0; 3]).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); // If we read the rest of the data, the notification should be gone. reader.read_exact(&mut [0; 2]).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(0))) .unwrap(); assert!(events.is_empty()); // After modifying the stream and sending more data, it should be oneshot. poller .modify_with_mode(&reader, Event::readable(reader_token), PollMode::Oneshot) .unwrap(); writer.write(&data).unwrap(); events.clear(); // BUG: Somehow, the notification here is delayed? poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); // After reading, the notification should vanish. reader.read(&mut [0; 5]).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(0))) .unwrap(); assert!(events.is_empty()); } #[test] fn edge_triggered() { // Create our streams. let (mut reader, mut writer) = tcp_pair().unwrap(); let reader_token = 1; // Create our poller and register our streams. let poller = Poller::new().unwrap(); if unsafe { poller.add_with_mode(&reader, Event::readable(reader_token), PollMode::Edge) } .is_err() { // Only panic if we're on a platform that should support level mode. cfg_if::cfg_if! { if #[cfg(all( any( target_os = "linux", target_os = "android", 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) ))] { panic!("Edge mode should be supported on this platform"); } else { return; } } } // Write some data to the writer. let data = [1, 2, 3, 4, 5]; writer.write_all(&data).unwrap(); // A "readable" notification should be delivered. let mut events = Events::new(); poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); // If we read some of the data, the notification should not still be available. reader.read_exact(&mut [0; 3]).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(0))) .unwrap(); assert!(events.is_empty()); // If we write more data, a notification should be delivered. writer.write_all(&data).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); // After modifying the stream and sending more data, it should be oneshot. poller .modify_with_mode(&reader, Event::readable(reader_token), PollMode::Oneshot) .unwrap(); writer.write_all(&data).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); } #[test] fn edge_oneshot_triggered() { // Create our streams. let (mut reader, mut writer) = tcp_pair().unwrap(); let reader_token = 1; // Create our poller and register our streams. let poller = Poller::new().unwrap(); if unsafe { poller.add_with_mode( &reader, Event::readable(reader_token), PollMode::EdgeOneshot, ) } .is_err() { // Only panic if we're on a platform that should support level mode. cfg_if::cfg_if! { if #[cfg(all( any( target_os = "linux", target_os = "android", 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) ))] { panic!("Edge mode should be supported on this platform"); } else { return; } } } // Write some data to the writer. let data = [1, 2, 3, 4, 5]; writer.write_all(&data).unwrap(); // A "readable" notification should be delivered. let mut events = Events::new(); poller .wait(&mut events, Some(Duration::from_secs(10))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); // If we read some of the data, the notification should not still be available. reader.read_exact(&mut [0; 3]).unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(0))) .unwrap(); assert!(events.is_empty()); // If we modify to re-enable the notification, it should be delivered. poller .modify_with_mode( &reader, Event::readable(reader_token), PollMode::EdgeOneshot, ) .unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_secs(0))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(reader_token) ); } fn tcp_pair() -> io::Result<(TcpStream, TcpStream)> { let listener = TcpListener::bind("127.0.0.1:0")?; let a = TcpStream::connect(listener.local_addr()?)?; let (b, _) = listener.accept()?; Ok((a, b)) } polling-3.4.0/tests/precision.rs000064400000000000000000000036751046102023000150120ustar 00000000000000use std::io; use std::time::{Duration, Instant}; use polling::{Events, Poller}; #[test] fn below_ms() -> io::Result<()> { let poller = Poller::new()?; let mut events = Events::new(); let dur = Duration::from_micros(100); let margin = Duration::from_micros(500); let mut lowest = Duration::from_secs(1000); for _ in 0..1_000 { let now = Instant::now(); let n = poller.wait(&mut events, Some(dur))?; let elapsed = now.elapsed(); assert_eq!(n, 0); assert!(elapsed >= dur, "{:?} < {:?}", elapsed, dur); lowest = lowest.min(elapsed); } if cfg!(all( any( target_os = "linux", target_os = "android", target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos", target_os = "freebsd", ), not(polling_test_poll_backend) )) { assert!(lowest < dur + margin); } Ok(()) } #[test] fn above_ms() -> io::Result<()> { let poller = Poller::new()?; let mut events = Events::new(); let dur = Duration::from_micros(3_100); let margin = Duration::from_micros(500); let mut lowest = Duration::from_secs(1000); for _ in 0..1_000 { let now = Instant::now(); let n = poller.wait(&mut events, Some(dur))?; let elapsed = now.elapsed(); assert_eq!(n, 0); assert!(elapsed >= dur, "{:?} < {:?}", elapsed, dur); lowest = lowest.min(elapsed); } 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", ), not(polling_test_poll_backend) )) { assert!(lowest < dur + margin); } Ok(()) } polling-3.4.0/tests/timeout.rs000064400000000000000000000012101046102023000144640ustar 00000000000000use std::io; use std::time::{Duration, Instant}; use polling::{Events, Poller}; #[test] fn twice() -> io::Result<()> { let poller = Poller::new()?; let mut events = Events::new(); for _ in 0..2 { let start = Instant::now(); poller.wait(&mut events, Some(Duration::from_secs(1)))?; let elapsed = start.elapsed(); assert!(elapsed >= Duration::from_secs(1)); } Ok(()) } #[test] fn non_blocking() -> io::Result<()> { let poller = Poller::new()?; let mut events = Events::new(); for _ in 0..100 { poller.wait(&mut events, Some(Duration::from_secs(0)))?; } Ok(()) } polling-3.4.0/tests/windows_post.rs000064400000000000000000000027111046102023000155440ustar 00000000000000//! Tests for the post() function on Windows. #![cfg(windows)] use polling::os::iocp::{CompletionPacket, PollerIocpExt}; use polling::{Event, Events, Poller}; use std::sync::Arc; use std::thread; use std::time::Duration; #[test] fn post_smoke() { let poller = Poller::new().unwrap(); let mut events = Events::new(); poller .post(CompletionPacket::new(Event::readable(1))) .unwrap(); poller.wait(&mut events, None).unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::readable(1) ); } #[test] fn post_multithread() { let poller = Arc::new(Poller::new().unwrap()); let mut events = Events::new(); thread::spawn({ let poller = Arc::clone(&poller); move || { for i in 0..3 { poller .post(CompletionPacket::new(Event::writable(i))) .unwrap(); thread::sleep(Duration::from_millis(100)); } } }); for i in 0..3 { poller .wait(&mut events, Some(Duration::from_secs(5))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!( events.iter().next().unwrap().with_no_extra(), Event::writable(i) ); events.clear(); } poller .wait(&mut events, Some(Duration::from_millis(10))) .unwrap(); assert_eq!(events.len(), 0); } polling-3.4.0/tests/windows_waitable.rs000064400000000000000000000063501046102023000163520ustar 00000000000000//! Tests for the waitable polling on Windows. #![cfg(windows)] use polling::os::iocp::PollerIocpExt; use polling::{Event, Events, PollMode, Poller}; use windows_sys::Win32::Foundation::CloseHandle; use windows_sys::Win32::System::Threading::{CreateEventW, ResetEvent, SetEvent}; use std::io; use std::os::windows::io::{AsRawHandle, RawHandle}; use std::os::windows::prelude::{AsHandle, BorrowedHandle}; use std::time::Duration; /// A basic wrapper around the Windows event object. struct EventHandle(RawHandle); impl Drop for EventHandle { fn drop(&mut self) { unsafe { CloseHandle(self.0 as _); } } } impl EventHandle { fn new(manual_reset: bool) -> io::Result { let handle = unsafe { CreateEventW( std::ptr::null_mut(), manual_reset as _, false as _, std::ptr::null(), ) }; if handle == 0 { Err(io::Error::last_os_error()) } else { Ok(Self(handle as _)) } } /// Reset the event object. fn reset(&self) -> io::Result<()> { if unsafe { ResetEvent(self.0 as _) } != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } /// Set the event object. fn set(&self) -> io::Result<()> { if unsafe { SetEvent(self.0 as _) } != 0 { Ok(()) } else { Err(io::Error::last_os_error()) } } } impl AsRawHandle for EventHandle { fn as_raw_handle(&self) -> RawHandle { self.0 } } impl AsHandle for EventHandle { fn as_handle(&self) -> BorrowedHandle<'_> { unsafe { BorrowedHandle::borrow_raw(self.0) } } } #[test] fn smoke() { let poller = Poller::new().unwrap(); let event = EventHandle::new(true).unwrap(); unsafe { poller .add_waitable(&event, Event::all(0), PollMode::Oneshot) .unwrap(); } let mut events = Events::new(); poller .wait(&mut events, Some(Duration::from_millis(100))) .unwrap(); assert!(events.is_empty()); // Signal the event. event.set().unwrap(); poller .wait(&mut events, Some(Duration::from_millis(100))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!(events.iter().next().unwrap().with_no_extra(), Event::all(0)); // Interest should be cleared. events.clear(); poller .wait(&mut events, Some(Duration::from_millis(100))) .unwrap(); assert!(events.is_empty()); // If we modify the waitable, it should be added again. poller .modify_waitable(&event, Event::all(0), PollMode::Oneshot) .unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_millis(100))) .unwrap(); assert_eq!(events.len(), 1); assert_eq!(events.iter().next().unwrap().with_no_extra(), Event::all(0)); // If we reset the event, it should not be signaled. event.reset().unwrap(); poller .modify_waitable(&event, Event::all(0), PollMode::Oneshot) .unwrap(); events.clear(); poller .wait(&mut events, Some(Duration::from_millis(100))) .unwrap(); assert!(events.is_empty()); }