cargo update tokio >= 1.24 (#1842)

This commit is contained in:
Sean OMeara 2023-01-12 21:41:29 +01:00 committed by GitHub
parent 39f3f5b2d9
commit 67a7534c21
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
1337 changed files with 813685 additions and 228172 deletions

84
zeroidc/Cargo.lock generated
View file

@ -530,14 +530,14 @@ checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d"
[[package]]
name = "mio"
version = "0.8.3"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "713d550d9b44d89174e066b7a6217ae06234c10cb47819a88290d2b353c31799"
checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys",
"windows-sys 0.42.0",
]
[[package]]
@ -871,7 +871,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2"
dependencies = [
"lazy_static",
"windows-sys",
"windows-sys 0.36.1",
]
[[package]]
@ -1086,19 +1086,19 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "1.19.2"
version = "1.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439"
checksum = "1d9f76183f91ecfb55e1d7d5602bd1d979e38a3a522fe900241cf195624d67ae"
dependencies = [
"autocfg",
"bytes",
"libc",
"memchr",
"mio",
"num_cpus",
"once_cell",
"pin-project-lite",
"socket2",
"winapi",
"windows-sys 0.42.0",
]
[[package]]
@ -1368,43 +1368,100 @@ version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
"windows_aarch64_msvc 0.36.1",
"windows_i686_gnu 0.36.1",
"windows_i686_msvc 0.36.1",
"windows_x86_64_gnu 0.36.1",
"windows_x86_64_msvc 0.36.1",
]
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc 0.42.0",
"windows_i686_gnu 0.42.0",
"windows_i686_msvc 0.42.0",
"windows_x86_64_gnu 0.42.0",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc 0.42.0",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6"
[[package]]
name = "windows_i686_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024"
[[package]]
name = "windows_i686_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
[[package]]
name = "winreg"
version = "0.10.1"
@ -1427,5 +1484,6 @@ dependencies = [
"serde",
"thiserror",
"time",
"tokio",
"url",
]

View file

@ -18,6 +18,7 @@ serde = "1.0"
time = { version = "0.3", features = ["formatting"] }
bytes = "1.1"
thiserror = "1"
tokio = { version = ">=1.24" }
[build-dependencies]
cbindgen = "0.20"

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,33 @@
# 0.8.5
## Changed
* Updated `windows-sys` to 0.42.0
(https://github.com/tokio-rs/mio/pull/1624).
* Officially document Wine as not supported, some people claimed it worked,
other claims it doesn't, but nobody stepped up to fix the problem
(https://github.com/tokio-rs/mio/pull/1596).
* Switch to GitHub Actions
(https://github.com/tokio-rs/mio/pull/1598, https://github.com/tokio-rs/mio/pull/1601).
* Documented the current Poll::poll time behaviour
(https://github.com/tokio-rs/mio/pull/1603).
## Fixed
* Timeout less than one millisecond becoming zero millsiconds
(https://github.com/tokio-rs/mio/pull/1615, https://github.com/tokio-rs/mio/pull/1616)
* Undefined reference to `epoll\_create1` on Android API level < 21.
(https://github.com/tokio-rs/mio/pull/1590).
# 0.8.4
## Added
* Support `Registery::try_clone` on `wasm32-wasi`
(https://github.com/tokio-rs/mio/pull/1576).
* Add docs about polling without registering event sources
(https://github.com/tokio-rs/mio/pull/1585).
# 0.8.3
## Changed
@ -12,7 +42,7 @@
* Improved support for Redox, making it possible to run on stable Rust
(https://github.com/tokio-rs/mio/pull/1555).
* Don't ignore EAGAIN in UDS connect call
(https://github.com/tokio-rs/mio/pull/)1564.
(https://github.com/tokio-rs/mio/pull/1564).
* Documentation of `TcpStream::connect`
(https://github.com/tokio-rs/mio/pull/1565).

84
zeroidc/vendor/mio/Cargo.lock generated vendored
View file

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
@ -17,58 +19,57 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.2.3"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [
"cfg-if",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasi",
]
[[package]]
name = "libc"
version = "0.2.122"
version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]]
name = "log"
version = "0.4.16"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "mio"
version = "0.8.3"
version = "0.8.5"
dependencies = [
"env_logger",
"libc",
"log",
"rand",
"wasi 0.11.0+wasi-snapshot-preview1",
"wasi",
"windows-sys",
]
[[package]]
name = "ppv-lite86"
version = "0.2.15"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "rand"
version = "0.8.4"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_hc",
]
[[package]]
@ -90,21 +91,6 @@ dependencies = [
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
dependencies = [
"rand_core",
]
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@ -113,43 +99,57 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.36.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f375ae76a43fd649c5a3482a4a3e28eced2267adaefa55422bf7e92696a7dac5"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.36.0"
name = "windows_aarch64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bee8cd327bbef19bf86d30bd66379f57905166d3103b0e2eff4a491b85e421d"
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
[[package]]
name = "windows_i686_gnu"
version = "0.36.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b759cc6e3d97970c98cffe461739e89ab6d424ba5e2e7d3b9b05a2d56116057"
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]]
name = "windows_i686_msvc"
version = "0.36.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0cee91bff283876711f91e7db0aa234438bc663a9d8304596df00b0a6fd6ef"
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]]
name = "windows_x86_64_gnu"
version = "0.36.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51e8c6f778aa4383b033ff785191aea0f1ebeceedc160c2c92f944ef7e191476"
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
[[package]]
name = "windows_x86_64_msvc"
version = "0.36.0"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd6a8b0b1ea4331e4db47192729fce42ac8a110fd22bb3abac555d8d7700f29"
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"

View file

@ -12,7 +12,7 @@
[package]
edition = "2018"
name = "mio"
version = "0.8.3"
version = "0.8.5"
authors = [
"Carl Lerche <me@carllerche.com>",
"Thomas de Zeeuw <thomasdezeeuw@gmail.com>",
@ -47,6 +47,7 @@ rustdoc-args = [
targets = [
"aarch64-apple-ios",
"aarch64-linux-android",
"wasm32-wasi",
"x86_64-apple-darwin",
"x86_64-pc-windows-msvc",
"x86_64-unknown-dragonfly",
@ -115,11 +116,11 @@ version = "0.11.0"
version = "0.2.121"
[target."cfg(windows)".dependencies.windows-sys]
version = "0.36"
version = "0.42"
features = [
"Win32_Storage_FileSystem",
"Win32_Foundation",
"Win32_Networking_WinSock",
"Win32_Storage_FileSystem",
"Win32_System_IO",
"Win32_System_WindowsProgramming",
]

View file

@ -6,15 +6,15 @@ overhead as possible over the OS abstractions.
[![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-url]
[![Build Status][azure-badge]][azure-url]
[![Build Status][actions-badge]][actions-url]
[![Build Status][cirrus-badge]][cirrus-url]
[crates-badge]: https://img.shields.io/crates/v/mio.svg
[crates-url]: https://crates.io/crates/mio
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.mio?branchName=master
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=2&branchName=master
[actions-badge]: https://github.com/tokio-rs/mio/workflows/CI/badge.svg
[actions-url]: https://github.com/tokio-rs/mio/actions?query=workflow%3ACI+branch%3Amaster
[cirrus-badge]: https://api.cirrus-ci.com/github/tokio-rs/mio.svg
[cirrus-url]: https://cirrus-ci.com/github/tokio-rs/mio
@ -139,7 +139,6 @@ 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!
@ -152,15 +151,16 @@ 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]
* Solaris, see [issue #1152]
* Wine, see [issue #1444]
[issue #1472]: https://github.com/tokio-rs/mio/issues/1472
[issue #1152]: https://github.com/tokio-rs/mio/issues/1152
[issue #1444]: https://github.com/tokio-rs/mio/issues/1444
## Community

View file

@ -2,7 +2,7 @@
// cargo run --example tcp_listenfd_server --features="os-poll net"
// or with wasi:
// cargo +nightly build --target wasm32-wasi --example tcp_listenfd_server --features="os-poll net"
// wasmtime run --tcplisten 127.0.0.1:9000 --env 'LISTEN_FDS=1' target/wasm32-wasi/debug/examples/tcp_server.wasm
// wasmtime run --tcplisten 127.0.0.1:9000 --env 'LISTEN_FDS=1' target/wasm32-wasi/debug/examples/tcp_listenfd_server.wasm
use mio::event::Event;
use mio::net::{TcpListener, TcpStream};
@ -48,7 +48,6 @@ fn main() -> io::Result<()> {
// Setup the TCP server socket.
let mut server = {
let stdlistener = get_first_listen_fd_listener().unwrap();
stdlistener.set_nonblocking(true)?;
println!("Using preopened socket FD 3");
println!("You can connect to the server using `nc`:");
match stdlistener.local_addr() {
@ -78,7 +77,7 @@ fn main() -> io::Result<()> {
// indicates we can accept an connection.
let (mut connection, address) = match server.accept() {
Ok((connection, address)) => (connection, address),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
Err(ref e) if would_block(e) => {
// If we get a `WouldBlock` error we know our
// listener has no more incoming connections queued,
// so we can return to polling and wait for some

View file

@ -269,49 +269,49 @@ impl TcpStream {
impl Read for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read(buf))
self.inner.do_io(|mut inner| inner.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read_vectored(bufs))
self.inner.do_io(|mut inner| inner.read_vectored(bufs))
}
}
impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read(buf))
self.inner.do_io(|mut inner| inner.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read_vectored(bufs))
self.inner.do_io(|mut inner| inner.read_vectored(bufs))
}
}
impl Write for TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write(buf))
self.inner.do_io(|mut inner| inner.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write_vectored(bufs))
self.inner.do_io(|mut inner| inner.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|inner| (&*inner).flush())
self.inner.do_io(|mut inner| inner.flush())
}
}
impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write(buf))
self.inner.do_io(|mut inner| inner.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write_vectored(bufs))
self.inner.do_io(|mut inner| inner.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|inner| (&*inner).flush())
self.inner.do_io(|mut inner| inner.flush())
}
}

View file

@ -144,49 +144,49 @@ impl UnixStream {
impl Read for UnixStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read(buf))
self.inner.do_io(|mut inner| inner.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read_vectored(bufs))
self.inner.do_io(|mut inner| inner.read_vectored(bufs))
}
}
impl<'a> Read for &'a UnixStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read(buf))
self.inner.do_io(|mut inner| inner.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).read_vectored(bufs))
self.inner.do_io(|mut inner| inner.read_vectored(bufs))
}
}
impl Write for UnixStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write(buf))
self.inner.do_io(|mut inner| inner.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write_vectored(bufs))
self.inner.do_io(|mut inner| inner.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|inner| (&*inner).flush())
self.inner.do_io(|mut inner| inner.flush())
}
}
impl<'a> Write for &'a UnixStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write(buf))
self.inner.do_io(|mut inner| inner.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|inner| (&*inner).write_vectored(bufs))
self.inner.do_io(|mut inner| inner.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|inner| (&*inner).flush())
self.inner.do_io(|mut inner| inner.flush())
}
}

View file

@ -187,6 +187,28 @@ use std::{fmt, io};
/// operations to go through Mio otherwise it is not able to update it's
/// internal state properly and won't generate events.
///
/// ### Polling without registering event sources
///
///
/// *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. On platforms that use epoll, kqueue or IOCP (see implementation
/// notes below) polling without previously registering [event sources] will
/// result in sleeping forever, only a process signal will be able to wake up
/// the thread.
///
/// On WASM/WASI this is different as it doesn't support process signals,
/// furthermore the WASI specification doesn't specify a behaviour in this
/// situation, thus it's up to the implementation what to do here. As an
/// example, the wasmtime runtime will return `EINVAL` in this situation, but
/// different runtimes may return different results. If you have further
/// insights or thoughts about this situation (and/or how Mio should handle it)
/// please add you comment to [pull request#1580].
///
/// [event sources]: crate::event::Source
/// [pull request#1580]: https://github.com/tokio-rs/mio/pull/1580
///
/// # Implementation notes
///
/// `Poll` is backed by the selector provided by the operating system.
@ -241,6 +263,13 @@ impl Poll {
/// the system selector. If this syscall fails, `Poll::new` will return
/// with the error.
///
/// close-on-exec flag is set on the file descriptors used by the selector to prevent
/// leaking it to executed processes. However, on some systems such as
/// old Linux systems that don't support `epoll_create1` syscall it is done
/// non-atomically, so a separate thread executing in parallel to this
/// function may accidentally leak the file descriptor if it executes a
/// new process before this function returns.
///
/// See [struct] level docs for more details.
///
/// [struct]: struct.Poll.html
@ -320,6 +349,10 @@ impl Poll {
/// of Mio would automatically retry the poll call if it was interrupted
/// (if `EINTR` was returned).
///
/// Currently if the `timeout` elapses without any readiness events
/// triggering this will return `Ok(())`. However we're not guaranteeing
/// this behaviour as this depends on the OS.
///
/// # Examples
///
/// A basic example -- establishing a `TcpStream` connection.
@ -635,7 +668,6 @@ impl Registry {
///
/// Event sources registered with this `Registry` will be registered with
/// the original `Registry` and `Poll` instance.
#[cfg(not(target_os = "wasi"))]
pub fn try_clone(&self) -> io::Result<Registry> {
self.selector
.try_clone()

View file

@ -11,7 +11,6 @@ pub type Events = Vec<Event>;
pub struct Selector {}
impl Selector {
#[cfg(not(target_os = "wasi"))]
pub fn try_clone(&self) -> io::Result<Selector> {
os_required!();
}

View file

@ -313,29 +313,29 @@ impl event::Source for Sender {
impl Write for Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write(buf))
self.inner.do_io(|mut sender| sender.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write_vectored(bufs))
self.inner.do_io(|mut sender| sender.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|sender| (&*sender).flush())
self.inner.do_io(|mut sender| sender.flush())
}
}
impl Write for &Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write(buf))
self.inner.do_io(|mut sender| sender.write(buf))
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).write_vectored(bufs))
self.inner.do_io(|mut sender| sender.write_vectored(bufs))
}
fn flush(&mut self) -> io::Result<()> {
self.inner.do_io(|sender| (&*sender).flush())
self.inner.do_io(|mut sender| sender.flush())
}
}
@ -478,21 +478,21 @@ impl event::Source for Receiver {
impl Read for Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read(buf))
self.inner.do_io(|mut sender| sender.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read_vectored(bufs))
self.inner.do_io(|mut sender| sender.read_vectored(bufs))
}
}
impl Read for &Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read(buf))
self.inner.do_io(|mut sender| sender.read(buf))
}
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
self.inner.do_io(|sender| (&*sender).read_vectored(bufs))
self.inner.do_io(|mut sender| sender.read_vectored(bufs))
}
}

View file

