Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

1
vendor/polling/.cargo-checksum.json vendored Normal file
View File

@@ -0,0 +1 @@
{"files":{"CHANGELOG.md":"0f91cec73082d9197c370ab0b2c9d265c767279e5db327d2a2d682c3f0503b42","Cargo.lock":"2e480f5a47693dc5095fafc3c97511fc6b2d17f8c9012e54e00aff073052c816","Cargo.toml":"ea3f2ed65b75e51a9ffdab8dfd2053e17ad68d0c99fa268ae635c7e2d89baa6c","Cross.toml":"42cf6eb709f8d22a27abdc52844b162a0df7607e08fdde89afb9a27bb28550d0","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"fa7ac320dbb9a346204de4cb36f555402ea641f9dd4ab9c0cf6adece806b5148","examples/tcp_client.rs":"617fc67e2e1cd998dfedc8337bd01e81a1c860e99b2242ae66e47ab22ec7dad4","examples/two-listeners.rs":"de5589e16d7581e7f376259cfc2a7517f475df84e367ab4b203e68710612c48d","examples/wait-signal.rs":"586d464270ea7b01da8614e763494a8e69329d4c5c7b53e1551f4cb82a412325","src/epoll.rs":"6d339c87ffc2dd14fae9988826c9ae79fa2e50f87bc23a2bf89c39cd09bb7c73","src/iocp/afd.rs":"93968b4bfd80b66e264967e7ed5da9263eb44b72f8d4fa7e73613ac82d8f05ce","src/iocp/mod.rs":"004da65394612012390057389c625948e6f73665376e362ea4b26fde05ed1566","src/iocp/port.rs":"865d51135ea5be2f29ddeb47a043a3f8b6b514dfab863e344b4da9839eb24c4f","src/kqueue.rs":"7138018ecdf24c60f1e51acdb8de925af46cffa148e231006d1b3b359d7a26a0","src/lib.rs":"176b0277d9adc8a5948c6fc4a0fe8dea554f540d9621b2f42026e6b649725353","src/os.rs":"76f021ca05682396bf8645aa9ba75b93a966dda962e1f4aed1dbf1c625c25733","src/os/iocp.rs":"937c654557fd65687ed924bbc4dce31ac37d112dd2fb5a1f78acae3852a61940","src/os/kqueue.rs":"ee7f2e934f9e712c8113aa31a78646eecf5ca09640d8521ac120e59eaee13f27","src/poll.rs":"b8cd8ecd8390a81ca357ce3cf1bb633c8b8fed1f25520913c55d3b951bd9759f","src/port.rs":"faba1bc412fb0f837dcbebba0adc6deca9bdc6fcd91d4284487cd0fcb842cd32","tests/concurrent_modification.rs":"bf1700178b6203c1cb5751bac10ec6c007b0e59f6be77d69a1ddabcbb6830305","tests/io.rs":"51748fc1384ff73d5874be24f4a4085f05d4295aaf847310555ca75743a778e6","tests/many_connections.rs":"080980d2b6a74e19e425980106f6745b9f0c89207194f9721d18f0a38c0216f7","tests/multiple_pollers.rs":"426ce09c471f4e8a7cbfc6568ad298eadf6fccda8225c2ce3422540e7d05d33c","tests/notify.rs":"b73a7a7a573ac96d482825d53b9bcf4948ae120fea0e11613750a02ca329e9b7","tests/other_modes.rs":"a1bf8b8d983ea41e59c4d2985fc4cd19a2075dbe1f56cf2734e6239614c6789a","tests/precision.rs":"f210111430e347f4aba83bf650836f6617cbfe3df70a986d6a5da0e6ff5c865a","tests/timeout.rs":"ba40c179420053eb1cd44e75535daa9e90eaa3789e202bb49807e4a0901bacc9","tests/windows_post.rs":"454095d0c631a25a759ebec26069710b9c3160c0ea4b6d59d77299ce85383e2a","tests/windows_waitable.rs":"6cb5ef9bda8bdf080adde1e21d252f807e16b29037f697c0838931f05e07a393"},"package":"5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"}

236
vendor/polling/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,236 @@
# Version 3.11.0
- Bump MSRV to 1.71. (#251)
- Update to `windows-sys` v0.61. (#251)
# Version 3.10.0
- Add `wait_deadline` function. (#226)
# Version 3.9.0
- Add a new optional `tracing` feature. When enabled, this feature adds logging
to the implementation. By default it is disabled. (#238)
- Update to `windows-sys` v0.60. (#239)
# Version 3.8.0
- Implement `AsRawFd` and `AsFd` for `Poller` on Redox OS. (#235)
- Update `hermit-abi` to v0.5.0. (#229)
- Update `rustix` to 1.0 (#230). This also fixed a bug in `wait` which (contradicting docs) cleared the events vector instead of appending to it.
- Add support for QNX. (#201)
# Version 3.7.4
- Add support for visionOS. (#217)
- Fix typos in documentation. (#216)
# Version 3.7.3
- Update to `windows-sys` v0.59. (#214)
# Version 3.7.2
- Update `hermit-abi` to v0.4.0. (#209)
# Version 3.7.1
- Fix a typo in `Event::is_err()`. (#204)
# Version 3.7.0
- Add support for the PS Vita as a platform. (#160)
# Version 3.6.0
- Add an `is_err` method to `Event` to tell when an error has occurred. (#189)
- Deprecate the `is_connect_failed` function. (#189)
- Add support for HermitOS to `polling`. (#194)
# Version 3.5.0
- Use the `epoll` backend when RedoxOS is enabled. (#190)
# 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

240
vendor/polling/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,240 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "bitflags"
version = "2.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
[[package]]
name = "cfg-if"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
[[package]]
name = "concurrent-queue"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
dependencies = [
"crossbeam-utils",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
[[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.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
"windows-sys 0.61.0",
]
[[package]]
name = "fastrand"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
[[package]]
name = "hermit-abi"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
[[package]]
name = "libc"
version = "0.2.175"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543"
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
[[package]]
name = "pin-project-lite"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
[[package]]
name = "polling"
version = "3.11.0"
dependencies = [
"cfg-if",
"concurrent-queue",
"easy-parallel",
"fastrand",
"hermit-abi",
"libc",
"pin-project-lite",
"rustix",
"signal-hook",
"socket2",
"tracing",
"windows-sys 0.61.0",
]
[[package]]
name = "rustix"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys",
"windows-sys 0.61.0",
]
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b"
dependencies = [
"libc",
]
[[package]]
name = "socket2"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
dependencies = [
"libc",
"windows-sys 0.59.0",
]
[[package]]
name = "tracing"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0"
dependencies = [
"pin-project-lite",
"tracing-core",
]
[[package]]
name = "tracing-core"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678"
[[package]]
name = "windows-link"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65"
[[package]]
name = "windows-sys"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-sys"
version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-targets"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_gnullvm",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
[[package]]
name = "windows_i686_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
[[package]]
name = "windows_i686_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"

171
vendor/polling/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,171 @@
# 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.71"
name = "polling"
version = "3.11.0"
authors = [
"Stjepan Glavina <stjepang@gmail.com>",
"John Nunley <dev@notgull.net>",
]
build = false
exclude = ["/.*"]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
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",
]
[lib]
name = "polling"
path = "src/lib.rs"
[[example]]
name = "tcp_client"
path = "examples/tcp_client.rs"
[[example]]
name = "two-listeners"
path = "examples/two-listeners.rs"
[[example]]
name = "wait-signal"
path = "examples/wait-signal.rs"
[[test]]
name = "concurrent_modification"
path = "tests/concurrent_modification.rs"
[[test]]
name = "io"
path = "tests/io.rs"
[[test]]
name = "many_connections"
path = "tests/many_connections.rs"
[[test]]
name = "multiple_pollers"
path = "tests/multiple_pollers.rs"
[[test]]
name = "notify"
path = "tests/notify.rs"
[[test]]
name = "other_modes"
path = "tests/other_modes.rs"
[[test]]
name = "precision"
path = "tests/precision.rs"
[[test]]
name = "timeout"
path = "tests/timeout.rs"
[[test]]
name = "windows_post"
path = "tests/windows_post.rs"
[[test]]
name = "windows_waitable"
path = "tests/windows_waitable.rs"
[dependencies.cfg-if]
version = "1"
[dependencies.tracing]
version = "0.1.37"
optional = true
default-features = false
[dev-dependencies.easy-parallel]
version = "3.1.0"
[dev-dependencies.fastrand]
version = "2.0.0"
[dev-dependencies.socket2]
version = "0.6.0"
[target.'cfg(all(unix, not(target_os="vita")))'.dev-dependencies.signal-hook]
version = "0.3.17"
[target.'cfg(any(unix, target_os = "fuchsia", target_os = "vxworks"))'.dependencies.rustix]
version = "1.0.5"
features = [
"event",
"fs",
"pipe",
"process",
"std",
"time",
]
default-features = false
[target.'cfg(target_os = "hermit")'.dependencies.hermit-abi]
version = "0.5.0"
[target."cfg(unix)".dev-dependencies.libc]
version = "0.2"
[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.61"
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",
]
[lints.rust.unexpected_cfgs]
level = "warn"
priority = 0
check-cfg = [
"cfg(polling_test_poll_backend)",
"cfg(polling_test_epoll_pipe)",
]

3
vendor/polling/Cross.toml vendored Normal file
View File

@@ -0,0 +1,3 @@
[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"

201
vendor/polling/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
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.

23
vendor/polling/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,23 @@
Permission 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.

77
vendor/polling/README.md vendored Normal file
View File

@@ -0,0 +1,77 @@
# 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, RedoxOS
- [kqueue](https://en.wikipedia.org/wiki/Kqueue): macOS, iOS, tvOS, watchOS, visionOS, 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, HermitOS, 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.
[`PollMode`]: https://docs.rs/polling/latest/polling/enum.PollMode.html
## 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.

36
vendor/polling/examples/tcp_client.rs vendored Normal file
View File

@@ -0,0 +1,36 @@
use 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_err().unwrap_or(false) {
println!("connect failed");
}
Ok(())
}

View File

@@ -0,0 +1,43 @@
use 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!(),
}
}
}
}

67
vendor/polling/examples/wait-signal.rs vendored Normal file
View File

@@ -0,0 +1,67 @@
#[cfg(all(
any(
target_vendor = "apple",
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_raw());
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_vendor = "apple",
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_vendor = "apple",
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.");
}

514
vendor/polling/src/epoll.rs vendored Normal file
View File

@@ -0,0 +1,514 @@
//! Bindings to epoll (Linux, Android).
use std::io;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::time::{Duration, Instant};
#[cfg(not(target_os = "redox"))]
use rustix::event::{eventfd, EventfdFlags};
#[cfg(not(target_os = "redox"))]
use rustix::time::{
timerfd_create, timerfd_settime, Itimerspec, TimerfdClockId, TimerfdFlags, TimerfdTimerFlags,
};
use rustix::buffer::spare_capacity;
use rustix::event::{epoll, Timespec};
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 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.
///
/// Redox does not support timerfd.
#[cfg(not(target_os = "redox"))]
timer_fd: Option<OwnedFd>,
}
impl Poller {
/// Creates a new poller.
pub fn new() -> io::Result<Poller> {
// 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()?;
#[cfg(not(target_os = "redox"))]
let timer_fd = timerfd_create(
TimerfdClockId::Monotonic,
TimerfdFlags::CLOEXEC | TimerfdFlags::NONBLOCK,
)
.ok();
let poller = Poller {
epoll_fd,
notifier,
#[cfg(not(target_os = "redox"))]
timer_fd,
};
unsafe {
#[cfg(not(target_os = "redox"))]
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,
)?;
}
#[cfg(feature = "tracing")]
tracing::trace!(
epoll_fd = ?poller.epoll_fd.as_raw_fd(),
notifier = ?poller.notifier,
"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<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"add",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
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<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"modify",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
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.
#[cfg_attr(not(feature = "tracing"), inline(always))]
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"delete",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?fd,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
epoll::delete(&self.epoll_fd, fd)?;
Ok(())
}
/// Waits for I/O events with an optional deadline.
#[allow(clippy::needless_update)]
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
#[cfg(not(target_os = "redox"))]
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,
)?;
}
#[cfg(not(target_os = "redox"))]
let timer_fd = &self.timer_fd;
#[cfg(target_os = "redox")]
let timer_fd: Option<core::convert::Infallible> = None;
// Timeout for epoll. In case of overflow, use no timeout.
let timeout = match (timer_fd, timeout) {
(_, Some(t)) if t == Duration::from_secs(0) => Some(Timespec::default()),
(None, Some(t)) => Timespec::try_from(t).ok(),
_ => None,
};
// Wait for I/O events.
epoll::wait(
&self.epoll_fd,
spare_capacity(&mut events.list),
timeout.as_ref(),
)?;
#[cfg(feature = "tracing")]
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<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
notifier = ?self.notifier,
);
#[cfg(feature = "tracing")]
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) {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"drop",
epoll_fd = ?self.epoll_fd.as_raw_fd(),
notifier = ?self.notifier,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
#[cfg(not(target_os = "redox"))]
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.
#[cfg(not(target_os = "redox"))]
const TS_ZERO: Timespec = unsafe { std::mem::transmute([0u8; std::mem::size_of::<Timespec>()]) };
/// 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: Vec<epoll::Event>,
}
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<Item = Event> + '_ {
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<bool> {
Some(
self.flags.contains(epoll::EventFlags::ERR)
&& self.flags.contains(epoll::EventFlags::HUP),
)
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
Some(self.flags.contains(epoll::EventFlags::ERR))
}
}
/// 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.
#[cfg(not(target_os = "redox"))]
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<Self> {
// Skip eventfd for testing if necessary.
#[cfg(not(target_os = "redox"))]
{
if !cfg!(polling_test_epoll_pipe) {
// Try to create an eventfd.
match eventfd(0, EventfdFlags::CLOEXEC | EventfdFlags::NONBLOCK) {
Ok(fd) => {
#[cfg(feature = "tracing")]
tracing::trace!("created eventfd for notifier");
return Ok(Notifier::EventFd(fd));
}
Err(_err) => {
#[cfg(feature = "tracing")]
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 {
#[cfg(not(target_os = "redox"))]
Notifier::EventFd(fd) => fd.as_fd(),
Notifier::Pipe {
read_pipe: read, ..
} => read.as_fd(),
}
}
/// Notify the poller.
fn notify(&self) {
match self {
#[cfg(not(target_os = "redox"))]
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 {
#[cfg(not(target_os = "redox"))]
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() {},
}
}
}

665
vendor/polling/src/iocp/afd.rs vendored Normal file
View File

@@ -0,0 +1,665 @@
//! 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::OnceLock;
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],
}
#[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 Default for AfdPollHandleInfo {
fn default() -> Self {
Self {
handle: ptr::null_mut(),
events: Default::default(),
status: Default::default(),
}
}
}
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<AfdPollInfo>>;
}
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<Self> {
$(
#[allow(clippy::missing_transmute_annotations)]
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 => {
#[cfg(feature = "tracing")]
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: OnceLock<io::Result<NtdllImports>> = OnceLock::new();
NTDLL_IMPORTS
.get_or_init(|| unsafe {
let ntdll = GetModuleHandleW(NTDLL_NAME.as_ptr() as *const _);
if ntdll.is_null() {
#[cfg(feature = "tracing")]
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<T> {
/// The handle to the AFD device.
handle: HANDLE,
/// We own `T`.
_marker: PhantomData<T>,
}
impl<T> fmt::Debug for Afd<T> {
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 as usize)
}
}
f.debug_struct("Afd")
.field("handle", &WriteAsHex(self.handle))
.finish()
}
}
impl<T> Drop for Afd<T> {
fn drop(&mut self) {
unsafe {
CloseHandle(self.handle);
}
}
}
impl<T> AsRawHandle for Afd<T> {
fn as_raw_handle(&self) -> RawHandle {
self.handle as _
}
}
impl<T: CompletionHandle> Afd<T>
where
T::Completion: AsIoStatusBlock + HasAfdInfo,
{
/// Create a new AFD device.
pub(super) fn new() -> io::Result<Self> {
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::<OBJECT_ATTRIBUTES>() as u32,
RootDirectory: ptr::null_mut(),
ObjectName: &mut device_name,
Attributes: 0,
SecurityDescriptor: ptr::null_mut(),
SecurityQualityOfService: ptr::null_mut(),
};
let mut handle = MaybeUninit::<HANDLE>::uninit();
let mut iosb = MaybeUninit::<IO_STATUS_BLOCK>::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 = 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::<IO_STATUS_BLOCK>();
// Set Status to pending
unsafe {
(*iosb).Anonymous.Status = STATUS_PENDING;
}
let ntdll = NtdllImports::get()?;
let result = unsafe {
ntdll.NtDeviceIoControlFile(
self.handle,
ptr::null_mut(),
ptr::null_mut(),
iosb.cast(),
iosb.cast(),
IOCTL_AFD_POLL,
poll_info.cast(),
size_of::<AfdPollInfo>() as u32,
poll_info.cast(),
size_of::<AfdPollInfo>() 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::<IO_STATUS_BLOCK>();
if (*iosb).Anonymous.Status != STATUS_PENDING {
return Ok(());
}
// Cancel the packet.
let mut cancel_iosb = MaybeUninit::<IO_STATUS_BLOCK>::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))
}
}
}
pin_project_lite::pin_project! {
/// An I/O status block paired with some auxiliary data.
#[repr(C)]
pub(super) struct IoStatusBlock<T> {
// The I/O status block.
iosb: UnsafeCell<IO_STATUS_BLOCK>,
// Whether or not the block is in use.
in_use: AtomicBool,
// The auxiliary data.
#[pin]
data: T,
// This block is not allowed to move.
#[pin]
_marker: PhantomPinned,
}
}
impl<T: fmt::Debug> fmt::Debug for IoStatusBlock<T> {
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<T: Send> Send for IoStatusBlock<T> {}
unsafe impl<T: Sync> Sync for IoStatusBlock<T> {}
impl<T> From<T> for IoStatusBlock<T> {
fn from(data: T) -> Self {
Self {
iosb: UnsafeCell::new(unsafe { std::mem::zeroed() }),
in_use: AtomicBool::new(false),
data,
_marker: PhantomPinned,
}
}
}
impl<T> IoStatusBlock<T> {
pub(super) fn iosb(self: Pin<&Self>) -> &UnsafeCell<IO_STATUS_BLOCK> {
self.project_ref().iosb
}
pub(super) fn data(self: Pin<&Self>) -> Pin<&T> {
self.project_ref().data
}
}
impl<T: HasAfdInfo> HasAfdInfo for IoStatusBlock<T> {
fn afd_info(self: Pin<&Self>) -> Pin<&UnsafeCell<AfdPollInfo>> {
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<T> AsIoStatusBlock for IoStatusBlock<T> {}
unsafe impl<T> Completion for IoStatusBlock<T> {
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<RawSocket> {
// 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<RawSocket> {
let mut out = MaybeUninit::<RawSocket>::uninit();
let mut bytes = 0u32;
let result = WSAIoctl(
sock as _,
ioctl,
ptr::null_mut(),
0,
out.as_mut_ptr().cast(),
size_of::<RawSocket>() as u32,
&mut bytes,
ptr::null_mut(),
None,
);
if result == SOCKET_ERROR {
return Err(io::Error::last_os_error());
}
Ok(out.assume_init())
}

1412
vendor/polling/src/iocp/mod.rs vendored Normal file

File diff suppressed because it is too large Load Diff

299
vendor/polling/src/iocp/port.rs vendored Normal file
View File

@@ -0,0 +1,299 @@
//! 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::ptr;
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<T: Completion> CompletionHandle for Pin<&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<T: Completion> CompletionHandle for Pin<Arc<T>> {
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<T> {
/// The underlying handle.
handle: HANDLE,
/// We own the status block.
_marker: PhantomData<T>,
}
impl<T> Drop for IoCompletionPort<T> {
fn drop(&mut self) {
unsafe {
CloseHandle(self.handle);
}
}
}
impl<T> AsRawHandle for IoCompletionPort<T> {
fn as_raw_handle(&self) -> RawHandle {
self.handle as _
}
}
impl<T> fmt::Debug for IoCompletionPort<T> {
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 as usize)
}
}
f.debug_struct("IoCompletionPort")
.field("handle", &WriteAsHex(self.handle))
.finish()
}
}
impl<T: CompletionHandle> IoCompletionPort<T> {
/// Create a new I/O completion port.
pub(super) fn new(threads: usize) -> io::Result<Self> {
let handle = unsafe {
CreateIoCompletionPort(
INVALID_HANDLE_VALUE,
ptr::null_mut(),
0,
threads.try_into().expect("too many threads"),
)
};
if handle.is_null() {
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.is_null() {
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<OverlappedEntry<T>>,
timeout: Option<Duration>,
) -> io::Result<usize> {
// Drop the current packets.
packets.clear();
let mut count = MaybeUninit::<u32>::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<T: CompletionHandle> {
/// The underlying entry.
entry: OVERLAPPED_ENTRY,
/// We own the status block.
_marker: PhantomData<T>,
}
impl<T: CompletionHandle> fmt::Debug for OverlappedEntry<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("OverlappedEntry { .. }")
}
}
impl<T: CompletionHandle> OverlappedEntry<T> {
/// 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<T: CompletionHandle> Drop for OverlappedEntry<T> {
fn drop(&mut self) {
drop(unsafe { self.packet() });
}
}

596
vendor/polling/src/kqueue.rs vendored Normal file
View File

@@ -0,0 +1,596 @@
//! Bindings to kqueue (macOS, iOS, tvOS, watchOS, visionOS, 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::Instant;
use rustix::buffer::spare_capacity;
use rustix::event::{kqueue, Timespec};
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<HashSet<SourceId>>,
/// 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<Poller> {
// 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)?;
#[cfg(feature = "tracing")]
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<()> {
#[cfg(feature = "tracing")]
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
};
#[cfg(feature = "tracing")]
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<A>(&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_timespec(
&self.kqueue_fd,
changelist,
spare_capacity(&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 deadline.
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Timeout for kevent. In case of overflow, use no timeout.
let timeout = match timeout {
Some(t) => Timespec::try_from(t).ok(),
None => None,
};
// Wait for I/O events.
let changelist = [];
let _res = unsafe {
kqueue::kevent_timespec(
&self.kqueue_fd,
&changelist,
spare_capacity(&mut events.list),
timeout.as_ref(),
)?
};
#[cfg(feature = "tracing")]
tracing::trace!(
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
res = ?_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<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
);
#[cfg(feature = "tracing")]
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) {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"drop",
kqueue_fd = ?self.kqueue_fd.as_raw_fd(),
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let _ = self.notify.deregister(self);
}
}
/// A list of reported I/O events.
pub struct Events {
list: Vec<kqueue::Event>,
}
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<Item = Event> + '_ {
// 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<bool> {
None
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
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_vendor = "apple",
))]
mod notify {
use super::Poller;
use rustix::event::kqueue;
use std::io;
#[cfg(feature = "tracing")]
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<Self> {
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.
#[cfg(feature = "tracing")]
pub(super) fn has_fd(&self, _fd: BorrowedFd<'_>) -> bool {
false
}
}
}
#[cfg(not(any(
target_os = "freebsd",
target_os = "dragonfly",
target_vendor = "apple",
)))]
mod notify {
use super::Poller;
use crate::{Event, PollMode, NOTIFY_KEY};
use std::io::{self, prelude::*};
#[cfg(feature = "tracing")]
use std::os::unix::io::BorrowedFd;
use std::os::unix::{
io::{AsFd, AsRawFd},
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<Self> {
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.
#[cfg(feature = "tracing")]
pub(super) fn has_fd(&self, fd: BorrowedFd<'_>) -> bool {
self.read_stream.as_raw_fd() == fd.as_raw_fd()
}
}
}

1131
vendor/polling/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

24
vendor/polling/src/os.rs vendored Normal file
View File

@@ -0,0 +1,24 @@
//! Platform-specific functionality.
#[cfg(all(
any(
target_vendor = "apple",
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)]
#[allow(dead_code)]
pub trait PollerSealed {}
impl PollerSealed for crate::Poller {}
}

253
vendor/polling/src/os/iocp.rs vendored Normal file
View File

@@ -0,0 +1,253 @@
//! Functionality that is only available 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<T: AsRawHandle + ?Sized> 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<T: AsHandle + ?Sized> AsWaitable for T {}

304
vendor/polling/src/os/kqueue.rs vendored Normal file
View File

@@ -0,0 +1,304 @@
//! 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<F: Filter>: 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_raw()), 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_raw()), 0, PollMode::Oneshot).unwrap();
///
/// // Re-register with a different key.
/// poller.modify_filter(Signal(rustix::process::Signal::INT.as_raw()), 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_raw()), 0, PollMode::Oneshot).unwrap();
///
/// // Remove the filter.
/// poller.delete_filter(Signal(rustix::process::Signal::INT.as_raw())).unwrap();
/// ```
fn delete_filter(&self, filter: F) -> io::Result<()>;
}
impl<F: Filter> PollerKqueueExt<F> 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<T: FilterSealed + ?Sized> 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<T: Filter + ?Sized> 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_named_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;
}
}

857
vendor/polling/src/poll.rs vendored Normal file
View File

@@ -0,0 +1,857 @@
//! 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::Instant;
#[cfg(not(target_os = "hermit"))]
use rustix::fd::{AsFd, AsRawFd, BorrowedFd};
#[cfg(target_os = "hermit")]
use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd};
use syscall::{poll, PollFd, PollFlags};
// 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<Fds>,
/// 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<PollFd<'static>>,
/// 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<RawFd, FdData>,
}
/// 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<Poller> {
let notify = notify::Notify::new()?;
#[cfg(feature = "tracing")]
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));
}
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"add",
notify_read = ?self.notify.fd().as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
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));
}
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"modify",
notify_read = ?self.notify.fd().as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
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));
}
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"delete",
notify_read = ?self.notify.fd().as_raw_fd(),
?fd,
);
#[cfg(feature = "tracing")]
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 deadline.
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
notify_read = ?self.notify.fd().as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
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();
}
let timeout =
deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Perform the poll.
let num_events = poll(&mut fds.poll_fds, timeout)?;
let notified = !fds.poll_fds[0].revents().is_empty();
let num_fd_events = if notified { num_events - 1 } else { num_events };
#[cfg(feature = "tracing")]
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<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
notify_read = ?self.notify.fd().as_raw_fd(),
);
#[cfg(feature = "tracing")]
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<Event>,
}
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<Item = Event> + '_ {
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<bool> {
Some(self.flags.contains(PollFlags::ERR) || self.flags.contains(PollFlags::HUP))
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
Some(self.flags.contains(PollFlags::ERR))
}
}
fn cvt_mode_as_remove(mode: PollMode) -> io::Result<bool> {
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(unix)]
mod syscall {
pub(super) use rustix::event::{PollFd, PollFlags};
pub(super) use rustix::event::Timespec;
#[cfg(target_os = "espidf")]
pub(super) use rustix::event::{eventfd, EventfdFlags};
#[cfg(target_os = "espidf")]
pub(super) use rustix::fd::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
#[cfg(target_os = "espidf")]
pub(super) use rustix::io::{read, write};
use std::io;
use std::time::Duration;
/// Safe wrapper around the `poll` system call.
pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option<Duration>) -> io::Result<usize> {
// Timeout for `poll`. In case of overflow, use no timeout.
let timeout = match timeout {
Some(timeout) => Timespec::try_from(timeout).ok(),
None => None,
};
Ok(rustix::event::poll(fds, timeout.as_ref())?)
}
}
#[cfg(target_os = "hermit")]
mod syscall {
// TODO: Remove this shim once HermitOS is supported in Rustix.
use std::fmt;
use std::io;
use std::marker::PhantomData;
use std::ops::BitOr;
use std::time::Duration;
pub(super) use std::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, OwnedFd, RawFd};
/// Create an eventfd.
pub(super) fn eventfd(count: u64, _flags: EventfdFlags) -> io::Result<OwnedFd> {
let fd = unsafe { hermit_abi::eventfd(count, 0) };
if fd < 0 {
Err(io::Error::from_raw_os_error(unsafe {
hermit_abi::get_errno()
}))
} else {
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
}
/// Read some bytes.
pub(super) fn read(fd: BorrowedFd<'_>, bytes: &mut [u8]) -> io::Result<usize> {
let count = unsafe { hermit_abi::read(fd.as_raw_fd(), bytes.as_mut_ptr(), bytes.len()) };
cvt(count)
}
/// Write some bytes.
pub(super) fn write(fd: BorrowedFd<'_>, bytes: &[u8]) -> io::Result<usize> {
let count = unsafe { hermit_abi::write(fd.as_raw_fd(), bytes.as_ptr(), bytes.len()) };
cvt(count)
}
/// Safe wrapper around the `poll` system call.
pub(super) fn poll(fds: &mut [PollFd<'_>], timeout: Option<Duration>) -> io::Result<usize> {
// Timeout in milliseconds for epoll. In case of overflow, use no timeout.
let mut timeout_ms = -1;
if let Some(t) = timeout {
if let Ok(ms) = i32::try_from(t.as_millis()) {
// Round up to a whole millisecond.
if Duration::from_millis(ms as u64) < t {
if let Some(ms) = ms.checked_add(1) {
timeout_ms = ms;
}
} else {
timeout_ms = ms;
}
}
}
let call = unsafe {
hermit_abi::poll(
fds.as_mut_ptr() as *mut hermit_abi::pollfd,
fds.len(),
timeout_ms,
)
};
cvt(call as isize)
}
/// Safe wrapper around `pollfd`.
#[repr(transparent)]
pub(super) struct PollFd<'a> {
inner: hermit_abi::pollfd,
_lt: PhantomData<BorrowedFd<'a>>,
}
impl<'a> PollFd<'a> {
pub(super) fn from_borrowed_fd(fd: BorrowedFd<'a>, inflags: PollFlags) -> Self {
Self {
inner: hermit_abi::pollfd {
fd: fd.as_raw_fd(),
events: inflags.0,
revents: 0,
},
_lt: PhantomData,
}
}
pub(super) fn revents(&self) -> PollFlags {
PollFlags(self.inner.revents)
}
}
impl AsFd for PollFd<'_> {
fn as_fd(&self) -> BorrowedFd<'_> {
unsafe { BorrowedFd::borrow_raw(self.inner.fd) }
}
}
impl fmt::Debug for PollFd<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PollFd")
.field("fd", &format_args!("0x{:x}", self.inner.fd))
.field("events", &PollFlags(self.inner.events))
.field("revents", &PollFlags(self.inner.revents))
.finish()
}
}
/// Wrapper around polling flags.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(super) struct PollFlags(i16);
impl PollFlags {
/// Empty set of flags.
pub(super) const fn empty() -> Self {
Self(0)
}
pub(super) const IN: PollFlags = PollFlags(hermit_abi::POLLIN);
pub(super) const OUT: PollFlags = PollFlags(hermit_abi::POLLOUT);
pub(super) const WRBAND: PollFlags = PollFlags(hermit_abi::POLLWRBAND);
pub(super) const ERR: PollFlags = PollFlags(hermit_abi::POLLERR);
pub(super) const HUP: PollFlags = PollFlags(hermit_abi::POLLHUP);
pub(super) const PRI: PollFlags = PollFlags(hermit_abi::POLLPRI);
/// Tell if this contains some flags.
pub(super) fn contains(self, flags: PollFlags) -> bool {
self.0 & flags.0 != 0
}
/// Set a flag.
pub(super) fn set(&mut self, flags: PollFlags, set: bool) {
if set {
self.0 |= flags.0;
} else {
self.0 &= !(flags.0);
}
}
/// Tell if this is empty.
pub(super) fn is_empty(self) -> bool {
self.0 == 0
}
/// Tell if this intersects with some flags.
pub(super) fn intersects(self, flags: PollFlags) -> bool {
self.contains(flags)
}
}
impl BitOr for PollFlags {
type Output = PollFlags;
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
#[derive(Debug, Copy, Clone)]
pub(super) struct EventfdFlags;
impl EventfdFlags {
pub(super) fn empty() -> Self {
Self
}
}
/// Convert a number to an actual result.
#[inline]
fn cvt(len: isize) -> io::Result<usize> {
if len < 0 {
Err(io::Error::from_raw_os_error(unsafe {
hermit_abi::get_errno()
}))
} else {
Ok(len as usize)
}
}
}
#[cfg(not(any(target_os = "espidf", target_os = "hermit")))]
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(any(target_os = "haiku", target_os = "nto")))]
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<Self> {
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(any(target_os = "haiku", target_os = "nto")))]
let (read_pipe, write_pipe) = pipe_with(PipeFlags::CLOEXEC).or_else(fallback_pipe)?;
#[cfg(any(target_os = "haiku", target_os = "nto"))]
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 notify 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> {
// Pipes on Vita do not guarantee that after `write` call succeeds, the
// data becomes immediately available for reading on the other side of the pipe.
// To ensure that the notification is not lost, the read side of the pipe is temporarily
// switched to blocking for a single `read` call.
#[cfg(target_os = "vita")]
rustix::fs::fcntl_setfl(
&self.read_pipe,
rustix::fs::fcntl_getfl(&self.read_pipe)? & !rustix::fs::OFlags::NONBLOCK,
)?;
let result = read(&self.read_pipe, &mut [0; 1]);
#[cfg(target_os = "vita")]
rustix::fs::fcntl_setfl(
&self.read_pipe,
rustix::fs::fcntl_getfl(&self.read_pipe)? | rustix::fs::OFlags::NONBLOCK,
)?;
result?;
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(any(target_os = "espidf", target_os = "hermit"))]
mod notify {
use std::io;
use std::mem;
use super::syscall::{
eventfd, read, write, AsFd, AsRawFd, BorrowedFd, EventfdFlags, OwnedFd, PollFlags, RawFd,
};
/// A notification pipe.
///
/// This implementation uses the `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<Self> {
// 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 io::Error::from(err) {
err if err.kind() == io::ErrorKind::PermissionDenied => {
// 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 => 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.as_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.as_fd(), &mut [0; mem::size_of::<u64>()])?;
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
}
}
}

