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(