@ -23,15 +23,41 @@ pub struct Selector {
impl Selector {
pub fn new() -> io::Result<Selector> {
#[cfg(not(target_os = "android"))]
let res = syscall!(epoll_create1(libc::EPOLL_CLOEXEC));
// On Android < API level 16 `epoll_create1` is not defined, so use a
// raw system call.
// According to libuv, `EPOLL_CLOEXEC` is not defined on Android API <
// 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform,
// so we use it instead.
#[cfg(target_os = "android")]
let flag = libc::O_CLOEXEC;
#[cfg(not(target_os = "android"))]
let flag = libc::EPOLL_CLOEXEC;
let res = syscall!(syscall(libc::SYS_epoll_create1, libc::O_CLOEXEC));
syscall!(epoll_create1(flag)).map(|ep| Selector {
let ep = match res {
Ok(ep) => ep as RawFd,
Err(err) => {
// When `epoll_create1` is not available fall back to use
// `epoll_create` followed by `fcntl`.
if let Some(libc::ENOSYS) = err.raw_os_error() {
match syscall!(epoll_create(1024)) {
Ok(ep) => match syscall!(fcntl(ep, libc::F_SETFD, libc::FD_CLOEXEC)) {
Ok(ep) => ep as RawFd,
Err(err) => {
// `fcntl` failed, cleanup `ep`.
let _ = unsafe { libc::close(ep) };
return Err(err);
}
},
Err(err) => return Err(err),
}
} else {
return Err(err);
}
}
};
Ok(Selector {
#[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
ep,
@ -61,7 +87,19 @@ impl Selector {
const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128;
let timeout = timeout
.map(|to| cmp::min(to.as_millis(), MAX_SAFE_TIMEOUT) as libc::c_int)
.map(|to| {
let to_ms = to.as_millis();
// as_millis() truncates, so round up to 1 ms as the documentation says can happen.
// This avoids turning submillisecond timeouts into immediate returns unless the
// caller explicitly requests that by specifying a zero timeout.
let to_ms = to_ms
+ if to_ms == 0 && to.subsec_nanos() != 0 {
1
} else {
0
};
cmp::min(MAX_SAFE_TIMEOUT, to_ms) as libc::c_int
})
.unwrap_or(-1);
events.clear();

View file

@ -40,7 +40,7 @@ cfg_os_poll! {
sockaddr.sun_family = libc::AF_UNIX as libc::sa_family_t;
let bytes = path.as_os_str().as_bytes();
match (bytes.get(0), bytes.len().cmp(&sockaddr.sun_path.len())) {
match (bytes.first(), bytes.len().cmp(&sockaddr.sun_path.len())) {
// Abstract paths don't need a null terminator
(Some(&0), Ordering::Greater) => {
return Err(io::Error::new(
@ -64,7 +64,7 @@ cfg_os_poll! {
let offset = path_offset(&sockaddr);
let mut socklen = offset + bytes.len();
match bytes.get(0) {
match bytes.first() {
// The struct has already been zeroes so the null byte for pathname
// addresses is already there.
Some(&0) | None => {}

View file

@ -77,6 +77,12 @@ impl Selector {
events.reserve(length);
debug_assert!(events.capacity() >= length);
#[cfg(debug_assertions)]
if length == 0 {
log::warn!(
"calling mio::Poll::poll with empty subscriptions, this likely not what you want"
);
}
let res = unsafe { wasi::poll_oneoff(subscriptions.as_ptr(), events.as_mut_ptr(), length) };
@ -110,6 +116,14 @@ impl Selector {
}
}
pub(crate) fn try_clone(&self) -> io::Result<Selector> {
Ok(Selector {
#[cfg(all(debug_assertions, feature = "net"))]
id: self.id,
subscriptions: self.subscriptions.clone(),
})
}
#[cfg(feature = "net")]
pub(crate) fn register(
&self,

View file

@ -224,7 +224,17 @@ impl CompletionStatus {
#[inline]
fn duration_millis(dur: Option<Duration>) -> u32 {
if let Some(dur) = dur {
std::cmp::min(dur.as_millis(), u32::MAX as u128) as u32
let dur_ms = dur.as_millis();
// as_millis() truncates, so round nonzero <1ms timeouts up to 1ms. This avoids turning
// submillisecond timeouts into immediate reutrns unless the caller explictly requests that
// by specifiying a zero timeout.
let dur_ms = dur_ms
+ if dur_ms == 0 && dur.subsec_nanos() != 0 {
1
} else {
0
};
std::cmp::min(dur_ms, u32::MAX as u128) as u32
} else {
u32::MAX
}

File diff suppressed because one or more lines are too long

View file

@ -1,3 +1,383 @@
# 1.24.1 (January 6, 2022)
This release fixes a compilation failure on targets without `AtomicU64` when using rustc older than 1.63. ([#5356])
[#5356]: https://github.com/tokio-rs/tokio/pull/5356
# 1.24.0 (January 5, 2022)
### Fixed
- rt: improve native `AtomicU64` support detection ([#5284])
### Added
- rt: add configuration option for max number of I/O events polled from the OS
per tick ([#5186])
- rt: add an environment variable for configuring the default number of worker
threads per runtime instance ([#4250])
### Changed
- sync: reduce MPSC channel stack usage ([#5294])
- io: reduce lock contention in I/O operations ([#5300])
- fs: speed up `read_dir()` by chunking operations ([#5309])
- rt: use internal `ThreadId` implementation ([#5329])
- test: don't auto-advance time when a `spawn_blocking` task is running ([#5115])
[#5186]: https://github.com/tokio-rs/tokio/pull/5186
[#5294]: https://github.com/tokio-rs/tokio/pull/5294
[#5284]: https://github.com/tokio-rs/tokio/pull/5284
[#4250]: https://github.com/tokio-rs/tokio/pull/4250
[#5300]: https://github.com/tokio-rs/tokio/pull/5300
[#5329]: https://github.com/tokio-rs/tokio/pull/5329
[#5115]: https://github.com/tokio-rs/tokio/pull/5115
[#5309]: https://github.com/tokio-rs/tokio/pull/5309
# 1.23.1 (January 4, 2022)
This release forward ports changes from 1.18.4.
### Fixed
- net: fix Windows named pipe server builder to maintain option when toggling
pipe mode ([#5336]).
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
# 1.23.0 (December 5, 2022)
### Fixed
- net: fix Windows named pipe connect ([#5208])
- io: support vectored writes for `ChildStdin` ([#5216])
- io: fix `async fn ready()` false positive for OS-specific events ([#5231])
### Changed
- runtime: `yield_now` defers task until after driver poll ([#5223])
- runtime: reduce amount of codegen needed per spawned task ([#5213])
- windows: replace `winapi` dependency with `windows-sys` ([#5204])
[#5208]: https://github.com/tokio-rs/tokio/pull/5208
[#5216]: https://github.com/tokio-rs/tokio/pull/5216
[#5213]: https://github.com/tokio-rs/tokio/pull/5213
[#5204]: https://github.com/tokio-rs/tokio/pull/5204
[#5223]: https://github.com/tokio-rs/tokio/pull/5223
[#5231]: https://github.com/tokio-rs/tokio/pull/5231
# 1.22.0 (November 17, 2022)
### Added
- runtime: add `Handle::runtime_flavor` ([#5138])
- sync: add `Mutex::blocking_lock_owned` ([#5130])
- sync: add `Semaphore::MAX_PERMITS` ([#5144])
- sync: add `merge()` to semaphore permits ([#4948])
- sync: add `mpsc::WeakUnboundedSender` ([#5189])
### Added (unstable)
- process: add `Command::process_group` ([#5114])
- runtime: export metrics about the blocking thread pool ([#5161])
- task: add `task::id()` and `task::try_id()` ([#5171])
### Fixed
- macros: don't take ownership of futures in macros ([#5087])
- runtime: fix Stacked Borrows violation in `LocalOwnedTasks` ([#5099])
- runtime: mitigate ABA with 32-bit queue indices when possible ([#5042])
- task: wake local tasks to the local queue when woken by the same thread ([#5095])
- time: panic in release mode when `mark_pending` called illegally ([#5093])
- runtime: fix typo in expect message ([#5169])
- runtime: fix `unsync_load` on atomic types ([#5175])
- task: elaborate safety comments in task deallocation ([#5172])
- runtime: fix `LocalSet` drop in thread local ([#5179])
- net: remove libc type leakage in a public API ([#5191])
- runtime: update the alignment of `CachePadded` ([#5106])
### Changed
- io: make `tokio::io::copy` continue filling the buffer when writer stalls ([#5066])
- runtime: remove `coop::budget` from `LocalSet::run_until` ([#5155])
- sync: make `Notify` panic safe ([#5154])
### Documented
- io: fix doc for `write_i8` to use signed integers ([#5040])
- net: fix doc typos for TCP and UDP `set_tos` methods ([#5073])
- net: fix function name in `UdpSocket::recv` documentation ([#5150])
- sync: typo in `TryLockError` for `RwLock::try_write` ([#5160])
- task: document that spawned tasks execute immediately ([#5117])
- time: document return type of `timeout` ([#5118])
- time: document that `timeout` checks only before poll ([#5126])
- sync: specify return type of `oneshot::Receiver` in docs ([#5198])
### Internal changes
- runtime: use const `Mutex::new` for globals ([#5061])
- runtime: remove `Option` around `mio::Events` in io driver ([#5078])
- runtime: remove a conditional compilation clause ([#5104])
- runtime: remove a reference to internal time handle ([#5107])
- runtime: misc time driver cleanup ([#5120])
- runtime: move signal driver to runtime module ([#5121])
- runtime: signal driver now uses I/O driver directly ([#5125])
- runtime: start decoupling I/O driver and I/O handle ([#5127])
- runtime: switch `io::handle` refs with scheduler:Handle ([#5128])
- runtime: remove Arc from I/O driver ([#5134])
- runtime: use signal driver handle via `scheduler::Handle` ([#5135])
- runtime: move internal clock fns out of context ([#5139])
- runtime: remove `runtime::context` module ([#5140])
- runtime: keep driver cfgs in `driver.rs` ([#5141])
- runtime: add `runtime::context` to unify thread-locals ([#5143])
- runtime: rename some confusing internal variables/fns ([#5151])
- runtime: move `coop` mod into `runtime` ([#5152])
- runtime: move budget state to context thread-local ([#5157])
- runtime: move park logic into runtime module ([#5158])
- runtime: move `Runtime` into its own file ([#5159])
- runtime: unify entering a runtime with `Handle::enter` ([#5163])
- runtime: remove handle reference from each scheduler ([#5166])
- runtime: move `enter` into `context` ([#5167])
- runtime: combine context and entered thread-locals ([#5168])
- runtime: fix accidental unsetting of current handle ([#5178])
- runtime: move `CoreStage` methods to `Core` ([#5182])
- sync: name mpsc semaphore types ([#5146])
[#4948]: https://github.com/tokio-rs/tokio/pull/4948
[#5040]: https://github.com/tokio-rs/tokio/pull/5040
[#5042]: https://github.com/tokio-rs/tokio/pull/5042
[#5061]: https://github.com/tokio-rs/tokio/pull/5061
[#5066]: https://github.com/tokio-rs/tokio/pull/5066
[#5073]: https://github.com/tokio-rs/tokio/pull/5073
[#5078]: https://github.com/tokio-rs/tokio/pull/5078
[#5087]: https://github.com/tokio-rs/tokio/pull/5087
[#5093]: https://github.com/tokio-rs/tokio/pull/5093
[#5095]: https://github.com/tokio-rs/tokio/pull/5095
[#5099]: https://github.com/tokio-rs/tokio/pull/5099
[#5104]: https://github.com/tokio-rs/tokio/pull/5104
[#5106]: https://github.com/tokio-rs/tokio/pull/5106
[#5107]: https://github.com/tokio-rs/tokio/pull/5107
[#5114]: https://github.com/tokio-rs/tokio/pull/5114
[#5117]: https://github.com/tokio-rs/tokio/pull/5117
[#5118]: https://github.com/tokio-rs/tokio/pull/5118
[#5120]: https://github.com/tokio-rs/tokio/pull/5120
[#5121]: https://github.com/tokio-rs/tokio/pull/5121
[#5125]: https://github.com/tokio-rs/tokio/pull/5125
[#5126]: https://github.com/tokio-rs/tokio/pull/5126
[#5127]: https://github.com/tokio-rs/tokio/pull/5127
[#5128]: https://github.com/tokio-rs/tokio/pull/5128
[#5130]: https://github.com/tokio-rs/tokio/pull/5130
[#5134]: https://github.com/tokio-rs/tokio/pull/5134
[#5135]: https://github.com/tokio-rs/tokio/pull/5135
[#5138]: https://github.com/tokio-rs/tokio/pull/5138
[#5138]: https://github.com/tokio-rs/tokio/pull/5138
[#5139]: https://github.com/tokio-rs/tokio/pull/5139
[#5140]: https://github.com/tokio-rs/tokio/pull/5140
[#5141]: https://github.com/tokio-rs/tokio/pull/5141
[#5143]: https://github.com/tokio-rs/tokio/pull/5143
[#5144]: https://github.com/tokio-rs/tokio/pull/5144
[#5144]: https://github.com/tokio-rs/tokio/pull/5144
[#5146]: https://github.com/tokio-rs/tokio/pull/5146
[#5150]: https://github.com/tokio-rs/tokio/pull/5150
[#5151]: https://github.com/tokio-rs/tokio/pull/5151
[#5152]: https://github.com/tokio-rs/tokio/pull/5152
[#5154]: https://github.com/tokio-rs/tokio/pull/5154
[#5155]: https://github.com/tokio-rs/tokio/pull/5155
[#5157]: https://github.com/tokio-rs/tokio/pull/5157
[#5158]: https://github.com/tokio-rs/tokio/pull/5158
[#5159]: https://github.com/tokio-rs/tokio/pull/5159
[#5160]: https://github.com/tokio-rs/tokio/pull/5160
[#5161]: https://github.com/tokio-rs/tokio/pull/5161
[#5163]: https://github.com/tokio-rs/tokio/pull/5163
[#5166]: https://github.com/tokio-rs/tokio/pull/5166
[#5167]: https://github.com/tokio-rs/tokio/pull/5167
[#5168]: https://github.com/tokio-rs/tokio/pull/5168
[#5169]: https://github.com/tokio-rs/tokio/pull/5169
[#5171]: https://github.com/tokio-rs/tokio/pull/5171
[#5172]: https://github.com/tokio-rs/tokio/pull/5172
[#5175]: https://github.com/tokio-rs/tokio/pull/5175
[#5178]: https://github.com/tokio-rs/tokio/pull/5178
[#5179]: https://github.com/tokio-rs/tokio/pull/5179
[#5182]: https://github.com/tokio-rs/tokio/pull/5182
[#5189]: https://github.com/tokio-rs/tokio/pull/5189
[#5191]: https://github.com/tokio-rs/tokio/pull/5191
[#5198]: https://github.com/tokio-rs/tokio/pull/5198
# 1.21.2 (September 27, 2022)
This release removes the dependency on the `once_cell` crate to restore the MSRV
of 1.21.x, which is the latest minor version at the time of release. ([#5048])
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
# 1.21.1 (September 13, 2022)
### Fixed
- net: fix dependency resolution for socket2 ([#5000])
- task: ignore failure to set TLS in `LocalSet` Drop ([#4976])
[#4976]: https://github.com/tokio-rs/tokio/pull/4976
[#5000]: https://github.com/tokio-rs/tokio/pull/5000
# 1.21.0 (September 2, 2022)
This release is the first release of Tokio to intentionally support WASM. The
`sync,macros,io-util,rt,time` features are stabilized on WASM. Additionally the
wasm32-wasi target is given unstable support for the `net` feature.
### Added
- net: add `device` and `bind_device` methods to TCP/UDP sockets ([#4882])
- net: add `tos` and `set_tos` methods to TCP and UDP sockets ([#4877])
- net: add security flags to named pipe `ServerOptions` ([#4845])
- signal: add more windows signal handlers ([#4924])
- sync: add `mpsc::Sender::max_capacity` method ([#4904])
- sync: implement Weak version of `mpsc::Sender` ([#4595])
- task: add `LocalSet::enter` ([#4765])
- task: stabilize `JoinSet` and `AbortHandle` ([#4920])
- tokio: add `track_caller` to public APIs ([#4805], [#4848], [#4852])
- wasm: initial support for `wasm32-wasi` target ([#4716])
### Fixed
- miri: improve miri compatibility by avoiding temporary references in `linked_list::Link` impls ([#4841])
- signal: don't register write interest on signal pipe ([#4898])
- sync: add `#[must_use]` to lock guards ([#4886])
- sync: fix hang when calling `recv` on closed and reopened broadcast channel ([#4867])
- task: propagate attributes on task-locals ([#4837])
### Changed
- fs: change panic to error in `File::start_seek` ([#4897])
- io: reduce syscalls in `poll_read` ([#4840])
- process: use blocking threadpool for child stdio I/O ([#4824])
- signal: make `SignalKind` methods const ([#4956])
### Internal changes
- rt: extract `basic_scheduler::Config` ([#4935])
- rt: move I/O driver into `runtime` module ([#4942])
- rt: rename internal scheduler types ([#4945])
### Documented
- chore: fix typos and grammar ([#4858], [#4894], [#4928])
- io: fix typo in `AsyncSeekExt::rewind` docs ([#4893])
- net: add documentation to `try_read()` for zero-length buffers ([#4937])
- runtime: remove incorrect panic section for `Builder::worker_threads` ([#4849])
- sync: doc of `watch::Sender::send` improved ([#4959])
- task: add cancel safety docs to `JoinHandle` ([#4901])
- task: expand on cancellation of `spawn_blocking` ([#4811])
- time: clarify that the first tick of `Interval::tick` happens immediately ([#4951])
### Unstable
- rt: add unstable option to disable the LIFO slot ([#4936])
- task: fix incorrect signature in `Builder::spawn_on` ([#4953])
- task: make `task::Builder::spawn*` methods fallible ([#4823])
[#4595]: https://github.com/tokio-rs/tokio/pull/4595
[#4716]: https://github.com/tokio-rs/tokio/pull/4716
[#4765]: https://github.com/tokio-rs/tokio/pull/4765
[#4805]: https://github.com/tokio-rs/tokio/pull/4805
[#4811]: https://github.com/tokio-rs/tokio/pull/4811
[#4823]: https://github.com/tokio-rs/tokio/pull/4823
[#4824]: https://github.com/tokio-rs/tokio/pull/4824
[#4837]: https://github.com/tokio-rs/tokio/pull/4837
[#4840]: https://github.com/tokio-rs/tokio/pull/4840
[#4841]: https://github.com/tokio-rs/tokio/pull/4841
[#4845]: https://github.com/tokio-rs/tokio/pull/4845
[#4848]: https://github.com/tokio-rs/tokio/pull/4848
[#4849]: https://github.com/tokio-rs/tokio/pull/4849
[#4852]: https://github.com/tokio-rs/tokio/pull/4852
[#4858]: https://github.com/tokio-rs/tokio/pull/4858
[#4867]: https://github.com/tokio-rs/tokio/pull/4867
[#4877]: https://github.com/tokio-rs/tokio/pull/4877
[#4882]: https://github.com/tokio-rs/tokio/pull/4882
[#4886]: https://github.com/tokio-rs/tokio/pull/4886
[#4893]: https://github.com/tokio-rs/tokio/pull/4893
[#4894]: https://github.com/tokio-rs/tokio/pull/4894
[#4897]: https://github.com/tokio-rs/tokio/pull/4897
[#4898]: https://github.com/tokio-rs/tokio/pull/4898
[#4901]: https://github.com/tokio-rs/tokio/pull/4901
[#4904]: https://github.com/tokio-rs/tokio/pull/4904
[#4920]: https://github.com/tokio-rs/tokio/pull/4920
[#4924]: https://github.com/tokio-rs/tokio/pull/4924
[#4928]: https://github.com/tokio-rs/tokio/pull/4928
[#4935]: https://github.com/tokio-rs/tokio/pull/4935
[#4936]: https://github.com/tokio-rs/tokio/pull/4936
[#4937]: https://github.com/tokio-rs/tokio/pull/4937
[#4942]: https://github.com/tokio-rs/tokio/pull/4942
[#4945]: https://github.com/tokio-rs/tokio/pull/4945
[#4951]: https://github.com/tokio-rs/tokio/pull/4951
[#4953]: https://github.com/tokio-rs/tokio/pull/4953
[#4956]: https://github.com/tokio-rs/tokio/pull/4956
[#4959]: https://github.com/tokio-rs/tokio/pull/4959
# 1.20.3 (January 3, 2022)
This release forward ports changes from 1.18.4.
### Fixed
- net: fix Windows named pipe server builder to maintain option when toggling
pipe mode ([#5336]).
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
# 1.20.2 (September 27, 2022)
This release removes the dependency on the `once_cell` crate to restore the MSRV
of the 1.20.x LTS release. ([#5048])
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
# 1.20.1 (July 25, 2022)
### Fixed
- chore: fix version detection in build script ([#4860])
[#4860]: https://github.com/tokio-rs/tokio/pull/4860
# 1.20.0 (July 12, 2022)
### Added
- tokio: add `track_caller` to public APIs ([#4772], [#4791], [#4793], [#4806], [#4808])
- sync: Add `has_changed` method to `watch::Ref` ([#4758])
### Changed
- time: remove `src/time/driver/wheel/stack.rs` ([#4766])
- rt: clean up arguments passed to basic scheduler ([#4767])
- net: be more specific about winapi features ([#4764])
- tokio: use const initialized thread locals where possible ([#4677])
- task: various small improvements to LocalKey ([#4795])
### Documented
- fs: warn about performance pitfall ([#4762])
- chore: fix spelling ([#4769])
- sync: document spurious failures in oneshot ([#4777])
- sync: add warning for watch in non-Send futures ([#4741])
- chore: fix typo ([#4798])
### Unstable
- joinset: rename `join_one` to `join_next` ([#4755])
- rt: unhandled panic config for current thread rt ([#4770])
[#4677]: https://github.com/tokio-rs/tokio/pull/4677
[#4741]: https://github.com/tokio-rs/tokio/pull/4741
[#4755]: https://github.com/tokio-rs/tokio/pull/4755
[#4758]: https://github.com/tokio-rs/tokio/pull/4758
[#4762]: https://github.com/tokio-rs/tokio/pull/4762
[#4764]: https://github.com/tokio-rs/tokio/pull/4764
[#4766]: https://github.com/tokio-rs/tokio/pull/4766
[#4767]: https://github.com/tokio-rs/tokio/pull/4767
[#4769]: https://github.com/tokio-rs/tokio/pull/4769
[#4770]: https://github.com/tokio-rs/tokio/pull/4770
[#4772]: https://github.com/tokio-rs/tokio/pull/4772
[#4777]: https://github.com/tokio-rs/tokio/pull/4777
[#4791]: https://github.com/tokio-rs/tokio/pull/4791
[#4793]: https://github.com/tokio-rs/tokio/pull/4793
[#4795]: https://github.com/tokio-rs/tokio/pull/4795
[#4798]: https://github.com/tokio-rs/tokio/pull/4798
[#4806]: https://github.com/tokio-rs/tokio/pull/4806
[#4808]: https://github.com/tokio-rs/tokio/pull/4808
# 1.19.2 (June 6, 2022)
This release fixes another bug in `Notified::enable`. ([#4751])
@ -62,6 +442,22 @@ This release fixes a bug in `Notified::enable`. ([#4747])
[#4729]: https://github.com/tokio-rs/tokio/pull/4729
[#4739]: https://github.com/tokio-rs/tokio/pull/4739
# 1.18.4 (January 3, 2022)
### Fixed
- net: fix Windows named pipe server builder to maintain option when toggling
pipe mode ([#5336]).
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
# 1.18.3 (September 27, 2022)
This release removes the dependency on the `once_cell` crate to restore the MSRV
of the 1.18.x LTS release. ([#5048])
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
# 1.18.2 (May 5, 2022)
Add missing features for the `winapi` dependency. ([#4663])
@ -1235,7 +1631,7 @@ Biggest changes are:
- Feature flags are simplified
- `rt-core` and `rt-util` are combined to `rt`
- `rt-threaded` is renamed to `rt-multi-thread` to match builder API
- `tcp`, `udp`, `uds`, `dns` are combied to `net`.
- `tcp`, `udp`, `uds`, `dns` are combined to `net`.
- `parking_lot` is included with `full`
### Changes
@ -1733,7 +2129,7 @@ Biggest changes are:
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` ([#1870]).
- `process::Child` fields are made public to match `std` ([#2014]).
- impl `Stream` for `sync::broadcast::Receiver` ([#2012]).
- `sync::RwLock` provides an asynchonous read-write lock ([#1699]).
- `sync::RwLock` provides an asynchronous read-write lock ([#1699]).
- `runtime::Handle::current` returns the handle for the current runtime ([#2040]).
- `StreamExt::filter` filters stream values according to a predicate ([#2001]).
- `StreamExt::filter_map` simultaneously filter and map stream values ([#2001]).
@ -1842,7 +2238,7 @@ Biggest changes are:
### Fixes
- calling `spawn_blocking` after runtime shutdown ([#1875]).
- `LocalSet` drop inifinite loop ([#1892]).
- `LocalSet` drop infinite loop ([#1892]).
- `LocalSet` hang under load ([#1905]).
- improved documentation ([#1865], [#1866], [#1868], [#1874], [#1876], [#1911]).

View file

@ -13,7 +13,7 @@
edition = "2018"
rust-version = "1.49"
name = "tokio"
version = "1.19.2"
version = "1.24.1"
authors = ["Tokio Contributors <team@tokio.rs>"]
description = """
An event-driven, non-blocking I/O platform for writing asynchronous I/O
@ -62,17 +62,13 @@ version = "2.2"
optional = true
[dependencies.mio]
version = "0.8.1"
version = "0.8.4"
optional = true
[dependencies.num_cpus]
version = "1.8.0"
optional = true
[dependencies.once_cell]
version = "1.5.2"
optional = true
[dependencies.parking_lot]
version = "0.12.0"
optional = true
@ -80,11 +76,6 @@ optional = true
[dependencies.pin-project-lite]
version = "0.2.0"
[dependencies.socket2]
version = "0.4.4"
features = ["all"]
optional = true
[dependencies.tokio-macros]
version = "1.7.0"
optional = true
@ -108,6 +99,9 @@ version = "0.1"
[dev-dependencies.tokio-test]
version = "0.4.0"
[build-dependencies.autocfg]
version = "1.1"
[features]
default = []
fs = []
@ -137,31 +131,36 @@ net = [
"mio/os-ext",
"mio/net",
"socket2",
"winapi/namedpipeapi",
"windows-sys/Win32_Foundation",
"windows-sys/Win32_Security",
"windows-sys/Win32_Storage_FileSystem",
"windows-sys/Win32_System_Pipes",
"windows-sys/Win32_System_SystemServices",
]
process = [
"bytes",
"once_cell",
"libc",
"mio/os-poll",
"mio/os-ext",
"mio/net",
"signal-hook-registry",
"winapi/threadpoollegacyapiset",
"windows-sys/Win32_Foundation",
"windows-sys/Win32_System_Threading",
"windows-sys/Win32_System_WindowsProgramming",
]
rt = ["once_cell"]
rt = []
rt-multi-thread = [
"num_cpus",
"rt",
]
signal = [
"once_cell",
"libc",
"mio/os-poll",
"mio/net",
"mio/os-ext",
"signal-hook-registry",
"winapi/consoleapi",
"windows-sys/Win32_Foundation",
"windows-sys/Win32_System_Console",
]
stats = []
sync = []
@ -172,6 +171,16 @@ test-util = [
]
time = []
[target."cfg(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), not(target_os = \"wasi\")))".dev-dependencies.wasm-bindgen-test]
version = "0.3.0"
[target."cfg(docsrs)".dependencies.windows-sys]
version = "0.42.0"
features = [
"Win32_Foundation",
"Win32_Security_Authorization",
]
[target."cfg(loom)".dev-dependencies.loom]
version = "0.5.2"
features = [
@ -179,20 +188,22 @@ features = [
"checkpoint",
]
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.proptest]
version = "1"
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.rand]
[target."cfg(not(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\")))".dev-dependencies.rand]
version = "0.8.0"
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.socket2]
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dependencies.socket2]
version = "0.4.4"
features = ["all"]
optional = true
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dev-dependencies.proptest]
version = "1"
[target."cfg(not(any(target_arch = \"wasm32\", target_arch = \"wasm64\")))".dev-dependencies.socket2]
version = "0.4"
[target."cfg(target_arch = \"wasm32\")".dev-dependencies.wasm-bindgen-test]
version = "0.3.0"
[target."cfg(target_os = \"freebsd\")".dev-dependencies.mio-aio]
version = "0.6.0"
version = "0.7.0"
features = ["tokio"]
[target."cfg(tokio_unstable)".dependencies.tracing]
@ -220,18 +231,9 @@ features = [
]
default-features = false
[target."cfg(windows)".dependencies.winapi]
version = "0.3.8"
features = [
"std",
"winsock2",
"mswsock",
"handleapi",
"ws2ipdef",
"ws2tcpip",
]
[target."cfg(windows)".dependencies.windows-sys]
version = "0.42.0"
optional = true
default-features = false
[target."cfg(windows)".dev-dependencies.ntapi]
version = "0.3.6"

View file

@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
```toml
[dependencies]
tokio = { version = "1.19.2", features = ["full"] }
tokio = { version = "1.24.1", features = ["full"] }
```
Then, on your main.rs:
@ -161,6 +161,16 @@ several other libraries, including:
[`mio`]: https://github.com/tokio-rs/mio
[`bytes`]: https://github.com/tokio-rs/bytes
## Changelog
The Tokio repository contains multiple crates. Each crate has its own changelog.
* `tokio` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio/CHANGELOG.md)
* `tokio-util` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-util/CHANGELOG.md)
* `tokio-stream` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-stream/CHANGELOG.md)
* `tokio-macros` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-macros/CHANGELOG.md)
* `tokio-test` - [view changelog](https://github.com/tokio-rs/tokio/blob/master/tokio-test/CHANGELOG.md)
## Supported Rust Versions
<!--
@ -192,18 +202,18 @@ warrants a patch release with a fix for the bug, it will be backported and
released as a new patch release for each LTS minor version. Our current LTS
releases are:
* `1.14.x` - LTS release until June 2022.
* `1.18.x` - LTS release until January 2023
* `1.18.x` - LTS release until June 2023
* `1.20.x` - LTS release until September 2023.
Each LTS release will continue to receive backported fixes for at least half a
year. If you wish to use a fixed minor release in your project, we recommend
that you use an LTS release.
Each LTS release will continue to receive backported fixes for at least a year.
If you wish to use a fixed minor release in your project, we recommend that you
use an LTS release.
To use a fixed minor version, you can specify the version with a tilde. For
example, to specify that you wish to use the newest `1.14.x` patch release, you
example, to specify that you wish to use the newest `1.18.x` patch release, you
can use the following dependency specification:
```text
tokio = { version = "~1.14", features = [...] }
tokio = { version = "~1.18", features = [...] }
```
## License

184
zeroidc/vendor/tokio/build.rs vendored Normal file
View file

@ -0,0 +1,184 @@
use autocfg::AutoCfg;
const CONST_THREAD_LOCAL_PROBE: &str = r#"
{
thread_local! {
static MY_PROBE: usize = const { 10 };
}
MY_PROBE.with(|val| *val)
}
"#;
const ADDR_OF_PROBE: &str = r#"
{
let my_var = 10;
::std::ptr::addr_of!(my_var)
}
"#;
const CONST_MUTEX_NEW_PROBE: &str = r#"
{
static MY_MUTEX: ::std::sync::Mutex<i32> = ::std::sync::Mutex::new(1);
*MY_MUTEX.lock().unwrap()
}
"#;
const TARGET_HAS_ATOMIC_PROBE: &str = r#"
{
#[cfg(target_has_atomic = "ptr")]
let _ = ();
}
"#;
const TARGET_ATOMIC_U64_PROBE: &str = r#"
{
use std::sync::atomic::AtomicU64 as _;
}
"#;
fn main() {
let mut enable_const_thread_local = false;
let mut enable_addr_of = false;
let mut enable_target_has_atomic = false;
let mut enable_const_mutex_new = false;
let mut target_needs_atomic_u64_fallback = false;
match AutoCfg::new() {
Ok(ac) => {
// These checks prefer to call only `probe_rustc_version` if that is
// enough to determine whether the feature is supported. This is
// because the `probe_expression` call involves a call to rustc,
// which the `probe_rustc_version` call avoids.
// Const-initialized thread locals were stabilized in 1.59.
if ac.probe_rustc_version(1, 60) {
enable_const_thread_local = true;
} else if ac.probe_rustc_version(1, 59) {
// This compiler claims to be 1.59, but there are some nightly
// compilers that claim to be 1.59 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2021-12-06.
if ac.probe_expression(CONST_THREAD_LOCAL_PROBE) {
enable_const_thread_local = true;
}
}
// The `addr_of` and `addr_of_mut` macros were stabilized in 1.51.
if ac.probe_rustc_version(1, 52) {
enable_addr_of = true;
} else if ac.probe_rustc_version(1, 51) {
// This compiler claims to be 1.51, but there are some nightly
// compilers that claim to be 1.51 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2021-01-31.
if ac.probe_expression(ADDR_OF_PROBE) {
enable_addr_of = true;
}
}
// The `target_has_atomic` cfg was stabilized in 1.60.
if ac.probe_rustc_version(1, 61) {
enable_target_has_atomic = true;
} else if ac.probe_rustc_version(1, 60) {
// This compiler claims to be 1.60, but there are some nightly
// compilers that claim to be 1.60 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2022-02-11.
if ac.probe_expression(TARGET_HAS_ATOMIC_PROBE) {
enable_target_has_atomic = true;
}
}
// If we can't tell using `target_has_atomic`, tell if the target
// has `AtomicU64` by trying to use it.
if !enable_target_has_atomic && !ac.probe_expression(TARGET_ATOMIC_U64_PROBE) {
target_needs_atomic_u64_fallback = true;
}
// The `Mutex::new` method was made const in 1.63.
if ac.probe_rustc_version(1, 64) {
enable_const_mutex_new = true;
} else if ac.probe_rustc_version(1, 63) {
// This compiler claims to be 1.63, but there are some nightly
// compilers that claim to be 1.63 without supporting the
// feature. Explicitly probe to check if code using them
// compiles.
//
// The oldest nightly that supports the feature is 2022-06-20.
if ac.probe_expression(CONST_MUTEX_NEW_PROBE) {
enable_const_mutex_new = true;
}
}
}
Err(e) => {
// If we couldn't detect the compiler version and features, just
// print a warning. This isn't a fatal error: we can still build
// Tokio, we just can't enable cfgs automatically.
println!(
"cargo:warning=tokio: failed to detect compiler features: {}",
e
);
}
}
if !enable_const_thread_local {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_const_thread_local"
autocfg::emit("tokio_no_const_thread_local")
}
if !enable_addr_of {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_addr_of"
autocfg::emit("tokio_no_addr_of")
}
if !enable_target_has_atomic {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_target_has_atomic"
autocfg::emit("tokio_no_target_has_atomic")
}
if !enable_const_mutex_new {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_const_mutex_new"
autocfg::emit("tokio_no_const_mutex_new")
}
if target_needs_atomic_u64_fallback {
// To disable this feature on compilers that support it, you can
// explicitly pass this flag with the following environment variable:
//
// RUSTFLAGS="--cfg tokio_no_atomic_u64"
autocfg::emit("tokio_no_atomic_u64")
}
let target = ::std::env::var("TARGET").unwrap_or_default();
// We emit cfgs instead of using `target_family = "wasm"` that requires Rust 1.54.
// Note that these cfgs are unavailable in `Cargo.toml`.
if target.starts_with("wasm") {
autocfg::emit("tokio_wasm");
if target.contains("wasi") {
autocfg::emit("tokio_wasi");
} else {
autocfg::emit("tokio_wasm_not_wasi");
}
}
}

View file

@ -188,12 +188,12 @@ readiness, the driver's tick is packed into the atomic `usize`.
The `ScheduledIo` readiness `AtomicUsize` is structured as:
```
| reserved | generation | driver tick | readinesss |
|----------+------------+--------------+------------|
| 1 bit | 7 bits + 8 bits + 16 bits |
| shutdown | generation | driver tick | readiness |
|----------+------------+--------------+-----------|
| 1 bit | 7 bits + 8 bits + 16 bits |
```
The `reserved` and `generation` components exist today.
The `shutdown` and `generation` components exist today.
The `readiness()` function returns a `ReadyEvent` value. This value includes the
`tick` component read with the resource's readiness value. When

View file

@ -0,0 +1,11 @@
# This config file is for the `cargo-check-external-types` tool that is run in CI.
# The following are types that are allowed to be exposed in Tokio's public API.
# The standard library is allowed by default.
allowed_external_types = [
"bytes::buf::buf_impl::Buf",
"bytes::buf::buf_mut::BufMut",
"tokio_macros::*",
]

View file

@ -21,4 +21,3 @@
pub enum NotDefinedHere {}
pub mod os;
pub mod winapi;

View file

@ -1,66 +0,0 @@
//! See [winapi].
//!
//! [winapi]: https://docs.rs/winapi
/// See [winapi::shared](https://docs.rs/winapi/*/winapi/shared/index.html).
pub mod shared {
/// See [winapi::shared::winerror](https://docs.rs/winapi/*/winapi/shared/winerror/index.html).
#[allow(non_camel_case_types)]
pub mod winerror {
/// See [winapi::shared::winerror::ERROR_ACCESS_DENIED][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_ACCESS_DENIED.html
pub type ERROR_ACCESS_DENIED = crate::doc::NotDefinedHere;
/// See [winapi::shared::winerror::ERROR_PIPE_BUSY][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_PIPE_BUSY.html
pub type ERROR_PIPE_BUSY = crate::doc::NotDefinedHere;
/// See [winapi::shared::winerror::ERROR_MORE_DATA][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_MORE_DATA.html
pub type ERROR_MORE_DATA = crate::doc::NotDefinedHere;
}
}
/// See [winapi::um](https://docs.rs/winapi/*/winapi/um/index.html).
pub mod um {
/// See [winapi::um::winbase](https://docs.rs/winapi/*/winapi/um/winbase/index.html).
#[allow(non_camel_case_types)]
pub mod winbase {
/// See [winapi::um::winbase::PIPE_TYPE_MESSAGE][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_MESSAGE.html
pub type PIPE_TYPE_MESSAGE = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::PIPE_TYPE_BYTE][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_BYTE.html
pub type PIPE_TYPE_BYTE = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::PIPE_CLIENT_END][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_CLIENT_END.html
pub type PIPE_CLIENT_END = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::PIPE_SERVER_END][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_SERVER_END.html
pub type PIPE_SERVER_END = crate::doc::NotDefinedHere;
/// See [winapi::um::winbase::SECURITY_IDENTIFICATION][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.SECURITY_IDENTIFICATION.html
pub type SECURITY_IDENTIFICATION = crate::doc::NotDefinedHere;
}
/// See [winapi::um::minwinbase](https://docs.rs/winapi/*/winapi/um/minwinbase/index.html).
#[allow(non_camel_case_types)]
pub mod minwinbase {
/// See [winapi::um::minwinbase::SECURITY_ATTRIBUTES][winapi]
///
/// [winapi]: https://docs.rs/winapi/*/winapi/um/minwinbase/constant.SECURITY_ATTRIBUTES.html
pub type SECURITY_ATTRIBUTES = crate::doc::NotDefinedHere;
}
}

View file

@ -565,29 +565,30 @@ impl AsyncSeek for File {
let me = self.get_mut();
let inner = me.inner.get_mut();
loop {
match inner.state {
Busy(_) => panic!("must wait for poll_complete before calling start_seek"),
Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
match inner.state {
Busy(_) => Err(io::Error::new(
io::ErrorKind::Other,
"other file operation is pending, call poll_complete before start_seek",
)),
Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
// Factor in any unread data from the buf
if !buf.is_empty() {
let n = buf.discard_read();
// Factor in any unread data from the buf
if !buf.is_empty() {
let n = buf.discard_read();
if let SeekFrom::Current(ref mut offset) = pos {
*offset += n;
}
if let SeekFrom::Current(ref mut offset) = pos {
*offset += n;
}
let std = me.std.clone();
inner.state = Busy(spawn_blocking(move || {
let res = (&*std).seek(pos);
(Operation::Seek(res), buf)
}));
return Ok(());
}
let std = me.std.clone();
inner.state = Busy(spawn_blocking(move || {
let res = (&*std).seek(pos);
(Operation::Seek(res), buf)
}));
Ok(())
}
}
}

View file

@ -955,3 +955,24 @@ fn partial_read_set_len_ok() {
assert_eq!(n, FOO.len());
assert_eq!(&buf[..n], FOO);
}
#[test]
fn busy_file_seek_error() {
let mut file = MockFile::default();
let mut seq = Sequence::new();
file.expect_inner_write()
.once()
.in_sequence(&mut seq)
.returning(|_| Err(io::ErrorKind::Other.into()));
let mut file = crate::io::BufReader::new(File::from_std(file));
{
let mut t = task::spawn(file.write(HELLO));
assert_ready_ok!(t.poll());
}
pool::run_one();
let mut t = task::spawn(file.seek(SeekFrom::Start(0)));
assert_ready_err!(t.poll());
}

View file

@ -81,7 +81,7 @@ impl Write for &'_ MockFile {
}
}
thread_local! {
tokio_thread_local! {
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
}

View file

@ -22,6 +22,24 @@
//! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
//! to a *backup* thread immediately.
//!
//! **Warning**: These adapters may create a large number of temporary tasks,
//! especially when reading large files. When performing a lot of operations
//! in one batch, it may be significantly faster to use [`spawn_blocking`]
//! directly:
//!
//! ```
//! use tokio::fs::File;
//! use std::io::{BufReader, BufRead};
//! async fn count_lines(file: File) -> Result<usize, std::io::Error> {
//! let file = file.into_std().await;
//! tokio::task::spawn_blocking(move || {
//! let line_count = BufReader::new(file).lines().count();
//! Ok(line_count)
//! }).await?
//! }
//! ```
//!
//! [`spawn_blocking`]: fn@crate::task::spawn_blocking
//! [`AsyncRead`]: trait@crate::io::AsyncRead
mod canonicalize;

View file

@ -542,7 +542,7 @@ feature! {
/// # Examples
///
/// ```no_run
/// use winapi::um::winbase::FILE_FLAG_DELETE_ON_CLOSE;
/// use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE;
/// use tokio::fs::OpenOptions;
///
/// # #[tokio::main]
@ -581,7 +581,7 @@ feature! {
/// # Examples
///
/// ```no_run
/// use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
/// use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN;
/// use tokio::fs::OpenOptions;
///
/// # #[tokio::main]
@ -624,7 +624,7 @@ feature! {
/// # Examples
///
/// ```no_run
/// use winapi::um::winbase::SECURITY_IDENTIFICATION;
/// use windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION;
/// use tokio::fs::OpenOptions;
///
/// # #[tokio::main]

View file

@ -1,3 +1,4 @@
#![allow(unreachable_pub)]
//! Mock version of std::fs::OpenOptions;
use mockall::mock;

View file

@ -1,5 +1,6 @@
use crate::fs::asyncify;
use std::collections::VecDeque;
use std::ffi::OsString;
use std::fs::{FileType, Metadata};
use std::future::Future;
@ -19,6 +20,8 @@ use crate::blocking::spawn_blocking;
#[cfg(not(test))]
use crate::blocking::JoinHandle;
const CHUNK_SIZE: usize = 32;
/// Returns a stream over the entries within a directory.
///
/// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
@ -29,12 +32,17 @@ use crate::blocking::JoinHandle;
/// [`spawn_blocking`]: crate::task::spawn_blocking
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
let path = path.as_ref().to_owned();
let std = asyncify(|| std::fs::read_dir(path)).await?;
asyncify(|| -> io::Result<ReadDir> {
let mut std = std::fs::read_dir(path)?;
let mut buf = VecDeque::with_capacity(CHUNK_SIZE);
ReadDir::next_chunk(&mut buf, &mut std);
Ok(ReadDir(State::Idle(Some(std))))
Ok(ReadDir(State::Idle(Some((buf, std)))))
})
.await
}
/// Reads the the entries in a directory.
/// Reads the entries in a directory.
///
/// This struct is returned from the [`read_dir`] function of this module and
/// will yield instances of [`DirEntry`]. Through a [`DirEntry`] information
@ -58,8 +66,8 @@ pub struct ReadDir(State);
#[derive(Debug)]
enum State {
Idle(Option<std::fs::ReadDir>),
Pending(JoinHandle<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
Idle(Option<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir)>),
Pending(JoinHandle<(VecDeque<io::Result<DirEntry>>, std::fs::ReadDir)>),
}
impl ReadDir {
@ -94,29 +102,57 @@ impl ReadDir {
pub fn poll_next_entry(&mut self, cx: &mut Context<'_>) -> Poll<io::Result<Option<DirEntry>>> {
loop {
match self.0 {
State::Idle(ref mut std) => {
let mut std = std.take().unwrap();
State::Idle(ref mut data) => {
let (buf, _) = data.as_mut().unwrap();
if let Some(ent) = buf.pop_front() {
return Poll::Ready(ent.map(Some));
};
let (mut buf, mut std) = data.take().unwrap();
self.0 = State::Pending(spawn_blocking(move || {
let ret = std.next();
(ret, std)
ReadDir::next_chunk(&mut buf, &mut std);
(buf, std)
}));
}
State::Pending(ref mut rx) => {
let (ret, std) = ready!(Pin::new(rx).poll(cx))?;
self.0 = State::Idle(Some(std));
let (mut buf, std) = ready!(Pin::new(rx).poll(cx))?;
let ret = match ret {
Some(Ok(std)) => Ok(Some(DirEntry(Arc::new(std)))),
let ret = match buf.pop_front() {
Some(Ok(x)) => Ok(Some(x)),
Some(Err(e)) => Err(e),
None => Ok(None),
};
self.0 = State::Idle(Some((buf, std)));
return Poll::Ready(ret);
}
}
}
}
fn next_chunk(buf: &mut VecDeque<io::Result<DirEntry>>, std: &mut std::fs::ReadDir) {
for ret in std.by_ref().take(CHUNK_SIZE) {
let success = ret.is_ok();
buf.push_back(ret.map(|std| DirEntry {
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks"
)))]
file_type: std.file_type().ok(),
std: Arc::new(std),
}));
if !success {
break;
}
}
}
}
feature! {
@ -160,7 +196,16 @@ feature! {
/// filesystem. Each entry can be inspected via methods to learn about the full
/// path or possibly other metadata through per-platform extension traits.
#[derive(Debug)]
pub struct DirEntry(Arc<std::fs::DirEntry>);
pub struct DirEntry {
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks"
)))]
file_type: Option<FileType>,
std: Arc<std::fs::DirEntry>,
}
impl DirEntry {
/// Returns the full path to the file that this entry represents.
@ -193,7 +238,7 @@ impl DirEntry {
///
/// The exact text, of course, depends on what files you have in `.`.
pub fn path(&self) -> PathBuf {
self.0.path()
self.std.path()
}
/// Returns the bare file name of this directory entry without any other
@ -214,7 +259,7 @@ impl DirEntry {
/// # }
/// ```
pub fn file_name(&self) -> OsString {
self.0.file_name()
self.std.file_name()
}
/// Returns the metadata for the file that this entry points at.
@ -248,7 +293,7 @@ impl DirEntry {
/// # }
/// ```
pub async fn metadata(&self) -> io::Result<Metadata> {
let std = self.0.clone();
let std = self.std.clone();
asyncify(move || std.metadata()).await
}
@ -283,13 +328,23 @@ impl DirEntry {
/// # }
/// ```
pub async fn file_type(&self) -> io::Result<FileType> {
let std = self.0.clone();
#[cfg(not(any(
target_os = "solaris",
target_os = "illumos",
target_os = "haiku",
target_os = "vxworks"
)))]
if let Some(file_type) = self.file_type {
return Ok(file_type);
}
let std = self.std.clone();
asyncify(move || std.file_type()).await
}
/// Returns a reference to the underlying `std::fs::DirEntry`.
#[cfg(unix)]
pub(super) fn as_inner(&self) -> &std::fs::DirEntry {
&self.0
&self.std
}
}

View file

@ -1,15 +1,22 @@
use std::future::Future;
cfg_rt! {
#[track_caller]
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
let mut e = crate::runtime::enter::enter(false);
let mut e = crate::runtime::context::try_enter_blocking_region().expect(
"Cannot block the current thread from within a runtime. This \
happens because a function attempted to block the current \
thread while the thread is being used to drive asynchronous \
tasks."
);
e.block_on(f).unwrap()
}
}
cfg_not_rt! {
#[track_caller]
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
let mut park = crate::park::thread::CachedParkThread::new();
let mut park = crate::runtime::park::CachedParkThread::new();
park.block_on(f).unwrap()
}
}

View file

@ -7,13 +7,23 @@ use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
// This struct is intentionally `!Unpin` when `F` is `!Unpin`. This is to
// mitigate the issue where rust puts noalias on mutable references to the
// `PollFn` type if it is `Unpin`. If the closure has ownership of a future,
// then this "leaks" and the future is affected by noalias too, which we don't
// want.
//
// See this thread for more information:
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
//
// The fact that `PollFn` is not `Unpin` when it shouldn't be is tested in
// `tests/async_send_sync.rs`.
/// Future for the [`poll_fn`] function.
pub struct PollFn<F> {
f: F,
}
impl<F> Unpin for PollFn<F> {}
/// Creates a new future wrapping around a function returning [`Poll`].
pub fn poll_fn<T, F>(f: F) -> PollFn<F>
where
@ -34,7 +44,17 @@ where
{
type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
(self.f)(cx)
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
// Safety: We never construct a `Pin<&mut F>` anywhere, so accessing `f`
// mutably in an unpinned way is sound.
//
// This use of unsafe cannot be replaced with the pin-project macro
// because:
// * If we put `#[pin]` on the field, then it gives us a `Pin<&mut F>`,
// which we can't use to call the closure.
// * If we don't put `#[pin]` on the field, then it makes `PollFn` be
// unconditionally `Unpin`, which we also don't want.
let me = unsafe { Pin::into_inner_unchecked(self) };
(me.f)(cx)
}
}

View file

@ -1,4 +1,6 @@
use crate::io::driver::{Handle, Interest, ReadyEvent, Registration};
use crate::io::Interest;
use crate::runtime::io::{ReadyEvent, Registration};
use crate::runtime::scheduler;
use mio::unix::SourceFd;
use std::io;
@ -167,12 +169,18 @@ pub struct AsyncFdReadyMutGuard<'a, T: AsRawFd> {
const ALL_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE);
impl<T: AsRawFd> AsyncFd<T> {
#[inline]
/// Creates an AsyncFd backed by (and taking ownership of) an object
/// implementing [`AsRawFd`]. The backing file descriptor is cached at the
/// time of creation.
///
/// This method must be called in the context of a tokio runtime.
///
/// # Panics
///
/// This function panics if there is no current reactor set, or if the `rt`
/// feature flag is not enabled.
#[inline]
#[track_caller]
pub fn new(inner: T) -> io::Result<Self>
where
T: AsRawFd,
@ -180,19 +188,26 @@ impl<T: AsRawFd> AsyncFd<T> {
Self::with_interest(inner, ALL_INTEREST)
}
#[inline]
/// Creates new instance as `new` with additional ability to customize interest,
/// allowing to specify whether file descriptor will be polled for read, write or both.
///
/// # Panics
///
/// This function panics if there is no current reactor set, or if the `rt`
/// feature flag is not enabled.
#[inline]
#[track_caller]
pub fn with_interest(inner: T, interest: Interest) -> io::Result<Self>
where
T: AsRawFd,
{
Self::new_with_handle_and_interest(inner, Handle::current(), interest)
Self::new_with_handle_and_interest(inner, scheduler::Handle::current(), interest)
}
#[track_caller]
pub(crate) fn new_with_handle_and_interest(
inner: T,
handle: Handle,
handle: scheduler::Handle,
interest: Interest,
) -> io::Result<Self> {
let fd = inner.as_raw_fd();

View file

@ -34,8 +34,9 @@ enum State<T> {
Busy(sys::Blocking<(io::Result<usize>, Buf, T)>),
}
cfg_io_std! {
cfg_io_blocking! {
impl<T> Blocking<T> {
#[cfg_attr(feature = "fs", allow(dead_code))]
pub(crate) fn new(inner: T) -> Blocking<T> {
Blocking {
inner: Some(inner),

View file

@ -1,6 +1,8 @@
//! Use POSIX AIO futures with Tokio.
use crate::io::driver::{Handle, Interest, ReadyEvent, Registration};
use crate::io::interest::Interest;
use crate::runtime::io::{ReadyEvent, Registration};
use crate::runtime::scheduler;
use mio::event::Source;
use mio::Registry;
use mio::Token;
@ -117,7 +119,7 @@ impl<E: AioSource> Aio<E> {
fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
let mut io = MioSource(io);
let handle = Handle::current();
let handle = scheduler::Handle::current();
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
Ok(Self { io, registration })
}

View file

@ -1,6 +1,6 @@
#![cfg_attr(not(feature = "net"), allow(dead_code, unreachable_pub))]
use crate::io::driver::Ready;
use crate::io::ready::Ready;
use std::fmt;
use std::ops;
@ -100,7 +100,7 @@ impl Interest {
self.0
}
pub(super) fn mask(self) -> Ready {
pub(crate) fn mask(self) -> Ready {
match self {
Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED,
Interest::WRITABLE => Ready::WRITABLE | Ready::WRITE_CLOSED,

View file

@ -1,5 +1,3 @@
#![cfg_attr(loom, allow(dead_code, unreachable_pub))]
//! Traits, helpers, and type definitions for asynchronous I/O functionality.
//!
//! This module is the asynchronous version of `std::io`. Primarily, it
@ -180,6 +178,12 @@
//! [`Sink`]: https://docs.rs/futures/0.3/futures/sink/trait.Sink.html
//! [`Stream`]: https://docs.rs/futures/0.3/futures/stream/trait.Stream.html
//! [`Write`]: std::io::Write
#![cfg_attr(
not(all(feature = "rt", feature = "net")),
allow(dead_code, unused_imports)
)]
cfg_io_blocking! {
pub(crate) mod blocking;
}
@ -205,15 +209,19 @@ pub use self::read_buf::ReadBuf;
pub use std::io::{Error, ErrorKind, Result, SeekFrom};
cfg_io_driver_impl! {
pub(crate) mod driver;
pub(crate) mod interest;
pub(crate) mod ready;
cfg_net! {
pub use driver::{Interest, Ready};
pub use interest::Interest;
pub use ready::Ready;
}
#[cfg_attr(tokio_wasi, allow(unused_imports))]
mod poll_evented;
#[cfg(not(loom))]
#[cfg_attr(tokio_wasi, allow(unused_imports))]
pub(crate) use poll_evented::PollEvented;
}

View file

@ -1,4 +1,6 @@
use crate::io::driver::{Handle, Interest, Registration};
use crate::io::interest::Interest;
use crate::runtime::io::Registration;
use crate::runtime::scheduler;
use mio::event::Source;
use std::fmt;
@ -11,7 +13,7 @@ cfg_io_driver! {
/// [`std::io::Write`] traits with the reactor that drives it.
///
/// `PollEvented` uses [`Registration`] internally to take a type that
/// implements [`mio::event::Source`] as well as [`std::io::Read`] and or
/// implements [`mio::event::Source`] as well as [`std::io::Read`] and/or
/// [`std::io::Write`] and associate it with a reactor that will drive it.
///
/// Once the [`mio::event::Source`] type is wrapped by `PollEvented`, it can be
@ -41,12 +43,12 @@ cfg_io_driver! {
/// [`poll_read_ready`] again will also indicate read readiness.
///
/// When the operation is attempted and is unable to succeed due to the I/O
/// resource not being ready, the caller must call `clear_readiness`.
/// resource not being ready, the caller must call [`clear_readiness`].
/// This clears the readiness state until a new readiness event is received.
///
/// This allows the caller to implement additional functions. For example,
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
/// `clear_read_ready`.
/// [`clear_readiness`].
///
/// ## Platform-specific events
///
@ -57,6 +59,7 @@ cfg_io_driver! {
/// [`AsyncRead`]: crate::io::AsyncRead
/// [`AsyncWrite`]: crate::io::AsyncWrite
/// [`TcpListener`]: crate::net::TcpListener
/// [`clear_readiness`]: Registration::clear_readiness
/// [`poll_read_ready`]: Registration::poll_read_ready
/// [`poll_write_ready`]: Registration::poll_write_ready
pub(crate) struct PollEvented<E: Source> {
@ -77,6 +80,7 @@ impl<E: Source> PollEvented<E> {
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
#[cfg_attr(feature = "signal", allow(unused))]
pub(crate) fn new(io: E) -> io::Result<Self> {
PollEvented::new_with_interest(io, Interest::READABLE | Interest::WRITABLE)
@ -97,15 +101,17 @@ impl<E: Source> PollEvented<E> {
/// a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter)
/// function.
#[track_caller]
#[cfg_attr(feature = "signal", allow(unused))]
pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
Self::new_with_interest_and_handle(io, interest, Handle::current())
Self::new_with_interest_and_handle(io, interest, scheduler::Handle::current())
}
#[track_caller]
pub(crate) fn new_with_interest_and_handle(
mut io: E,
interest: Interest,
handle: Handle,
handle: scheduler::Handle,
) -> io::Result<Self> {
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
Ok(Self {
@ -115,11 +121,7 @@ impl<E: Source> PollEvented<E> {
}
/// Returns a reference to the registration.
#[cfg(any(
feature = "net",
all(unix, feature = "process"),
all(unix, feature = "signal"),
))]
#[cfg(any(feature = "net"))]
pub(crate) fn registration(&self) -> &Registration {
&self.registration
}
@ -134,7 +136,7 @@ impl<E: Source> PollEvented<E> {
}
feature! {
#![any(feature = "net", feature = "process")]
#![any(feature = "net", all(unix, feature = "process"))]
use crate::io::ReadBuf;
use std::task::{Context, Poll};
@ -151,16 +153,32 @@ feature! {
{
use std::io::Read;
let n = ready!(self.registration.poll_read_io(cx, || {
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]);
self.io.as_ref().unwrap().read(b)
}))?;
loop {
let evt = ready!(self.registration.poll_read_ready(cx))?;
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
// buffer.
buf.assume_init(n);
buf.advance(n);
Poll::Ready(Ok(()))
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]);
let len = b.len();
match self.io.as_ref().unwrap().read(b) {
Ok(n) => {
// if we read a partially full buffer, this is sufficient on unix to show
// that the socket buffer has been drained
if n > 0 && (!cfg!(windows) && n < len) {
self.registration.clear_readiness(evt);
}
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the
// buffer.
buf.assume_init(n);
buf.advance(n);
return Poll::Ready(Ok(()));
},
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.registration.clear_readiness(evt);
}
Err(e) => return Poll::Ready(Err(e)),
}
}
}
pub(crate) fn poll_write<'a>(&'a self, cx: &mut Context<'_>, buf: &[u8]) -> Poll<io::Result<usize>>
@ -168,10 +186,29 @@ feature! {
&'a E: io::Write + 'a,
{
use std::io::Write;
self.registration.poll_write_io(cx, || self.io.as_ref().unwrap().write(buf))
loop {
let evt = ready!(self.registration.poll_write_ready(cx))?;
match self.io.as_ref().unwrap().write(buf) {
Ok(n) => {
// if we write only part of our buffer, this is sufficient on unix to show
// that the socket buffer is full
if n > 0 && (!cfg!(windows) && n < buf.len()) {
self.registration.clear_readiness(evt);
}
return Poll::Ready(Ok(n));
},
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.registration.clear_readiness(evt);
}
Err(e) => return Poll::Ready(Err(e)),
}
}
}
#[cfg(feature = "net")]
#[cfg(any(feature = "net", feature = "process"))]
pub(crate) fn poll_write_vectored<'a>(
&'a self,
cx: &mut Context<'_>,

View file

@ -152,6 +152,7 @@ impl<'a> ReadBuf<'a> {
///
/// Panics if `self.remaining()` is less than `n`.
#[inline]
#[track_caller]
pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] {
assert!(self.remaining() >= n, "n overflows remaining");
@ -195,6 +196,7 @@ impl<'a> ReadBuf<'a> {
///
/// Panics if the filled region of the buffer would become larger than the initialized region.
#[inline]
#[track_caller]
pub fn advance(&mut self, n: usize) {
let new = self.filled.checked_add(n).expect("filled overflow");
self.set_filled(new);
@ -211,6 +213,7 @@ impl<'a> ReadBuf<'a> {
///
/// Panics if the filled region of the buffer would become larger than the initialized region.
#[inline]
#[track_caller]
pub fn set_filled(&mut self, n: usize) {
assert!(
n <= self.initialized,
@ -241,6 +244,7 @@ impl<'a> ReadBuf<'a> {
///
/// Panics if `self.remaining()` is less than `buf.len()`.
#[inline]
#[track_caller]
pub fn put_slice(&mut self, buf: &[u8]) {
assert!(
self.remaining() >= buf.len(),

View file

@ -12,7 +12,7 @@ const WRITE_CLOSED: usize = 0b0_1000;
///
/// `Ready` tracks which operation an I/O resource is ready to perform.
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
#[derive(Clone, Copy, PartialEq, PartialOrd)]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ready(usize);
impl Ready {

View file

@ -74,6 +74,7 @@ impl<T> ReadHalf<T> {
/// same `split` operation this method will panic.
/// This can be checked ahead of time by comparing the stream ID
/// of the two halves.
#[track_caller]
pub fn unsplit(self, wr: WriteHalf<T>) -> T {
if self.is_pair_of(&wr) {
drop(wr);

View file

@ -69,7 +69,7 @@ cfg_io_util! {
/// Creates a future which will rewind to the beginning of the stream.
///
/// This is convenience method, equivalent to to `self.seek(SeekFrom::Start(0))`.
/// This is convenience method, equivalent to `self.seek(SeekFrom::Start(0))`.
fn rewind(&mut self) -> Seek<'_, Self>
where
Self: Unpin,

View file

@ -406,7 +406,7 @@ cfg_io_util! {
/// ```
fn write_u8(&mut self, n: u8) -> WriteU8;
/// Writes an unsigned 8-bit integer to the underlying writer.
/// Writes a signed 8-bit integer to the underlying writer.
///
/// Equivalent to:
///
@ -425,7 +425,7 @@ cfg_io_util! {
///
/// # Examples
///
/// Write unsigned 8 bit integers to a `AsyncWrite`:
/// Write signed 8 bit integers to a `AsyncWrite`:
///
/// ```rust
/// use tokio::io::{self, AsyncWriteExt};
@ -434,10 +434,10 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> {
/// let mut writer = Vec::new();
///
/// writer.write_u8(2).await?;
/// writer.write_u8(5).await?;
/// writer.write_i8(-2).await?;
/// writer.write_i8(126).await?;
///
/// assert_eq!(writer, b"\x02\x05");
/// assert_eq!(writer, b"\xFE\x7E");
/// Ok(())
/// }
/// ```

View file

@ -27,6 +27,51 @@ impl CopyBuffer {
}
}
fn poll_fill_buf<R>(
&mut self,
cx: &mut Context<'_>,
reader: Pin<&mut R>,
) -> Poll<io::Result<()>>
where
R: AsyncRead + ?Sized,
{
let me = &mut *self;
let mut buf = ReadBuf::new(&mut me.buf);
buf.set_filled(me.cap);
let res = reader.poll_read(cx, &mut buf);
if let Poll::Ready(Ok(_)) = res {
let filled_len = buf.filled().len();
me.read_done = me.cap == filled_len;
me.cap = filled_len;
}
res
}
fn poll_write_buf<R, W>(
&mut self,
cx: &mut Context<'_>,
mut reader: Pin<&mut R>,
mut writer: Pin<&mut W>,
) -> Poll<io::Result<usize>>
where
R: AsyncRead + ?Sized,
W: AsyncWrite + ?Sized,
{
let me = &mut *self;
match writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]) {
Poll::Pending => {
// Top up the buffer towards full if we can read a bit more
// data - this should improve the chances of a large write
if !me.read_done && me.cap < me.buf.len() {
ready!(me.poll_fill_buf(cx, reader.as_mut()))?;
}
Poll::Pending
}
res => res,
}
}
pub(super) fn poll_copy<R, W>(
&mut self,
cx: &mut Context<'_>,
@ -41,10 +86,10 @@ impl CopyBuffer {
// If our buffer is empty, then we need to read some data to
// continue.
if self.pos == self.cap && !self.read_done {
let me = &mut *self;
let mut buf = ReadBuf::new(&mut me.buf);
self.pos = 0;
self.cap = 0;
match reader.as_mut().poll_read(cx, &mut buf) {
match self.poll_fill_buf(cx, reader.as_mut()) {
Poll::Ready(Ok(_)) => (),
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
Poll::Pending => {
@ -58,20 +103,11 @@ impl CopyBuffer {
return Poll::Pending;
}
}
let n = buf.filled().len();
if n == 0 {
self.read_done = true;
} else {
self.pos = 0;
self.cap = n;
}
}
// If our buffer has some data, let's write it out!
while self.pos < self.cap {
let me = &mut *self;
let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?;
let i = ready!(self.poll_write_buf(cx, reader.as_mut(), writer.as_mut()))?;
if i == 0 {
return Poll::Ready(Err(io::Error::new(
io::ErrorKind::WriteZero,

View file

@ -77,7 +77,7 @@ impl fmt::Debug for Empty {
cfg_coop! {
fn poll_proceed_and_make_progress(cx: &mut Context<'_>) -> Poll<()> {
let coop = ready!(crate::coop::poll_proceed(cx));
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
coop.made_progress();
Poll::Ready(())
}

View file

@ -233,7 +233,7 @@ impl AsyncRead for Pipe {
cx: &mut task::Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<std::io::Result<()>> {
let coop = ready!(crate::coop::poll_proceed(cx));
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
let ret = self.poll_read_internal(cx, buf);
if ret.is_ready() {
@ -261,7 +261,7 @@ impl AsyncWrite for Pipe {
cx: &mut task::Context<'_>,
buf: &[u8],
) -> Poll<std::io::Result<usize>> {
let coop = ready!(crate::coop::poll_proceed(cx));
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
let ret = self.poll_write_internal(cx, buf);
if ret.is_ready() {

View file

@ -48,7 +48,7 @@ where
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<usize>> {
let me = self.project();
let mut buf = ReadBuf::new(*me.buf);
let mut buf = ReadBuf::new(me.buf);
ready!(Pin::new(me.reader).poll_read(cx, &mut buf))?;
Poll::Ready(Ok(buf.filled().len()))
}

View file

@ -1,7 +1,9 @@
#![allow(
clippy::cognitive_complexity,
clippy::large_enum_variant,
clippy::needless_doctest_main
clippy::module_inception,
clippy::needless_doctest_main,
clippy::declare_interior_mutable_const
)]
#![warn(
missing_debug_implementations,
@ -16,6 +18,7 @@
))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, allow(unused_attributes))]
#![cfg_attr(loom, allow(dead_code, unreachable_pub))]
//! A runtime for writing reliable network applications without compromising speed.
//!
@ -152,7 +155,7 @@
//! provide the functionality you need.
//!
//! Using the runtime requires the "rt" or "rt-multi-thread" feature flags, to
//! enable the basic [single-threaded scheduler][rt] and the [thread-pool
//! enable the current-thread [single-threaded scheduler][rt] and the [multi-thread
//! scheduler][rt-multi-thread], respectively. See the [`runtime` module
//! documentation][rt-features] for details. In addition, the "macros" feature
//! flag enables the `#[tokio::main]` and `#[tokio::test]` attributes.
@ -171,12 +174,15 @@
//! swapping the currently running task on each thread. However, this kind of
//! swapping can only happen at `.await` points, so code that spends a long time
//! without reaching an `.await` will prevent other tasks from running. To
//! combat this, Tokio provides two kinds of threads: Core threads and blocking
//! threads. The core threads are where all asynchronous code runs, and Tokio
//! will by default spawn one for each CPU core. The blocking threads are
//! spawned on demand, can be used to run blocking code that would otherwise
//! block other tasks from running and are kept alive when not used for a certain
//! amount of time which can be configured with [`thread_keep_alive`].
//! combat this, Tokio provides two kinds of threads: Core threads and blocking threads.
//!
//! The core threads are where all asynchronous code runs, and Tokio will by default
//! spawn one for each CPU core. You can use the environment variable `TOKIO_WORKER_THREADS`
//! to override the default value.
//!
//! The blocking threads are spawned on demand, can be used to run blocking code
//! that would otherwise block other tasks from running and are kept alive when
//! not used for a certain amount of time which can be configured with [`thread_keep_alive`].
//! Since it is not possible for Tokio to swap out blocking tasks, like it
//! can do with asynchronous code, the upper limit on the number of blocking
//! threads is very large. These limits can be configured on the [`Builder`].
@ -309,7 +315,7 @@
//! need.
//!
//! - `full`: Enables all features listed below except `test-util` and `tracing`.
//! - `rt`: Enables `tokio::spawn`, the basic (current thread) scheduler,
//! - `rt`: Enables `tokio::spawn`, the current-thread scheduler,
//! and non-scheduler utilities.
//! - `rt-multi-thread`: Enables the heavier, multi-threaded, work-stealing scheduler.
//! - `io-util`: Enables the IO based `Ext` traits.
@ -325,20 +331,15 @@
//! - `signal`: Enables all `tokio::signal` types.
//! - `fs`: Enables `tokio::fs` types.
//! - `test-util`: Enables testing based infrastructure for the Tokio runtime.
//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's
//! synchronization primitives internally. Also, this
//! dependency is necessary to construct some of our primitives
//! in a const context. MSRV may increase according to the
//! _parking_lot_ release in use.
//!
//! _Note: `AsyncRead` and `AsyncWrite` traits do not require any features and are
//! always available._
//!
//! ### Internal features
//!
//! These features do not expose any new API, but influence internal
//! implementation aspects of Tokio, and can pull in additional
//! dependencies.
//!
//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's
//! synchronization primitives internally. MSRV may increase according to the
//! _parking_lot_ release in use.
//!
//! ### Unstable features
//!
//! Some feature flags are only available when specifying the `tokio_unstable` flag:
@ -347,9 +348,12 @@
//!
//! Likewise, some parts of the API are only available with the same flag:
//!
//! - [`task::JoinSet`]
//! - [`task::Builder`]
//!
//! - Some methods on [`task::JoinSet`]
//! - [`runtime::RuntimeMetrics`]
//! - [`runtime::Builder::unhandled_panic`]
//! - [`task::Id`]
//!
//! This flag enables **unstable** features. The public API of these features
//! may break in 1.x releases. To enable these features, the `--cfg
//! tokio_unstable` argument must be passed to `rustc` when compiling. This
@ -379,6 +383,39 @@
//!
//! [unstable features]: https://internals.rust-lang.org/t/feature-request-unstable-opt-in-non-transitive-crate-features/16193#why-not-a-crate-feature-2
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
//!
//! ## WASM support
//!
//! Tokio has some limited support for the WASM platform. Without the
//! `tokio_unstable` flag, the following features are supported:
//!
//! * `sync`
//! * `macros`
//! * `io-util`
//! * `rt`
//! * `time`
//!
//! Enabling any other feature (including `full`) will cause a compilation
//! failure.
//!
//! The `time` module will only work on WASM platforms that have support for
//! timers (e.g. wasm32-wasi). The timing functions will panic if used on a WASM
//! platform that does not support timers.
//!
//! Note also that if the runtime becomes indefinitely idle, it will panic
//! immediately instead of blocking forever. On platforms that don't support
//! time, this means that the runtime can never be idle in any way.
//!
//! ### Unstable WASM support
//!
//! Tokio also has unstable support for some additional WASM features. This
//! requires the use of the `tokio_unstable` flag.
//!
//! Using this flag enables the use of `tokio::net` on the wasm32-wasi target.
//! However, not all methods are available on the networking types as WASI
//! currently does not support the creation of new sockets from within WASM.
//! Because of this, sockets must currently be created via the `FromRawFd`
//! trait.
// Test that pointer width is compatible. This asserts that e.g. usize is at
// least 32 bits, which a lot of components in Tokio currently assumes.
@ -393,6 +430,37 @@ compile_error! {
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
}
// Ensure that our build script has correctly set cfg flags for wasm.
//
// Each condition is written all(a, not(b)). This should be read as
// "if a, then we must also have b".
#[cfg(any(
all(target_arch = "wasm32", not(tokio_wasm)),
all(target_arch = "wasm64", not(tokio_wasm)),
all(target_family = "wasm", not(tokio_wasm)),
all(target_os = "wasi", not(tokio_wasm)),
all(target_os = "wasi", not(tokio_wasi)),
all(target_os = "wasi", tokio_wasm_not_wasi),
all(tokio_wasm, not(any(target_arch = "wasm32", target_arch = "wasm64"))),
all(tokio_wasm_not_wasi, not(tokio_wasm)),
all(tokio_wasi, not(tokio_wasm))
))]
compile_error!("Tokio's build script has incorrectly detected wasm.");
#[cfg(all(
not(tokio_unstable),
tokio_wasm,
any(
feature = "fs",
feature = "io-std",
feature = "net",
feature = "process",
feature = "rt-multi-thread",
feature = "signal"
)
))]
compile_error!("Only features sync,macros,io-util,rt,time are supported on wasm.");
// Includes re-exports used by macros.
//
// This module is not intended to be part of the public API. In general, any
@ -411,20 +479,25 @@ pub mod io;
pub mod net;
mod loom;
mod park;
cfg_process! {
pub mod process;
}
#[cfg(any(feature = "net", feature = "fs", feature = "io-std"))]
#[cfg(any(
feature = "fs",
feature = "io-std",
feature = "net",
all(windows, feature = "process"),
))]
mod blocking;
cfg_rt! {
pub mod runtime;
}
pub(crate) mod coop;
cfg_not_rt! {
pub(crate) mod runtime;
}
cfg_signal! {
pub mod signal;
@ -508,14 +581,6 @@ pub(crate) use self::doc::os;
#[allow(unused)]
pub(crate) use std::os;
#[cfg(docsrs)]
#[allow(unused)]
pub(crate) use self::doc::winapi;
#[cfg(all(not(docsrs), windows, feature = "net"))]
#[allow(unused)]
pub(crate) use winapi;
cfg_macros! {
/// Implementation detail of the `select!` macro. This macro is **not**
/// intended to be used as part of the public API and is permitted to

View file

@ -25,6 +25,13 @@ pub(crate) mod sync {
}
}
pub(crate) use loom::sync::*;
pub(crate) mod atomic {
pub(crate) use loom::sync::atomic::*;
// TODO: implement a loom version
pub(crate) type StaticAtomicU64 = std::sync::atomic::AtomicU64;
}
}
pub(crate) mod rand {
@ -38,3 +45,8 @@ pub(crate) mod sys {
2
}
}
pub(crate) mod thread {
pub use loom::lazy_static::AccessError;
pub use loom::thread::*;
}

View file

@ -1,34 +0,0 @@
use std::fmt;
use std::ops::{Deref, DerefMut};
/// `AtomicPtr` providing an additional `load_unsync` function.
pub(crate) struct AtomicPtr<T> {
inner: std::sync::atomic::AtomicPtr<T>,
}
impl<T> AtomicPtr<T> {
pub(crate) fn new(ptr: *mut T) -> AtomicPtr<T> {
let inner = std::sync::atomic::AtomicPtr::new(ptr);
AtomicPtr { inner }
}
}
impl<T> Deref for AtomicPtr<T> {
type Target = std::sync::atomic::AtomicPtr<T>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for AtomicPtr<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T> fmt::Debug for AtomicPtr<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.deref().fmt(fmt)
}
}

View file

@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
use std::fmt;
use std::ops::Deref;
/// `AtomicU16` providing an additional `load_unsync` function.
/// `AtomicU16` providing an additional `unsync_load` function.
pub(crate) struct AtomicU16 {
inner: UnsafeCell<std::sync::atomic::AtomicU16>,
}
@ -23,7 +23,7 @@ impl AtomicU16 {
/// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> u16 {
*(*self.inner.get()).get_mut()
core::ptr::read(self.inner.get() as *const u16)
}
}

View file

@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
use std::fmt;
use std::ops::Deref;
/// `AtomicU32` providing an additional `load_unsync` function.
/// `AtomicU32` providing an additional `unsync_load` function.
pub(crate) struct AtomicU32 {
inner: UnsafeCell<std::sync::atomic::AtomicU32>,
}
@ -15,6 +15,16 @@ impl AtomicU32 {
let inner = UnsafeCell::new(std::sync::atomic::AtomicU32::new(val));
AtomicU32 { inner }
}
/// Performs an unsynchronized load.
///
/// # Safety
///
/// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> u32 {
core::ptr::read(self.inner.get() as *const u32)
}
}
impl Deref for AtomicU32 {

View file

@ -7,80 +7,13 @@
// `#[cfg(target_has_atomic = "64")]`.
// Refs: https://github.com/rust-lang/rust/tree/master/src/librustc_target
cfg_has_atomic_u64! {
pub(crate) use std::sync::atomic::AtomicU64;
#[path = "atomic_u64_native.rs"]
mod imp;
}
cfg_not_has_atomic_u64! {
use crate::loom::sync::Mutex;
use std::sync::atomic::Ordering;
#[derive(Debug)]
pub(crate) struct AtomicU64 {
inner: Mutex<u64>,
}
impl AtomicU64 {
pub(crate) fn new(val: u64) -> Self {
Self {
inner: Mutex::new(val),
}
}
pub(crate) fn load(&self, _: Ordering) -> u64 {
*self.inner.lock()
}
pub(crate) fn store(&self, val: u64, _: Ordering) {
*self.inner.lock() = val;
}
pub(crate) fn fetch_add(&self, val: u64, _: Ordering) -> u64 {
let mut lock = self.inner.lock();
let prev = *lock;
*lock = prev + val;
prev
}
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
let mut lock = self.inner.lock();
let prev = *lock;
*lock = prev | val;
prev
}
pub(crate) fn compare_exchange(
&self,
current: u64,
new: u64,
_success: Ordering,
_failure: Ordering,
) -> Result<u64, u64> {
let mut lock = self.inner.lock();
if *lock == current {
*lock = new;
Ok(current)
} else {
Err(*lock)
}
}
pub(crate) fn compare_exchange_weak(
&self,
current: u64,
new: u64,
success: Ordering,
failure: Ordering,
) -> Result<u64, u64> {
self.compare_exchange(current, new, success, failure)
}
}
impl Default for AtomicU64 {
fn default() -> AtomicU64 {
Self {
inner: Mutex::new(0),
}
}
}
#[path = "atomic_u64_as_mutex.rs"]
mod imp;
}
pub(crate) use imp::{AtomicU64, StaticAtomicU64};

View file

@ -0,0 +1,76 @@
use crate::loom::sync::Mutex;
use std::sync::atomic::Ordering;
cfg_has_const_mutex_new! {
#[path = "atomic_u64_static_const_new.rs"]
mod static_macro;
}
cfg_not_has_const_mutex_new! {
#[path = "atomic_u64_static_once_cell.rs"]
mod static_macro;
}
pub(crate) use static_macro::StaticAtomicU64;
#[derive(Debug)]
pub(crate) struct AtomicU64 {
inner: Mutex<u64>,
}
impl AtomicU64 {
pub(crate) fn load(&self, _: Ordering) -> u64 {
*self.inner.lock()
}
pub(crate) fn store(&self, val: u64, _: Ordering) {
*self.inner.lock() = val;
}
pub(crate) fn fetch_add(&self, val: u64, _: Ordering) -> u64 {
let mut lock = self.inner.lock();
let prev = *lock;
*lock = prev + val;
prev
}
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
let mut lock = self.inner.lock();
let prev = *lock;
*lock = prev | val;
prev
}
pub(crate) fn compare_exchange(
&self,
current: u64,
new: u64,
_success: Ordering,
_failure: Ordering,
) -> Result<u64, u64> {
let mut lock = self.inner.lock();
if *lock == current {
*lock = new;
Ok(current)
} else {
Err(*lock)
}
}
pub(crate) fn compare_exchange_weak(
&self,
current: u64,
new: u64,
success: Ordering,
failure: Ordering,
) -> Result<u64, u64> {
self.compare_exchange(current, new, success, failure)
}
}
impl Default for AtomicU64 {
fn default() -> AtomicU64 {
AtomicU64::new(u64::default())
}
}

View file

@ -0,0 +1,4 @@
pub(crate) use std::sync::atomic::{AtomicU64, Ordering};
/// Alias `AtomicU64` to `StaticAtomicU64`
pub(crate) type StaticAtomicU64 = AtomicU64;

View file

@ -0,0 +1,12 @@
use super::AtomicU64;
use crate::loom::sync::Mutex;
pub(crate) type StaticAtomicU64 = AtomicU64;
impl AtomicU64 {
pub(crate) const fn new(val: u64) -> Self {
Self {
inner: Mutex::const_new(val),
}
}
}

View file

@ -0,0 +1,57 @@
use super::AtomicU64;
use crate::loom::sync::{atomic::Ordering, Mutex};
use crate::util::once_cell::OnceCell;
pub(crate) struct StaticAtomicU64 {
init: u64,
cell: OnceCell<Mutex<u64>>,
}
impl AtomicU64 {
pub(crate) fn new(val: u64) -> Self {
Self {
inner: Mutex::new(val),
}
}
}
impl StaticAtomicU64 {
pub(crate) const fn new(val: u64) -> StaticAtomicU64 {
StaticAtomicU64 {
init: val,
cell: OnceCell::new(),
}
}
pub(crate) fn load(&self, order: Ordering) -> u64 {
*self.inner().lock()
}
pub(crate) fn fetch_add(&self, val: u64, order: Ordering) -> u64 {
let mut lock = self.inner().lock();
let prev = *lock;
*lock = prev + val;
prev
}
pub(crate) fn compare_exchange_weak(
&self,
current: u64,
new: u64,
_success: Ordering,
_failure: Ordering,
) -> Result<u64, u64> {
let mut lock = self.inner().lock();
if *lock == current {
*lock = new;
Ok(current)
} else {
Err(*lock)
}
}
fn inner(&self) -> &Mutex<u64> {
self.cell.get(|| Mutex::new(self.init))
}
}

View file

@ -1,34 +0,0 @@
use std::cell::UnsafeCell;
use std::fmt;
use std::ops::Deref;
/// `AtomicU8` providing an additional `load_unsync` function.
pub(crate) struct AtomicU8 {
inner: UnsafeCell<std::sync::atomic::AtomicU8>,
}
unsafe impl Send for AtomicU8 {}
unsafe impl Sync for AtomicU8 {}
impl AtomicU8 {
pub(crate) const fn new(val: u8) -> AtomicU8 {
let inner = UnsafeCell::new(std::sync::atomic::AtomicU8::new(val));
AtomicU8 { inner }
}
}
impl Deref for AtomicU8 {
type Target = std::sync::atomic::AtomicU8;
fn deref(&self) -> &Self::Target {
// safety: it is always safe to access `&self` fns on the inner value as
// we never perform unsafe mutations.
unsafe { &*self.inner.get() }
}
}
impl fmt::Debug for AtomicU8 {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.deref().fmt(fmt)
}
}

View file

@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
use std::fmt;
use std::ops;
/// `AtomicUsize` providing an additional `load_unsync` function.
/// `AtomicUsize` providing an additional `unsync_load` function.
pub(crate) struct AtomicUsize {
inner: UnsafeCell<std::sync::atomic::AtomicUsize>,
}
@ -23,7 +23,7 @@ impl AtomicUsize {
/// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> usize {
*(*self.inner.get()).get_mut()
core::ptr::read(self.inner.get() as *const usize)
}
pub(crate) fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R {

View file

@ -1,10 +1,8 @@
#![cfg_attr(any(not(feature = "full"), loom), allow(unused_imports, dead_code))]
mod atomic_ptr;
mod atomic_u16;
mod atomic_u32;
mod atomic_u64;
mod atomic_u8;
mod atomic_usize;
mod mutex;
#[cfg(feature = "parking_lot")]
@ -71,21 +69,39 @@ pub(crate) mod sync {
pub(crate) use crate::loom::std::mutex::Mutex;
pub(crate) mod atomic {
pub(crate) use crate::loom::std::atomic_ptr::AtomicPtr;
pub(crate) use crate::loom::std::atomic_u16::AtomicU16;
pub(crate) use crate::loom::std::atomic_u32::AtomicU32;
pub(crate) use crate::loom::std::atomic_u64::AtomicU64;
pub(crate) use crate::loom::std::atomic_u8::AtomicU8;
pub(crate) use crate::loom::std::atomic_u64::{AtomicU64, StaticAtomicU64};
pub(crate) use crate::loom::std::atomic_usize::AtomicUsize;
pub(crate) use std::sync::atomic::{fence, AtomicBool, Ordering};
pub(crate) use std::sync::atomic::{fence, AtomicBool, AtomicPtr, AtomicU8, Ordering};
}
}
pub(crate) mod sys {
#[cfg(feature = "rt-multi-thread")]
pub(crate) fn num_cpus() -> usize {
usize::max(1, num_cpus::get())
const ENV_WORKER_THREADS: &str = "TOKIO_WORKER_THREADS";
match std::env::var(ENV_WORKER_THREADS) {
Ok(s) => {
let n = s.parse().unwrap_or_else(|e| {
panic!(
"\"{}\" must be usize, error: {}, value: {}",
ENV_WORKER_THREADS, e, s
)
});
assert!(n > 0, "\"{}\" cannot be set to 0", ENV_WORKER_THREADS);
n
}
Err(std::env::VarError::NotPresent) => usize::max(1, num_cpus::get()),
Err(std::env::VarError::NotUnicode(e)) => {
panic!(
"\"{}\" must be valid unicode, error: {:?}",
ENV_WORKER_THREADS, e
)
}
}
}
#[cfg(not(feature = "rt-multi-thread"))]
@ -102,7 +118,7 @@ pub(crate) mod thread {
#[allow(unused_imports)]
pub(crate) use std::thread::{
current, panicking, park, park_timeout, sleep, spawn, Builder, JoinHandle, LocalKey,
Result, Thread, ThreadId,
current, panicking, park, park_timeout, sleep, spawn, AccessError, Builder, JoinHandle,
LocalKey, Result, Thread, ThreadId,
};
}

View file

@ -12,6 +12,12 @@ impl<T> Mutex<T> {
Mutex(sync::Mutex::new(t))
}
#[inline]
#[cfg(not(tokio_no_const_mutex_new))]
pub(crate) const fn const_new(t: T) -> Mutex<T> {
Mutex(sync::Mutex::new(t))
}
#[inline]
pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
match self.0.lock() {

View file

@ -52,7 +52,7 @@ impl<T> Mutex<T> {
}
#[inline]
#[cfg(all(feature = "parking_lot", not(all(loom, test)),))]
#[cfg(all(feature = "parking_lot", not(all(loom, test))))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))]
pub(crate) const fn const_new(t: T) -> Mutex<T> {
Mutex(PhantomData, parking_lot::const_mutex(t))

View file

@ -0,0 +1,53 @@
//! This module defines a macro that lets you go from a raw pointer to a struct
//! to a raw pointer to a field of the struct.
#[cfg(not(tokio_no_addr_of))]
macro_rules! generate_addr_of_methods {
(
impl<$($gen:ident)*> $struct_name:ty {$(
$(#[$attrs:meta])*
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
&self$(.$field_name:tt)+
}
)*}
) => {
impl<$($gen)*> $struct_name {$(
$(#[$attrs])*
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
let me = me.as_ptr();
let field = ::std::ptr::addr_of_mut!((*me) $(.$field_name)+ );
::core::ptr::NonNull::new_unchecked(field)
}
)*}
};
}
// The `addr_of_mut!` macro is only available for MSRV at least 1.51.0. This
// version of the macro uses a workaround for older versions of rustc.
#[cfg(tokio_no_addr_of)]
macro_rules! generate_addr_of_methods {
(
impl<$($gen:ident)*> $struct_name:ty {$(
$(#[$attrs:meta])*
$vis:vis unsafe fn $fn_name:ident(self: NonNull<Self>) -> NonNull<$field_type:ty> {
&self$(.$field_name:tt)+
}
)*}
) => {
impl<$($gen)*> $struct_name {$(
$(#[$attrs])*
$vis unsafe fn $fn_name(me: ::core::ptr::NonNull<Self>) -> ::core::ptr::NonNull<$field_type> {
let me = me.as_ptr();
let me_u8 = me as *mut u8;
let field_offset = {
let me_ref = &*me;
let field_ref_u8 = (&me_ref $(.$field_name)+ ) as *const $field_type as *const u8;
field_ref_u8.offset_from(me_u8)
};
::core::ptr::NonNull::new_unchecked(me_u8.offset(field_offset).cast())
}
)*}
};
}

View file

@ -61,6 +61,7 @@ macro_rules! cfg_fs {
($($item:item)*) => {
$(
#[cfg(feature = "fs")]
#[cfg(not(tokio_wasi))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
$item
)*
@ -69,7 +70,11 @@ macro_rules! cfg_fs {
macro_rules! cfg_io_blocking {
($($item:item)*) => {
$( #[cfg(any(feature = "io-std", feature = "fs"))] $item )*
$( #[cfg(any(
feature = "io-std",
feature = "fs",
all(windows, feature = "process"),
))] $item )*
}
}
@ -78,12 +83,12 @@ macro_rules! cfg_io_driver {
$(
#[cfg(any(
feature = "net",
feature = "process",
all(unix, feature = "process"),
all(unix, feature = "signal"),
))]
#[cfg_attr(docsrs, doc(cfg(any(
feature = "net",
feature = "process",
all(unix, feature = "process"),
all(unix, feature = "signal"),
))))]
$item
@ -96,7 +101,7 @@ macro_rules! cfg_io_driver_impl {
$(
#[cfg(any(
feature = "net",
feature = "process",
all(unix, feature = "process"),
all(unix, feature = "signal"),
))]
$item
@ -109,7 +114,7 @@ macro_rules! cfg_not_io_driver {
$(
#[cfg(not(any(
feature = "net",
feature = "process",
all(unix, feature = "process"),
all(unix, feature = "signal"),
)))]
$item
@ -247,6 +252,7 @@ macro_rules! cfg_process {
#[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
#[cfg(not(loom))]
#[cfg(not(tokio_wasi))]
$item
)*
}
@ -275,6 +281,7 @@ macro_rules! cfg_signal {
#[cfg(feature = "signal")]
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
#[cfg(not(loom))]
#[cfg(not(tokio_wasi))]
$item
)*
}
@ -290,6 +297,13 @@ macro_rules! cfg_signal_internal {
}
}
macro_rules! cfg_signal_internal_and_unix {
($($item:item)*) => {
#[cfg(unix)]
cfg_signal_internal! { $($item)* }
}
}
macro_rules! cfg_not_signal_internal {
($($item:item)*) => {
$(
@ -334,7 +348,7 @@ macro_rules! cfg_not_rt {
macro_rules! cfg_rt_multi_thread {
($($item:item)*) => {
$(
#[cfg(feature = "rt-multi-thread")]
#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))]
#[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
$item
)*
@ -447,12 +461,14 @@ macro_rules! cfg_not_coop {
macro_rules! cfg_has_atomic_u64 {
($($item:item)*) => {
$(
#[cfg(not(any(
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "riscv32"
)))]
#[cfg_attr(
not(tokio_no_target_has_atomic),
cfg(all(target_has_atomic = "64", not(tokio_no_atomic_u64))
))]
#[cfg_attr(
tokio_no_target_has_atomic,
cfg(not(tokio_no_atomic_u64))
)]
$item
)*
}
@ -461,12 +477,62 @@ macro_rules! cfg_has_atomic_u64 {
macro_rules! cfg_not_has_atomic_u64 {
($($item:item)*) => {
$(
#[cfg(any(
target_arch = "arm",
target_arch = "mips",
target_arch = "powerpc",
target_arch = "riscv32"
))]
#[cfg_attr(
not(tokio_no_target_has_atomic),
cfg(any(not(target_has_atomic = "64"), tokio_no_atomic_u64)
))]
#[cfg_attr(
tokio_no_target_has_atomic,
cfg(tokio_no_atomic_u64)
)]
$item
)*
}
}
macro_rules! cfg_has_const_mutex_new {
($($item:item)*) => {
$(
#[cfg(all(
not(all(loom, test)),
any(
feature = "parking_lot",
not(tokio_no_const_mutex_new)
)
))]
$item
)*
}
}
macro_rules! cfg_not_has_const_mutex_new {
($($item:item)*) => {
$(
#[cfg(not(all(
not(all(loom, test)),
any(
feature = "parking_lot",
not(tokio_no_const_mutex_new)
)
)))]
$item
)*
}
}
macro_rules! cfg_not_wasi {
($($item:item)*) => {
$(
#[cfg(not(tokio_wasi))]
$item
)*
}
}
macro_rules! cfg_is_wasm_not_wasi {
($($item:item)*) => {
$(
#[cfg(tokio_wasm_not_wasi)]
$item
)*
}

View file

@ -72,8 +72,18 @@ macro_rules! join {
// Safety: nothing must be moved out of `futures`. This is to satisfy
// the requirement of `Pin::new_unchecked` called below.
//
// We can't use the `pin!` macro for this because `futures` is a tuple
// and the standard library provides no way to pin-project to the fields
// of a tuple.
let mut futures = ( $( maybe_done($e), )* );
// This assignment makes sure that the `poll_fn` closure only has a
// reference to the futures, instead of taking ownership of them. This
// mitigates the issue described in
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
let mut futures = &mut futures;
// Each time the future created by poll_fn is polled, a different future will be polled first
// to ensure every future passed to join! gets a chance to make progress even if
// one of the futures consumes the whole budget.
@ -106,7 +116,7 @@ macro_rules! join {
to_run -= 1;
// Extract the future for this branch from the tuple.
let ( $($skip,)* fut, .. ) = &mut futures;
let ( $($skip,)* fut, .. ) = &mut *futures;
// Safety: future is stored on the stack above
// and never moved.

View file

@ -15,6 +15,9 @@ mod ready;
#[macro_use]
mod thread_local;
#[macro_use]
mod addr_of;
cfg_trace! {
#[macro_use]
mod trace;

View file

@ -10,7 +10,7 @@ macro_rules! scoped_thread_local {
$vis static $name: $crate::macros::scoped_tls::ScopedKey<$ty>
= $crate::macros::scoped_tls::ScopedKey {
inner: {
thread_local!(static FOO: ::std::cell::Cell<*const ()> = {
tokio_thread_local!(static FOO: ::std::cell::Cell<*const ()> = const {
std::cell::Cell::new(::std::ptr::null())
});
&FOO

View file

@ -460,8 +460,18 @@ macro_rules! select {
let mut output = {
// Safety: Nothing must be moved out of `futures`. This is to
// satisfy the requirement of `Pin::new_unchecked` called below.
//
// We can't use the `pin!` macro for this because `futures` is a
// tuple and the standard library provides no way to pin-project to
// the fields of a tuple.
let mut futures = ( $( $fut , )+ );
// This assignment makes sure that the `poll_fn` closure only has a
// reference to the futures, instead of taking ownership of them.
// This mitigates the issue described in
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
let mut futures = &mut futures;
$crate::macros::support::poll_fn(|cx| {
// Track if any branch returns pending. If no branch completes
// **or** returns pending, this implies that all branches are
@ -497,7 +507,7 @@ macro_rules! select {
// Extract the future for this branch from the
// tuple
let ( $($skip,)* fut, .. ) = &mut futures;
let ( $($skip,)* fut, .. ) = &mut *futures;
// Safety: future is stored on the stack above
// and never moved.

View file

@ -1,7 +1,11 @@
cfg_macros! {
pub use crate::future::poll_fn;
pub use crate::future::maybe_done::maybe_done;
pub use crate::util::thread_rng_n;
#[doc(hidden)]
pub fn thread_rng_n(n: u32) -> u32 {
crate::runtime::context::thread_rng_n(n)
}
}
pub use std::future::Future;

View file

@ -1,4 +1,32 @@
#[cfg(all(loom, test))]
macro_rules! thread_local {
macro_rules! tokio_thread_local {
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty = const { $expr:expr } $(;)?) => {
loom::thread_local! {
$(#[$attrs])*
$vis static $name: $ty = $expr;
}
};
($($tts:tt)+) => { loom::thread_local!{ $($tts)+ } }
}
#[cfg(not(tokio_no_const_thread_local))]
#[cfg(not(all(loom, test)))]
macro_rules! tokio_thread_local {
($($tts:tt)+) => {
::std::thread_local!{ $($tts)+ }
}
}
#[cfg(tokio_no_const_thread_local)]
#[cfg(not(all(loom, test)))]
macro_rules! tokio_thread_local {
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty = const { $expr:expr } $(;)?) => {
::std::thread_local! {
$(#[$attrs])*
$vis static $name: $ty = $expr;
}
};
($($tts:tt)+) => { ::std::thread_local!{ $($tts)+ } }
}

View file

@ -118,8 +118,18 @@ macro_rules! try_join {
// Safety: nothing must be moved out of `futures`. This is to satisfy
// the requirement of `Pin::new_unchecked` called below.
//
// We can't use the `pin!` macro for this because `futures` is a tuple
// and the standard library provides no way to pin-project to the fields
// of a tuple.
let mut futures = ( $( maybe_done($e), )* );
// This assignment makes sure that the `poll_fn` closure only has a
// reference to the futures, instead of taking ownership of them. This
// mitigates the issue described in
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
let mut futures = &mut futures;
// Each time the future created by poll_fn is polled, a different future will be polled first
// to ensure every future passed to join! gets a chance to make progress even if
// one of the futures consumes the whole budget.
@ -152,7 +162,7 @@ macro_rules! try_join {
to_run -= 1;
// Extract the future for this branch from the tuple.
let ( $($skip,)* fut, .. ) = &mut futures;
let ( $($skip,)* fut, .. ) = &mut *futures;
// Safety: future is stored on the stack above
// and never moved.

View file

@ -244,7 +244,7 @@ cfg_net! {
type Future = <str as sealed::ToSocketAddrsPriv>::Future;
fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
(&self[..]).to_socket_addrs(sealed::Internal)
self[..].to_socket_addrs(sealed::Internal)
}
}
}

View file

@ -23,8 +23,10 @@
//! [`UnixDatagram`]: UnixDatagram
mod addr;
#[cfg(feature = "net")]
pub(crate) use addr::to_socket_addrs;
cfg_not_wasi! {
#[cfg(feature = "net")]
pub(crate) use addr::to_socket_addrs;
}
pub use addr::ToSocketAddrs;
cfg_net! {
@ -33,11 +35,13 @@ cfg_net! {
pub mod tcp;
pub use tcp::listener::TcpListener;
pub use tcp::socket::TcpSocket;
pub use tcp::stream::TcpStream;
cfg_not_wasi! {
pub use tcp::socket::TcpSocket;
mod udp;
pub use udp::UdpSocket;
mod udp;
pub use udp::UdpSocket;
}
}
cfg_net_unix! {

View file

@ -1,6 +1,9 @@
use crate::io::{Interest, PollEvented};
use crate::net::tcp::TcpStream;
use crate::net::{to_socket_addrs, ToSocketAddrs};
cfg_not_wasi! {
use crate::net::{to_socket_addrs, ToSocketAddrs};
}
use std::convert::TryFrom;
use std::fmt;
@ -55,68 +58,70 @@ cfg_net! {
}
impl TcpListener {
/// Creates a new TcpListener, which will be bound to the specified address.
///
/// The returned listener is ready for accepting connections.
///
/// Binding with a port number of 0 will request that the OS assigns a port
/// to this listener. The port allocated can be queried via the `local_addr`
/// method.
///
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
/// If `addr` yields multiple addresses, bind will be attempted with each of
/// the addresses until one succeeds and returns the listener. If none of
/// the addresses succeed in creating a listener, the error returned from
/// the last attempt (the last address) is returned.
///
/// This function sets the `SO_REUSEADDR` option on the socket.
///
/// To configure the socket before binding, you can use the [`TcpSocket`]
/// type.
///
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
/// [`TcpSocket`]: struct@crate::net::TcpSocket
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpListener;
///
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
///
/// // use the listener
///
/// # let _ = listener;
/// Ok(())
/// }
/// ```
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
let addrs = to_socket_addrs(addr).await?;
cfg_not_wasi! {
/// Creates a new TcpListener, which will be bound to the specified address.
///
/// The returned listener is ready for accepting connections.
///
/// Binding with a port number of 0 will request that the OS assigns a port
/// to this listener. The port allocated can be queried via the `local_addr`
/// method.
///
/// The address type can be any implementor of the [`ToSocketAddrs`] trait.
/// If `addr` yields multiple addresses, bind will be attempted with each of
/// the addresses until one succeeds and returns the listener. If none of
/// the addresses succeed in creating a listener, the error returned from
/// the last attempt (the last address) is returned.
///
/// This function sets the `SO_REUSEADDR` option on the socket.
///
/// To configure the socket before binding, you can use the [`TcpSocket`]
/// type.
///
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
/// [`TcpSocket`]: struct@crate::net::TcpSocket
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpListener;
///
/// use std::io;
///
/// #[tokio::main]
/// async fn main() -> io::Result<()> {
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
///
/// // use the listener
///
/// # let _ = listener;
/// Ok(())
/// }
/// ```
pub async fn bind<A: ToSocketAddrs>(addr: A) -> io::Result<TcpListener> {
let addrs = to_socket_addrs(addr).await?;
let mut last_err = None;
let mut last_err = None;
for addr in addrs {
match TcpListener::bind_addr(addr) {
Ok(listener) => return Ok(listener),
Err(e) => last_err = Some(e),
for addr in addrs {
match TcpListener::bind_addr(addr) {
Ok(listener) => return Ok(listener),
Err(e) => last_err = Some(e),
}
}
Err(last_err.unwrap_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"could not resolve to any address",
)
}))
}
Err(last_err.unwrap_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"could not resolve to any address",
)
}))
}
fn bind_addr(addr: SocketAddr) -> io::Result<TcpListener> {
let listener = mio::net::TcpListener::bind(addr)?;
TcpListener::new(listener)
fn bind_addr(addr: SocketAddr) -> io::Result<TcpListener> {
let listener = mio::net::TcpListener::bind(addr)?;
TcpListener::new(listener)
}
}
/// Accepts a new incoming connection from this listener.
@ -216,11 +221,13 @@ impl TcpListener {
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
/// This function panics if it is not called from within a runtime with
/// IO enabled.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
let io = mio::net::TcpListener::from_std(listener);
let io = PollEvented::new(io)?;
@ -267,11 +274,22 @@ impl TcpListener {
.map(|io| io.into_raw_socket())
.map(|raw_socket| unsafe { std::net::TcpListener::from_raw_socket(raw_socket) })
}
#[cfg(tokio_wasi)]
{
use std::os::wasi::io::{FromRawFd, IntoRawFd};
self.io
.into_inner()
.map(|io| io.into_raw_fd())
.map(|raw_fd| unsafe { std::net::TcpListener::from_raw_fd(raw_fd) })
}
}
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
let io = PollEvented::new(listener)?;
Ok(TcpListener { io })
cfg_not_wasi! {
pub(crate) fn new(listener: mio::net::TcpListener) -> io::Result<TcpListener> {
let io = PollEvented::new(listener)?;
Ok(TcpListener { io })
}
}
/// Returns the local address that this listener is bound to.
@ -384,6 +402,20 @@ mod sys {
}
}
cfg_unstable! {
#[cfg(tokio_wasi)]
mod sys {
use super::TcpListener;
use std::os::wasi::prelude::*;
impl AsRawFd for TcpListener {
fn as_raw_fd(&self) -> RawFd {
self.io.as_raw_fd()
}
}
}
}
#[cfg(windows)]
mod sys {
use super::TcpListener;

View file

@ -2,7 +2,9 @@
pub(crate) mod listener;
pub(crate) mod socket;
cfg_not_wasi! {
pub(crate) mod socket;
}
mod split;
pub use split::{ReadHalf, WriteHalf};

View file

@ -398,6 +398,89 @@ impl TcpSocket {
self.inner.linger()
}
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
///
/// [`set_tos`]: Self::set_tos
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn tos(&self) -> io::Result<u32> {
self.inner.tos()
}
/// Sets the value for the `IP_TOS` option on this socket.
///
/// This value sets the type-of-service field that is used in every packet
/// sent from this socket.
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
self.inner.set_tos(tos)
}
/// Gets the value for the `SO_BINDTODEVICE` option on this socket
///
/// This value gets the socket binded device's interface name.
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",))]
#[cfg_attr(
docsrs,
doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",)))
)]
pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
self.inner.device()
}
/// Sets the value for the `SO_BINDTODEVICE` option on this socket
///
/// If a socket is bound to an interface, only packets received from that
/// particular interface are processed by the socket. Note that this only
/// works for some socket types, particularly `AF_INET` sockets.
///
/// If `interface` is `None` or an empty string it removes the binding.
#[cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
#[cfg_attr(
docsrs,
doc(cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))))
)]
pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
self.inner.bind_device(interface)
}
/// Gets the local address of this socket.
///
/// Will fail on windows if called before `bind`.

View file

@ -145,6 +145,12 @@ impl ReadHalf<'_> {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// # Cancel safety
@ -190,8 +196,12 @@ impl ReadHalf<'_> {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
/// and will no longer yield data. If the stream is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The stream's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the stream is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.try_read(buf)
@ -269,6 +279,12 @@ impl WriteHalf<'_> {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// # Cancel safety

View file

@ -200,6 +200,12 @@ impl OwnedReadHalf {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// # Cancel safety
@ -245,8 +251,12 @@ impl OwnedReadHalf {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
/// and will no longer yield data. If the stream is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The stream's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the stream is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.try_read(buf)
@ -351,6 +361,12 @@ impl OwnedWriteHalf {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// This function is equivalent to [`TcpStream::ready`].
///
/// # Cancel safety
@ -474,12 +490,12 @@ impl AsyncWrite for OwnedWriteHalf {
impl AsRef<TcpStream> for OwnedReadHalf {
fn as_ref(&self) -> &TcpStream {
&*self.inner
&self.inner
}
}
impl AsRef<TcpStream> for OwnedWriteHalf {
fn as_ref(&self) -> &TcpStream {
&*self.inner
&self.inner
}
}

View file

@ -1,8 +1,12 @@
use crate::future::poll_fn;
cfg_not_wasi! {
use crate::future::poll_fn;
use crate::net::{to_socket_addrs, ToSocketAddrs};
use std::time::Duration;
}
use crate::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
use crate::net::{to_socket_addrs, ToSocketAddrs};
use std::convert::TryFrom;
use std::fmt;
@ -10,7 +14,6 @@ use std::io;
use std::net::{Shutdown, SocketAddr};
use std::pin::Pin;
use std::task::{Context, Poll};
use std::time::Duration;
cfg_io_util! {
use bytes::BufMut;
@ -70,86 +73,88 @@ cfg_net! {
}
impl TcpStream {
/// Opens a TCP connection to a remote host.
///
/// `addr` is an address of the remote host. Anything which implements the
/// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
/// yields multiple addresses, connect will be attempted with each of the
/// addresses until a connection is successful. If none of the addresses
/// result in a successful connection, the error returned from the last
/// connection attempt (the last address) is returned.
///
/// To configure the socket before connecting, you can use the [`TcpSocket`]
/// type.
///
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
/// [`TcpSocket`]: struct@crate::net::TcpSocket
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
/// use tokio::io::AsyncWriteExt;
/// use std::error::Error;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// // Connect to a peer
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// // Write some data.
/// stream.write_all(b"hello world!").await?;
///
/// Ok(())
/// }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
let addrs = to_socket_addrs(addr).await?;
cfg_not_wasi! {
/// Opens a TCP connection to a remote host.
///
/// `addr` is an address of the remote host. Anything which implements the
/// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
/// yields multiple addresses, connect will be attempted with each of the
/// addresses until a connection is successful. If none of the addresses
/// result in a successful connection, the error returned from the last
/// connection attempt (the last address) is returned.
///
/// To configure the socket before connecting, you can use the [`TcpSocket`]
/// type.
///
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
/// [`TcpSocket`]: struct@crate::net::TcpSocket
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
/// use tokio::io::AsyncWriteExt;
/// use std::error::Error;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn Error>> {
/// // Connect to a peer
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// // Write some data.
/// stream.write_all(b"hello world!").await?;
///
/// Ok(())
/// }
/// ```
///
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
///
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> {
let addrs = to_socket_addrs(addr).await?;
let mut last_err = None;
let mut last_err = None;
for addr in addrs {
match TcpStream::connect_addr(addr).await {
Ok(stream) => return Ok(stream),
Err(e) => last_err = Some(e),
for addr in addrs {
match TcpStream::connect_addr(addr).await {
Ok(stream) => return Ok(stream),
Err(e) => last_err = Some(e),
}
}
Err(last_err.unwrap_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"could not resolve to any address",
)
}))
}
Err(last_err.unwrap_or_else(|| {
io::Error::new(
io::ErrorKind::InvalidInput,
"could not resolve to any address",
)
}))
}
/// Establishes a connection to the specified `addr`.
async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
let sys = mio::net::TcpStream::connect(addr)?;
TcpStream::connect_mio(sys).await
}
pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result<TcpStream> {
let stream = TcpStream::new(sys)?;
// Once we've connected, wait for the stream to be writable as
// that's when the actual connection has been initiated. Once we're
// writable we check for `take_socket_error` to see if the connect
// actually hit an error or not.
//
// If all that succeeded then we ship everything on up.
poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?;
if let Some(e) = stream.io.take_error()? {
return Err(e);
/// Establishes a connection to the specified `addr`.
async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
let sys = mio::net::TcpStream::connect(addr)?;
TcpStream::connect_mio(sys).await
}
Ok(stream)
pub(crate) async fn connect_mio(sys: mio::net::TcpStream) -> io::Result<TcpStream> {
let stream = TcpStream::new(sys)?;
// Once we've connected, wait for the stream to be writable as
// that's when the actual connection has been initiated. Once we're
// writable we check for `take_socket_error` to see if the connect
// actually hit an error or not.
//
// If all that succeeded then we ship everything on up.
poll_fn(|cx| stream.io.registration().poll_write_ready(cx)).await?;
if let Some(e) = stream.io.take_error()? {
return Err(e);
}
Ok(stream)
}
}
pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result<TcpStream> {
@ -181,11 +186,13 @@ impl TcpStream {
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
/// This function panics if it is not called from within a runtime with
/// IO enabled.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
let io = mio::net::TcpStream::from_std(stream);
let io = PollEvented::new(io)?;
@ -244,6 +251,15 @@ impl TcpStream {
.map(|io| io.into_raw_socket())
.map(|raw_socket| unsafe { std::net::TcpStream::from_raw_socket(raw_socket) })
}
#[cfg(tokio_wasi)]
{
use std::os::wasi::io::{FromRawFd, IntoRawFd};
self.io
.into_inner()
.map(|io| io.into_raw_fd())
.map(|raw_fd| unsafe { std::net::TcpStream::from_raw_fd(raw_fd) })
}
}
/// Returns the local address that this stream is bound to.
@ -361,6 +377,12 @@ impl TcpStream {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
@ -531,8 +553,12 @@ impl TcpStream {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
/// and will no longer yield data. If the stream is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The stream's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the stream is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
///
/// # Examples
@ -944,7 +970,7 @@ impl TcpStream {
/// Tries to read or write from the socket using a user-provided IO operation.
///
/// If the socket is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the socket by manually
/// should attempt to perform IO operation on the socket by manually
/// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value
@ -963,6 +989,11 @@ impl TcpStream {
/// defined on the Tokio `TcpStream` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
///
/// [`readable()`]: TcpStream::readable()
@ -1077,52 +1108,54 @@ impl TcpStream {
self.io.set_nodelay(nodelay)
}
/// Reads the linger duration for this socket by getting the `SO_LINGER`
/// option.
///
/// For more information about this option, see [`set_linger`].
///
/// [`set_linger`]: TcpStream::set_linger
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// println!("{:?}", stream.linger()?);
/// # Ok(())
/// # }
/// ```
pub fn linger(&self) -> io::Result<Option<Duration>> {
socket2::SockRef::from(self).linger()
}
cfg_not_wasi! {
/// Reads the linger duration for this socket by getting the `SO_LINGER`
/// option.
///
/// For more information about this option, see [`set_linger`].
///
/// [`set_linger`]: TcpStream::set_linger
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// println!("{:?}", stream.linger()?);
/// # Ok(())
/// # }
/// ```
pub fn linger(&self) -> io::Result<Option<Duration>> {
socket2::SockRef::from(self).linger()
}
/// Sets the linger duration of this socket by setting the SO_LINGER option.
///
/// This option controls the action taken when a stream has unsent messages and the stream is
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
/// data or until the time expires.
///
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a
/// way that allows the process to continue as quickly as possible.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// stream.set_linger(None)?;
/// # Ok(())
/// # }
/// ```
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
socket2::SockRef::from(self).set_linger(dur)
/// Sets the linger duration of this socket by setting the SO_LINGER option.
///
/// This option controls the action taken when a stream has unsent messages and the stream is
/// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
/// data or until the time expires.
///
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a
/// way that allows the process to continue as quickly as possible.
///
/// # Examples
///
/// ```no_run
/// use tokio::net::TcpStream;
///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?;
///
/// stream.set_linger(None)?;
/// # Ok(())
/// # }
/// ```
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
socket2::SockRef::from(self).set_linger(dur)
}
}
/// Gets the value of the `IP_TTL` option for this socket.
@ -1315,3 +1348,15 @@ mod sys {
}
}
}
#[cfg(all(tokio_unstable, tokio_wasi))]
mod sys {
use super::TcpStream;
use std::os::wasi::prelude::*;
impl AsRawFd for TcpStream {
fn as_raw_fd(&self) -> RawFd {
self.io.as_raw_fd()
}
}
}

View file

@ -170,6 +170,7 @@ impl UdpSocket {
UdpSocket::new(sys)
}
#[track_caller]
fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> {
let io = PollEvented::new(socket)?;
Ok(UdpSocket { io })
@ -210,6 +211,7 @@ impl UdpSocket {
/// # Ok(())
/// # }
/// ```
#[track_caller]
pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
let io = mio::net::UdpSocket::from_std(socket);
UdpSocket::new(io)
@ -257,6 +259,10 @@ impl UdpSocket {
}
}
fn as_socket(&self) -> socket2::SockRef<'_> {
socket2::SockRef::from(self)
}
/// Returns the local address that this socket is bound to.
///
/// # Example
@ -351,7 +357,9 @@ impl UdpSocket {
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`.
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
@ -734,7 +742,7 @@ impl UdpSocket {
///
/// # Cancel safety
///
/// This method is cancel safe. If `recv_from` is used as the event in a
/// This method is cancel safe. If `recv` is used as the event in a
/// [`tokio::select!`](crate::select) statement and some other branch
/// completes first, it is guaranteed that no messages were received on this
/// socket.
@ -919,7 +927,7 @@ impl UdpSocket {
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
// buffer.
let n = (&*self.io).recv(dst)?;
let n = (*self.io).recv(dst)?;
unsafe {
buf.advance_mut(n);
@ -983,7 +991,7 @@ impl UdpSocket {
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
// buffer.
let (n, addr) = (&*self.io).recv_from(dst)?;
let (n, addr) = (*self.io).recv_from(dst)?;
unsafe {
buf.advance_mut(n);
@ -1265,7 +1273,7 @@ impl UdpSocket {
/// Tries to read or write from the socket using a user-provided IO operation.
///
/// If the socket is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the socket by manually
/// should attempt to perform IO operation on the socket by manually
/// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value
@ -1284,6 +1292,11 @@ impl UdpSocket {
/// defined on the Tokio `UdpSocket` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
///
/// [`readable()`]: UdpSocket::readable()
@ -1508,6 +1521,89 @@ impl UdpSocket {
self.io.set_ttl(ttl)
}
/// Gets the value of the `IP_TOS` option for this socket.
///
/// For more information about this option, see [`set_tos`].
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
///
/// [`set_tos`]: Self::set_tos
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn tos(&self) -> io::Result<u32> {
self.as_socket().tos()
}
/// Sets the value for the `IP_TOS` option on this socket.
///
/// This value sets the type-of-service field that is used in every packet
/// sent from this socket.
///
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
// https://docs.rs/socket2/0.4.2/src/socket2/socket.rs.html#1178
#[cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
)))]
#[cfg_attr(
docsrs,
doc(cfg(not(any(
target_os = "fuchsia",
target_os = "redox",
target_os = "solaris",
target_os = "illumos",
))))
)]
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
self.as_socket().set_tos(tos)
}
/// Gets the value for the `SO_BINDTODEVICE` option on this socket
///
/// This value gets the socket-bound device's interface name.
#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",))]
#[cfg_attr(
docsrs,
doc(cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux",)))
)]
pub fn device(&self) -> io::Result<Option<Vec<u8>>> {
self.as_socket().device()
}
/// Sets the value for the `SO_BINDTODEVICE` option on this socket
///
/// If a socket is bound to an interface, only packets received from that
/// particular interface are processed by the socket. Note that this only
/// works for some socket types, particularly `AF_INET` sockets.
///
/// If `interface` is `None` or an empty string it removes the binding.
#[cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux")))]
#[cfg_attr(
docsrs,
doc(cfg(all(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))))
)]
pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
self.as_socket().bind_device(interface)
}
/// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
///
/// This function specifies a new multicast group for this socket to join.

View file

@ -104,7 +104,9 @@ impl UnixDatagram {
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`.
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
@ -430,7 +432,8 @@ impl UnixDatagram {
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
/// This function panics if it is not called from within a runtime with
/// IO enabled.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a Tokio runtime, otherwise runtime can be set
@ -457,6 +460,7 @@ impl UnixDatagram {
/// # Ok(())
/// # }
/// ```
#[track_caller]
pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
let socket = mio::net::UnixDatagram::from_std(datagram);
let io = PollEvented::new(socket)?;
@ -844,7 +848,7 @@ impl UnixDatagram {
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
// buffer.
let (n, addr) = (&*self.io).recv_from(dst)?;
let (n, addr) = (*self.io).recv_from(dst)?;
unsafe {
buf.advance_mut(n);
@ -907,7 +911,7 @@ impl UnixDatagram {
// Safety: We trust `UnixDatagram::recv` to have filled up `n` bytes in the
// buffer.
let n = (&*self.io).recv(dst)?;
let n = (*self.io).recv(dst)?;
unsafe {
buf.advance_mut(n);
@ -1212,7 +1216,7 @@ impl UnixDatagram {
/// Tries to read or write from the socket using a user-provided IO operation.
///
/// If the socket is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the socket by manually
/// should attempt to perform IO operation on the socket by manually
/// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value
@ -1231,6 +1235,11 @@ impl UnixDatagram {
/// defined on the Tokio `UnixDatagram` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
///
/// [`readable()`]: UnixDatagram::readable()

View file

@ -54,11 +54,13 @@ impl UnixListener {
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
/// This function panics if it is not called from within a runtime with
/// IO enabled.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn bind<P>(path: P) -> io::Result<UnixListener>
where
P: AsRef<Path>,
@ -77,11 +79,13 @@ impl UnixListener {
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
/// This function panics if it is not called from within a runtime with
/// IO enabled.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn from_std(listener: net::UnixListener) -> io::Result<UnixListener> {
let listener = mio::net::UnixListener::from_std(listener);
let io = PollEvented::new(listener)?;

View file

@ -1,5 +1,4 @@
//! Unix domain socket utility types.
// This module does not currently provide any public API, but it was
// unintentionally defined as a public module. Hide it from the documentation
// instead of changing it to a private module to avoid breakage.
@ -22,3 +21,15 @@ pub(crate) use stream::UnixStream;
mod ucred;
pub use ucred::UCred;
/// A type representing process and process group IDs.
#[allow(non_camel_case_types)]
pub type uid_t = u32;
/// A type representing user ID.
#[allow(non_camel_case_types)]
pub type gid_t = u32;
/// A type representing group ID.
#[allow(non_camel_case_types)]
pub type pid_t = i32;

View file

@ -100,8 +100,12 @@ impl ReadHalf<'_> {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
/// and will no longer yield data. If the stream is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The stream's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the stream is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.try_read(buf)
@ -178,6 +182,12 @@ impl WriteHalf<'_> {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method

View file

@ -114,6 +114,12 @@ impl OwnedReadHalf {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
@ -155,8 +161,12 @@ impl OwnedReadHalf {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
/// and will no longer yield data. If the stream is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The stream's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the stream is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.try_read(buf)
@ -261,6 +271,12 @@ impl OwnedWriteHalf {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
@ -382,12 +398,12 @@ impl AsyncWrite for OwnedWriteHalf {
impl AsRef<UnixStream> for OwnedReadHalf {
fn as_ref(&self) -> &UnixStream {
&*self.inner
&self.inner
}
}
impl AsRef<UnixStream> for OwnedWriteHalf {
fn as_ref(&self) -> &UnixStream {
&*self.inner
&self.inner
}
}

View file

@ -22,8 +22,8 @@ cfg_io_util! {
cfg_net_unix! {
/// A structure representing a connected Unix socket.
///
/// This socket can be connected directly with `UnixStream::connect` or accepted
/// from a listener with `UnixListener::incoming`. Additionally, a pair of
/// This socket can be connected directly with [`UnixStream::connect`] or accepted
/// from a listener with [`UnixListener::accept`]. Additionally, a pair of
/// anonymous Unix sockets can be created with `UnixStream::pair`.
///
/// To shut down the stream in the write direction, you can call the
@ -32,6 +32,7 @@ cfg_net_unix! {
/// the stream in one direction.
///
/// [`shutdown()`]: fn@crate::io::AsyncWriteExt::shutdown
/// [`UnixListener::accept`]: crate::net::UnixListener::accept
pub struct UnixStream {
io: PollEvented<mio::net::UnixStream>,
}
@ -65,6 +66,12 @@ impl UnixStream {
/// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket.
///
/// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Cancel safety
///
/// This method is cancel safe. Once a readiness event occurs, the method
@ -239,8 +246,12 @@ impl UnixStream {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
/// and will no longer yield data. If the stream is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The stream's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the stream is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
///
/// # Examples
@ -656,7 +667,7 @@ impl UnixStream {
/// Tries to read or write from the socket using a user-provided IO operation.
///
/// If the socket is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the socket by manually
/// should attempt to perform IO operation on the socket by manually
/// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value
@ -675,6 +686,11 @@ impl UnixStream {
/// defined on the Tokio `UnixStream` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
///
/// [`readable()`]: UnixStream::readable()
@ -699,11 +715,13 @@ impl UnixStream {
///
/// # Panics
///
/// This function panics if thread-local runtime is not set.
/// This function panics if it is not called from within a runtime with
/// IO enabled.
///
/// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
let stream = mio::net::UnixStream::from_std(stream);
let io = PollEvented::new(stream)?;

View file

@ -1,24 +1,24 @@
use libc::{gid_t, pid_t, uid_t};
use crate::net::unix;
/// Credentials of a process.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UCred {
/// PID (process ID) of the process.
pid: Option<pid_t>,
pid: Option<unix::pid_t>,
/// UID (user ID) of the process.
uid: uid_t,
uid: unix::uid_t,
/// GID (group ID) of the process.
gid: gid_t,
gid: unix::gid_t,
}
impl UCred {
/// Gets UID (user ID) of the process.
pub fn uid(&self) -> uid_t {
pub fn uid(&self) -> unix::uid_t {
self.uid
}
/// Gets GID (group ID) of the process.
pub fn gid(&self) -> gid_t {
pub fn gid(&self) -> unix::gid_t {
self.gid
}
@ -26,7 +26,7 @@ impl UCred {
///
/// This is only implemented under Linux, Android, iOS, macOS, Solaris and
/// Illumos. On other platforms this will always return `None`.
pub fn pid(&self) -> Option<pid_t> {
pub fn pid(&self) -> Option<unix::pid_t> {
self.pid
}
}
@ -48,7 +48,7 @@ pub(crate) use self::impl_solaris::get_peer_cred;
#[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))]
pub(crate) mod impl_linux {
use crate::net::unix::UnixStream;
use crate::net::unix::{self, UnixStream};
use libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
use std::{io, mem};
@ -87,9 +87,9 @@ pub(crate) mod impl_linux {
);
if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
Ok(super::UCred {
uid: ucred.uid,
gid: ucred.gid,
pid: Some(ucred.pid),
uid: ucred.uid as unix::uid_t,
gid: ucred.gid as unix::gid_t,
pid: Some(ucred.pid as unix::pid_t),
})
} else {
Err(io::Error::last_os_error())
@ -100,7 +100,7 @@ pub(crate) mod impl_linux {
#[cfg(any(target_os = "netbsd"))]
pub(crate) mod impl_netbsd {
use crate::net::unix::UnixStream;
use crate::net::unix::{self, UnixStream};
use libc::{c_void, getsockopt, socklen_t, unpcbid, LOCAL_PEEREID, SOL_SOCKET};
use std::io;
@ -129,9 +129,9 @@ pub(crate) mod impl_netbsd {
);
if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() {
Ok(super::UCred {
uid: unpcbid.unp_euid,
gid: unpcbid.unp_egid,
pid: Some(unpcbid.unp_pid),
uid: unpcbid.unp_euid as unix::uid_t,
gid: unpcbid.unp_egid as unix::gid_t,
pid: Some(unpcbid.unp_pid as unix::pid_t),
})
} else {
Err(io::Error::last_os_error())
@ -142,7 +142,7 @@ pub(crate) mod impl_netbsd {
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
pub(crate) mod impl_bsd {
use crate::net::unix::UnixStream;
use crate::net::unix::{self, UnixStream};
use libc::getpeereid;
use std::io;
@ -160,8 +160,8 @@ pub(crate) mod impl_bsd {
if ret == 0 {
Ok(super::UCred {
uid: uid.assume_init(),
gid: gid.assume_init(),
uid: uid.assume_init() as unix::uid_t,
gid: gid.assume_init() as unix::gid_t,
pid: None,
})
} else {
@ -173,7 +173,7 @@ pub(crate) mod impl_bsd {
#[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) mod impl_macos {
use crate::net::unix::UnixStream;
use crate::net::unix::{self, UnixStream};
use libc::{c_void, getpeereid, getsockopt, pid_t, LOCAL_PEEREPID, SOL_LOCAL};
use std::io;
@ -207,9 +207,9 @@ pub(crate) mod impl_macos {
if ret == 0 {
Ok(super::UCred {
uid: uid.assume_init(),
gid: gid.assume_init(),
pid: Some(pid.assume_init()),
uid: uid.assume_init() as unix::uid_t,
gid: gid.assume_init() as unix::gid_t,
pid: Some(pid.assume_init() as unix::pid_t),
})
} else {
Err(io::Error::last_os_error())
@ -220,7 +220,7 @@ pub(crate) mod impl_macos {
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
pub(crate) mod impl_solaris {
use crate::net::unix::UnixStream;
use crate::net::unix::{self, UnixStream};
use std::io;
use std::os::unix::io::AsRawFd;
use std::ptr;
@ -240,9 +240,9 @@ pub(crate) mod impl_solaris {
libc::ucred_free(cred);
Ok(super::UCred {
uid,
gid,
pid: Some(pid),
uid: uid as unix::uid_t,
gid: gid as unix::gid_t,
pid: Some(pid as unix::pid_t),
})
} else {
Err(io::Error::last_os_error())

View file

@ -20,21 +20,18 @@ cfg_io_util! {
#[cfg(not(docsrs))]
mod doc {
pub(super) use crate::os::windows::ffi::OsStrExt;
pub(super) use crate::winapi::shared::minwindef::{DWORD, FALSE};
pub(super) use crate::winapi::um::fileapi;
pub(super) use crate::winapi::um::handleapi;
pub(super) use crate::winapi::um::namedpipeapi;
pub(super) use crate::winapi::um::winbase;
pub(super) use crate::winapi::um::winnt;
pub(super) mod windows_sys {
pub(crate) use windows_sys::{
Win32::Foundation::*, Win32::Storage::FileSystem::*, Win32::System::Pipes::*,
Win32::System::SystemServices::*,
};
}
pub(super) use mio::windows as mio_windows;
}
// NB: none of these shows up in public API, so don't document them.
#[cfg(docsrs)]
mod doc {
pub type DWORD = crate::doc::NotDefinedHere;
pub(super) mod mio_windows {
pub type NamedPipe = crate::doc::NotDefinedHere;
}
@ -101,7 +98,6 @@ use self::doc::*;
/// # Ok(()) }
/// ```
///
/// [`ERROR_PIPE_BUSY`]: crate::winapi::shared::winerror::ERROR_PIPE_BUSY
/// [Windows named pipe]: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
#[derive(Debug)]
pub struct NamedPipeServer {
@ -192,17 +188,15 @@ impl NamedPipeServer {
/// # Ok(()) }
/// ```
pub async fn connect(&self) -> io::Result<()> {
loop {
match self.io.connect() {
Ok(()) => break,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io.registration().readiness(Interest::WRITABLE).await?;
}
Err(e) => return Err(e),
match self.io.connect() {
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
self.io
.registration()
.async_io(Interest::WRITABLE, || self.io.connect())
.await
}
x => x,
}
Ok(())
}
/// Disconnects the server end of a named pipe instance from a client
@ -211,7 +205,7 @@ impl NamedPipeServer {
/// ```
/// use tokio::io::AsyncWriteExt;
/// use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
/// use winapi::shared::winerror;
/// use windows_sys::Win32::Foundation::ERROR_PIPE_NOT_CONNECTED;
///
/// const PIPE_NAME: &str = r"\\.\pipe\tokio-named-pipe-disconnect";
///
@ -231,7 +225,7 @@ impl NamedPipeServer {
/// // Write fails with an OS-specific error after client has been
/// // disconnected.
/// let e = client.write(b"ping").await.unwrap_err();
/// assert_eq!(e.raw_os_error(), Some(winerror::ERROR_PIPE_NOT_CONNECTED as i32));
/// assert_eq!(e.raw_os_error(), Some(ERROR_PIPE_NOT_CONNECTED as i32));
/// # Ok(()) }
/// ```
pub fn disconnect(&self) -> io::Result<()> {
@ -244,6 +238,12 @@ impl NamedPipeServer {
/// can be used to concurrently read / write to the same pipe on a single
/// task without splitting the pipe.
///
/// The function may complete without the pipe being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Examples
///
/// Concurrently read and write to the pipe on the same task without
@ -403,8 +403,12 @@ impl NamedPipeServer {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the pipe's read half is closed
/// and will no longer yield data. If the pipe is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The pipe's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the pipe is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
///
/// # Examples
@ -536,7 +540,7 @@ impl NamedPipeServer {
/// Tries to read data from the stream into the provided buffer, advancing the
/// buffer's internal cursor, returning how many bytes were read.
///
/// Receives any pending data from the socket but does not wait for new data
/// Receives any pending data from the pipe but does not wait for new data
/// to arrive. On success, returns the number of bytes read. Because
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
/// the async task and can exist entirely on the stack.
@ -567,7 +571,7 @@ impl NamedPipeServer {
/// let server = named_pipe::ServerOptions::new().create(PIPE_NAME)?;
///
/// loop {
/// // Wait for the socket to be readable
/// // Wait for the pipe to be readable
/// server.readable().await?;
///
/// let mut buf = Vec::with_capacity(4096);
@ -808,27 +812,32 @@ impl NamedPipeServer {
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
}
/// Tries to read or write from the socket using a user-provided IO operation.
/// Tries to read or write from the pipe using a user-provided IO operation.
///
/// If the socket is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the socket by manually
/// If the pipe is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the pipe by manually
/// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a
/// pipe is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value
/// of the closure is then returned by `try_io`.
///
/// If the socket is not ready, then the closure is not called
/// If the pipe is not ready, then the closure is not called
/// and a `WouldBlock` error is returned.
///
/// The closure should only return a `WouldBlock` error if it has performed
/// an IO operation on the socket that failed due to the socket not being
/// an IO operation on the pipe that failed due to the pipe not being
/// ready. Returning a `WouldBlock` error in any other situation will
/// incorrectly clear the readiness flag, which can cause the socket to
/// incorrectly clear the readiness flag, which can cause the pipe to
/// behave incorrectly.
///
/// The closure should not perform the IO operation using any of the
/// methods defined on the Tokio `NamedPipeServer` type, as this will mess with
/// the readiness flag and can cause the socket to behave incorrectly.
/// the readiness flag and can cause the pipe to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
///
@ -903,7 +912,7 @@ impl AsRawHandle for NamedPipeServer {
/// use std::time::Duration;
/// use tokio::net::windows::named_pipe::ClientOptions;
/// use tokio::time;
/// use winapi::shared::winerror;
/// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
///
/// const PIPE_NAME: &str = r"\\.\pipe\named-pipe-idiomatic-client";
///
@ -911,7 +920,7 @@ impl AsRawHandle for NamedPipeServer {
/// let client = loop {
/// match ClientOptions::new().open(PIPE_NAME) {
/// Ok(client) => break client,
/// Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
/// Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
/// Err(e) => return Err(e),
/// }
///
@ -922,7 +931,7 @@ impl AsRawHandle for NamedPipeServer {
/// # Ok(()) }
/// ```
///
/// [`ERROR_PIPE_BUSY`]: crate::winapi::shared::winerror::ERROR_PIPE_BUSY
/// [`ERROR_PIPE_BUSY`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_PIPE_BUSY.html
/// [Windows named pipe]: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
#[derive(Debug)]
pub struct NamedPipeClient {
@ -986,6 +995,12 @@ impl NamedPipeClient {
/// can be used to concurrently read / write to the same pipe on a single
/// task without splitting the pipe.
///
/// The function may complete without the pipe being ready. This is a
/// false-positive and attempting an operation will return with
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
/// [`Ready`] set, so you should always check the returned value and possibly
/// wait again if the requested states are not set.
///
/// # Examples
///
/// Concurrently read and write to the pipe on the same task without
@ -1143,8 +1158,12 @@ impl NamedPipeClient {
/// # Return
///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
/// number of bytes read. `Ok(0)` indicates the pipe's read half is closed
/// and will no longer yield data. If the pipe is not ready to read data
/// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
///
/// 1. The pipe's read half is closed and will no longer yield data.
/// 2. The specified buffer was 0 bytes in length.
///
/// If the pipe is not ready to read data,
/// `Err(io::ErrorKind::WouldBlock)` is returned.
///
/// # Examples
@ -1274,7 +1293,7 @@ impl NamedPipeClient {
/// Tries to read data from the stream into the provided buffer, advancing the
/// buffer's internal cursor, returning how many bytes were read.
///
/// Receives any pending data from the socket but does not wait for new data
/// Receives any pending data from the pipe but does not wait for new data
/// to arrive. On success, returns the number of bytes read. Because
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
/// the async task and can exist entirely on the stack.
@ -1305,7 +1324,7 @@ impl NamedPipeClient {
/// let client = named_pipe::ClientOptions::new().open(PIPE_NAME)?;
///
/// loop {
/// // Wait for the socket to be readable
/// // Wait for the pipe to be readable
/// client.readable().await?;
///
/// let mut buf = Vec::with_capacity(4096);
@ -1543,27 +1562,32 @@ impl NamedPipeClient {
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
}
/// Tries to read or write from the socket using a user-provided IO operation.
/// Tries to read or write from the pipe using a user-provided IO operation.
///
/// If the socket is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the socket by manually
/// If the pipe is ready, the provided closure is called. The closure
/// should attempt to perform IO operation from the pipe by manually
/// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a
/// pipe is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value
/// of the closure is then returned by `try_io`.
///
/// If the socket is not ready, then the closure is not called
/// If the pipe is not ready, then the closure is not called
/// and a `WouldBlock` error is returned.
///
/// The closure should only return a `WouldBlock` error if it has performed
/// an IO operation on the socket that failed due to the socket not being
/// an IO operation on the pipe that failed due to the pipe not being
/// ready. Returning a `WouldBlock` error in any other situation will
/// incorrectly clear the readiness flag, which can cause the socket to
/// incorrectly clear the readiness flag, which can cause the pipe to
/// behave incorrectly.
///
/// The closure should not perform the IO operation using any of the methods
/// defined on the Tokio `NamedPipeClient` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly.
/// readiness flag and can cause the pipe to behave incorrectly.
///
/// This method is not intended to be used with combined interests.
/// The closure should perform only one type of IO operation, so it should not
/// require more than one ready state. This method may panic or sleep forever
/// if it is called with a combined interest.
///
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
///
@ -1641,12 +1665,12 @@ macro_rules! bool_flag {
/// See [`ServerOptions::create`].
#[derive(Debug, Clone)]
pub struct ServerOptions {
open_mode: DWORD,
pipe_mode: DWORD,
max_instances: DWORD,
out_buffer_size: DWORD,
in_buffer_size: DWORD,
default_timeout: DWORD,
open_mode: u32,
pipe_mode: u32,
max_instances: u32,
out_buffer_size: u32,
in_buffer_size: u32,
default_timeout: u32,
}
impl ServerOptions {
@ -1663,9 +1687,9 @@ impl ServerOptions {
/// ```
pub fn new() -> ServerOptions {
ServerOptions {
open_mode: winbase::PIPE_ACCESS_DUPLEX | winbase::FILE_FLAG_OVERLAPPED,
pipe_mode: winbase::PIPE_TYPE_BYTE | winbase::PIPE_REJECT_REMOTE_CLIENTS,
max_instances: winbase::PIPE_UNLIMITED_INSTANCES,
open_mode: windows_sys::PIPE_ACCESS_DUPLEX | windows_sys::FILE_FLAG_OVERLAPPED,
pipe_mode: windows_sys::PIPE_TYPE_BYTE | windows_sys::PIPE_REJECT_REMOTE_CLIENTS,
max_instances: windows_sys::PIPE_UNLIMITED_INSTANCES,
out_buffer_size: 65536,
in_buffer_size: 65536,
default_timeout: 0,
@ -1681,11 +1705,10 @@ impl ServerOptions {
///
/// [`dwPipeMode`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
pub fn pipe_mode(&mut self, pipe_mode: PipeMode) -> &mut Self {
self.pipe_mode = match pipe_mode {
PipeMode::Byte => winbase::PIPE_TYPE_BYTE,
PipeMode::Message => winbase::PIPE_TYPE_MESSAGE,
};
let is_msg = matches!(pipe_mode, PipeMode::Message);
// Pipe mode is implemented as a bit flag 0x4. Set is message and unset
// is byte.
bool_flag!(self.pipe_mode, is_msg, windows_sys::PIPE_TYPE_MESSAGE);
self
}
@ -1781,7 +1804,7 @@ impl ServerOptions {
/// # Ok(()) }
/// ```
pub fn access_inbound(&mut self, allowed: bool) -> &mut Self {
bool_flag!(self.open_mode, allowed, winbase::PIPE_ACCESS_INBOUND);
bool_flag!(self.open_mode, allowed, windows_sys::PIPE_ACCESS_INBOUND);
self
}
@ -1879,7 +1902,7 @@ impl ServerOptions {
/// # Ok(()) }
/// ```
pub fn access_outbound(&mut self, allowed: bool) -> &mut Self {
bool_flag!(self.open_mode, allowed, winbase::PIPE_ACCESS_OUTBOUND);
bool_flag!(self.open_mode, allowed, windows_sys::PIPE_ACCESS_OUTBOUND);
self
}
@ -1950,7 +1973,113 @@ impl ServerOptions {
bool_flag!(
self.open_mode,
first,
winbase::FILE_FLAG_FIRST_PIPE_INSTANCE
windows_sys::FILE_FLAG_FIRST_PIPE_INSTANCE
);
self
}
/// Requests permission to modify the pipe's discretionary access control list.
///
/// This corresponds to setting [`WRITE_DAC`] in dwOpenMode.
///
/// # Examples
///
/// ```
/// use std::{io, os::windows::prelude::AsRawHandle, ptr};
//
/// use tokio::net::windows::named_pipe::ServerOptions;
/// use windows_sys::{
/// Win32::Foundation::ERROR_SUCCESS,
/// Win32::Security::DACL_SECURITY_INFORMATION,
/// Win32::Security::Authorization::{SetSecurityInfo, SE_KERNEL_OBJECT},
/// };
///
/// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe";
///
/// # #[tokio::main] async fn main() -> io::Result<()> {
/// let mut pipe_template = ServerOptions::new();
/// pipe_template.write_dac(true);
/// let pipe = pipe_template.create(PIPE_NAME)?;
///
/// unsafe {
/// assert_eq!(
/// ERROR_SUCCESS,
/// SetSecurityInfo(
/// pipe.as_raw_handle() as _,
/// SE_KERNEL_OBJECT,
/// DACL_SECURITY_INFORMATION,
/// ptr::null_mut(),
/// ptr::null_mut(),
/// ptr::null_mut(),
/// ptr::null_mut(),
/// )
/// );
/// }
///
/// # Ok(()) }
/// ```
///
/// ```
/// use std::{io, os::windows::prelude::AsRawHandle, ptr};
//
/// use tokio::net::windows::named_pipe::ServerOptions;
/// use windows_sys::{
/// Win32::Foundation::ERROR_ACCESS_DENIED,
/// Win32::Security::DACL_SECURITY_INFORMATION,
/// Win32::Security::Authorization::{SetSecurityInfo, SE_KERNEL_OBJECT},
/// };
///
/// const PIPE_NAME: &str = r"\\.\pipe\write_dac_pipe_fail";
///
/// # #[tokio::main] async fn main() -> io::Result<()> {
/// let mut pipe_template = ServerOptions::new();
/// pipe_template.write_dac(false);
/// let pipe = pipe_template.create(PIPE_NAME)?;
///
/// unsafe {
/// assert_eq!(
/// ERROR_ACCESS_DENIED,
/// SetSecurityInfo(
/// pipe.as_raw_handle() as _,
/// SE_KERNEL_OBJECT,
/// DACL_SECURITY_INFORMATION,
/// ptr::null_mut(),
/// ptr::null_mut(),
/// ptr::null_mut(),
/// ptr::null_mut(),
/// )
/// );
/// }
///
/// # Ok(()) }
/// ```
///
/// [`WRITE_DAC`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
pub fn write_dac(&mut self, requested: bool) -> &mut Self {
bool_flag!(self.open_mode, requested, windows_sys::WRITE_DAC);
self
}
/// Requests permission to modify the pipe's owner.
///
/// This corresponds to setting [`WRITE_OWNER`] in dwOpenMode.
///
/// [`WRITE_OWNER`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
pub fn write_owner(&mut self, requested: bool) -> &mut Self {
bool_flag!(self.open_mode, requested, windows_sys::WRITE_OWNER);
self
}
/// Requests permission to modify the pipe's system access control list.
///
/// This corresponds to setting [`ACCESS_SYSTEM_SECURITY`] in dwOpenMode.
///
/// [`ACCESS_SYSTEM_SECURITY`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
pub fn access_system_security(&mut self, requested: bool) -> &mut Self {
bool_flag!(
self.open_mode,
requested,
windows_sys::ACCESS_SYSTEM_SECURITY
);
self
}
@ -1962,7 +2091,11 @@ impl ServerOptions {
///
/// [`PIPE_REJECT_REMOTE_CLIENTS`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea#pipe_reject_remote_clients
pub fn reject_remote_clients(&mut self, reject: bool) -> &mut Self {
bool_flag!(self.pipe_mode, reject, winbase::PIPE_REJECT_REMOTE_CLIENTS);
bool_flag!(
self.pipe_mode,
reject,
windows_sys::PIPE_REJECT_REMOTE_CLIENTS
);
self
}
@ -1984,7 +2117,7 @@ impl ServerOptions {
/// ```
/// use std::io;
/// use tokio::net::windows::named_pipe::{ServerOptions, ClientOptions};
/// use winapi::shared::winerror;
/// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
///
/// const PIPE_NAME: &str = r"\\.\pipe\tokio-named-pipe-max-instances";
///
@ -2000,11 +2133,11 @@ impl ServerOptions {
///
/// // Too many servers!
/// let e = server.create(PIPE_NAME).unwrap_err();
/// assert_eq!(e.raw_os_error(), Some(winerror::ERROR_PIPE_BUSY as i32));
/// assert_eq!(e.raw_os_error(), Some(ERROR_PIPE_BUSY as i32));
///
/// // Still too many servers even if we specify a higher value!
/// let e = server.max_instances(100).create(PIPE_NAME).unwrap_err();
/// assert_eq!(e.raw_os_error(), Some(winerror::ERROR_PIPE_BUSY as i32));
/// assert_eq!(e.raw_os_error(), Some(ERROR_PIPE_BUSY as i32));
/// # Ok(()) }
/// ```
///
@ -2020,9 +2153,10 @@ impl ServerOptions {
/// let builder = ServerOptions::new().max_instances(255);
/// # Ok(()) }
/// ```
#[track_caller]
pub fn max_instances(&mut self, instances: usize) -> &mut Self {
assert!(instances < 255, "cannot specify more than 254 instances");
self.max_instances = instances as DWORD;
self.max_instances = instances as u32;
self
}
@ -2032,7 +2166,7 @@ impl ServerOptions {
///
/// [`nOutBufferSize`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
pub fn out_buffer_size(&mut self, buffer: u32) -> &mut Self {
self.out_buffer_size = buffer as DWORD;
self.out_buffer_size = buffer;
self
}
@ -2042,7 +2176,7 @@ impl ServerOptions {
///
/// [`nInBufferSize`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea
pub fn in_buffer_size(&mut self, buffer: u32) -> &mut Self {
self.in_buffer_size = buffer as DWORD;
self.in_buffer_size = buffer;
self
}
@ -2099,7 +2233,7 @@ impl ServerOptions {
///
/// [`create`]: ServerOptions::create
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
/// [`SECURITY_ATTRIBUTES`]: crate::winapi::um::minwinbase::SECURITY_ATTRIBUTES
/// [`SECURITY_ATTRIBUTES`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Security/struct.SECURITY_ATTRIBUTES.html
pub unsafe fn create_with_security_attributes_raw(
&self,
addr: impl AsRef<OsStr>,
@ -2107,7 +2241,7 @@ impl ServerOptions {
) -> io::Result<NamedPipeServer> {
let addr = encode_addr(addr);
let h = namedpipeapi::CreateNamedPipeW(
let h = windows_sys::CreateNamedPipeW(
addr.as_ptr(),
self.open_mode,
self.pipe_mode,
@ -2118,11 +2252,11 @@ impl ServerOptions {
attrs as *mut _,
);
if h == handleapi::INVALID_HANDLE_VALUE {
if h == windows_sys::INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error());
}
NamedPipeServer::from_raw_handle(h)
NamedPipeServer::from_raw_handle(h as _)
}
}
@ -2132,8 +2266,8 @@ impl ServerOptions {
/// See [`ClientOptions::open`].
#[derive(Debug, Clone)]
pub struct ClientOptions {
desired_access: DWORD,
security_qos_flags: DWORD,
desired_access: u32,
security_qos_flags: u32,
}
impl ClientOptions {
@ -2152,8 +2286,9 @@ impl ClientOptions {
/// ```
pub fn new() -> Self {
Self {
desired_access: winnt::GENERIC_READ | winnt::GENERIC_WRITE,
security_qos_flags: winbase::SECURITY_IDENTIFICATION | winbase::SECURITY_SQOS_PRESENT,
desired_access: windows_sys::GENERIC_READ | windows_sys::GENERIC_WRITE,
security_qos_flags: windows_sys::SECURITY_IDENTIFICATION
| windows_sys::SECURITY_SQOS_PRESENT,
}
}
@ -2164,7 +2299,7 @@ impl ClientOptions {
/// [`GENERIC_READ`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
pub fn read(&mut self, allowed: bool) -> &mut Self {
bool_flag!(self.desired_access, allowed, winnt::GENERIC_READ);
bool_flag!(self.desired_access, allowed, windows_sys::GENERIC_READ);
self
}
@ -2175,7 +2310,7 @@ impl ClientOptions {
/// [`GENERIC_WRITE`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
pub fn write(&mut self, allowed: bool) -> &mut Self {
bool_flag!(self.desired_access, allowed, winnt::GENERIC_WRITE);
bool_flag!(self.desired_access, allowed, windows_sys::GENERIC_WRITE);
self
}
@ -2198,11 +2333,11 @@ impl ClientOptions {
/// automatically when using this method.
///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
/// [`SECURITY_IDENTIFICATION`]: crate::winapi::um::winbase::SECURITY_IDENTIFICATION
/// [`SECURITY_IDENTIFICATION`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Storage/FileSystem/constant.SECURITY_IDENTIFICATION.html
/// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
pub fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
// See: https://github.com/rust-lang/rust/pull/58216
self.security_qos_flags = flags | winbase::SECURITY_SQOS_PRESENT;
self.security_qos_flags = flags | windows_sys::SECURITY_SQOS_PRESENT;
self
}
@ -2227,8 +2362,7 @@ impl ClientOptions {
/// but the server is not currently waiting for a connection. Please see the
/// examples for how to check for this error.
///
/// [`ERROR_PIPE_BUSY`]: crate::winapi::shared::winerror::ERROR_PIPE_BUSY
/// [`winapi`]: crate::winapi
/// [`ERROR_PIPE_BUSY`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_PIPE_BUSY.html
/// [enabled I/O]: crate::runtime::Builder::enable_io
/// [Tokio Runtime]: crate::runtime::Runtime
///
@ -2239,7 +2373,7 @@ impl ClientOptions {
/// use std::time::Duration;
/// use tokio::net::windows::named_pipe::ClientOptions;
/// use tokio::time;
/// use winapi::shared::winerror;
/// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
///
/// const PIPE_NAME: &str = r"\\.\pipe\mynamedpipe";
///
@ -2247,7 +2381,7 @@ impl ClientOptions {
/// let client = loop {
/// match ClientOptions::new().open(PIPE_NAME) {
/// Ok(client) => break client,
/// Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
/// Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
/// Err(e) => return Err(e),
/// }
///
@ -2277,7 +2411,7 @@ impl ClientOptions {
///
/// [`open`]: ClientOptions::open
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
/// [`SECURITY_ATTRIBUTES`]: crate::winapi::um::minwinbase::SECURITY_ATTRIBUTES
/// [`SECURITY_ATTRIBUTES`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Security/struct.SECURITY_ATTRIBUTES.html
pub unsafe fn open_with_security_attributes_raw(
&self,
addr: impl AsRef<OsStr>,
@ -2286,28 +2420,28 @@ impl ClientOptions {
let addr = encode_addr(addr);
// NB: We could use a platform specialized `OpenOptions` here, but since
// we have access to winapi it ultimately doesn't hurt to use
// we have access to windows_sys it ultimately doesn't hurt to use
// `CreateFile` explicitly since it allows the use of our already
// well-structured wide `addr` to pass into CreateFileW.
let h = fileapi::CreateFileW(
let h = windows_sys::CreateFileW(
addr.as_ptr(),
self.desired_access,
0,
attrs as *mut _,
fileapi::OPEN_EXISTING,
windows_sys::OPEN_EXISTING,
self.get_flags(),
ptr::null_mut(),
0,
);
if h == handleapi::INVALID_HANDLE_VALUE {
if h == windows_sys::INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error());
}
NamedPipeClient::from_raw_handle(h)
NamedPipeClient::from_raw_handle(h as _)
}
fn get_flags(&self) -> u32 {
self.security_qos_flags | winbase::FILE_FLAG_OVERLAPPED
self.security_qos_flags | windows_sys::FILE_FLAG_OVERLAPPED
}
}
@ -2320,16 +2454,19 @@ pub enum PipeMode {
/// Data is written to the pipe as a stream of bytes. The pipe does not
/// distinguish bytes written during different write operations.
///
/// Corresponds to [`PIPE_TYPE_BYTE`][crate::winapi::um::winbase::PIPE_TYPE_BYTE].
/// Corresponds to [`PIPE_TYPE_BYTE`].
///
/// [`PIPE_TYPE_BYTE`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_TYPE_BYTE.html
Byte,
/// Data is written to the pipe as a stream of messages. The pipe treats the
/// bytes written during each write operation as a message unit. Any reading
/// on a named pipe returns [`ERROR_MORE_DATA`] when a message is not read
/// completely.
///
/// Corresponds to [`PIPE_TYPE_MESSAGE`][crate::winapi::um::winbase::PIPE_TYPE_MESSAGE].
/// Corresponds to [`PIPE_TYPE_MESSAGE`].
///
/// [`ERROR_MORE_DATA`]: crate::winapi::shared::winerror::ERROR_MORE_DATA
/// [`ERROR_MORE_DATA`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/Foundation/constant.ERROR_MORE_DATA.html
/// [`PIPE_TYPE_MESSAGE`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_TYPE_MESSAGE.html
Message,
}
@ -2339,11 +2476,15 @@ pub enum PipeMode {
pub enum PipeEnd {
/// The named pipe refers to the client end of a named pipe instance.
///
/// Corresponds to [`PIPE_CLIENT_END`][crate::winapi::um::winbase::PIPE_CLIENT_END].
/// Corresponds to [`PIPE_CLIENT_END`].
///
/// [`PIPE_CLIENT_END`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_CLIENT_END.html
Client,
/// The named pipe refers to the server end of a named pipe instance.
///
/// Corresponds to [`PIPE_SERVER_END`][crate::winapi::um::winbase::PIPE_SERVER_END].
/// Corresponds to [`PIPE_SERVER_END`].
///
/// [`PIPE_SERVER_END`]: https://docs.rs/windows-sys/latest/windows_sys/Win32/System/Pipes/constant.PIPE_SERVER_END.html
Server,
}
@ -2381,26 +2522,26 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
let mut in_buffer_size = 0;
let mut max_instances = 0;
let result = namedpipeapi::GetNamedPipeInfo(
handle,
let result = windows_sys::GetNamedPipeInfo(
handle as _,
&mut flags,
&mut out_buffer_size,
&mut in_buffer_size,
&mut max_instances,
);
if result == FALSE {
if result == 0 {
return Err(io::Error::last_os_error());
}
let mut end = PipeEnd::Client;
let mut mode = PipeMode::Byte;
if flags & winbase::PIPE_SERVER_END != 0 {
if flags & windows_sys::PIPE_SERVER_END != 0 {
end = PipeEnd::Server;
}
if flags & winbase::PIPE_TYPE_MESSAGE != 0 {
if flags & windows_sys::PIPE_TYPE_MESSAGE != 0 {
mode = PipeMode::Message;
}
@ -2412,3 +2553,48 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
max_instances,
})
}
#[cfg(test)]
mod test {
use self::windows_sys::{PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_BYTE, PIPE_TYPE_MESSAGE};
use super::*;
#[test]
fn opts_default_pipe_mode() {
let opts = ServerOptions::new();
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
}
#[test]
fn opts_unset_reject_remote() {
let mut opts = ServerOptions::new();
opts.reject_remote_clients(false);
assert_eq!(opts.pipe_mode & PIPE_REJECT_REMOTE_CLIENTS, 0);
}
#[test]
fn opts_set_pipe_mode_maintains_reject_remote_clients() {
let mut opts = ServerOptions::new();
opts.pipe_mode(PipeMode::Byte);
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
opts.reject_remote_clients(false);
opts.pipe_mode(PipeMode::Byte);
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE);
opts.reject_remote_clients(true);
opts.pipe_mode(PipeMode::Byte);
assert_eq!(opts.pipe_mode, PIPE_TYPE_BYTE | PIPE_REJECT_REMOTE_CLIENTS);
opts.reject_remote_clients(false);
opts.pipe_mode(PipeMode::Message);
assert_eq!(opts.pipe_mode, PIPE_TYPE_MESSAGE);
opts.reject_remote_clients(true);
opts.pipe_mode(PipeMode::Message);
assert_eq!(
opts.pipe_mode,
PIPE_TYPE_MESSAGE | PIPE_REJECT_REMOTE_CLIENTS
);
}
}

View file

@ -1,74 +0,0 @@
#![cfg_attr(not(feature = "full"), allow(dead_code))]
use crate::park::{Park, Unpark};
use std::fmt;
use std::time::Duration;
pub(crate) enum Either<A, B> {
A(A),
B(B),
}
impl<A, B> Park for Either<A, B>
where
A: Park,
B: Park,
{
type Unpark = Either<A::Unpark, B::Unpark>;
type Error = Either<A::Error, B::Error>;
fn unpark(&self) -> Self::Unpark {
match self {
Either::A(a) => Either::A(a.unpark()),
Either::B(b) => Either::B(b.unpark()),
}
}
fn park(&mut self) -> Result<(), Self::Error> {
match self {
Either::A(a) => a.park().map_err(Either::A),
Either::B(b) => b.park().map_err(Either::B),
}
}
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error> {
match self {
Either::A(a) => a.park_timeout(duration).map_err(Either::A),
Either::B(b) => b.park_timeout(duration).map_err(Either::B),
}
}
fn shutdown(&mut self) {
match self {
Either::A(a) => a.shutdown(),
Either::B(b) => b.shutdown(),
}
}
}
impl<A, B> Unpark for Either<A, B>
where
A: Unpark,
B: Unpark,
{
fn unpark(&self) {
match self {
Either::A(a) => a.unpark(),
Either::B(b) => b.unpark(),
}
}
}
impl<A, B> fmt::Debug for Either<A, B>
where
A: fmt::Debug,
B: fmt::Debug,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Either::A(a) => a.fmt(fmt),
Either::B(b) => b.fmt(fmt),
}
}
}

View file

@ -1,117 +0,0 @@
//! Abstraction over blocking and unblocking the current thread.
//!
//! Provides an abstraction over blocking the current thread. This is similar to
//! the park / unpark constructs provided by `std` but made generic. This allows
//! embedding custom functionality to perform when the thread is blocked.
//!
//! A blocked `Park` instance is unblocked by calling `unpark` on its
//! `Unpark` handle.
//!
//! The `ParkThread` struct implements `Park` using `thread::park` to put the
//! thread to sleep. The Tokio reactor also implements park, but uses
//! `mio::Poll` to block the thread instead.
//!
//! The `Park` trait is composable. A timer implementation might decorate a
//! `Park` implementation by checking if any timeouts have elapsed after the
//! inner `Park` implementation unblocks.
//!
//! # Model
//!
//! Conceptually, each `Park` instance has an associated token, which is
//! initially not present:
//!
//! * The `park` method blocks the current thread unless or until the token is
//! available, at which point it atomically consumes the token.
//! * The `unpark` method atomically makes the token available if it wasn't
//! already.
//!
//! Some things to note:
//!
//! * If `unpark` is called before `park`, the next call to `park` will
//! **not** block the thread.
//! * **Spurious** wakeups are permitted, i.e., the `park` method may unblock
//! even if `unpark` was not called.
//! * `park_timeout` does the same as `park` but allows specifying a maximum
//! time to block the thread for.
cfg_rt! {
pub(crate) mod either;
}
#[cfg(any(feature = "rt", feature = "sync"))]
pub(crate) mod thread;
use std::fmt::Debug;
use std::sync::Arc;
use std::time::Duration;
/// Blocks the current thread.
pub(crate) trait Park {
/// Unpark handle type for the `Park` implementation.
type Unpark: Unpark;
/// Error returned by `park`.
type Error: Debug;
/// Gets a new `Unpark` handle associated with this `Park` instance.
fn unpark(&self) -> Self::Unpark;
/// Blocks the current thread unless or until the token is available.
///
/// A call to `park` does not guarantee that the thread will remain blocked
/// forever, and callers should be prepared for this possibility. This
/// function may wakeup spuriously for any reason.
///
/// # Panics
///
/// This function **should** not panic, but ultimately, panics are left as
/// an implementation detail. Refer to the documentation for the specific
/// `Park` implementation.
fn park(&mut self) -> Result<(), Self::Error>;
/// Parks the current thread for at most `duration`.
///
/// This function is the same as `park` but allows specifying a maximum time
/// to block the thread for.
///
/// Same as `park`, there is no guarantee that the thread will remain
/// blocked for any amount of time. Spurious wakeups are permitted for any
/// reason.
///
/// # Panics
///
/// This function **should** not panic, but ultimately, panics are left as
/// an implementation detail. Refer to the documentation for the specific
/// `Park` implementation.
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
/// Releases all resources holded by the parker for proper leak-free shutdown.
fn shutdown(&mut self);
}
/// Unblock a thread blocked by the associated `Park` instance.
pub(crate) trait Unpark: Sync + Send + 'static {
/// Unblocks a thread that is blocked by the associated `Park` handle.
///
/// Calling `unpark` atomically makes available the unpark token, if it is
/// not already available.
///
/// # Panics
///
/// This function **should** not panic, but ultimately, panics are left as
/// an implementation detail. Refer to the documentation for the specific
/// `Unpark` implementation.
fn unpark(&self);
}
impl Unpark for Box<dyn Unpark> {
fn unpark(&self) {
(**self).unpark()
}
}
impl Unpark for Arc<dyn Unpark> {
fn unpark(&self) {
(**self).unpark()
}
}

View file

@ -690,6 +690,36 @@ impl Command {
self
}
/// Sets the process group ID (PGID) of the child process. Equivalent to a
/// setpgid call in the child process, but may be more efficient.
///
/// Process groups determine which processes receive signals.
///
/// **Note**: This is an [unstable API][unstable] but will be stabilised once
/// tokio's MSRV is sufficiently new. See [the documentation on
/// unstable features][unstable] for details about using unstable features.
///
/// If you want similar behaviour without using this unstable feature you can
/// create a [`std::process::Command`] and convert that into a
/// [`tokio::process::Command`] using the `From` trait.
///
/// [unstable]: crate#unstable-features
/// [`tokio::process::Command`]: crate::process::Command
///
/// ```no_run
/// use tokio::process::Command;
///
/// let command = Command::new("ls")
/// .process_group(0);
/// ```
#[cfg(unix)]
#[cfg(tokio_unstable)]
#[cfg_attr(docsrs, doc(cfg(all(unix, tokio_unstable))))]
pub fn process_group(&mut self, pgroup: i32) -> &mut Command {
self.std.process_group(pgroup);
self
}
/// Executes the command as a child process, returning a handle to it.
///
/// By default, stdin, stdout and stderr are inherited from the parent.
@ -924,7 +954,7 @@ where
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// Keep track of task budget
let coop = ready!(crate::coop::poll_proceed(cx));
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
let ret = Pin::new(&mut self.inner).poll(cx);
@ -1285,41 +1315,51 @@ impl ChildStderr {
impl AsyncWrite for ChildStdin {
fn poll_write(
self: Pin<&mut Self>,
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.inner.poll_write(cx, buf)
Pin::new(&mut self.inner).poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_flush(cx)
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.inner).poll_shutdown(cx)
}
fn poll_write_vectored(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
Pin::new(&mut self.inner).poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
self.inner.is_write_vectored()
}
}
impl AsyncRead for ChildStdout {
fn poll_read(
self: Pin<&mut Self>,
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
// Safety: pipes support reading into uninitialized memory
unsafe { self.inner.poll_read(cx, buf) }
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}
impl AsyncRead for ChildStderr {
fn poll_read(
self: Pin<&mut Self>,
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
// Safety: pipes support reading into uninitialized memory
unsafe { self.inner.poll_read(cx, buf) }
Pin::new(&mut self.inner).poll_read(cx, buf)
}
}

View file

@ -21,23 +21,20 @@
//! processes in general aren't scalable (e.g. millions) so it shouldn't be that
//! bad in theory...
pub(crate) mod driver;
pub(crate) mod orphan;
use orphan::{OrphanQueue, OrphanQueueImpl, Wait};
mod reap;
use reap::Reaper;
use crate::io::PollEvented;
use crate::io::{AsyncRead, AsyncWrite, PollEvented, ReadBuf};
use crate::process::kill::Kill;
use crate::process::SpawnedChild;
use crate::signal::unix::driver::Handle as SignalHandle;
use crate::runtime::signal::Handle as SignalHandle;
use crate::signal::unix::{signal, Signal, SignalKind};
use mio::event::Source;
use mio::unix::SourceFd;
use once_cell::sync::Lazy;
use std::fmt;
use std::fs::File;
use std::future::Future;
@ -64,25 +61,41 @@ impl Kill for StdChild {
}
}
static ORPHAN_QUEUE: Lazy<OrphanQueueImpl<StdChild>> = Lazy::new(OrphanQueueImpl::new);
cfg_not_has_const_mutex_new! {
fn get_orphan_queue() -> &'static OrphanQueueImpl<StdChild> {
use crate::util::once_cell::OnceCell;
static ORPHAN_QUEUE: OnceCell<OrphanQueueImpl<StdChild>> = OnceCell::new();
ORPHAN_QUEUE.get(OrphanQueueImpl::new)
}
}
cfg_has_const_mutex_new! {
fn get_orphan_queue() -> &'static OrphanQueueImpl<StdChild> {
static ORPHAN_QUEUE: OrphanQueueImpl<StdChild> = OrphanQueueImpl::new();
&ORPHAN_QUEUE
}
}
pub(crate) struct GlobalOrphanQueue;
impl fmt::Debug for GlobalOrphanQueue {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
ORPHAN_QUEUE.fmt(fmt)
get_orphan_queue().fmt(fmt)
}
}
impl GlobalOrphanQueue {
fn reap_orphans(handle: &SignalHandle) {
ORPHAN_QUEUE.reap_orphans(handle)
pub(crate) fn reap_orphans(handle: &SignalHandle) {
get_orphan_queue().reap_orphans(handle)
}
}
impl OrphanQueue<StdChild> for GlobalOrphanQueue {
fn push_orphan(&self, orphan: StdChild) {
ORPHAN_QUEUE.push_orphan(orphan)
get_orphan_queue().push_orphan(orphan)
}
}
@ -143,7 +156,7 @@ impl Future for Child {
#[derive(Debug)]
pub(crate) struct Pipe {
// Actually a pipe and not a File. However, we are reusing `File` to get
// Actually a pipe is not a File. However, we are reusing `File` to get
// close on drop. This is a similar trick as `mio`.
fd: File,
}
@ -169,6 +182,10 @@ impl<'a> io::Write for &'a Pipe {
fn flush(&mut self) -> io::Result<()> {
(&self.fd).flush()
}
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
(&self.fd).write_vectored(bufs)
}
}
impl AsRawFd for Pipe {
@ -177,8 +194,8 @@ impl AsRawFd for Pipe {
}
}
pub(crate) fn convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio> {
let mut fd = io.into_inner()?.fd;
pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result<Stdio> {
let mut fd = io.inner.into_inner()?.fd;
// Ensure that the fd to be inherited is set to *blocking* mode, as this
// is the default that virtually all programs expect to have. Those
@ -213,7 +230,62 @@ impl Source for Pipe {
}
}
pub(crate) type ChildStdio = PollEvented<Pipe>;
pub(crate) struct ChildStdio {
inner: PollEvented<Pipe>,
}
impl fmt::Debug for ChildStdio {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
self.inner.fmt(fmt)
}
}
impl AsRawFd for ChildStdio {
fn as_raw_fd(&self) -> RawFd {
self.inner.as_raw_fd()
}
}
impl AsyncWrite for ChildStdio {
fn poll_write(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
self.inner.poll_write(cx, buf)
}
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(()))
}
fn poll_write_vectored(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
bufs: &[io::IoSlice<'_>],
) -> Poll<Result<usize, io::Error>> {
self.inner.poll_write_vectored(cx, bufs)
}
fn is_write_vectored(&self) -> bool {
true
}
}
impl AsyncRead for ChildStdio {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
// Safety: pipes support reading into uninitialized memory
unsafe { self.inner.poll_read(cx, buf) }
}
}
fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
unsafe {
@ -238,7 +310,7 @@ fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()>
Ok(())
}
pub(super) fn stdio<T>(io: T) -> io::Result<PollEvented<Pipe>>
pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
where
T: IntoRawFd,
{
@ -246,5 +318,5 @@ where
let mut pipe = Pipe::from(io);
set_nonblocking(&mut pipe, true)?;
PollEvented::new(pipe)
PollEvented::new(pipe).map(|inner| ChildStdio { inner })
}

View file

@ -1,5 +1,5 @@
use crate::loom::sync::{Mutex, MutexGuard};
use crate::signal::unix::driver::Handle as SignalHandle;
use crate::runtime::signal::Handle as SignalHandle;
use crate::signal::unix::{signal_with_handle, SignalKind};
use crate::sync::watch;
use std::io;
@ -43,10 +43,21 @@ pub(crate) struct OrphanQueueImpl<T> {
}
impl<T> OrphanQueueImpl<T> {
pub(crate) fn new() -> Self {
Self {
sigchild: Mutex::new(None),
queue: Mutex::new(Vec::new()),
cfg_not_has_const_mutex_new! {
pub(crate) fn new() -> Self {
Self {
sigchild: Mutex::new(None),
queue: Mutex::new(Vec::new()),
}
}
}
cfg_has_const_mutex_new! {
pub(crate) const fn new() -> Self {
Self {
sigchild: Mutex::const_new(None),
queue: Mutex::const_new(Vec::new()),
}
}
}
@ -120,8 +131,8 @@ where
#[cfg(all(test, not(loom)))]
pub(crate) mod test {
use super::*;
use crate::io::driver::Driver as IoDriver;
use crate::signal::unix::driver::{Driver as SignalDriver, Handle as SignalHandle};
use crate::runtime::io::Driver as IoDriver;
use crate::runtime::signal::{Driver as SignalDriver, Handle as SignalHandle};
use crate::sync::watch;
use std::cell::{Cell, RefCell};
use std::io;
@ -283,7 +294,8 @@ pub(crate) mod test {
#[cfg_attr(miri, ignore)] // Miri does not support epoll.
#[test]
fn does_not_register_signal_if_queue_empty() {
let signal_driver = IoDriver::new().and_then(SignalDriver::new).unwrap();
let (io_driver, io_handle) = IoDriver::new(1024).unwrap();
let signal_driver = SignalDriver::new(io_driver, &io_handle).unwrap();
let handle = signal_driver.handle();
let orphanage = OrphanQueueImpl::new();

View file

@ -15,29 +15,31 @@
//! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot
//! from then on out.
use crate::io::PollEvented;
use crate::io::{blocking::Blocking, AsyncRead, AsyncWrite, ReadBuf};
use crate::process::kill::Kill;
use crate::process::SpawnedChild;
use crate::sync::oneshot;
use mio::windows::NamedPipe;
use std::fmt;
use std::fs::File as StdFile;
use std::future::Future;
use std::io;
use std::os::windows::prelude::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle};
use std::os::windows::prelude::{AsRawHandle, IntoRawHandle, RawHandle};
use std::pin::Pin;
use std::process::Stdio;
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
use std::ptr;
use std::task::Context;
use std::task::Poll;
use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::um::handleapi::{DuplicateHandle, INVALID_HANDLE_VALUE};
use winapi::um::processthreadsapi::GetCurrentProcess;
use winapi::um::threadpoollegacyapiset::UnregisterWaitEx;
use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE};
use winapi::um::winnt::{
BOOLEAN, DUPLICATE_SAME_ACCESS, HANDLE, PVOID, WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE,
use std::sync::Arc;
use std::task::{Context, Poll};
use windows_sys::{
Win32::Foundation::{
DuplicateHandle, BOOLEAN, DUPLICATE_SAME_ACCESS, HANDLE, INVALID_HANDLE_VALUE,
},
Win32::System::Threading::{
GetCurrentProcess, RegisterWaitForSingleObject, UnregisterWaitEx, WT_EXECUTEINWAITTHREAD,
WT_EXECUTEONLYONCE,
},
Win32::System::WindowsProgramming::INFINITE,
};
#[must_use = "futures do nothing unless polled"]
@ -119,11 +121,11 @@ impl Future for Child {
}
let (tx, rx) = oneshot::channel();
let ptr = Box::into_raw(Box::new(Some(tx)));
let mut wait_object = ptr::null_mut();
let mut wait_object = 0;
let rc = unsafe {
RegisterWaitForSingleObject(
&mut wait_object,
inner.child.as_raw_handle(),
inner.child.as_raw_handle() as _,
Some(callback),
ptr as *mut _,
INFINITE,
@ -162,37 +164,106 @@ impl Drop for Waiting {
}
}
unsafe extern "system" fn callback(ptr: PVOID, _timer_fired: BOOLEAN) {
unsafe extern "system" fn callback(ptr: *mut std::ffi::c_void, _timer_fired: BOOLEAN) {
let complete = &mut *(ptr as *mut Option<oneshot::Sender<()>>);
let _ = complete.take().unwrap().send(());
}
pub(crate) type ChildStdio = PollEvented<NamedPipe>;
#[derive(Debug)]
struct ArcFile(Arc<StdFile>);
pub(super) fn stdio<T>(io: T) -> io::Result<PollEvented<NamedPipe>>
impl io::Read for ArcFile {
fn read(&mut self, bytes: &mut [u8]) -> io::Result<usize> {
(&*self.0).read(bytes)
}
}
impl io::Write for ArcFile {
fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
(&*self.0).write(bytes)
}
fn flush(&mut self) -> io::Result<()> {
(&*self.0).flush()
}
}
#[derive(Debug)]
pub(crate) struct ChildStdio {
// Used for accessing the raw handle, even if the io version is busy
raw: Arc<StdFile>,
// For doing I/O operations asynchronously
io: Blocking<ArcFile>,
}
impl AsRawHandle for ChildStdio {
fn as_raw_handle(&self) -> RawHandle {
self.raw.as_raw_handle()
}
}
impl AsyncRead for ChildStdio {
fn poll_read(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_read(cx, buf)
}
}
impl AsyncWrite for ChildStdio {
fn poll_write(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Pin::new(&mut self.io).poll_write(cx, buf)
}
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_flush(cx)
}
fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Pin::new(&mut self.io).poll_shutdown(cx)
}
}
pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
where
T: IntoRawHandle,
{
let pipe = unsafe { NamedPipe::from_raw_handle(io.into_raw_handle()) };
PollEvented::new(pipe)
use std::os::windows::prelude::FromRawHandle;
let raw = Arc::new(unsafe { StdFile::from_raw_handle(io.into_raw_handle()) });
let io = Blocking::new(ArcFile(raw.clone()));
Ok(ChildStdio { raw, io })
}
pub(crate) fn convert_to_stdio(io: PollEvented<NamedPipe>) -> io::Result<Stdio> {
let named_pipe = io.into_inner()?;
pub(crate) fn convert_to_stdio(child_stdio: ChildStdio) -> io::Result<Stdio> {
let ChildStdio { raw, io } = child_stdio;
drop(io); // Try to drop the Arc count here
Arc::try_unwrap(raw)
.or_else(|raw| duplicate_handle(&*raw))
.map(Stdio::from)
}
fn duplicate_handle<T: AsRawHandle>(io: &T) -> io::Result<StdFile> {
use std::os::windows::prelude::FromRawHandle;
// Mio does not implement `IntoRawHandle` for `NamedPipe`, so we'll manually
// duplicate the handle here...
unsafe {
let mut dup_handle = INVALID_HANDLE_VALUE;
let cur_proc = GetCurrentProcess();
let status = DuplicateHandle(
cur_proc,
named_pipe.as_raw_handle(),
io.as_raw_handle() as _,
cur_proc,
&mut dup_handle,
0 as DWORD,
FALSE,
0,
0,
DUPLICATE_SAME_ACCESS,
);
@ -200,6 +271,6 @@ pub(crate) fn convert_to_stdio(io: PollEvented<NamedPipe>) -> io::Result<Stdio>
return Err(io::Error::last_os_error());
}
Ok(Stdio::from_raw_handle(dup_handle))
Ok(StdFile::from_raw_handle(dup_handle as _))
}
}

View file

@ -4,16 +4,19 @@
//! compilation.
mod pool;
pub(crate) use pool::{spawn_blocking, BlockingPool, Mandatory, Spawner, Task};
pub(crate) use pool::{spawn_blocking, BlockingPool, Spawner};
cfg_fs! {
pub(crate) use pool::spawn_mandatory_blocking;
}
cfg_trace! {
pub(crate) use pool::Mandatory;
}
mod schedule;
mod shutdown;
mod task;
pub(crate) use schedule::NoopSchedule;
pub(crate) use task::BlockingTask;
use crate::runtime::Builder;

View file

@ -2,15 +2,16 @@
use crate::loom::sync::{Arc, Condvar, Mutex};
use crate::loom::thread;
use crate::runtime::blocking::schedule::NoopSchedule;
use crate::runtime::blocking::shutdown;
use crate::runtime::blocking::schedule::BlockingSchedule;
use crate::runtime::blocking::{shutdown, BlockingTask};
use crate::runtime::builder::ThreadNameFn;
use crate::runtime::context;
use crate::runtime::task::{self, JoinHandle};
use crate::runtime::{Builder, Callback, ToHandle};
use crate::runtime::{Builder, Callback, Handle};
use std::collections::{HashMap, VecDeque};
use std::fmt;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration;
pub(crate) struct BlockingPool {
@ -23,6 +24,53 @@ pub(crate) struct Spawner {
inner: Arc<Inner>,
}
#[derive(Default)]
pub(crate) struct SpawnerMetrics {
num_threads: AtomicUsize,
num_idle_threads: AtomicUsize,
queue_depth: AtomicUsize,
}
impl SpawnerMetrics {
fn num_threads(&self) -> usize {
self.num_threads.load(Ordering::Relaxed)
}
fn num_idle_threads(&self) -> usize {
self.num_idle_threads.load(Ordering::Relaxed)
}
cfg_metrics! {
fn queue_depth(&self) -> usize {
self.queue_depth.load(Ordering::Relaxed)
}
}
fn inc_num_threads(&self) {
self.num_threads.fetch_add(1, Ordering::Relaxed);
}
fn dec_num_threads(&self) {
self.num_threads.fetch_sub(1, Ordering::Relaxed);
}
fn inc_num_idle_threads(&self) {
self.num_idle_threads.fetch_add(1, Ordering::Relaxed);
}
fn dec_num_idle_threads(&self) -> usize {
self.num_idle_threads.fetch_sub(1, Ordering::Relaxed)
}
fn inc_queue_depth(&self) {
self.queue_depth.fetch_add(1, Ordering::Relaxed);
}
fn dec_queue_depth(&self) {
self.queue_depth.fetch_sub(1, Ordering::Relaxed);
}
}
struct Inner {
/// State shared between worker threads.
shared: Mutex<Shared>,
@ -47,12 +95,13 @@ struct Inner {
// Customizable wait timeout.
keep_alive: Duration,
// Metrics about the pool.
metrics: SpawnerMetrics,
}
struct Shared {
queue: VecDeque<Task>,
num_th: usize,
num_idle: u32,
num_notify: u32,
shutdown: bool,
shutdown_tx: Option<shutdown::Sender>,
@ -71,7 +120,7 @@ struct Shared {
}
pub(crate) struct Task {
task: task::UnownedTask<NoopSchedule>,
task: task::UnownedTask<BlockingSchedule>,
mandatory: Mandatory,
}
@ -82,8 +131,27 @@ pub(crate) enum Mandatory {
NonMandatory,
}
pub(crate) enum SpawnError {
/// Pool is shutting down and the task was not scheduled
ShuttingDown,
/// There are no worker threads available to take the task
/// and the OS failed to spawn a new one
NoThreads(io::Error),
}
impl From<SpawnError> for io::Error {
fn from(e: SpawnError) -> Self {
match e {
SpawnError::ShuttingDown => {
io::Error::new(io::ErrorKind::Other, "blocking pool shutting down")
}
SpawnError::NoThreads(e) => e,
}
}
}
impl Task {
pub(crate) fn new(task: task::UnownedTask<NoopSchedule>, mandatory: Mandatory) -> Task {
pub(crate) fn new(task: task::UnownedTask<BlockingSchedule>, mandatory: Mandatory) -> Task {
Task { task, mandatory }
}
@ -105,12 +173,13 @@ const KEEP_ALIVE: Duration = Duration::from_secs(10);
/// Tasks will be scheduled as non-mandatory, meaning they may not get executed
/// in case of runtime shutdown.
#[track_caller]
#[cfg_attr(tokio_wasi, allow(dead_code))]
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let rt = context::current();
let rt = Handle::current();
rt.spawn_blocking(func)
}
@ -128,8 +197,8 @@ cfg_fs! {
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let rt = context::current();
rt.as_inner().spawn_mandatory_blocking(&rt, func)
let rt = Handle::current();
rt.inner.blocking_spawner().spawn_mandatory_blocking(&rt, func)
}
}
@ -145,8 +214,6 @@ impl BlockingPool {
inner: Arc::new(Inner {
shared: Mutex::new(Shared {
queue: VecDeque::new(),
num_th: 0,
num_idle: 0,
num_notify: 0,
shutdown: false,
shutdown_tx: Some(shutdown_tx),
@ -161,6 +228,7 @@ impl BlockingPool {
before_stop: builder.before_stop.clone(),
thread_cap,
keep_alive,
metrics: Default::default(),
}),
},
shutdown_rx,
@ -220,7 +288,104 @@ impl fmt::Debug for BlockingPool {
// ===== impl Spawner =====
impl Spawner {
pub(crate) fn spawn(&self, task: Task, rt: &dyn ToHandle) -> Result<(), ()> {
#[track_caller]
pub(crate) fn spawn_blocking<F, R>(&self, rt: &Handle, func: F) -> JoinHandle<R>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let (join_handle, spawn_result) =
if cfg!(debug_assertions) && std::mem::size_of::<F>() > 2048 {
self.spawn_blocking_inner(Box::new(func), Mandatory::NonMandatory, None, rt)
} else {
self.spawn_blocking_inner(func, Mandatory::NonMandatory, None, rt)
};
match spawn_result {
Ok(()) => join_handle,
// Compat: do not panic here, return the join_handle even though it will never resolve
Err(SpawnError::ShuttingDown) => join_handle,
Err(SpawnError::NoThreads(e)) => {
panic!("OS can't spawn worker thread: {}", e)
}
}
}
cfg_fs! {
#[track_caller]
#[cfg_attr(any(
all(loom, not(test)), // the function is covered by loom tests
test
), allow(dead_code))]
pub(crate) fn spawn_mandatory_blocking<F, R>(&self, rt: &Handle, func: F) -> Option<JoinHandle<R>>
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let (join_handle, spawn_result) = if cfg!(debug_assertions) && std::mem::size_of::<F>() > 2048 {
self.spawn_blocking_inner(
Box::new(func),
Mandatory::Mandatory,
None,
rt,
)
} else {
self.spawn_blocking_inner(
func,
Mandatory::Mandatory,
None,
rt,
)
};
if spawn_result.is_ok() {
Some(join_handle)
} else {
None
}
}
}
#[track_caller]
pub(crate) fn spawn_blocking_inner<F, R>(
&self,
func: F,
is_mandatory: Mandatory,
name: Option<&str>,
rt: &Handle,
) -> (JoinHandle<R>, Result<(), SpawnError>)
where
F: FnOnce() -> R + Send + 'static,
R: Send + 'static,
{
let fut = BlockingTask::new(func);
let id = task::Id::next();
#[cfg(all(tokio_unstable, feature = "tracing"))]
let fut = {
use tracing::Instrument;
let location = std::panic::Location::caller();
let span = tracing::trace_span!(
target: "tokio::task::blocking",
"runtime.spawn",
kind = %"blocking",
task.name = %name.unwrap_or_default(),
task.id = id.as_u64(),
"fn" = %std::any::type_name::<F>(),
spawn.location = %format_args!("{}:{}:{}", location.file(), location.line(), location.column()),
);
fut.instrument(span)
};
#[cfg(not(all(tokio_unstable, feature = "tracing")))]
let _ = name;
let (task, handle) = task::unowned(fut, BlockingSchedule::new(rt), id);
let spawned = self.spawn_task(Task::new(task, is_mandatory), rt);
(handle, spawned)
}
fn spawn_task(&self, task: Task, rt: &Handle) -> Result<(), SpawnError> {
let mut shared = self.inner.shared.lock();
if shared.shutdown {
@ -230,15 +395,16 @@ impl Spawner {
task.task.shutdown();
// no need to even push this task; it would never get picked up
return Err(());
return Err(SpawnError::ShuttingDown);
}
shared.queue.push_back(task);
self.inner.metrics.inc_queue_depth();
if shared.num_idle == 0 {
if self.inner.metrics.num_idle_threads() == 0 {
// No threads are able to process the task.
if shared.num_th == self.inner.thread_cap {
if self.inner.metrics.num_threads() == self.inner.thread_cap {
// At max number of threads
} else {
assert!(shared.shutdown_tx.is_some());
@ -249,11 +415,14 @@ impl Spawner {
match self.spawn_thread(shutdown_tx, rt, id) {
Ok(handle) => {
shared.num_th += 1;
self.inner.metrics.inc_num_threads();
shared.worker_thread_index += 1;
shared.worker_threads.insert(id, handle);
}
Err(ref e) if is_temporary_os_thread_error(e) && shared.num_th > 0 => {
Err(ref e)
if is_temporary_os_thread_error(e)
&& self.inner.metrics.num_threads() > 0 =>
{
// OS temporarily failed to spawn a new thread.
// The task will be picked up eventually by a currently
// busy thread.
@ -261,7 +430,7 @@ impl Spawner {
Err(e) => {
// The OS refused to spawn the thread and there is no thread
// to pick up the task that has just been pushed to the queue.
panic!("OS can't spawn worker thread: {}", e)
return Err(SpawnError::NoThreads(e));
}
}
}
@ -272,7 +441,7 @@ impl Spawner {
// exactly. Thread libraries may generate spurious
// wakeups, this counter is used to keep us in a
// consistent state.
shared.num_idle -= 1;
self.inner.metrics.dec_num_idle_threads();
shared.num_notify += 1;
self.inner.condvar.notify_one();
}
@ -283,7 +452,7 @@ impl Spawner {
fn spawn_thread(
&self,
shutdown_tx: shutdown::Sender,
rt: &dyn ToHandle,
rt: &Handle,
id: usize,
) -> std::io::Result<thread::JoinHandle<()>> {
let mut builder = thread::Builder::new().name((self.inner.thread_name)());
@ -292,17 +461,33 @@ impl Spawner {
builder = builder.stack_size(stack_size);
}
let rt = rt.to_handle();
let rt = rt.clone();
builder.spawn(move || {
// Only the reference should be moved into the closure
let _enter = crate::runtime::context::enter(rt.clone());
rt.as_inner().blocking_spawner.inner.run(id);
let _enter = rt.enter();
rt.inner.blocking_spawner().inner.run(id);
drop(shutdown_tx);
})
}
}
cfg_metrics! {
impl Spawner {
pub(crate) fn num_threads(&self) -> usize {
self.inner.metrics.num_threads()
}
pub(crate) fn num_idle_threads(&self) -> usize {
self.inner.metrics.num_idle_threads()
}
pub(crate) fn queue_depth(&self) -> usize {
self.inner.metrics.queue_depth()
}
}
}
// Tells whether the error when spawning a thread is temporary.
#[inline]
fn is_temporary_os_thread_error(error: &std::io::Error) -> bool {
@ -321,6 +506,7 @@ impl Inner {
'main: loop {
// BUSY
while let Some(task) = shared.queue.pop_front() {
self.metrics.dec_queue_depth();
drop(shared);
task.run();
@ -328,7 +514,7 @@ impl Inner {
}
// IDLE
shared.num_idle += 1;
self.metrics.inc_num_idle_threads();
while !shared.shutdown {
let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap();
@ -362,6 +548,7 @@ impl Inner {
if shared.shutdown {
// Drain the queue
while let Some(task) = shared.queue.pop_front() {
self.metrics.dec_queue_depth();
drop(shared);
task.shutdown_or_run_if_mandatory();
@ -372,7 +559,7 @@ impl Inner {
// Work was produced, and we "took" it (by decrementing num_notify).
// This means that num_idle was decremented once for our wakeup.
// But, since we are exiting, we need to "undo" that, as we'll stay idle.
shared.num_idle += 1;
self.metrics.inc_num_idle_threads();
// NOTE: Technically we should also do num_notify++ and notify again,
// but since we're shutting down anyway, that won't be necessary.
break;
@ -380,17 +567,17 @@ impl Inner {
}
// Thread exit
shared.num_th -= 1;
self.metrics.dec_num_threads();
// num_idle should now be tracked exactly, panic
// with a descriptive message if it is not the
// case.
shared.num_idle = shared
.num_idle
.checked_sub(1)
.expect("num_idle underflowed on thread exit");
let prev_idle = self.metrics.dec_num_idle_threads();
if prev_idle < self.metrics.num_idle_threads() {
panic!("num_idle_threads underflowed on thread exit")
}
if shared.shutdown && shared.num_th == 0 {
if shared.shutdown && self.metrics.num_threads() == 0 {
self.condvar.notify_one();
}

View file

@ -1,15 +1,52 @@
#[cfg(feature = "test-util")]
use crate::runtime::scheduler;
use crate::runtime::task::{self, Task};
use crate::runtime::Handle;
/// `task::Schedule` implementation that does nothing. This is unique to the
/// blocking scheduler as tasks scheduled are not really futures but blocking
/// operations.
/// `task::Schedule` implementation that does nothing (except some bookkeeping
/// in test-util builds). This is unique to the blocking scheduler as tasks
/// scheduled are not really futures but blocking operations.
///
/// We avoid storing the task by forgetting it in `bind` and re-materializing it
/// in `release.
pub(crate) struct NoopSchedule;
/// in `release`.
pub(crate) struct BlockingSchedule {
#[cfg(feature = "test-util")]
handle: Handle,
}
impl task::Schedule for NoopSchedule {
impl BlockingSchedule {
#[cfg_attr(not(feature = "test-util"), allow(unused_variables))]
pub(crate) fn new(handle: &Handle) -> Self {
#[cfg(feature = "test-util")]
{
match &handle.inner {
scheduler::Handle::CurrentThread(handle) => {
handle.driver.clock.inhibit_auto_advance();
}
#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))]
scheduler::Handle::MultiThread(_) => {}
}
}
BlockingSchedule {
#[cfg(feature = "test-util")]
handle: handle.clone(),
}
}
}
impl task::Schedule for BlockingSchedule {
fn release(&self, _task: &Task<Self>) -> Option<Task<Self>> {
#[cfg(feature = "test-util")]
{
match &self.handle.inner {
scheduler::Handle::CurrentThread(handle) => {
handle.driver.clock.allow_auto_advance();
handle.driver.unpark();
}
#[cfg(all(feature = "rt-multi-thread", not(tokio_wasi)))]
scheduler::Handle::MultiThread(_) => {}
}
}
None
}

Some files were not shown because too many files have changed in this diff Show more