287
vendor/polling/src/port.rs vendored Normal file
View File

@@ -0,0 +1,287 @@
//! Bindings to event port (illumos, Solaris).
use std::io;
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, RawFd};
use std::time::Instant;
use rustix::buffer::spare_capacity;
use rustix::event::{port, PollFlags, Timespec};
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<Poller> {
let port_fd = port::create()?;
let flags = fcntl_getfd(&port_fd)?;
fcntl_setfd(&port_fd, flags | FdFlags::CLOEXEC)?;
#[cfg(feature = "tracing")]
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<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"modify",
port_fd = ?self.port_fd.as_raw_fd(),
?fd,
?ev,
);
#[cfg(feature = "tracing")]
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::associate_fd(&self.port_fd, fd, flags, ev.key as _)?;
}
Ok(())
}
/// Deletes a file descriptor.
pub fn delete(&self, fd: BorrowedFd<'_>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"delete",
port_fd = ?self.port_fd.as_raw_fd(),
?fd,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let result = unsafe { 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 deadline.
pub fn wait_deadline(&self, events: &mut Events, deadline: Option<Instant>) -> io::Result<()> {
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"wait",
port_fd = ?self.port_fd.as_raw_fd(),
?deadline,
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
let timeout = deadline.map(|deadline| deadline.saturating_duration_since(Instant::now()));
// Timeout for `port::getn`. In case of overflow, use no timeout.
let timeout = match timeout {
Some(t) => Timespec::try_from(t).ok(),
None => None,
};
// Wait for I/O events.
let res = port::getn(
&self.port_fd,
spare_capacity(&mut events.list),
1,
timeout.as_ref(),
);
#[cfg(feature = "tracing")]
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;
#[cfg(feature = "tracing")]
let span = tracing::trace_span!(
"notify",
port_fd = ?self.port_fd.as_raw_fd(),
);
#[cfg(feature = "tracing")]
let _enter = span.enter();
// Use port_send to send a notification to the 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<port::Event>,
}
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<Item = Event> + '_ {
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<bool> {
Some(self.flags.contains(PollFlags::ERR) && self.flags.contains(PollFlags::HUP))
}
#[inline]
pub fn is_err(&self) -> Option<bool> {
Some(self.flags.contains(PollFlags::ERR))
}
}

View File

@@ -0,0 +1,131 @@
use 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::<io::Result<()>>();
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::<io::Result<()>>()?;
assert_eq!(events.len(), 1);
assert_eq!(
events.iter().next().unwrap().with_no_extra(),
Event::readable(0)
);
Ok(())
}
#[cfg(all(unix, not(target_os = "vita")))]
#[test]
fn concurrent_interruption() -> io::Result<()> {
struct MakeItSend<T>(T);
unsafe impl<T> Send for MakeItSend<T> {}
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::<io::Result<()>>()?;
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))
}

