From 535717f46f68cb4cbaca2d6b09fa7bbfed168813 Mon Sep 17 00:00:00 2001 From: bdbai Date: Thu, 10 Feb 2022 00:33:13 +0800 Subject: [PATCH] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit 5234b5f19dd79b817cd74fc4fe25c9aad2e3e24c Author: Thomas de Zeeuw Date: Sat Nov 13 12:57:29 2021 +0100 Release v0.8.0 commit 41a494b0def1bb9d841c6f975231abf18325c979 Author: Thomas de Zeeuw Date: Sat Nov 13 12:59:43 2021 +0100 Fix Clippy warning commit a8c57568f4dd85d173da6757e4fe669db2a517ff Author: Thomas de Zeeuw Date: Sun Nov 7 12:45:33 2021 +0100 Add changelog for v0.8 commit 7029a353e6ccb2d50b90daa459e05973277464d3 Author: Thomas de Zeeuw Date: Sun Nov 7 12:34:33 2021 +0100 Add v0.7.14 change log From commit 064af846f3c2c99ce1f1a47760aa795cfa43b9ad commit dca2134ef355b3c0d00e8e338e44e7d9ed63edac Author: Thomas de Zeeuw Date: Sun Nov 7 11:26:23 2021 +0100 Fix feature flags for some tests files The test util module requires both the "os-poll" and "net" features. commit b9f089b90df3a00474e358bf628344c750f1e4d3 Author: Thomas de Zeeuw Date: Sun Nov 7 11:19:09 2021 +0100 Remove cfg attributes for Solaris We never really supported Solaris, we pretended it implemented epoll, but it never did see https://github.com/tokio-rs/mio/issues/1152. As no one ever committed to being a maintainer for the port I'm removing it now with this commit. Instead replace it with illumuos on the CI, which we do support (as it supports epoll) and for which we do have maintainers. commit 7d86108a203c332be780c588c7e600920fc2c324 Author: Thomas de Zeeuw Date: Sun Nov 7 11:53:32 2021 +0100 Add section about raw fd to portability guidelines commit 3be58110658448a3977ec1c0c273dda625869466 Author: Thomas de Zeeuw Date: Sun Nov 7 12:09:53 2021 +0100 Add note about short receive on datagram sockets Talking about the differences between OSs. commit 3ca57f34b164b017093bf98d430dcd6eb9da4988 Author: Thomas de Zeeuw Date: Sat Nov 6 15:20:54 2021 +0100 Document unconnected TcpStream returned by TcpStream::connect commit 47cf59c2e59ecb4ba518c8dc97841dcb8236f214 Author: Thomas de Zeeuw Date: Sat Nov 6 15:04:56 2021 +0100 Deregister connection before dropping it in TCP example commit 05009e4f60335fa00e9ea6a118595548afee0607 Author: Thomas de Zeeuw Date: Sat Nov 6 14:53:45 2021 +0100 Document that Mio report OOB data in Event::is_readable Reporting Out-of-band (OOB) as readable it could leave applications open to DoS attacks. However because Mio uses edge-triggers most applications won't actually be effected. commit 44666e894f593108d61fbd852e257c4c0947ca6f Author: Thomas de Zeeuw Date: Sat Nov 6 14:41:54 2021 +0100 Fix match_like_matches_macro Clippy lint We've updated our MSVR since the comment above it. commit f8695a7b35e66544843decefb50c658671b78650 Author: Thomas de Zeeuw Date: Sat Nov 6 14:39:42 2021 +0100 Update Rustc nightly version in CI commit f4b9252ad41735c711f3cab6e7a8b7eec8cde256 Author: Ben Noordhuis Date: Sun Oct 10 16:45:25 2021 +0200 Add sys::unix::SocketAddr::as_abstract_namespace() Fixes #1517. commit 04e0ca4d351c14340f8c6902443382700b8995c2 Author: Thomas de Zeeuw Date: Tue Sep 28 18:16:01 2021 +0200 Update change log with v0.7.x releases Contains the work in the following commits: * v0.7.8 20b7298fe0d3da04b965ec4a379db0247cd632be. * v0.7.9 07bc32f27b4f2488de91a66cfd489eae97551635. * v0.7.10 b7006d710821f1d6aa0d5a63948dc21b4433ddbf. * v0.7.11 772c692150c711d7bdd72b5c6287072b8914e519. * v0.7.12 7adfb75fdefe98e3b57438bafa36c09a85377777. * v0.7.13 75f41fb304d299dfbc07679d15193e03273c4597. commit e55ec59cf287dd4bdc06c70bfadea74f85c8bf09 Author: Thomas de Zeeuw Date: Thu Oct 7 20:21:22 2021 +0200 Install nightly Rust on CI for install cargo-hack commit 499004f874298aa456c12bb730f7a7ab5fe5c181 Author: Thomas de Zeeuw Date: Thu Oct 7 20:14:36 2021 +0200 Install Cargo-hack using nightly on CI Cargo-hack's (transient) dependency bitflags has updated its MSRV. commit e9e91ffeb31246f25d7ccdf781853632b3f00b72 Author: Thomas de Zeeuw Date: Tue Sep 28 19:59:15 2021 +0200 Fix Clippy warnings on Windows Seems this isn't check on the CI. commit b48cce69f6b3d158212f2f4b6643bb64e18e8121 Author: Thomas de Zeeuw Date: Tue Sep 28 19:51:19 2021 +0200 Fix Clippy warnings commit 37aec3e142a653aba7b25d9ac4c690f946318bd7 Author: Thomas de Zeeuw Date: Tue Sep 28 19:45:26 2021 +0200 Fix dead_code warnings for Windows commit 02e9be41f27daf822575444fdd2b3067433a5996 Author: Thomas de Zeeuw Date: Tue Sep 28 19:32:35 2021 +0200 Remove TcpSocket type The socket2 crate provide all the functionality and more. Furthermore supporting all socket options is beyond the scope of Mio. The easier migration is to the socket2 crate, using the Socket or SockRef types. The migration for Tokio is tracked in https://github.com/tokio-rs/tokio/issues/4135. commit d4ce420212cda9471302adda5c12da820111ca08 Author: Rémi Lauzier Date: Tue Jul 6 14:21:17 2021 -0400 Update dev-dependencies commit fbcc8499c4115117793741486e528703afd989f6 Author: Thomas de Zeeuw Date: Sat Jul 3 12:44:57 2021 +0200 Change port in connect_error Hopefully this port is actually not used. Also check Event::is_write_closed since we expect that to be true. commit bfbcd9ddc4ef59ff916933e6a886781263ae55ed Author: Jake Shadle Date: Fri Jul 2 15:17:17 2021 +0200 Move wine from unsupported commit 21ddf94d3191dc2385d53ca8f2ac5a7b5ca5af96 Author: Ivan Enderlin Date: Tue Jun 22 22:36:09 2021 +0200 chore: Make Clippy happy (bis). commit 6d62f5d5d131576087fd4a2fcd8db0c4587e83cf Author: Ivan Enderlin Date: Mon Jun 21 16:41:21 2021 +0200 chore: Make Clippy happy. commit 6eb1efa97b5626ff0aa2665a4ecc9d3f6c9a3dd9 Author: Ivan Enderlin Date: Mon Jun 21 16:22:16 2021 +0200 feat: Move `poll::selector` to `Registry::selector`. commit 441367ba70b78989f4de577917f6e8ddfd744063 Author: Thomas de Zeeuw Date: Sun Jun 13 00:33:17 2021 +0200 Fix Selector::try_clone Calls fcntl F_DUPFD_CLOEXEC expects two arguments; the command (F_DUPFD_CLOEXEC) and an argument for the command. In this case an lower bound for the resulting file descriptor. Because we didn't provide a value it would take whatever value was left in the register from whatever code used it before the system call. This caused Waker::new to fail, see issue https://github.com/tokio-rs/mio/issues/1497. commit cbcaedf3699619739f33c3671d0dcb8b97847b92 Author: Thomas de Zeeuw Date: Sat Jun 12 20:39:12 2021 +0200 Set FD_CLOEXEC flag on duplicated kqueue Poll Same as commit c52635c76a59be28d0bf287a0bad6d6871a2e36c, but for kqueue. commit c52635c76a59be28d0bf287a0bad6d6871a2e36c Author: Tim Zhang Date: Tue May 25 11:40:54 2021 +0800 Set the close-on-exec flag for the duplicate epoll_fd The close-on-exec flag (FD_CLOEXEC; see fcntl(2)) for the duplicate descriptor created by dup(2) is off. We can use fcntl + F_DUPFD_CLOEXEC to dup the epoll_fd to fix this issue. Fixes: tokio-rs/tokio#3809 Signed-off-by: Tim Zhang commit 2246ffb41c5c364d74d6265c9bb6ac0f31734c4d Author: Taiki Endo Date: Sun May 23 16:06:15 2021 +0900 Use ubuntu-18.04 instead of ubuntu-16.04 commit 0cfba5df3a58f137041f6df7216a37bf9905a438 Author: cdcode Date: Sun Jun 6 22:42:26 2021 +0100 Small spelling correction in example commit 22e885859bb481ae4c2827ab48552c3159fcc7f8 Author: Thomas de Zeeuw Date: Thu May 13 17:09:57 2021 +0200 Update outdated comment commit 607a12f4116ecf82241f3971e6b71be5af056f14 Author: Thomas de Zeeuw Date: Mon May 10 12:10:28 2021 +0200 Replace x86_64-sun-solaris with x86_64-pc-solaris https://github.com/rust-lang/rust/pull/82216 removed the x86_64-sun-solaris target from rustup, changing it to use x86_64-pc-solaris instead. Related issues: * https://github.com/rust-lang/rust/issues/85098 commit 27a6a3c43505aab3f5c97e09dbd5ed85f7c76e72 Author: Thomas de Zeeuw Date: Mon May 10 11:56:41 2021 +0200 Avoid cast pointers to usize in windows::NamedPipe Changes the Inner::ptr_from_* methods to use ptr::wrapping_sub rather then casting to usize. commit e316b21d3946682b6f7a8cae5c49d9ea405dea87 Author: Thomas de Zeeuw Date: Wed May 5 12:13:47 2021 +0200 Replace offset constants with methods in Windows NamedPipe commit 9e13732f88e285d36ffce506f6a51077ec7780e6 Author: Thomas de Zeeuw Date: Mon Apr 12 20:26:53 2021 +0200 Reorder NamedPipe fields Moving the Overlapped fields to the start to make it easier to determine the offsets and hopefully incur less breakage once external fields change size. Note that the Overlapped fields internally uses miow::Overlapped, which in turn is a OVERLAPPED struct as found in the winapi crate and has a stable layout (as defined by the Windows API). commit db0d74cce5d492d5f8381aadf27877fe8806d2b1 Author: Thomas de Zeeuw Date: Mon Apr 12 20:03:24 2021 +0200 Remove unsound offset_of macro And replace it with constants that define the offsets to the fields. It's not a pretty solution, but it's one without UB. commit 1667a7027382bd43470bc43e5982531a2e14b7ba Author: Rob Ede Date: Thu Apr 1 17:01:01 2021 +0100 remove manual doc versioning --- .github/CODEOWNERS | 2 + CHANGELOG.md | 54 +++- Cargo.toml | 20 +- Makefile | 8 +- README.md | 10 +- azure-pipelines.yml | 8 +- ci/azure-clippy.yml | 2 +- ci/azure-cross-compile.yml | 16 +- ci/azure-minimal-versions.yml | 2 +- ci/azure-rustfmt.yml | 7 +- ci/azure-test-stable.yml | 8 +- examples/tcp_server.rs | 8 +- examples/udp_server.rs | 2 +- src/event/event.rs | 9 + src/event/source.rs | 4 +- src/interest.rs | 8 +- src/io_source.rs | 26 +- src/lib.rs | 12 +- src/net/tcp/listener.rs | 18 +- src/net/tcp/stream.rs | 43 ++- src/net/udp.rs | 23 ++ src/poll.rs | 80 ++++-- src/sys/mod.rs | 38 +++ src/sys/shell/tcp.rs | 114 +------- src/sys/unix/net.rs | 7 +- src/sys/unix/pipe.rs | 3 +- src/sys/unix/selector/epoll.rs | 4 +- src/sys/unix/selector/kqueue.rs | 4 +- src/sys/unix/selector/mod.rs | 24 +- src/sys/unix/sourcefd.rs | 22 +- src/sys/unix/tcp.rs | 431 ++--------------------------- src/sys/unix/uds/listener.rs | 11 +- src/sys/unix/uds/mod.rs | 6 +- src/sys/unix/uds/socketaddr.rs | 20 +- src/sys/unix/waker.rs | 2 - src/sys/windows/afd.rs | 237 ++++++++++++++++ src/sys/windows/io_status_block.rs | 40 +++ src/sys/windows/net.rs | 8 +- src/sys/windows/overlapped.rs | 37 +++ src/sys/windows/queue.rs | 6 +- src/sys/windows/selector.rs | 19 +- src/sys/windows/tcp.rs | 4 +- src/token.rs | 4 +- src/waker.rs | 6 +- tests/aio.rs | 2 +- tests/poll.rs | 8 +- tests/regressions.rs | 3 +- tests/tcp.rs | 17 +- tests/tcp_stream.rs | 44 +-- tests/udp_socket.rs | 12 +- tests/unix_listener.rs | 13 + tests/unix_pipe.rs | 2 +- tests/unix_stream.rs | 14 +- tests/util/mod.rs | 67 ++++- tests/waker.rs | 2 +- tests/win_named_pipe.rs | 6 +- 56 files changed, 841 insertions(+), 766 deletions(-) create mode 100644 .github/CODEOWNERS create mode 100644 src/sys/windows/afd.rs create mode 100644 src/sys/windows/io_status_block.rs create mode 100644 src/sys/windows/overlapped.rs diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..f860f8e95 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Windows changes +/src/sys/windows/ @carllerche \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 72cb770ae..65dce4f32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,54 @@ +# 0.8.0 + +## Removed + +* Deprecated features (https://github.com/tokio-rs/mio/commit/105f8f2afb57b01ddea716a0aa9720f226c520e3): + * extra-docs (always enabled) + * tcp (replaced with "net" feature). + * udp (replaced with "net" feature). + * uds (replaced with "net" feature). + * pipe (replaced with "os-ext" feature). +* `TcpSocket` type + (https://github.com/tokio-rs/mio/commit/02e9be41f27daf822575444fdd2b3067433a5996). + The socket2 crate provides all the functionality and more. +* Support for Solaris, it never really worked anyway + (https://github.com/tokio-rs/mio/pull/1528). + +## Changes + +* Update minimum Rustc version (MSVR) to 1.46.0 + (https://github.com/tokio-rs/mio/commit/5c577efecd23750a9a3e0f6ad080ab98f14a255d). + +## Added + +* `UdpSocket::peer_addr` + (https://github.com/tokio-rs/mio/commit/5fc104d08e0e74c8a19247f7cba0f058699fc438). + +# 0.7.14 + +## Fixes + +* Remove use unsound internal macro (#1519). + +## Added + +* `sys::unix::SocketAddr::as_abstract_namespace()` (#1520). + +# 0.7.13 + +## Fixes + +* Fix `Registry::try_clone` invalid usage of `F_DUPFD_CLOEXEC` (#1497, + https://github.com/tokio-rs/mio/commit/2883f5c1f35bf1a59682c5ffc4afe6b97d7d6e68). + +# 0.7.12 (yanked) + +## Fixes + +* Set `FD_CLOEXEC` when calling `Registry::try_clone` + (https://github.com/tokio-rs/mio/commit/d1617b567ff6bc669d71e367d22e0e93ff7e2e24 for epoll and + (https://github.com/tokio-rs/mio/commit/b367a05e408ca90a26383c3aa16d8a16f019dc59 for kqueue). + # 0.7.11 ## Fixes @@ -12,6 +63,8 @@ * Fix an instance of not doc(cfg(.*)) (https://github.com/tokio-rs/mio/commit/25e8f911357c740034f10a170dfa4ea1b28234ce). +# 0.7.9 + ## Fixes * Fix error handling in `NamedPipe::write` @@ -46,7 +99,6 @@ themselves already (https://github.com/tokio-rs/mio/commit/1be481dcbbcb6906364008b5d61e7f53cddc3eb3). - ## Fixes * Underflow in `SocketAddr::address` diff --git a/Cargo.toml b/Cargo.toml index 7f18d26ef..878486e6b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,11 +2,9 @@ edition = "2018" name = "mio" # When releasing to crates.io: -# - Update html_root_url. # - Update CHANGELOG.md. -# - Update doc URL. # - Create git tag -version = "0.7.11" +version = "0.8.0" license = "MIT" authors = [ "Carl Lerche ", @@ -14,7 +12,6 @@ authors = [ "Tokio Contributors ", ] description = "Lightweight non-blocking IO" -documentation = "https://docs.rs/mio/0.7.7" homepage = "https://github.com/tokio-rs/mio" repository = "https://github.com/tokio-rs/mio" readme = "README.md" @@ -41,14 +38,6 @@ os-ext = ["os-poll"] # Enables `mio::net` module containing networking primitives. net = [] -# Deprecated features, will be removed in a future version. -extra-docs = [] # Docs are now always present. -tcp = ["net"] # Replaced with "net" feature. -udp = ["net"] # Replaced with "net" feature. -uds = ["net"] # Replaced with "net" feature. -pipe = ["os-ext"] # Replaced with "os-ext" feature. -os-util = ["os-ext"]# Replaced with "os-ext" feature. - [dependencies] iovec = "0.1.2" net2 = "0.2.33" @@ -63,8 +52,8 @@ winapi = { version = "0.3", features = ["winsock2", "mswsock", "mstcpip"] } ntapi = "0.3" [dev-dependencies] -env_logger = { version = "0.6.2", default-features = false } -rand = "0.4" +env_logger = { version = "0.8.4", default-features = false } +rand = "0.8" [package.metadata.docs.rs] all-features = true @@ -74,15 +63,14 @@ targets = [ "aarch64-linux-android", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", - "x86_64-sun-solaris", "x86_64-unknown-dragonfly", "x86_64-unknown-freebsd", + "x86_64-unknown-illumos", "x86_64-unknown-linux-gnu", "x86_64-unknown-netbsd", "x86_64-unknown-openbsd", ] - [package.metadata.playground] features = ["os-poll", "os-ext", "net"] diff --git a/Makefile b/Makefile index 1adb13600..d46c20b72 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Targets available via Rustup that are supported. -TARGETS ?= "aarch64-apple-ios" "aarch64-linux-android" "x86_64-apple-darwin" "x86_64-pc-windows-msvc" "x86_64-sun-solaris" "x86_64-unknown-freebsd" "x86_64-unknown-linux-gnu" "x86_64-unknown-netbsd" +TARGETS ?= "aarch64-apple-ios" "aarch64-linux-android" "x86_64-apple-darwin" "x86_64-pc-windows-msvc" "x86_64-unknown-freebsd" "x86_64-unknown-illumos" "x86_64-unknown-linux-gnu" "x86_64-unknown-netbsd" test: cargo test --all-features @@ -7,13 +7,13 @@ test: # Test everything for the current OS/architecture and check all targets in # $TARGETS. test_all: check_all_targets - cargo hack test --feature-powerset --skip guide,extra-docs,tcp,udp,uds,pipe,os-util - cargo hack test --feature-powerset --skip guide,extra-docs,tcp,udp,uds,pipe,os-util --release + cargo hack test --feature-powerset + cargo hack test --feature-powerset --release # Check all targets using all features. check_all_targets: $(TARGETS) $(TARGETS): - cargo hack check --target $@ --feature-powerset --skip guide,extra-docs,tcp,udp,uds,pipe,os-util + cargo hack check --target $@ --feature-powerset # Installs all required targets for `check_all_targets`. install_targets: diff --git a/README.md b/README.md index c8653f8ad..c18b300ab 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,7 @@ or higher-level libraries. Currently supported platforms: -* Android +* Android (API level 21) * DragonFly BSD * FreeBSD * Linux @@ -139,6 +139,7 @@ Currently supported platforms: * Windows * iOS * macOS +* Wine (version 6.11+, see [issue #1444]) There are potentially others. If you find that Mio works on another platform, submit a PR to update the list! @@ -151,6 +152,13 @@ The Windows implementation for polling sockets is using the [wepoll] strategy. This uses the Windows AFD system to access socket readiness events. [wepoll]: https://github.com/piscisaureus/wepoll +[issue #1444]: https://github.com/tokio-rs/mio/issues/1444 + +### Unsupported + +* Haiku, see [issue #1472] + +[issue #1472]: https://github.com/tokio-rs/mio/issues/1472 ## Community diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e673e28a9..6e74acf3e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,5 +1,5 @@ -trigger: ["master", "v0.6.x"] -pr: ["master", "v0.6.x"] +trigger: ["master", "v0.6.x", "v0.7.x"] +pr: ["master", "v0.6.x", "v0.7.x"] jobs: # Check formatting @@ -27,7 +27,7 @@ jobs: name: nightly displayName: Nightly # Pin nightly to avoid being impacted by breakage - rust_version: nightly-2019-11-14 + rust_version: nightly-2021-11-05 benches: true # This represents the minimum Rust version supported by @@ -39,7 +39,7 @@ jobs: parameters: name: minrust displayName: Min Rust - rust_version: 1.39.0 + rust_version: 1.46.0 cmd: check cross: true diff --git a/ci/azure-clippy.yml b/ci/azure-clippy.yml index 8e6e1b285..3efc95152 100644 --- a/ci/azure-clippy.yml +++ b/ci/azure-clippy.yml @@ -3,7 +3,7 @@ jobs: displayName: Clippy pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 steps: - template: azure-install-rust.yml diff --git a/ci/azure-cross-compile.yml b/ci/azure-cross-compile.yml index 763828a43..fb62cc437 100644 --- a/ci/azure-cross-compile.yml +++ b/ci/azure-cross-compile.yml @@ -1,5 +1,5 @@ parameters: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 jobs: - job: ${{ parameters.name }} @@ -15,24 +15,24 @@ jobs: target: aarch64-apple-ios Android_ARM: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 target: arm-linux-androideabi Android_ARM64: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 target: aarch64-linux-android Android_32: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 target: i686-unknown-linux-gnu NetBSD: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 target: x86_64-unknown-netbsd - Solaris: - vmImage: ubuntu-16.04 - target: x86_64-sun-solaris + illumos: + vmImage: ubuntu-18.04 + target: x86_64-unknown-illumos pool: vmImage: $(vmImage) diff --git a/ci/azure-minimal-versions.yml b/ci/azure-minimal-versions.yml index f4219c263..66d6ddec2 100644 --- a/ci/azure-minimal-versions.yml +++ b/ci/azure-minimal-versions.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: Linux: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 Windows: vmImage: vs2017-win2016 pool: diff --git a/ci/azure-rustfmt.yml b/ci/azure-rustfmt.yml index 655b4af38..1a5f1abea 100644 --- a/ci/azure-rustfmt.yml +++ b/ci/azure-rustfmt.yml @@ -3,7 +3,7 @@ jobs: - job: ${{ parameters.name }} displayName: Check rustfmt pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 steps: - template: azure-install-rust.yml parameters: @@ -12,5 +12,8 @@ jobs: rustup component add rustfmt displayName: Install rustfmt - script: | - cargo fmt --all -- --check + # FIXME: for some reason this doesn't actually check all files. + # So instead we run `rustfmt` directly on each file. + #cargo fmt --all -- --check + find src tests examples -type f -iname "*.rs" | xargs rustfmt --check displayName: Check formatting diff --git a/ci/azure-test-stable.yml b/ci/azure-test-stable.yml index 12cb725f2..3c9a8f0a4 100644 --- a/ci/azure-test-stable.yml +++ b/ci/azure-test-stable.yml @@ -8,7 +8,7 @@ jobs: strategy: matrix: Linux: - vmImage: ubuntu-16.04 + vmImage: ubuntu-18.04 ${{ if parameters.cross }}: MacOS: @@ -28,8 +28,10 @@ jobs: - ${{ if eq(parameters.cmd, 'test') }}: - script: | - cargo install cargo-hack - cargo hack check --feature-powerset --skip guide,extra-docs,tcp,udp,uds,pipe,os-util + # Cargo-hack's dependency bitflags has a higher MSVR then us. + rustup install nightly + rustup run nightly cargo install cargo-hack + cargo hack check --feature-powerset displayName: Check feature powerset - script: cargo ${{ parameters.cmd }} --all-features diff --git a/examples/tcp_server.rs b/examples/tcp_server.rs index 42426ee96..6347ab6de 100644 --- a/examples/tcp_server.rs +++ b/examples/tcp_server.rs @@ -1,5 +1,5 @@ // You can run this example from the root of the mio repo: -// cargo run --example tcp_server --features="os-poll tcp" +// cargo run --example tcp_server --features="os-poll net" use mio::event::Event; use mio::net::{TcpListener, TcpStream}; use mio::{Events, Interest, Poll, Registry, Token}; @@ -36,7 +36,7 @@ fn main() -> io::Result<()> { println!("You can connect to the server using `nc`:"); println!(" $ nc 127.0.0.1 9000"); - println!("You'll see our welcome message and anything you type we'll be printed here."); + println!("You'll see our welcome message and anything you type will be printed here."); loop { poll.poll(&mut events, None)?; @@ -82,7 +82,9 @@ fn main() -> io::Result<()> { false }; if done { - connections.remove(&token); + if let Some(mut connection) = connections.remove(&token) { + poll.registry().deregister(&mut connection)?; + } } } } diff --git a/examples/udp_server.rs b/examples/udp_server.rs index febb66210..ed6881d99 100644 --- a/examples/udp_server.rs +++ b/examples/udp_server.rs @@ -1,5 +1,5 @@ // You can run this example from the root of the mio repo: -// cargo run --example udp_server --features="os-poll udp" +// cargo run --example udp_server --features="os-poll net" use log::warn; use mio::net::UdpSocket; use mio::{Events, Interest, Poll, Token}; diff --git a/src/event/event.rs b/src/event/event.rs index 8147d8330..9dc478de0 100644 --- a/src/event/event.rs +++ b/src/event/event.rs @@ -25,6 +25,15 @@ impl Event { } /// Returns true if the event contains readable readiness. + /// + /// # Notes + /// + /// Out-of-band (OOB) data also triggers readable events. But must + /// application don't actually read OOB data, this could leave an + /// application open to a Denial-of-Service (Dos) attack, see + /// . + /// However because Mio uses edge-triggers it will not result in an infinite + /// loop as described in the article above. pub fn is_readable(&self) -> bool { sys::event::is_readable(&self.inner) } diff --git a/src/event/source.rs b/src/event/source.rs index f38268ab6..4f9c6635a 100644 --- a/src/event/source.rs +++ b/src/event/source.rs @@ -38,8 +38,8 @@ use std::io; /// /// Implementing `Source` on a struct containing a socket: /// -#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] -#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] +#[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// use mio::{Interest, Registry, Token}; /// use mio::event::Source; /// use mio::net::TcpStream; diff --git a/src/interest.rs b/src/interest.rs index 308b7c1d6..d703cb3ff 100644 --- a/src/interest.rs +++ b/src/interest.rs @@ -17,8 +17,8 @@ use std::{fmt, ops}; pub struct Interest(NonZeroU8); // These must be unique. -const READABLE: u8 = 0b0_001; -const WRITABLE: u8 = 0b0_010; +const READABLE: u8 = 0b0001; +const WRITABLE: u8 = 0b0010; // The following are not available on all platforms. #[cfg_attr( not(any( @@ -29,9 +29,9 @@ const WRITABLE: u8 = 0b0_010; )), allow(dead_code) )] -const AIO: u8 = 0b0_100; +const AIO: u8 = 0b0100; #[cfg_attr(not(target_os = "freebsd"), allow(dead_code))] -const LIO: u8 = 0b1_000; +const LIO: u8 = 0b1000; impl Interest { /// Returns a `Interest` set representing readable interests. diff --git a/src/io_source.rs b/src/io_source.rs index 962ff2f43..772339ba3 100644 --- a/src/io_source.rs +++ b/src/io_source.rs @@ -7,8 +7,6 @@ use std::os::windows::io::AsRawSocket; use std::sync::atomic::{AtomicUsize, Ordering}; use std::{fmt, io}; -#[cfg(any(unix, debug_assertions))] -use crate::poll; use crate::sys::IoSourceState; use crate::{event, Interest, Registry, Token}; @@ -101,7 +99,9 @@ where ) -> io::Result<()> { #[cfg(debug_assertions)] self.selector_id.associate(registry)?; - poll::selector(registry).register(self.inner.as_raw_fd(), token, interests) + registry + .selector() + .register(self.inner.as_raw_fd(), token, interests) } fn reregister( @@ -112,13 +112,15 @@ where ) -> io::Result<()> { #[cfg(debug_assertions)] self.selector_id.check_association(registry)?; - poll::selector(registry).reregister(self.inner.as_raw_fd(), token, interests) + registry + .selector() + .reregister(self.inner.as_raw_fd(), token, interests) } fn deregister(&mut self, registry: &Registry) -> io::Result<()> { #[cfg(debug_assertions)] self.selector_id.remove_association(registry)?; - poll::selector(registry).deregister(self.inner.as_raw_fd()) + registry.selector().deregister(self.inner.as_raw_fd()) } } @@ -179,10 +181,18 @@ impl SelectorId { /// `sys::Selector`. Valid selector ids start at 1. const UNASSOCIATED: usize = 0; + /// Create a new `SelectorId`. + #[cfg(not(windows))] + const fn new() -> SelectorId { + SelectorId { + id: AtomicUsize::new(Self::UNASSOCIATED), + } + } + /// Associate an I/O source with `registry`, returning an error if its /// already registered. fn associate(&self, registry: &Registry) -> io::Result<()> { - let registry_id = poll::selector(®istry).id(); + let registry_id = registry.selector().id(); let previous_id = self.id.swap(registry_id, Ordering::AcqRel); if previous_id == Self::UNASSOCIATED { @@ -199,7 +209,7 @@ impl SelectorId { /// error if its registered with a different `Registry` or not registered at /// all. fn check_association(&self, registry: &Registry) -> io::Result<()> { - let registry_id = poll::selector(®istry).id(); + let registry_id = registry.selector().id(); let id = self.id.load(Ordering::Acquire); if id == registry_id { @@ -220,7 +230,7 @@ impl SelectorId { /// Remove a previously made association from `registry`, returns an error /// if it was not previously associated with `registry`. fn remove_association(&self, registry: &Registry) -> io::Result<()> { - let registry_id = poll::selector(®istry).id(); + let registry_id = registry.selector().id(); let previous_id = self.id.swap(Self::UNASSOCIATED, Ordering::AcqRel); if previous_id == registry_id { diff --git a/src/lib.rs b/src/lib.rs index 686919819..70f4b5387 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#![doc(html_root_url = "https://docs.rs/mio/0.7.11")] #![deny( // missing_docs, missing_debug_implementations, @@ -45,10 +44,9 @@ #[macro_use] mod macros; -#[macro_use] -mod sys; mod interest; mod poll; +mod sys; mod token; mod waker; @@ -175,8 +173,8 @@ pub mod guide { //! //! [event source]: ../event/trait.Source.html //! - #![cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] - #![cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] + #![cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] + #![cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] //! # use mio::net::TcpListener; //! # use mio::{Poll, Token, Interest}; //! # fn main() -> std::io::Result<()> { @@ -214,8 +212,8 @@ pub mod guide { //! [poll]: ../struct.Poll.html#method.poll //! [event sources]: ../event/trait.Source.html //! - #![cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] - #![cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] + #![cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] + #![cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] //! # use std::io; //! # use std::time::Duration; //! # use mio::net::TcpListener; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index da276f3b6..21bffbaff 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -5,8 +5,11 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; use std::{fmt, io}; -use super::{TcpSocket, TcpStream}; use crate::io_source::IoSource; +use crate::net::TcpStream; +#[cfg(unix)] +use crate::sys::tcp::set_reuseaddr; +use crate::sys::tcp::{bind, listen, new_for_addr}; use crate::{event, sys, Interest, Registry, Token}; /// A structure representing a socket server @@ -50,7 +53,11 @@ impl TcpListener { /// 3. Bind the socket to the specified address. /// 4. Calls `listen` on the socket to prepare it to receive new connections. pub fn bind(addr: SocketAddr) -> io::Result { - let socket = TcpSocket::new_for_addr(addr)?; + let socket = new_for_addr(addr)?; + #[cfg(unix)] + let listener = unsafe { TcpListener::from_raw_fd(socket) }; + #[cfg(windows)] + let listener = unsafe { TcpListener::from_raw_socket(socket as _) }; // On platforms with Berkeley-derived sockets, this allows to quickly // rebind a socket, without needing to wait for the OS to clean up the @@ -60,10 +67,11 @@ impl TcpListener { // which allows “socket hijacking”, so we explicitly don't set it here. // https://docs.microsoft.com/en-us/windows/win32/winsock/using-so-reuseaddr-and-so-exclusiveaddruse #[cfg(not(windows))] - socket.set_reuseaddr(true)?; + set_reuseaddr(&listener.inner, true)?; - socket.bind(addr)?; - socket.listen(1024) + bind(&listener.inner, addr)?; + listen(&listener.inner, 1024)?; + Ok(listener) } /// Creates a new `TcpListener` from a standard `net::TcpListener`. diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index cdbd46a48..029f186b4 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -7,7 +7,7 @@ use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; use crate::io_source::IoSource; -use crate::net::TcpSocket; +use crate::sys::tcp::{connect, new_for_addr}; use crate::{event, Interest, Registry, Token}; /// A non-blocking TCP stream between a local socket and a remote socket. @@ -49,9 +49,38 @@ pub struct TcpStream { impl TcpStream { /// Create a new TCP stream and issue a non-blocking connect to the /// specified address. + /// + /// # Notes + /// + /// The returned `TcpStream` may not be connected (and thus usable), unlike + /// the API found in `std::net::TcpStream`. Because Mio issues a + /// *non-blocking* connect it will not block the thread and instead return + /// an unconnected `TcpStream`. + /// + /// Ensuring the returned stream is connected is surprisingly complex when + /// considering cross-platform support. Doing this properly should follow + /// the steps below, an example implementation can be found + /// [here](https://github.com/Thomasdezeeuw/heph/blob/0c4f1ab3eaf08bea1d65776528bfd6114c9f8374/src/net/tcp/stream.rs#L560-L622). + /// + /// 1. Call `TcpStream::connect` + /// 2. Register the returned stream with at least [read interest]. + /// 3. Wait for a (readable) event. + /// 4. Check `TcpStream::peer_addr`. If it returns `libc::EINPROGRESS` or + /// `ErrorKind::NotConnected` it means the stream is not yet connected, + /// go back to step 3. If it returns an address it means the stream is + /// connected, go to step 5. If another error is returned something + /// whent wrong. + /// 5. Now the stream can be used. + /// + /// [read interest]: Interest::READABLE pub fn connect(addr: SocketAddr) -> io::Result { - let socket = TcpSocket::new_for_addr(addr)?; - socket.connect(addr) + let socket = new_for_addr(addr)?; + #[cfg(unix)] + let stream = unsafe { TcpStream::from_raw_fd(socket) }; + #[cfg(windows)] + let stream = unsafe { TcpStream::from_raw_socket(socket as _) }; + connect(&stream.inner, addr)?; + Ok(stream) } /// Creates a new `TcpStream` from a standard `net::TcpStream`. @@ -103,7 +132,7 @@ impl TcpStream { /// /// On Windows make sure the stream is connected before calling this method, /// by receiving an (writable) event. Trying to set `nodelay` on an - /// unconnected `TcpStream` is undefined behavior. + /// unconnected `TcpStream` is unspecified behavior. pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { self.inner.set_nodelay(nodelay) } @@ -118,7 +147,7 @@ impl TcpStream { /// /// On Windows make sure the stream is connected before calling this method, /// by receiving an (writable) event. Trying to get `nodelay` on an - /// unconnected `TcpStream` is undefined behavior. + /// unconnected `TcpStream` is unspecified behavior. pub fn nodelay(&self) -> io::Result { self.inner.nodelay() } @@ -132,7 +161,7 @@ impl TcpStream { /// /// On Windows make sure the stream is connected before calling this method, /// by receiving an (writable) event. Trying to set `ttl` on an - /// unconnected `TcpStream` is undefined behavior. + /// unconnected `TcpStream` is unspecified behavior. pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { self.inner.set_ttl(ttl) } @@ -145,7 +174,7 @@ impl TcpStream { /// /// On Windows make sure the stream is connected before calling this method, /// by receiving an (writable) event. Trying to get `ttl` on an - /// unconnected `TcpStream` is undefined behavior. + /// unconnected `TcpStream` is unspecified behavior. /// /// [link]: #method.set_ttl pub fn ttl(&self) -> io::Result { diff --git a/src/net/udp.rs b/src/net/udp.rs index c5c3ba92f..8cfe4e456 100644 --- a/src/net/udp.rs +++ b/src/net/udp.rs @@ -161,6 +161,29 @@ impl UdpSocket { self.inner.local_addr() } + /// Returns the socket address of the remote peer this socket was connected to. + /// + /// # Examples + /// + #[cfg_attr(feature = "os-poll", doc = "```")] + #[cfg_attr(not(feature = "os-poll"), doc = "```ignore")] + /// # use std::error::Error; + /// # + /// # fn main() -> Result<(), Box> { + /// use mio::net::UdpSocket; + /// + /// let addr = "127.0.0.1:0".parse()?; + /// let peer_addr = "127.0.0.1:11100".parse()?; + /// let socket = UdpSocket::bind(addr)?; + /// socket.connect(peer_addr)?; + /// assert_eq!(socket.peer_addr()?.ip(), peer_addr.ip()); + /// # Ok(()) + /// # } + /// ``` + pub fn peer_addr(&self) -> io::Result { + self.inner.peer_addr() + } + /// Sends data on the socket to the given address. On success, returns the /// number of bytes written. /// diff --git a/src/poll.rs b/src/poll.rs index a6f4ab0b5..fd643fdd0 100644 --- a/src/poll.rs +++ b/src/poll.rs @@ -30,8 +30,8 @@ use std::{fmt, io}; /// /// A basic example -- establishing a `TcpStream` connection. /// -#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] -#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] +#[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll, Interest, Token}; @@ -127,8 +127,8 @@ use std::{fmt, io}; /// /// For example: /// -#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] -#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] +#[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -163,6 +163,30 @@ use std::{fmt, io}; /// /// [event sources]: ./event/trait.Source.html /// +/// ### Accessing raw fd/socket/handle +/// +/// Mio makes it possible for many types to be converted into a raw file +/// descriptor (fd, Unix), socket (Windows) or handle (Windows). This makes it +/// possible to support more operations on the type than Mio supports, for +/// example it makes [mio-aio] possible. However accessing the raw fd is not +/// without it's pitfalls. +/// +/// Specifically performing I/O operations outside of Mio on these types (via +/// the raw fd) has unspecified behaviour. It could cause no more events to be +/// generated for the type even though it returned `WouldBlock` (in an operation +/// directly accessing the fd). The behaviour is OS specific and Mio can only +/// guarantee cross-platform behaviour if it can control the I/O. +/// +/// [mio-aio]: https://github.com/asomers/mio-aio +/// +/// *The following is **not** guaranteed, just a description of the current +/// situation!* Mio is allowed to change the following without it being considered +/// a breaking change, don't depend on this, it's just here to inform the user. +/// Currently the kqueue and epoll implementation support direct I/O operations +/// on the fd without Mio's knowledge. Windows however needs **all** I/O +/// operations to go through Mio otherwise it is not able to update it's +/// internal state properly and won't generate events. +/// /// # Implementation notes /// /// `Poll` is backed by the selector provided by the operating system. @@ -172,13 +196,12 @@ use std::{fmt, io}; /// | Android | [epoll] | /// | DragonFly BSD | [kqueue] | /// | FreeBSD | [kqueue] | +/// | iOS | [kqueue] | +/// | illumos | [epoll] | /// | Linux | [epoll] | /// | NetBSD | [kqueue] | /// | OpenBSD | [kqueue] | -/// | Solaris | [epoll] | -/// | illumos | [epoll] | /// | Windows | [IOCP] | -/// | iOS | [kqueue] | /// | macOS | [kqueue] | /// /// On all supported platforms, socket operations are handled by using the @@ -260,8 +283,8 @@ impl Poll { /// /// A basic example -- establishing a `TcpStream` connection. /// - #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] - #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] + #[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Poll, Interest, Token}; @@ -382,7 +405,7 @@ impl Registry { /// /// # Arguments /// - /// `source: &S: event::Source`: This is the source of events that the + /// `source: &mut S: event::Source`: This is the source of events that the /// `Poll` instance should monitor for readiness state changes. /// /// `token: Token`: The caller picks a token to associate with the socket. @@ -409,7 +432,7 @@ impl Registry { /// Callers must ensure that if a source being registered with a `Poll` /// instance was previously registered with that `Poll` instance, then a /// call to [`deregister`] has already occurred. Consecutive calls to - /// `register` is undefined behavior. + /// `register` is unspecified behavior. /// /// Unless otherwise specified, the caller should assume that once an event /// source is registered with a `Poll` instance, it is bound to that `Poll` @@ -425,8 +448,8 @@ impl Registry { /// /// # Examples /// - #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] - #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] + #[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -495,7 +518,7 @@ impl Registry { /// requested for the handle. /// /// The event source must have previously been registered with this instance - /// of `Poll`, otherwise the behavior is undefined. + /// of `Poll`, otherwise the behavior is unspecified. /// /// See the [`register`] documentation for details about the function /// arguments and see the [`struct`] docs for a high level overview of @@ -503,8 +526,8 @@ impl Registry { /// /// # Examples /// - #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] - #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] + #[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -562,16 +585,16 @@ impl Registry { /// the poll. /// /// The event source must have previously been registered with this instance - /// of `Poll`, otherwise the behavior is undefined. + /// of `Poll`, otherwise the behavior is unspecified. /// /// A handle can be passed back to `register` after it has been /// deregistered; however, it must be passed back to the **same** `Poll` - /// instance, otherwise the behavior is undefined. + /// instance, otherwise the behavior is unspecified. /// /// # Examples /// - #[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] - #[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] + #[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] + #[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # use std::net; /// # fn main() -> Result<(), Box> { @@ -624,9 +647,15 @@ impl Registry { /// instance. #[cfg(debug_assertions)] pub(crate) fn register_waker(&self) { - if self.selector.register_waker() { - panic!("Only a single `Waker` can be active per `Poll` instance"); - } + assert!( + !self.selector.register_waker(), + "Only a single `Waker` can be active per `Poll` instance" + ); + } + + /// Get access to the `sys::Selector`. + pub(crate) fn selector(&self) -> &sys::Selector { + &self.selector } } @@ -643,11 +672,6 @@ impl AsRawFd for Registry { } } -/// Get access to the `sys::Selector` from `Registry`. -pub(crate) fn selector(registry: &Registry) -> &sys::Selector { - ®istry.selector -} - cfg_os_poll! { #[cfg(unix)] #[test] diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 30fefc349..106c7a0f2 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -14,6 +14,44 @@ //! * `tcp` and `udp` modules: see the [`crate::net`] module. //! * `Waker`: see [`crate::Waker`]. +#[cfg(not(windows))] +cfg_os_poll! { + macro_rules! debug_detail { + ( + $type: ident ($event_type: ty), $test: path, + $($(#[$target: meta])* $libc: ident :: $flag: ident),+ $(,)* + ) => { + struct $type($event_type); + + impl fmt::Debug for $type { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut written_one = false; + $( + $(#[$target])* + #[allow(clippy::bad_bit_mask)] // Apparently some flags are zero. + { + // Windows doesn't use `libc` but the `afd` module. + if $test(&self.0, &$libc :: $flag) { + if !written_one { + write!(f, "{}", stringify!($flag))?; + written_one = true; + } else { + write!(f, "|{}", stringify!($flag))?; + } + } + } + )+ + if !written_one { + write!(f, "(empty)") + } else { + Ok(()) + } + } + } + }; + } +} + #[cfg(unix)] cfg_os_poll! { mod unix; diff --git a/src/sys/shell/tcp.rs b/src/sys/shell/tcp.rs index 0ed225f71..60dfe70f6 100644 --- a/src/sys/shell/tcp.rs +++ b/src/sys/shell/tcp.rs @@ -1,127 +1,27 @@ -use crate::net::TcpKeepalive; use std::io; use std::net::{self, SocketAddr}; -use std::time::Duration; -pub(crate) type TcpSocket = i32; - -pub(crate) fn new_v4_socket() -> io::Result { - os_required!(); -} - -pub(crate) fn new_v6_socket() -> io::Result { - os_required!(); -} - -pub(crate) fn bind(_socket: TcpSocket, _addr: SocketAddr) -> io::Result<()> { - os_required!(); -} - -pub(crate) fn connect(_: TcpSocket, _addr: SocketAddr) -> io::Result { - os_required!(); -} - -pub(crate) fn listen(_: TcpSocket, _: u32) -> io::Result { +pub(crate) fn new_for_addr(_: SocketAddr) -> io::Result { os_required!(); } -pub(crate) fn close(_: TcpSocket) { +pub(crate) fn bind(_: &net::TcpListener, _: SocketAddr) -> io::Result<()> { os_required!(); } -pub(crate) fn set_reuseaddr(_: TcpSocket, _: bool) -> io::Result<()> { +pub(crate) fn connect(_: &net::TcpStream, _: SocketAddr) -> io::Result<()> { os_required!(); } -pub(crate) fn get_reuseaddr(_: TcpSocket) -> io::Result { +pub(crate) fn listen(_: &net::TcpListener, _: u32) -> io::Result<()> { os_required!(); } -#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] -pub(crate) fn set_reuseport(_: TcpSocket, _: bool) -> io::Result<()> { - os_required!(); -} - -#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] -pub(crate) fn get_reuseport(_: TcpSocket) -> io::Result { - os_required!(); -} - -pub(crate) fn set_linger(_: TcpSocket, _: Option) -> io::Result<()> { - os_required!(); -} - -pub(crate) fn get_linger(_: TcpSocket) -> io::Result> { - os_required!(); -} - -pub(crate) fn set_recv_buffer_size(_: TcpSocket, _: u32) -> io::Result<()> { - os_required!(); -} - -pub(crate) fn get_recv_buffer_size(_: TcpSocket) -> io::Result { - os_required!(); -} - -pub(crate) fn set_send_buffer_size(_: TcpSocket, _: u32) -> io::Result<()> { - os_required!(); -} - -pub(crate) fn get_send_buffer_size(_: TcpSocket) -> io::Result { - os_required!(); -} - -pub(crate) fn set_keepalive(_: TcpSocket, _: bool) -> io::Result<()> { - os_required!(); -} - -pub(crate) fn get_keepalive(_: TcpSocket) -> io::Result { - os_required!(); -} - -pub(crate) fn set_keepalive_params(_: TcpSocket, _: TcpKeepalive) -> io::Result<()> { - os_required!() -} - -#[cfg(any( - target_os = "android", - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", - target_os = "solaris", -))] -pub(crate) fn get_keepalive_time(_: TcpSocket) -> io::Result> { - os_required!() -} - -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", -))] -pub(crate) fn get_keepalive_interval(_: TcpSocket) -> io::Result> { - os_required!() -} - -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", -))] -pub(crate) fn get_keepalive_retries(_: TcpSocket) -> io::Result> { - os_required!() -} - -pub fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { +#[cfg(unix)] +pub(crate) fn set_reuseaddr(_: &net::TcpListener, _: bool) -> io::Result<()> { os_required!(); } -pub(crate) fn get_localaddr(_: TcpSocket) -> io::Result { +pub(crate) fn accept(_: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { os_required!(); } diff --git a/src/sys/unix/net.rs b/src/sys/unix/net.rs index 2f8d618c0..78f1387b1 100644 --- a/src/sys/unix/net.rs +++ b/src/sys/unix/net.rs @@ -41,9 +41,8 @@ pub(crate) fn new_socket(domain: libc::c_int, socket_type: libc::c_int) -> io::R .map(|_| socket) }); - // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. Not sure about - // Solaris, couldn't find anything online. - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))] + // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. + #[cfg(any(target_os = "ios", target_os = "macos"))] let socket = socket.and_then(|socket| { // For platforms that don't support flags in socket, we need to // set the flags ourselves. @@ -124,7 +123,7 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, libc::socklen_ target_os = "openbsd" ))] sin6_len: 0, - #[cfg(any(target_os = "solaris", target_os = "illumos"))] + #[cfg(target_os = "illumos")] __sin6_src_id: 0, }; diff --git a/src/sys/unix/pipe.rs b/src/sys/unix/pipe.rs index ccf5252d5..c899dfb2d 100644 --- a/src/sys/unix/pipe.rs +++ b/src/sys/unix/pipe.rs @@ -162,7 +162,7 @@ pub fn new() -> io::Result<(Sender, Receiver)> { } } - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))] + #[cfg(any(target_os = "ios", target_os = "macos"))] unsafe { // For platforms that don't have `pipe2(2)` we need to manually set the // correct flags on the file descriptor. @@ -192,7 +192,6 @@ pub fn new() -> io::Result<(Sender, Receiver)> { target_os = "openbsd", target_os = "ios", target_os = "macos", - target_os = "solaris", target_os = "illumos", )))] compile_error!("unsupported target for `mio::unix::pipe`"); diff --git a/src/sys/unix/selector/epoll.rs b/src/sys/unix/selector/epoll.rs index 76ee7f91a..f4430909b 100644 --- a/src/sys/unix/selector/epoll.rs +++ b/src/sys/unix/selector/epoll.rs @@ -41,7 +41,7 @@ impl Selector { } pub fn try_clone(&self) -> io::Result { - syscall!(dup(self.ep)).map(|ep| Selector { + syscall!(fcntl(self.ep, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|ep| Selector { // It's the same selector, so we use the same id. #[cfg(debug_assertions)] id: self.id, @@ -222,7 +222,7 @@ pub mod event { libc::EPOLLET, libc::EPOLLRDHUP, libc::EPOLLONESHOT, - #[cfg(any(target_os = "linux", target_os = "solaris"))] + #[cfg(target_os = "linux")] libc::EPOLLEXCLUSIVE, #[cfg(any(target_os = "android", target_os = "linux"))] libc::EPOLLWAKEUP, diff --git a/src/sys/unix/selector/kqueue.rs b/src/sys/unix/selector/kqueue.rs index 34f534028..b7a01a51c 100644 --- a/src/sys/unix/selector/kqueue.rs +++ b/src/sys/unix/selector/kqueue.rs @@ -87,7 +87,7 @@ impl Selector { } pub fn try_clone(&self) -> io::Result { - syscall!(dup(self.kq)).map(|kq| Selector { + syscall!(fcntl(self.kq, libc::F_DUPFD_CLOEXEC, super::LOWEST_FD)).map(|kq| Selector { // It's the same selector, so we use the same id. #[cfg(debug_assertions)] id: self.id, @@ -285,7 +285,7 @@ fn kevent_register( Err(err) } }) - .and_then(|()| check_errors(&changes, ignored_errors)) + .and_then(|()| check_errors(changes, ignored_errors)) } /// Check all events for possible errors, it returns the first error found. diff --git a/src/sys/unix/selector/mod.rs b/src/sys/unix/selector/mod.rs index 752589817..da61e14d7 100644 --- a/src/sys/unix/selector/mod.rs +++ b/src/sys/unix/selector/mod.rs @@ -1,17 +1,7 @@ -#[cfg(any( - target_os = "android", - target_os = "illumos", - target_os = "linux", - target_os = "solaris" -))] +#[cfg(any(target_os = "android", target_os = "illumos", target_os = "linux"))] mod epoll; -#[cfg(any( - target_os = "android", - target_os = "illumos", - target_os = "linux", - target_os = "solaris" -))] +#[cfg(any(target_os = "android", target_os = "illumos", target_os = "linux"))] pub(crate) use self::epoll::{event, Event, Events, Selector}; #[cfg(any( @@ -33,3 +23,13 @@ mod kqueue; target_os = "openbsd" ))] pub(crate) use self::kqueue::{event, Event, Events, Selector}; + +/// Lowest file descriptor used in `Selector::try_clone`. +/// +/// # Notes +/// +/// Usually fds 0, 1 and 2 are standard in, out and error. Some application +/// blindly assume this to be true, which means using any one of those a select +/// could result in some interesting and unexpected errors. Avoid that by using +/// an fd that doesn't have a pre-determined usage. +const LOWEST_FD: libc::c_int = 3; diff --git a/src/sys/unix/sourcefd.rs b/src/sys/unix/sourcefd.rs index ba52b3855..84e776d21 100644 --- a/src/sys/unix/sourcefd.rs +++ b/src/sys/unix/sourcefd.rs @@ -1,4 +1,4 @@ -use crate::{event, poll, Interest, Registry, Token}; +use crate::{event, Interest, Registry, Token}; use std::io; use std::os::unix::io::RawFd; @@ -25,8 +25,14 @@ use std::os::unix::io::RawFd; /// /// Basic usage. /// -#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] -#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] +#[cfg_attr( + all(feature = "os-poll", feature = "net", feature = "os-ext"), + doc = "```" +)] +#[cfg_attr( + not(all(feature = "os-poll", feature = "net", feature = "os-ext")), + doc = "```ignore" +)] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Interest, Poll, Token}; @@ -51,8 +57,8 @@ use std::os::unix::io::RawFd; /// /// Implementing [`event::Source`] for a custom type backed by a [`RawFd`]. /// -#[cfg_attr(all(feature = "os-poll", features = "os-ext"), doc = "```")] -#[cfg_attr(not(all(feature = "os-poll", features = "os-ext")), doc = "```ignore")] +#[cfg_attr(all(feature = "os-poll", feature = "os-ext"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", feature = "os-ext")), doc = "```ignore")] /// use mio::{event, Interest, Registry, Token}; /// use mio::unix::SourceFd; /// @@ -92,7 +98,7 @@ impl<'a> event::Source for SourceFd<'a> { token: Token, interests: Interest, ) -> io::Result<()> { - poll::selector(registry).register(*self.0, token, interests) + registry.selector().register(*self.0, token, interests) } fn reregister( @@ -101,10 +107,10 @@ impl<'a> event::Source for SourceFd<'a> { token: Token, interests: Interest, ) -> io::Result<()> { - poll::selector(registry).reregister(*self.0, token, interests) + registry.selector().reregister(*self.0, token, interests) } fn deregister(&mut self, registry: &Registry) -> io::Result<()> { - poll::selector(registry).deregister(*self.0) + registry.selector().deregister(*self.0) } } diff --git a/src/sys/unix/tcp.rs b/src/sys/unix/tcp.rs index 59642c610..5b02cfcb5 100644 --- a/src/sys/unix/tcp.rs +++ b/src/sys/unix/tcp.rs @@ -1,428 +1,57 @@ use std::convert::TryInto; use std::io; -use std::mem; use std::mem::{size_of, MaybeUninit}; use std::net::{self, SocketAddr}; use std::os::unix::io::{AsRawFd, FromRawFd}; -use std::time::Duration; use crate::sys::unix::net::{new_socket, socket_addr, to_socket_addr}; -use crate::net::TcpKeepalive; -#[cfg(any(target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))] -use libc::SO_KEEPALIVE as KEEPALIVE_TIME; -#[cfg(any(target_os = "macos", target_os = "ios"))] -use libc::TCP_KEEPALIVE as KEEPALIVE_TIME; -#[cfg(not(any( - target_os = "macos", - target_os = "ios", - target_os = "openbsd", - target_os = "netbsd", - target_os = "haiku" -)))] -use libc::TCP_KEEPIDLE as KEEPALIVE_TIME; -pub type TcpSocket = libc::c_int; - -pub(crate) fn new_v4_socket() -> io::Result { - new_socket(libc::AF_INET, libc::SOCK_STREAM) -} - -pub(crate) fn new_v6_socket() -> io::Result { - new_socket(libc::AF_INET6, libc::SOCK_STREAM) +pub(crate) fn new_for_addr(address: SocketAddr) -> io::Result { + let domain = match address { + SocketAddr::V4(_) => libc::AF_INET, + SocketAddr::V6(_) => libc::AF_INET6, + }; + new_socket(domain, libc::SOCK_STREAM) } -pub(crate) fn bind(socket: TcpSocket, addr: SocketAddr) -> io::Result<()> { +pub(crate) fn bind(socket: &net::TcpListener, addr: SocketAddr) -> io::Result<()> { let (raw_addr, raw_addr_length) = socket_addr(&addr); - syscall!(bind(socket, raw_addr.as_ptr(), raw_addr_length))?; + syscall!(bind(socket.as_raw_fd(), raw_addr.as_ptr(), raw_addr_length))?; Ok(()) } -pub(crate) fn connect(socket: TcpSocket, addr: SocketAddr) -> io::Result { +pub(crate) fn connect(socket: &net::TcpStream, addr: SocketAddr) -> io::Result<()> { let (raw_addr, raw_addr_length) = socket_addr(&addr); - match syscall!(connect(socket, raw_addr.as_ptr(), raw_addr_length)) { - Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => { - Err(err) - } - _ => { - Ok(unsafe { net::TcpStream::from_raw_fd(socket) }) - } + match syscall!(connect( + socket.as_raw_fd(), + raw_addr.as_ptr(), + raw_addr_length + )) { + Err(err) if err.raw_os_error() != Some(libc::EINPROGRESS) => Err(err), + _ => Ok(()), } } -pub(crate) fn listen(socket: TcpSocket, backlog: u32) -> io::Result { +pub(crate) fn listen(socket: &net::TcpListener, backlog: u32) -> io::Result<()> { let backlog = backlog.try_into().unwrap_or(i32::max_value()); - syscall!(listen(socket, backlog))?; - Ok(unsafe { net::TcpListener::from_raw_fd(socket) }) -} - -pub(crate) fn close(socket: TcpSocket) { - let _ = unsafe { net::TcpStream::from_raw_fd(socket) }; + syscall!(listen(socket.as_raw_fd(), backlog))?; + Ok(()) } -pub(crate) fn set_reuseaddr(socket: TcpSocket, reuseaddr: bool) -> io::Result<()> { +pub(crate) fn set_reuseaddr(socket: &net::TcpListener, reuseaddr: bool) -> io::Result<()> { let val: libc::c_int = if reuseaddr { 1 } else { 0 }; syscall!(setsockopt( - socket, + socket.as_raw_fd(), libc::SOL_SOCKET, libc::SO_REUSEADDR, &val as *const libc::c_int as *const libc::c_void, size_of::() as libc::socklen_t, - )) - .map(|_| ()) -} - -pub(crate) fn get_reuseaddr(socket: TcpSocket) -> io::Result { - let mut optval: libc::c_int = 0; - let mut optlen = mem::size_of::() as libc::socklen_t; - - syscall!(getsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_REUSEADDR, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(optval != 0) -} - -#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] -pub(crate) fn set_reuseport(socket: TcpSocket, reuseport: bool) -> io::Result<()> { - let val: libc::c_int = if reuseport { 1 } else { 0 }; - - syscall!(setsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_REUSEPORT, - &val as *const libc::c_int as *const libc::c_void, - size_of::() as libc::socklen_t, - )) - .map(|_| ()) -} - -#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))] -pub(crate) fn get_reuseport(socket: TcpSocket) -> io::Result { - let mut optval: libc::c_int = 0; - let mut optlen = mem::size_of::() as libc::socklen_t; - - syscall!(getsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_REUSEPORT, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(optval != 0) -} - -pub(crate) fn get_localaddr(socket: TcpSocket) -> io::Result { - let mut addr: libc::sockaddr_storage = unsafe { std::mem::zeroed() }; - let mut length = size_of::() as libc::socklen_t; - - syscall!(getsockname( - socket, - &mut addr as *mut _ as *mut _, - &mut length ))?; - - unsafe { to_socket_addr(&addr) } -} - -pub(crate) fn set_linger(socket: TcpSocket, dur: Option) -> io::Result<()> { - let val: libc::linger = libc::linger { - l_onoff: if dur.is_some() { 1 } else { 0 }, - l_linger: dur - .map(|dur| dur.as_secs() as libc::c_int) - .unwrap_or_default(), - }; - syscall!(setsockopt( - socket, - libc::SOL_SOCKET, - #[cfg(target_vendor = "apple")] - libc::SO_LINGER_SEC, - #[cfg(not(target_vendor = "apple"))] - libc::SO_LINGER, - &val as *const libc::linger as *const libc::c_void, - size_of::() as libc::socklen_t, - )) - .map(|_| ()) -} - -pub(crate) fn get_linger(socket: TcpSocket) -> io::Result> { - let mut val: libc::linger = unsafe { std::mem::zeroed() }; - let mut len = mem::size_of::() as libc::socklen_t; - - syscall!(getsockopt( - socket, - libc::SOL_SOCKET, - #[cfg(target_vendor = "apple")] - libc::SO_LINGER_SEC, - #[cfg(not(target_vendor = "apple"))] - libc::SO_LINGER, - &mut val as *mut _ as *mut _, - &mut len, - ))?; - - if val.l_onoff == 0 { - Ok(None) - } else { - Ok(Some(Duration::from_secs(val.l_linger as u64))) - } -} - -pub(crate) fn set_recv_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> { - let size = size.try_into().ok().unwrap_or_else(i32::max_value); - syscall!(setsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_RCVBUF, - &size as *const _ as *const libc::c_void, - size_of::() as libc::socklen_t - )) - .map(|_| ()) -} - -pub(crate) fn get_recv_buffer_size(socket: TcpSocket) -> io::Result { - let mut optval: libc::c_int = 0; - let mut optlen = size_of::() as libc::socklen_t; - syscall!(getsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_RCVBUF, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(optval as u32) -} - -pub(crate) fn set_send_buffer_size(socket: TcpSocket, size: u32) -> io::Result<()> { - let size = size.try_into().ok().unwrap_or_else(i32::max_value); - syscall!(setsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_SNDBUF, - &size as *const _ as *const libc::c_void, - size_of::() as libc::socklen_t - )) - .map(|_| ()) -} - -pub(crate) fn get_send_buffer_size(socket: TcpSocket) -> io::Result { - let mut optval: libc::c_int = 0; - let mut optlen = size_of::() as libc::socklen_t; - - syscall!(getsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_SNDBUF, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(optval as u32) -} - -pub(crate) fn set_keepalive(socket: TcpSocket, keepalive: bool) -> io::Result<()> { - let val: libc::c_int = if keepalive { 1 } else { 0 }; - syscall!(setsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_KEEPALIVE, - &val as *const _ as *const libc::c_void, - size_of::() as libc::socklen_t - )) - .map(|_| ()) -} - -pub(crate) fn get_keepalive(socket: TcpSocket) -> io::Result { - let mut optval: libc::c_int = 0; - let mut optlen = mem::size_of::() as libc::socklen_t; - - syscall!(getsockopt( - socket, - libc::SOL_SOCKET, - libc::SO_KEEPALIVE, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(optval != 0) -} - -pub(crate) fn set_keepalive_params(socket: TcpSocket, keepalive: TcpKeepalive) -> io::Result<()> { - if let Some(dur) = keepalive.time { - set_keepalive_time(socket, dur)?; - } - - #[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", - ))] - { - if let Some(dur) = keepalive.interval { - set_keepalive_interval(socket, dur)?; - } - - if let Some(retries) = keepalive.retries { - set_keepalive_retries(socket, retries)?; - } - } - - Ok(()) } -fn set_keepalive_time(socket: TcpSocket, time: Duration) -> io::Result<()> { - let time_secs = time - .as_secs() - .try_into() - .ok() - .unwrap_or_else(i32::max_value); - syscall!(setsockopt( - socket, - libc::IPPROTO_TCP, - KEEPALIVE_TIME, - &(time_secs as libc::c_int) as *const _ as *const libc::c_void, - size_of::() as libc::socklen_t - )) - .map(|_| ()) -} - -pub(crate) fn get_keepalive_time(socket: TcpSocket) -> io::Result> { - if !get_keepalive(socket)? { - return Ok(None); - } - - let mut optval: libc::c_int = 0; - let mut optlen = mem::size_of::() as libc::socklen_t; - syscall!(getsockopt( - socket, - libc::IPPROTO_TCP, - KEEPALIVE_TIME, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(Some(Duration::from_secs(optval as u64))) -} - -/// Linux, FreeBSD, and NetBSD support setting the keepalive interval via -/// `TCP_KEEPINTVL`. -/// See: -/// - https://man7.org/linux/man-pages/man7/tcp.7.html -/// - https://www.freebsd.org/cgi/man.cgi?query=tcp#end -/// - http://man.netbsd.org/tcp.4#DESCRIPTION -/// -/// OpenBSD does not: -/// https://man.openbsd.org/tcp -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", -))] -fn set_keepalive_interval(socket: TcpSocket, interval: Duration) -> io::Result<()> { - let interval_secs = interval - .as_secs() - .try_into() - .ok() - .unwrap_or_else(i32::max_value); - syscall!(setsockopt( - socket, - libc::IPPROTO_TCP, - libc::TCP_KEEPINTVL, - &(interval_secs as libc::c_int) as *const _ as *const libc::c_void, - size_of::() as libc::socklen_t - )) - .map(|_| ()) -} - -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", -))] -pub(crate) fn get_keepalive_interval(socket: TcpSocket) -> io::Result> { - if !get_keepalive(socket)? { - return Ok(None); - } - - let mut optval: libc::c_int = 0; - let mut optlen = mem::size_of::() as libc::socklen_t; - syscall!(getsockopt( - socket, - libc::IPPROTO_TCP, - libc::TCP_KEEPINTVL, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(Some(Duration::from_secs(optval as u64))) -} - -/// Linux, macOS/iOS, FreeBSD, and NetBSD support setting the number of TCP -/// keepalive retries via `TCP_KEEPCNT`. -/// See: -/// - https://man7.org/linux/man-pages/man7/tcp.7.html -/// - https://www.freebsd.org/cgi/man.cgi?query=tcp#end -/// - http://man.netbsd.org/tcp.4#DESCRIPTION -/// -/// OpenBSD does not: -/// https://man.openbsd.org/tcp -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", -))] -fn set_keepalive_retries(socket: TcpSocket, retries: u32) -> io::Result<()> { - let retries = retries.try_into().ok().unwrap_or_else(i32::max_value); - syscall!(setsockopt( - socket, - libc::IPPROTO_TCP, - libc::TCP_KEEPCNT, - &(retries as libc::c_int) as *const _ as *const libc::c_void, - size_of::() as libc::socklen_t - )) - .map(|_| ()) -} - -#[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "ios", - target_os = "freebsd", - target_os = "netbsd", -))] -pub(crate) fn get_keepalive_retries(socket: TcpSocket) -> io::Result> { - if !get_keepalive(socket)? { - return Ok(None); - } - - let mut optval: libc::c_int = 0; - let mut optlen = mem::size_of::() as libc::socklen_t; - syscall!(getsockopt( - socket, - libc::IPPROTO_TCP, - libc::TCP_KEEPCNT, - &mut optval as *mut _ as *mut _, - &mut optlen, - ))?; - - Ok(Some(optval as u32)) -} - -pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { +pub(crate) fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, SocketAddr)> { let mut addr: MaybeUninit = MaybeUninit::uninit(); let mut length = size_of::() as libc::socklen_t; @@ -456,13 +85,9 @@ pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, Socket // OSes inherit the non-blocking flag from the listener, so we just have to // set `CLOEXEC`. #[cfg(any( - all( - target_arch = "x86", - target_os = "android" - ), - target_os = "ios", - target_os = "macos", - target_os = "solaris" + all(target_arch = "x86", target_os = "android"), + target_os = "ios", + target_os = "macos", ))] let stream = { syscall!(accept( @@ -473,11 +98,11 @@ pub fn accept(listener: &net::TcpListener) -> io::Result<(net::TcpStream, Socket .map(|socket| unsafe { net::TcpStream::from_raw_fd(socket) }) .and_then(|s| { syscall!(fcntl(s.as_raw_fd(), libc::F_SETFD, libc::FD_CLOEXEC))?; - + // See https://github.com/tokio-rs/mio/issues/1450 - #[cfg(all(target_arch = "x86",target_os = "android"))] + #[cfg(all(target_arch = "x86", target_os = "android"))] syscall!(fcntl(s.as_raw_fd(), libc::F_SETFL, libc::O_NONBLOCK))?; - + Ok(s) }) }?; diff --git a/src/sys/unix/uds/listener.rs b/src/sys/unix/uds/listener.rs index 547ff5705..b6218427f 100644 --- a/src/sys/unix/uds/listener.rs +++ b/src/sys/unix/uds/listener.rs @@ -42,7 +42,6 @@ pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, So target_os = "ios", target_os = "macos", target_os = "netbsd", - target_os = "solaris", // Android x86's seccomp profile forbids calls to `accept4(2)` // See https://github.com/tokio-rs/mio/issues/1445 for details all( @@ -65,11 +64,7 @@ pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, So target_os = "ios", target_os = "macos", target_os = "netbsd", - target_os = "solaris", - all( - target_arch = "x86", - target_os = "android" - ) + all(target_arch = "x86", target_os = "android") ))] let socket = syscall!(accept( listener.as_raw_fd(), @@ -83,9 +78,9 @@ pub(crate) fn accept(listener: &net::UnixListener) -> io::Result<(UnixStream, So syscall!(fcntl(socket, libc::F_SETFD, libc::FD_CLOEXEC))?; // See https://github.com/tokio-rs/mio/issues/1450 - #[cfg(all(target_arch = "x86",target_os = "android"))] + #[cfg(all(target_arch = "x86", target_os = "android"))] syscall!(fcntl(socket, libc::F_SETFL, libc::O_NONBLOCK))?; - + Ok(s) }); diff --git a/src/sys/unix/uds/mod.rs b/src/sys/unix/uds/mod.rs index 3ec829f0c..8e28a9573 100644 --- a/src/sys/unix/uds/mod.rs +++ b/src/sys/unix/uds/mod.rs @@ -77,20 +77,20 @@ cfg_os_poll! { fn pair(flags: libc::c_int) -> io::Result<(T, T)> where T: FromRawFd, { - #[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "solaris")))] + #[cfg(not(any(target_os = "ios", target_os = "macos")))] let flags = flags | libc::SOCK_NONBLOCK | libc::SOCK_CLOEXEC; let mut fds = [-1; 2]; syscall!(socketpair(libc::AF_UNIX, flags, 0, fds.as_mut_ptr()))?; let pair = unsafe { (T::from_raw_fd(fds[0]), T::from_raw_fd(fds[1])) }; - // Darwin and Solaris do not have SOCK_NONBLOCK or SOCK_CLOEXEC. + // Darwin doesn't have SOCK_NONBLOCK or SOCK_CLOEXEC. // // In order to set those flags, additional `fcntl` sys calls must be // performed. If a `fnctl` fails after the sockets have been created, // the file descriptors will leak. Creating `pair` above ensures that if // there is an error, the file descriptors are closed. - #[cfg(any(target_os = "ios", target_os = "macos", target_os = "solaris"))] + #[cfg(any(target_os = "ios", target_os = "macos"))] { syscall!(fcntl(fds[0], libc::F_SETFL, libc::O_NONBLOCK))?; syscall!(fcntl(fds[0], libc::F_SETFD, libc::FD_CLOEXEC))?; diff --git a/src/sys/unix/uds/socketaddr.rs b/src/sys/unix/uds/socketaddr.rs index a9f9ea915..4c7c41161 100644 --- a/src/sys/unix/uds/socketaddr.rs +++ b/src/sys/unix/uds/socketaddr.rs @@ -78,14 +78,8 @@ cfg_os_poll! { /// Documentation reflected in [`SocketAddr`] /// /// [`SocketAddr`]: std::os::unix::net::SocketAddr - // FIXME: The matches macro requires rust 1.42.0 and we still support 1.39.0 - #[allow(clippy::match_like_matches_macro)] pub fn is_unnamed(&self) -> bool { - if let AddressKind::Unnamed = self.address() { - true - } else { - false - } + matches!(self.address(), AddressKind::Unnamed) } /// Returns the contents of this address if it is a `pathname` address. @@ -100,6 +94,18 @@ cfg_os_poll! { None } } + + /// Returns the contents of this address if it is an abstract namespace + /// without the leading null byte. + // Link to std::os::unix::net::SocketAddr pending + // https://github.com/rust-lang/rust/issues/85410. + pub fn as_abstract_namespace(&self) -> Option<&[u8]> { + if let AddressKind::Abstract(path) = self.address() { + Some(path) + } else { + None + } + } } } diff --git a/src/sys/unix/waker.rs b/src/sys/unix/waker.rs index a7cf484e5..684fee981 100644 --- a/src/sys/unix/waker.rs +++ b/src/sys/unix/waker.rs @@ -103,7 +103,6 @@ pub use self::kqueue::Waker; target_os = "illumos", target_os = "netbsd", target_os = "openbsd", - target_os = "solaris" ))] mod pipe { use crate::sys::unix::Selector; @@ -175,6 +174,5 @@ mod pipe { target_os = "illumos", target_os = "netbsd", target_os = "openbsd", - target_os = "solaris" ))] pub use self::pipe::Waker; diff --git a/src/sys/windows/afd.rs b/src/sys/windows/afd.rs new file mode 100644 index 000000000..6eae3bc03 --- /dev/null +++ b/src/sys/windows/afd.rs @@ -0,0 +1,237 @@ +use ntapi::ntioapi::{IO_STATUS_BLOCK_u, IO_STATUS_BLOCK}; +use ntapi::ntioapi::{NtCancelIoFileEx, NtDeviceIoControlFile}; +use ntapi::ntrtl::RtlNtStatusToDosError; +use std::fmt; +use std::fs::File; +use std::io; +use std::mem::size_of; +use std::os::windows::io::AsRawHandle; +use std::ptr::null_mut; +use winapi::shared::ntdef::{HANDLE, LARGE_INTEGER, NTSTATUS, PVOID, ULONG}; +use winapi::shared::ntstatus::{STATUS_NOT_FOUND, STATUS_PENDING, STATUS_SUCCESS}; + +const IOCTL_AFD_POLL: ULONG = 0x00012024; + +/// Winsock2 AFD driver instance. +/// +/// All operations are unsafe due to IO_STATUS_BLOCK parameter are being used by Afd driver during STATUS_PENDING before I/O Completion Port returns its result. +#[derive(Debug)] +pub struct Afd { + fd: File, +} + +#[repr(C)] +#[derive(Debug)] +pub struct AfdPollHandleInfo { + pub handle: HANDLE, + pub events: ULONG, + pub status: NTSTATUS, +} + +unsafe impl Send for AfdPollHandleInfo {} + +#[repr(C)] +pub struct AfdPollInfo { + pub timeout: LARGE_INTEGER, + // Can have only value 1. + pub number_of_handles: ULONG, + pub exclusive: ULONG, + pub handles: [AfdPollHandleInfo; 1], +} + +impl fmt::Debug for AfdPollInfo { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("AfdPollInfo").finish() + } +} + +impl Afd { + /// Poll `Afd` instance with `AfdPollInfo`. + /// + /// # Unsafety + /// + /// This function is unsafe due to memory of `IO_STATUS_BLOCK` still being used by `Afd` instance while `Ok(false)` (`STATUS_PENDING`). + /// `iosb` needs to be untouched after the call while operation is in effective at ALL TIME except for `cancel` method. + /// So be careful not to `poll` twice while polling. + /// User should deallocate there overlapped value when error to prevent memory leak. + pub unsafe fn poll( + &self, + info: &mut AfdPollInfo, + iosb: *mut IO_STATUS_BLOCK, + overlapped: PVOID, + ) -> io::Result { + let info_ptr: PVOID = info as *mut _ as PVOID; + (*iosb).u.Status = STATUS_PENDING; + let status = NtDeviceIoControlFile( + self.fd.as_raw_handle(), + null_mut(), + None, + overlapped, + iosb, + IOCTL_AFD_POLL, + info_ptr, + size_of::() as u32, + info_ptr, + size_of::() as u32, + ); + match status { + STATUS_SUCCESS => Ok(true), + STATUS_PENDING => Ok(false), + _ => Err(io::Error::from_raw_os_error( + RtlNtStatusToDosError(status) as i32 + )), + } + } + + /// Cancel previous polled request of `Afd`. + /// + /// iosb needs to be used by `poll` first for valid `cancel`. + /// + /// # Unsafety + /// + /// This function is unsafe due to memory of `IO_STATUS_BLOCK` still being used by `Afd` instance while `Ok(false)` (`STATUS_PENDING`). + /// Use it only with request is still being polled so that you have valid `IO_STATUS_BLOCK` to use. + /// User should NOT deallocate there overlapped value after the `cancel` to prevent double free. + pub unsafe fn cancel(&self, iosb: *mut IO_STATUS_BLOCK) -> io::Result<()> { + if (*iosb).u.Status != STATUS_PENDING { + return Ok(()); + } + + let mut cancel_iosb = IO_STATUS_BLOCK { + u: IO_STATUS_BLOCK_u { Status: 0 }, + Information: 0, + }; + let status = NtCancelIoFileEx(self.fd.as_raw_handle(), iosb, &mut cancel_iosb); + if status == STATUS_SUCCESS || status == STATUS_NOT_FOUND { + return Ok(()); + } + Err(io::Error::from_raw_os_error( + RtlNtStatusToDosError(status) as i32 + )) + } +} + +cfg_io_source! { + use std::mem::zeroed; + use std::os::windows::io::{FromRawHandle, RawHandle}; + use std::sync::atomic::{AtomicUsize, Ordering}; + + use miow::iocp::CompletionPort; + use ntapi::ntioapi::{NtCreateFile, FILE_OPEN}; + use winapi::shared::ntdef::{OBJECT_ATTRIBUTES, UNICODE_STRING, USHORT, WCHAR}; + use winapi::um::handleapi::INVALID_HANDLE_VALUE; + use winapi::um::winbase::{SetFileCompletionNotificationModes, FILE_SKIP_SET_EVENT_ON_HANDLE}; + use winapi::um::winnt::{SYNCHRONIZE, FILE_SHARE_READ, FILE_SHARE_WRITE}; + + const AFD_HELPER_ATTRIBUTES: OBJECT_ATTRIBUTES = OBJECT_ATTRIBUTES { + Length: size_of::() as ULONG, + RootDirectory: null_mut(), + ObjectName: &AFD_OBJ_NAME as *const _ as *mut _, + Attributes: 0, + SecurityDescriptor: null_mut(), + SecurityQualityOfService: null_mut(), + }; + + const AFD_OBJ_NAME: UNICODE_STRING = UNICODE_STRING { + Length: (AFD_HELPER_NAME.len() * size_of::()) as USHORT, + MaximumLength: (AFD_HELPER_NAME.len() * size_of::()) as USHORT, + Buffer: AFD_HELPER_NAME.as_ptr() as *mut _, + }; + + const AFD_HELPER_NAME: &[WCHAR] = &[ + '\\' as _, + 'D' as _, + 'e' as _, + 'v' as _, + 'i' as _, + 'c' as _, + 'e' as _, + '\\' as _, + 'A' as _, + 'f' as _, + 'd' as _, + '\\' as _, + 'M' as _, + 'i' as _, + 'o' as _ + ]; + + static NEXT_TOKEN: AtomicUsize = AtomicUsize::new(0); + + impl AfdPollInfo { + pub fn zeroed() -> AfdPollInfo { + unsafe { zeroed() } + } + } + + impl Afd { + /// Create new Afd instance. + pub fn new(cp: &CompletionPort) -> io::Result { + let mut afd_helper_handle: HANDLE = INVALID_HANDLE_VALUE; + let mut iosb = IO_STATUS_BLOCK { + u: IO_STATUS_BLOCK_u { Status: 0 }, + Information: 0, + }; + + unsafe { + let status = NtCreateFile( + &mut afd_helper_handle as *mut _, + SYNCHRONIZE, + &AFD_HELPER_ATTRIBUTES as *const _ as *mut _, + &mut iosb, + null_mut(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN, + 0, + null_mut(), + 0, + ); + if status != STATUS_SUCCESS { + let raw_err = io::Error::from_raw_os_error( + RtlNtStatusToDosError(status) as i32 + ); + let msg = format!("Failed to open \\Device\\Afd\\Mio: {}", raw_err); + return Err(io::Error::new(raw_err.kind(), msg)); + } + let fd = File::from_raw_handle(afd_helper_handle as RawHandle); + // Increment by 2 to reserve space for other types of handles. + // Non-AFD types (currently only NamedPipe), use odd numbered + // tokens. This allows the selector to differentate between them + // and dispatch events accordingly. + let token = NEXT_TOKEN.fetch_add(2, Ordering::Relaxed) + 2; + let afd = Afd { fd }; + cp.add_handle(token, &afd.fd)?; + match SetFileCompletionNotificationModes( + afd_helper_handle, + FILE_SKIP_SET_EVENT_ON_HANDLE, + ) { + 0 => Err(io::Error::last_os_error()), + _ => Ok(afd), + } + } + } + } +} + +pub const POLL_RECEIVE: u32 = 0b0_0000_0001; +pub const POLL_RECEIVE_EXPEDITED: u32 = 0b0_0000_0010; +pub const POLL_SEND: u32 = 0b0_0000_0100; +pub const POLL_DISCONNECT: u32 = 0b0_0000_1000; +pub const POLL_ABORT: u32 = 0b0_0001_0000; +pub const POLL_LOCAL_CLOSE: u32 = 0b0_0010_0000; +// Not used as it indicated in each event where a connection is connected, not +// just the first time a connection is established. +// Also see https://github.com/piscisaureus/wepoll/commit/8b7b340610f88af3d83f40fb728e7b850b090ece. +pub const POLL_CONNECT: u32 = 0b0_0100_0000; +pub const POLL_ACCEPT: u32 = 0b0_1000_0000; +pub const POLL_CONNECT_FAIL: u32 = 0b1_0000_0000; + +pub const KNOWN_EVENTS: u32 = POLL_RECEIVE + | POLL_RECEIVE_EXPEDITED + | POLL_SEND + | POLL_DISCONNECT + | POLL_ABORT + | POLL_LOCAL_CLOSE + | POLL_ACCEPT + | POLL_CONNECT_FAIL; diff --git a/src/sys/windows/io_status_block.rs b/src/sys/windows/io_status_block.rs new file mode 100644 index 000000000..3e6033496 --- /dev/null +++ b/src/sys/windows/io_status_block.rs @@ -0,0 +1,40 @@ +use std::fmt; +use std::ops::{Deref, DerefMut}; + +use ntapi::ntioapi::IO_STATUS_BLOCK; + +pub struct IoStatusBlock(IO_STATUS_BLOCK); + +cfg_io_source! { + use ntapi::ntioapi::IO_STATUS_BLOCK_u; + + impl IoStatusBlock { + pub fn zeroed() -> Self { + Self(IO_STATUS_BLOCK { + u: IO_STATUS_BLOCK_u { Status: 0 }, + Information: 0, + }) + } + } +} + +unsafe impl Send for IoStatusBlock {} + +impl Deref for IoStatusBlock { + type Target = IO_STATUS_BLOCK; + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for IoStatusBlock { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl fmt::Debug for IoStatusBlock { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("IoStatusBlock").finish() + } +} diff --git a/src/sys/windows/net.rs b/src/sys/windows/net.rs index 2de98fa70..db1896f19 100644 --- a/src/sys/windows/net.rs +++ b/src/sys/windows/net.rs @@ -4,10 +4,10 @@ use std::net::SocketAddr; use std::sync::Once; use winapi::ctypes::c_int; -use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR}; use winapi::shared::in6addr::{in6_addr_u, IN6_ADDR}; -use winapi::shared::ws2def::{AF_INET, AF_INET6, ADDRESS_FAMILY, SOCKADDR, SOCKADDR_IN}; -use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH, SOCKADDR_IN6_LH_u}; +use winapi::shared::inaddr::{in_addr_S_un, IN_ADDR}; +use winapi::shared::ws2def::{ADDRESS_FAMILY, AF_INET, AF_INET6, SOCKADDR, SOCKADDR_IN}; +use winapi::shared::ws2ipdef::{SOCKADDR_IN6_LH_u, SOCKADDR_IN6_LH}; use winapi::um::winsock2::{ioctlsocket, socket, FIONBIO, INVALID_SOCKET, SOCKET}; /// Initialise the network stack for Windows. @@ -80,7 +80,7 @@ pub(crate) fn socket_addr(addr: &SocketAddr) -> (SocketAddrCRepr, c_int) { let sockaddr = SocketAddrCRepr { v4: sockaddr_in }; (sockaddr, mem::size_of::() as c_int) - }, + } SocketAddr::V6(ref addr) => { let sin6_addr = unsafe { let mut u = mem::zeroed::(); diff --git a/src/sys/windows/overlapped.rs b/src/sys/windows/overlapped.rs new file mode 100644 index 000000000..837b78b60 --- /dev/null +++ b/src/sys/windows/overlapped.rs @@ -0,0 +1,37 @@ +use crate::sys::windows::Event; + +use std::cell::UnsafeCell; +use std::fmt; + +#[cfg(feature = "os-ext")] +use winapi::um::minwinbase::OVERLAPPED; +use winapi::um::minwinbase::OVERLAPPED_ENTRY; + +#[repr(C)] +pub(crate) struct Overlapped { + inner: UnsafeCell, + pub(crate) callback: fn(&OVERLAPPED_ENTRY, Option<&mut Vec>), +} + +#[cfg(feature = "os-ext")] +impl Overlapped { + pub(crate) fn new(cb: fn(&OVERLAPPED_ENTRY, Option<&mut Vec>)) -> Overlapped { + Overlapped { + inner: UnsafeCell::new(miow::Overlapped::zero()), + callback: cb, + } + } + + pub(crate) fn as_ptr(&self) -> *const OVERLAPPED { + unsafe { (*self.inner.get()).raw() } + } +} + +impl fmt::Debug for Overlapped { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("Overlapped").finish() + } +} + +unsafe impl Send for Overlapped {} +unsafe impl Sync for Overlapped {} diff --git a/src/sys/windows/queue.rs b/src/sys/windows/queue.rs index 1f8800af8..778b420cb 100644 --- a/src/sys/windows/queue.rs +++ b/src/sys/windows/queue.rs @@ -210,7 +210,7 @@ impl event::Source for Registration { interests: Interest, ) -> io::Result<()> { self.inner.update( - &poll::selector(registry).readiness_queue, + ®istry.selector().readiness_queue, token, Ready::from_interests(interests), PollOpt::edge(), @@ -224,7 +224,7 @@ impl event::Source for Registration { interests: Interest, ) -> io::Result<()> { self.inner.update( - &poll::selector(registry).readiness_queue, + ®istry.selector().readiness_queue, token, Ready::from_interests(interests), PollOpt::edge(), @@ -233,7 +233,7 @@ impl event::Source for Registration { fn deregister(&mut self, registry: &poll::Registry) -> io::Result<()> { self.inner.update( - &poll::selector(registry).readiness_queue, + ®istry.selector().readiness_queue, Token(0), Ready::EMPTY, PollOpt::empty(), diff --git a/src/sys/windows/selector.rs b/src/sys/windows/selector.rs index bc67e58f1..8601b5635 100644 --- a/src/sys/windows/selector.rs +++ b/src/sys/windows/selector.rs @@ -1,5 +1,5 @@ use crate::event::Source; -use crate::poll::{self, Registry}; +use crate::poll::Registry; use crate::sys::windows::buffer_pool::BufferPool; use crate::sys::windows::lazycell::AtomicLazyCell; use crate::sys::windows::{Event, PollOpt, ReadinessQueue, Ready, Registration, SetReadiness}; @@ -230,7 +230,7 @@ impl Binding { token: Token, registry: &Registry, ) -> io::Result<()> { - let selector = poll::selector(registry); + let selector = registry.selector(); drop(self.selector.fill(selector.inner.clone())); self.check_same_selector(registry)?; selector.inner.port.add_socket(usize::from(token), handle) @@ -256,7 +256,7 @@ impl Binding { } fn check_same_selector(&self, registry: &Registry) -> io::Result<()> { - let selector = poll::selector(registry); + let selector = registry.selector(); match self.selector.borrow() { Some(prev) if prev.identical(&selector.inner) => Ok(()), Some(_) | None => Err(other("socket already registered")), @@ -357,7 +357,7 @@ impl ReadyBinding { } let (r, s) = Registration::new( - &poll::selector(registry).readiness_queue, + ®istry.selector().readiness_queue, token, events, PollOpt::edge(), @@ -467,18 +467,15 @@ impl Events { } macro_rules! overlapped2arc { - ($e:expr, $t:ty, $($field:ident).+) => ({ - let offset = offset_of!($t, $($field).+); + ($e:expr, $t:ty, $field:ident) => ({ + let temp = ::std::mem::MaybeUninit::<$t>::uninit(); + let temp_ptr = temp.as_ptr(); + let offset = ::std::ptr::addr_of!((*temp_ptr).$field) as usize - temp_ptr as usize; debug_assert!(offset < mem::size_of::<$t>()); FromRawArc::from_raw(($e as usize - offset) as *mut $t) }) } -macro_rules! offset_of { - ($t:ty, $($field:ident).+) => ( - &(*(0 as *const $t)).$($field).+ as *const _ as usize - ) -} #[repr(C)] pub(crate) struct Overlapped { inner: UnsafeCell, diff --git a/src/sys/windows/tcp.rs b/src/sys/windows/tcp.rs index c35377017..b49412d6f 100644 --- a/src/sys/windows/tcp.rs +++ b/src/sys/windows/tcp.rs @@ -217,11 +217,11 @@ impl TcpStream { } pub fn set_linger(&self, dur: Option) -> io::Result<()> { - self.imp.inner.socket.set_linger(dur) + net2::TcpStreamExt::set_linger(&self.imp.inner.socket, dur) } pub fn linger(&self) -> io::Result> { - self.imp.inner.socket.linger() + net2::TcpStreamExt::linger(&self.imp.inner.socket) } pub fn take_error(&self) -> io::Result> { diff --git a/src/token.rs b/src/token.rs index d8a1fd16a..91601cde0 100644 --- a/src/token.rs +++ b/src/token.rs @@ -17,8 +17,8 @@ /// /// [`slab`]: https://crates.io/crates/slab /// -#[cfg_attr(all(feature = "os-poll", features = "net"), doc = "```")] -#[cfg_attr(not(all(feature = "os-poll", features = "net")), doc = "```ignore")] +#[cfg_attr(all(feature = "os-poll", feature = "net"), doc = "```")] +#[cfg_attr(not(all(feature = "os-poll", feature = "net")), doc = "```ignore")] /// # use std::error::Error; /// # fn main() -> Result<(), Box> { /// use mio::{Events, Interest, Poll, Token}; diff --git a/src/waker.rs b/src/waker.rs index bc73029d3..b0cfe36b6 100644 --- a/src/waker.rs +++ b/src/waker.rs @@ -1,4 +1,4 @@ -use crate::{poll, sys, Registry, Token}; +use crate::{sys, Registry, Token}; use std::io; @@ -19,7 +19,7 @@ use std::io; /// Only a single `Waker` can be active per [`Poll`], if multiple threads need /// access to the `Waker` it can be shared via for example an `Arc`. What /// happens if multiple `Waker`s are registered with the same `Poll` is -/// undefined. +/// unspecified. /// /// # Implementation notes /// @@ -84,7 +84,7 @@ impl Waker { pub fn new(registry: &Registry, token: Token) -> io::Result { #[cfg(debug_assertions)] registry.register_waker(); - sys::Waker::new(poll::selector(®istry), token).map(|inner| Waker { inner }) + sys::Waker::new(registry.selector(), token).map(|inner| Waker { inner }) } /// Wake up the [`Poll`] associated with this `Waker`. diff --git a/tests/aio.rs b/tests/aio.rs index e7b56e71f..b8c1b47b0 100644 --- a/tests/aio.rs +++ b/tests/aio.rs @@ -1,5 +1,5 @@ -#![cfg(feature = "os-poll")] #![cfg(any(target_os = "freebsd", target_os = "dragonfly"))] +#![cfg(all(feature = "os-poll", feature = "net"))] use mio::{event::Source, Events, Interest, Poll, Registry, Token}; use std::{ diff --git a/tests/poll.rs b/tests/poll.rs index 2cda5912e..86fdc21cf 100644 --- a/tests/poll.rs +++ b/tests/poll.rs @@ -132,7 +132,7 @@ fn drop_cancels_interest_and_shuts_down() { Ok(_) => (), Err(err) => { if err.kind() != io::ErrorKind::UnexpectedEof { - panic!(err); + panic!("{}", err); } } } @@ -393,7 +393,7 @@ fn reregister_interest_token_usage() { // This test checks the following register constraint: // The event source must **not** have been previously registered with this -// instance of `Poll`, otherwise the behavior is undefined. +// instance of `Poll`, otherwise the behavior is unspecified. // // This test is done on Windows and epoll platforms where registering a // source twice is defined behavior that fail with an error code. @@ -484,7 +484,7 @@ fn poll_ok_after_cancelling_pending_ops() { // This test checks the following reregister constraint: // The event source must have previously been registered with this instance -// of `Poll`, otherwise the behavior is undefined. +// of `Poll`, otherwise the behavior is unspecified. // // This test is done on Windows and epoll platforms where reregistering a // source without a previous register is defined behavior that fail with an @@ -508,7 +508,7 @@ fn reregister_without_register() { // This test checks the following register/deregister constraint: // The event source must have previously been registered with this instance -// of `Poll`, otherwise the behavior is undefined. +// of `Poll`, otherwise the behavior is unspecified. // // This test is done on Windows and epoll platforms where deregistering a // source without a previous register is defined behavior that fail with an diff --git a/tests/regressions.rs b/tests/regressions.rs index 2e2bacd00..f41c6cae5 100644 --- a/tests/regressions.rs +++ b/tests/regressions.rs @@ -9,7 +9,7 @@ use mio::net::{TcpListener, TcpStream}; use mio::{Events, Interest, Poll, Token, Waker}; mod util; -use util::{any_local_address, init, init_with_poll, temp_file}; +use util::{any_local_address, init, init_with_poll}; const ID1: Token = Token(1); const WAKE_TOKEN: Token = Token(10); @@ -109,6 +109,7 @@ fn issue_1205() { #[cfg(unix)] fn issue_1403() { use mio::net::UnixDatagram; + use util::temp_file; init(); diff --git a/tests/tcp.rs b/tests/tcp.rs index a2f1739ce..6ff38d2ca 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -1,6 +1,6 @@ #![cfg(all(feature = "os-poll", feature = "net"))] -use mio::net::{TcpListener, TcpSocket, TcpStream}; +use mio::net::{TcpListener, TcpStream}; use mio::{Events, Interest, Poll, Token}; use std::io::{self, Read, Write}; use std::net::{self, Shutdown}; @@ -12,7 +12,7 @@ use std::time::Duration; mod util; use util::{ any_local_address, assert_send, assert_sync, expect_events, expect_no_events, init, - init_with_poll, ExpectEvent, + init_with_poll, set_linger_zero, ExpectEvent, }; const LISTEN: Token = Token(0); @@ -481,9 +481,8 @@ fn connection_reset_by_peer() { let addr = listener.local_addr().unwrap(); // Connect client - let client = TcpSocket::new_v4().unwrap(); - client.set_linger(Some(Duration::from_millis(0))).unwrap(); - let mut client = client.connect(addr).unwrap(); + let mut client = TcpStream::connect(addr).unwrap(); + set_linger_zero(&client); // Register server poll.registry() @@ -549,13 +548,10 @@ fn connection_reset_by_peer() { #[test] fn connect_error() { - init(); - - let mut poll = Poll::new().unwrap(); - let mut events = Events::with_capacity(16); + let (mut poll, mut events) = init_with_poll(); // Pick a "random" port that shouldn't be in use. - let mut stream = match TcpStream::connect("127.0.0.1:38381".parse().unwrap()) { + let mut stream = match TcpStream::connect("127.0.0.1:58381".parse().unwrap()) { Ok(l) => l, Err(ref e) if e.kind() == io::ErrorKind::ConnectionRefused => { // Connection failed synchronously. This is not a bug, but it @@ -575,6 +571,7 @@ fn connect_error() { for event in &events { if event.token() == Token(0) { assert!(event.is_writable()); + assert!(event.is_write_closed()); break 'outer; } } diff --git a/tests/tcp_stream.rs b/tests/tcp_stream.rs index 81f991344..a0c7d3b79 100644 --- a/tests/tcp_stream.rs +++ b/tests/tcp_stream.rs @@ -1,17 +1,14 @@ #![cfg(all(feature = "os-poll", feature = "net"))] use std::io::{self, IoSlice, IoSliceMut, Read, Write}; -use std::mem::forget; use std::net::{self, Shutdown, SocketAddr}; #[cfg(unix)] use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd}; -#[cfg(windows)] -use std::os::windows::io::{AsRawSocket, FromRawSocket}; use std::sync::{mpsc::channel, Arc, Barrier}; use std::thread; use std::time::Duration; -use mio::net::{TcpSocket, TcpStream}; +use mio::net::TcpStream; use mio::{Interest, Token}; #[macro_use] @@ -21,7 +18,7 @@ use util::init; use util::{ any_local_address, any_local_ipv6_address, assert_send, assert_socket_close_on_exec, assert_socket_non_blocking, assert_sync, assert_would_block, expect_events, expect_no_events, - init_with_poll, ExpectEvent, Readiness, + init_with_poll, set_linger_zero, ExpectEvent, Readiness, }; const DATA1: &[u8] = b"Hello world!"; @@ -91,7 +88,7 @@ where assert_eq!(stream.peer_addr().unwrap(), addr); assert!(stream.local_addr().unwrap().ip().is_loopback()); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); stream.flush().unwrap(); @@ -108,7 +105,7 @@ where assert_would_block(stream.read(&mut buf)); - let bufs = [IoSlice::new(&DATA1), IoSlice::new(&DATA2)]; + let bufs = [IoSlice::new(DATA1), IoSlice::new(DATA2)]; let n = stream .write_vectored(&bufs) .expect("unable to write vectored to stream"); @@ -146,7 +143,7 @@ fn set_get_ttl() { let mut stream = TcpStream::connect(address).unwrap(); // on Windows: the stream must be connected before setting the ttl, otherwise - // it is undefined behavior, register and expect a WRITABLE here to make sure + // it is unspecified behavior, register and expect a WRITABLE here to make sure // the stream is connected poll.registry() .register(&mut stream, ID1, Interest::WRITABLE) @@ -178,7 +175,7 @@ fn get_ttl_without_previous_set() { let mut stream = TcpStream::connect(address).unwrap(); // on Windows: the stream must be connected before getting the ttl, otherwise - // it is undefined behavior, register and expect a WRITABLE here to make sure + // it is unspecified behavior, register and expect a WRITABLE here to make sure // the stream is connected poll.registry() .register(&mut stream, ID1, Interest::WRITABLE) @@ -208,7 +205,7 @@ fn set_get_nodelay() { let mut stream = TcpStream::connect(address).unwrap(); // on Windows: the stream must be connected before setting the nodelay, otherwise - // it is undefined behavior, register and expect a WRITABLE here to make sure + // it is unspecified behavior, register and expect a WRITABLE here to make sure // the stream is connected poll.registry() .register(&mut stream, ID1, Interest::WRITABLE) @@ -240,7 +237,7 @@ fn get_nodelay_without_previous_set() { let mut stream = TcpStream::connect(address).unwrap(); // on Windows: the stream must be connected before setting the nodelay, otherwise - // it is undefined behavior, register and expect a WRITABLE here to make sure + // it is unspecified behavior, register and expect a WRITABLE here to make sure // the stream is connected poll.registry() .register(&mut stream, ID1, Interest::WRITABLE) @@ -280,7 +277,7 @@ fn shutdown_read() { vec![ExpectEvent::new(ID1, Interest::WRITABLE)], ); - checked_write!(stream.write(&DATA2)); + checked_write!(stream.write(DATA2)); expect_events( &mut poll, @@ -328,7 +325,7 @@ fn shutdown_write() { vec![ExpectEvent::new(ID1, Interest::WRITABLE)], ); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); stream.shutdown(Shutdown::Write).unwrap(); @@ -368,7 +365,7 @@ fn shutdown_both() { vec![ExpectEvent::new(ID1, Interest::WRITABLE)], ); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); expect_events( &mut poll, @@ -498,7 +495,7 @@ fn no_events_after_deregister() { assert_would_block(stream.peek(&mut buf)); assert_would_block(stream.read(&mut buf)); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); stream.flush().unwrap(); expect_no_events(&mut poll, &mut events); @@ -545,12 +542,7 @@ fn tcp_shutdown_client_read_close_event() { #[test] #[cfg_attr(windows, ignore = "fails; client write_closed events are not found")] #[cfg_attr( - any( - target_os = "android", - target_os = "illumos", - target_os = "linux", - target_os = "solaris" - ), + any(target_os = "android", target_os = "illumos", target_os = "linux"), ignore = "fails; client write_closed events are not found" )] fn tcp_shutdown_client_write_close_event() { @@ -800,13 +792,3 @@ fn hup_event_on_disconnect() { vec![ExpectEvent::new(Token(1), Interest::READABLE)], ); } - -fn set_linger_zero(socket: &TcpStream) { - #[cfg(windows)] - let s = unsafe { TcpSocket::from_raw_socket(socket.as_raw_socket()) }; - #[cfg(unix)] - let s = unsafe { TcpSocket::from_raw_fd(socket.as_raw_fd()) }; - - s.set_linger(Some(Duration::from_millis(0))).unwrap(); - forget(s); -} diff --git a/tests/udp_socket.rs b/tests/udp_socket.rs index e5e5fa4d5..05caf9d21 100644 --- a/tests/udp_socket.rs +++ b/tests/udp_socket.rs @@ -193,10 +193,10 @@ fn set_get_broadcast() { let socket1 = UdpSocket::bind(any_local_address()).unwrap(); socket1.set_broadcast(true).unwrap(); - assert_eq!(socket1.broadcast().unwrap(), true); + assert!(socket1.broadcast().unwrap()); socket1.set_broadcast(false).unwrap(); - assert_eq!(socket1.broadcast().unwrap(), false); + assert!(!socket1.broadcast().unwrap()); assert!(socket1.take_error().unwrap().is_none()); } @@ -215,10 +215,10 @@ fn set_get_multicast_loop_v4() { let socket1 = UdpSocket::bind(any_local_address()).unwrap(); socket1.set_multicast_loop_v4(true).unwrap(); - assert_eq!(socket1.multicast_loop_v4().unwrap(), true); + assert!(socket1.multicast_loop_v4().unwrap()); socket1.set_multicast_loop_v4(false).unwrap(); - assert_eq!(socket1.multicast_loop_v4().unwrap(), false); + assert!(!socket1.multicast_loop_v4().unwrap()); assert!(socket1.take_error().unwrap().is_none()); } @@ -257,10 +257,10 @@ fn set_get_multicast_loop_v6() { let socket1 = UdpSocket::bind(any_local_ipv6_address()).unwrap(); socket1.set_multicast_loop_v6(true).unwrap(); - assert_eq!(socket1.multicast_loop_v6().unwrap(), true); + assert!(socket1.multicast_loop_v6().unwrap()); socket1.set_multicast_loop_v6(false).unwrap(); - assert_eq!(socket1.multicast_loop_v6().unwrap(), false); + assert!(!socket1.multicast_loop_v6().unwrap()); assert!(socket1.take_error().unwrap().is_none()); } diff --git a/tests/unix_listener.rs b/tests/unix_listener.rs index d89ef5bc6..0aeda8153 100644 --- a/tests/unix_listener.rs +++ b/tests/unix_listener.rs @@ -133,6 +133,19 @@ fn unix_listener_deregister() { handle.join().unwrap(); } +#[cfg(target_os = "linux")] +#[test] +fn unix_listener_abstract_namesapce() { + use rand::Rng; + let num: u64 = rand::thread_rng().gen(); + let name = format!("\u{0000}-mio-abstract-uds-{}", num); + let listener = UnixListener::bind(&name).unwrap(); + assert_eq!( + listener.local_addr().unwrap().as_abstract_namespace(), + Some(&name.as_bytes()[1..]), + ); +} + fn smoke_test(new_listener: F, test_name: &'static str) where F: FnOnce(&Path) -> io::Result, diff --git a/tests/unix_pipe.rs b/tests/unix_pipe.rs index 645f8b7b2..a83e3833b 100644 --- a/tests/unix_pipe.rs +++ b/tests/unix_pipe.rs @@ -1,4 +1,4 @@ -#![cfg(all(unix, feature = "os-poll", feature = "os-ext"))] +#![cfg(all(unix, feature = "os-poll", feature = "os-ext", feature = "net"))] use std::io::{Read, Write}; use std::process::{Command, Stdio}; diff --git a/tests/unix_stream.rs b/tests/unix_stream.rs index 647efbf10..79b7c3d4b 100644 --- a/tests/unix_stream.rs +++ b/tests/unix_stream.rs @@ -111,13 +111,13 @@ fn unix_stream_pair() { let mut buf = [0; DEFAULT_BUF_SIZE]; assert_would_block(s1.read(&mut buf)); - checked_write!(s1.write(&DATA1)); + checked_write!(s1.write(DATA1)); s1.flush().unwrap(); expect_read!(s2.read(&mut buf), DATA1); assert_would_block(s2.read(&mut buf)); - checked_write!(s2.write(&DATA2)); + checked_write!(s2.write(DATA2)); s2.flush().unwrap(); expect_read!(s1.read(&mut buf), DATA2); @@ -163,7 +163,7 @@ fn unix_stream_shutdown_read() { vec![ExpectEvent::new(TOKEN_1, Interest::WRITABLE)], ); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); expect_events( &mut poll, &mut events, @@ -217,7 +217,7 @@ fn unix_stream_shutdown_write() { vec![ExpectEvent::new(TOKEN_1, Interest::WRITABLE)], ); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); expect_events( &mut poll, &mut events, @@ -272,7 +272,7 @@ fn unix_stream_shutdown_both() { vec![ExpectEvent::new(TOKEN_1, Interest::WRITABLE)], ); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); expect_events( &mut poll, &mut events, @@ -433,7 +433,7 @@ where let mut buf = [0; DEFAULT_BUF_SIZE]; assert_would_block(stream.read(&mut buf)); - checked_write!(stream.write(&DATA1)); + checked_write!(stream.write(DATA1)); stream.flush().unwrap(); expect_events( &mut poll, @@ -445,7 +445,7 @@ where assert!(stream.take_error().unwrap().is_none()); - let bufs = [IoSlice::new(&DATA1), IoSlice::new(&DATA2)]; + let bufs = [IoSlice::new(DATA1), IoSlice::new(DATA2)]; let wrote = stream.write_vectored(&bufs).unwrap(); assert_eq!(wrote, DATA1_LEN + DATA2_LEN); expect_events( diff --git a/tests/util/mod.rs b/tests/util/mod.rs index c1198dd76..3e4bb5f11 100644 --- a/tests/util/mod.rs +++ b/tests/util/mod.rs @@ -1,7 +1,8 @@ // Not all functions are used by all tests. #![allow(dead_code, unused_macros)] -#![cfg(any(feature = "os-poll", feature = "net"))] +#![cfg(all(feature = "os-poll", feature = "net"))] +use std::mem::size_of; use std::net::SocketAddr; use std::ops::BitOr; #[cfg(unix)] @@ -13,6 +14,7 @@ use std::{env, fmt, fs, io}; use log::{error, warn}; use mio::event::Event; +use mio::net::TcpStream; use mio::{Events, Interest, Poll, Token}; pub fn init() { @@ -66,14 +68,14 @@ impl ExpectEvent { #[derive(Debug)] pub struct Readiness(usize); -const READABLE: usize = 0b00_000_001; -const WRITABLE: usize = 0b00_000_010; -const AIO: usize = 0b00_000_100; -const LIO: usize = 0b00_001_000; -const ERROR: usize = 0b00_010_000; -const READ_CLOSED: usize = 0b00_100_000; -const WRITE_CLOSED: usize = 0b01_000_000; -const PRIORITY: usize = 0b10_000_000; +const READABLE: usize = 0b0000_0001; +const WRITABLE: usize = 0b0000_0010; +const AIO: usize = 0b0000_0100; +const LIO: usize = 0b0000_1000; +const ERROR: usize = 0b00010000; +const READ_CLOSED: usize = 0b0010_0000; +const WRITE_CLOSED: usize = 0b0100_0000; +const PRIORITY: usize = 0b1000_0000; impl Readiness { pub const READABLE: Readiness = Readiness(READABLE); @@ -236,6 +238,53 @@ pub fn any_local_ipv6_address() -> SocketAddr { "[::1]:0".parse().unwrap() } +#[cfg(unix)] +pub fn set_linger_zero(socket: &TcpStream) { + let val = libc::linger { + l_onoff: 1, + l_linger: 0, + }; + let res = unsafe { + libc::setsockopt( + socket.as_raw_fd(), + libc::SOL_SOCKET, + #[cfg(target_vendor = "apple")] + libc::SO_LINGER_SEC, + #[cfg(not(target_vendor = "apple"))] + libc::SO_LINGER, + &val as *const libc::linger as *const libc::c_void, + size_of::() as libc::socklen_t, + ) + }; + assert_eq!(res, 0); +} + +#[cfg(windows)] +pub fn set_linger_zero(socket: &TcpStream) { + use std::os::windows::io::AsRawSocket; + use winapi::um::winsock2::{linger, setsockopt, SOCKET_ERROR, SOL_SOCKET, SO_LINGER}; + + let val = linger { + l_onoff: 1, + l_linger: 0, + }; + + let res = unsafe { + setsockopt( + socket.as_raw_socket() as _, + SOL_SOCKET, + SO_LINGER, + &val as *const _ as *const _, + size_of::() as _, + ) + }; + assert!( + res != SOCKET_ERROR, + "error setting linger: {}", + io::Error::last_os_error() + ); +} + /// Returns a path to a temporary file using `name` as filename. pub fn temp_file(name: &'static str) -> PathBuf { let mut path = temp_dir(); diff --git a/tests/waker.rs b/tests/waker.rs index 02678ffea..33998a187 100644 --- a/tests/waker.rs +++ b/tests/waker.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "os-poll")] +#![cfg(all(feature = "os-poll", feature = "net"))] use mio::{Events, Poll, Token, Waker}; use std::sync::{Arc, Barrier}; diff --git a/tests/win_named_pipe.rs b/tests/win_named_pipe.rs index 5d4d3022d..e1451f0df 100644 --- a/tests/win_named_pipe.rs +++ b/tests/win_named_pipe.rs @@ -121,8 +121,7 @@ fn connect_before_client() { let mut events = Events::with_capacity(128); t!(poll.poll(&mut events, Some(Duration::new(0, 0)))); - let e = events.iter().collect::>(); - assert_eq!(e.len(), 0); + assert_eq!(events.iter().count(), 0); assert_eq!( server.connect().err().unwrap().kind(), io::ErrorKind::WouldBlock @@ -157,8 +156,7 @@ fn connect_after_client() { let mut events = Events::with_capacity(128); t!(poll.poll(&mut events, Some(Duration::new(0, 0)))); - let e = events.iter().collect::>(); - assert_eq!(e.len(), 0); + assert_eq!(events.iter().count(), 0); let mut client = client(&name); t!(poll.registry().register(