144
vendor/polling/tests/io.rs vendored Normal file
View File

@@ -0,0 +1,144 @@
use 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();
}
/// Test that calling `wait` appends events, as [documented], rather than
/// overwriting them.
///
/// [documented]: https://docs.rs/polling/latest/polling/struct.Poller.html#method.wait
#[test]
fn append_events() {
#[cfg(unix)]
use std::os::unix::io::AsRawFd;
#[cfg(windows)]
use std::os::windows::io::AsRawSocket;
// Create a few sockets.
let mut pairs = Vec::new();
for _ in 0..4 {
let (read, write) = tcp_pair().unwrap();
pairs.push((read, write));
}
// Add the sockets to the poller.
let poller = Poller::new().unwrap();
unsafe {
for (read, _write) in &pairs {
#[cfg(unix)]
let read = read.as_raw_fd();
#[cfg(windows)]
let read = read.as_raw_socket();
poller.add(read, Event::readable(1)).unwrap();
}
}
// Trigger read events on the sockets and reuse the event list to test
// that events are appended.
let mut events = Events::new();
for (index, (_read, ref mut write)) in pairs.iter_mut().enumerate() {
// Write to the socket prompting a reader readiness event.
write.write_all(&[1]).unwrap();
assert_eq!(
poller
.wait(&mut events, Some(Duration::from_secs(1)))
.unwrap(),
index + 1
);
assert_eq!(events.len(), index + 1);
for event in events.iter() {
assert_eq!(event.with_no_extra(), Event::readable(1));
}
}
for (read, _write) in &pairs {
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))
}

View File

@@ -0,0 +1,66 @@
//! 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::<Vec<_>>();
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))
}

358
vendor/polling/tests/multiple_pollers.rs vendored Normal file
View File

@@ -0,0 +1,358 @@
//! 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))
}

38
vendor/polling/tests/notify.rs vendored Normal file
View File

@@ -0,0 +1,38 @@
use 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(())
}

273
vendor/polling/tests/other_modes.rs vendored Normal file
View File

@@ -0,0 +1,273 @@
//! 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_vendor = "apple",
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_vendor = "apple",
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))
}

72
vendor/polling/tests/precision.rs vendored Normal file
View File

@@ -0,0 +1,72 @@
use 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_vendor = "apple",
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_vendor = "apple",
target_os = "freebsd",
),
not(polling_test_poll_backend)
)) {
assert!(lowest < dur + margin);
}
Ok(())
}

32
vendor/polling/tests/timeout.rs vendored Normal file
View File

@@ -0,0 +1,32 @@
use 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(())
}

64
vendor/polling/tests/windows_post.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
//! 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);
}

138
vendor/polling/tests/windows_waitable.rs vendored Normal file
View File

@@ -0,0 +1,138 @@
//! 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<Self> {
let handle = unsafe {
CreateEventW(
std::ptr::null_mut(),
manual_reset as _,
false as _,
std::ptr::null(),
)
};
if handle.is_null() {
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());
}