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

View File

@ -18,6 +18,7 @@ serde = "1.0"
time = { version = "0.3", features = ["formatting"] } time = { version = "0.3", features = ["formatting"] }
bytes = "1.1" bytes = "1.1"
thiserror = "1" thiserror = "1"
tokio = { version = ">=1.24" }
[build-dependencies] [build-dependencies]
cbindgen = "0.20" 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 # 0.8.3
## Changed ## Changed
@ -12,7 +42,7 @@
* Improved support for Redox, making it possible to run on stable Rust * Improved support for Redox, making it possible to run on stable Rust
(https://github.com/tokio-rs/mio/pull/1555). (https://github.com/tokio-rs/mio/pull/1555).
* Don't ignore EAGAIN in UDS connect call * 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` * Documentation of `TcpStream::connect`
(https://github.com/tokio-rs/mio/pull/1565). (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. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -17,58 +19,57 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.3" version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"wasi 0.10.2+wasi-snapshot-preview1", "wasi",
] ]
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.122" version = "0.2.126"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.16" version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]] [[package]]
name = "mio" name = "mio"
version = "0.8.3" version = "0.8.5"
dependencies = [ dependencies = [
"env_logger", "env_logger",
"libc", "libc",
"log", "log",
"rand", "rand",
"wasi 0.11.0+wasi-snapshot-preview1", "wasi",
"windows-sys", "windows-sys",
] ]
[[package]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.15" version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.4" version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [ dependencies = [
"libc", "libc",
"rand_chacha", "rand_chacha",
"rand_core", "rand_core",
"rand_hc",
] ]
[[package]] [[package]]
@ -90,21 +91,6 @@ dependencies = [
"getrandom", "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]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -113,43 +99,57 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.36.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f375ae76a43fd649c5a3482a4a3e28eced2267adaefa55422bf7e92696a7dac5" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [ dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc", "windows_aarch64_msvc",
"windows_i686_gnu", "windows_i686_gnu",
"windows_i686_msvc", "windows_i686_msvc",
"windows_x86_64_gnu", "windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc", "windows_x86_64_msvc",
] ]
[[package]] [[package]]
name = "windows_aarch64_msvc" name = "windows_aarch64_gnullvm"
version = "0.36.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "windows_i686_gnu" name = "windows_i686_gnu"
version = "0.36.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b759cc6e3d97970c98cffe461739e89ab6d424ba5e2e7d3b9b05a2d56116057" checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
[[package]] [[package]]
name = "windows_i686_msvc" name = "windows_i686_msvc"
version = "0.36.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a0cee91bff283876711f91e7db0aa234438bc663a9d8304596df00b0a6fd6ef" checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
[[package]] [[package]]
name = "windows_x86_64_gnu" name = "windows_x86_64_gnu"
version = "0.36.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "windows_x86_64_msvc" name = "windows_x86_64_msvc"
version = "0.36.0" version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd6a8b0b1ea4331e4db47192729fce42ac8a110fd22bb3abac555d8d7700f29" checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"

View File

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

View File

@ -6,15 +6,15 @@ overhead as possible over the OS abstractions.
[![Crates.io][crates-badge]][crates-url] [![Crates.io][crates-badge]][crates-url]
[![MIT licensed][mit-badge]][mit-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] [![Build Status][cirrus-badge]][cirrus-url]
[crates-badge]: https://img.shields.io/crates/v/mio.svg [crates-badge]: https://img.shields.io/crates/v/mio.svg
[crates-url]: https://crates.io/crates/mio [crates-url]: https://crates.io/crates/mio
[mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg
[mit-url]: LICENSE [mit-url]: LICENSE
[azure-badge]: https://dev.azure.com/tokio-rs/Tokio/_apis/build/status/tokio-rs.mio?branchName=master [actions-badge]: https://github.com/tokio-rs/mio/workflows/CI/badge.svg
[azure-url]: https://dev.azure.com/tokio-rs/Tokio/_build/latest?definitionId=2&branchName=master [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-badge]: https://api.cirrus-ci.com/github/tokio-rs/mio.svg
[cirrus-url]: https://cirrus-ci.com/github/tokio-rs/mio [cirrus-url]: https://cirrus-ci.com/github/tokio-rs/mio
@ -139,7 +139,6 @@ Currently supported platforms:
* Windows * Windows
* iOS * iOS
* macOS * macOS
* Wine (version 6.11+, see [issue #1444])
There are potentially others. If you find that Mio works on another There are potentially others. If you find that Mio works on another
platform, submit a PR to update the list! 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. This uses the Windows AFD system to access socket readiness events.
[wepoll]: https://github.com/piscisaureus/wepoll [wepoll]: https://github.com/piscisaureus/wepoll
[issue #1444]: https://github.com/tokio-rs/mio/issues/1444
### Unsupported ### Unsupported
* Haiku, see [issue #1472] * Haiku, see [issue #1472]
* Solaris, see [issue #1152] * Solaris, see [issue #1152]
* Wine, see [issue #1444]
[issue #1472]: https://github.com/tokio-rs/mio/issues/1472 [issue #1472]: https://github.com/tokio-rs/mio/issues/1472
[issue #1152]: https://github.com/tokio-rs/mio/issues/1152 [issue #1152]: https://github.com/tokio-rs/mio/issues/1152
[issue #1444]: https://github.com/tokio-rs/mio/issues/1444
## Community ## Community

View File

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

View File

@ -269,49 +269,49 @@ impl TcpStream {
impl Read for TcpStream { impl Read for TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 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> { 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 { impl<'a> Read for &'a TcpStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 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> { 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 { impl Write for TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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> { 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<()> { 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 { impl<'a> Write for &'a TcpStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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> { 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<()> { 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 { impl Read for UnixStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 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> { 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 { impl<'a> Read for &'a UnixStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 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> { 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 { impl Write for UnixStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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> { 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<()> { 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 { impl<'a> Write for &'a UnixStream {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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> { 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<()> { 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 /// operations to go through Mio otherwise it is not able to update it's
/// internal state properly and won't generate events. /// 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 /// # Implementation notes
/// ///
/// `Poll` is backed by the selector provided by the operating system. /// `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 /// the system selector. If this syscall fails, `Poll::new` will return
/// with the error. /// 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. /// See [struct] level docs for more details.
/// ///
/// [struct]: struct.Poll.html /// [struct]: struct.Poll.html
@ -320,6 +349,10 @@ impl Poll {
/// of Mio would automatically retry the poll call if it was interrupted /// of Mio would automatically retry the poll call if it was interrupted
/// (if `EINTR` was returned). /// (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 /// # Examples
/// ///
/// A basic example -- establishing a `TcpStream` connection. /// A basic example -- establishing a `TcpStream` connection.
@ -635,7 +668,6 @@ impl Registry {
/// ///
/// Event sources registered with this `Registry` will be registered with /// Event sources registered with this `Registry` will be registered with
/// the original `Registry` and `Poll` instance. /// the original `Registry` and `Poll` instance.
#[cfg(not(target_os = "wasi"))]
pub fn try_clone(&self) -> io::Result<Registry> { pub fn try_clone(&self) -> io::Result<Registry> {
self.selector self.selector
.try_clone() .try_clone()

View File

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

View File

@ -313,29 +313,29 @@ impl event::Source for Sender {
impl Write for Sender { impl Write for Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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> { 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<()> { 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 { impl Write for &Sender {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> { 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> { 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<()> { 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 { impl Read for Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 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> { 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 { impl Read for &Receiver {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { 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> { 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 { impl Selector {
pub fn new() -> io::Result<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 < // According to libuv, `EPOLL_CLOEXEC` is not defined on Android API <
// 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform, // 21. But `EPOLL_CLOEXEC` is an alias for `O_CLOEXEC` on that platform,
// so we use it instead. // so we use it instead.
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
let flag = libc::O_CLOEXEC; let res = syscall!(syscall(libc::SYS_epoll_create1, libc::O_CLOEXEC));
#[cfg(not(target_os = "android"))]
let flag = libc::EPOLL_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)] #[cfg(debug_assertions)]
id: NEXT_ID.fetch_add(1, Ordering::Relaxed), id: NEXT_ID.fetch_add(1, Ordering::Relaxed),
ep, ep,
@ -61,7 +87,19 @@ impl Selector {
const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128; const MAX_SAFE_TIMEOUT: u128 = libc::c_int::max_value() as u128;
let timeout = timeout 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); .unwrap_or(-1);
events.clear(); events.clear();

View File

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

View File

@ -77,6 +77,12 @@ impl Selector {
events.reserve(length); events.reserve(length);
debug_assert!(events.capacity() >= 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) }; 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")] #[cfg(feature = "net")]
pub(crate) fn register( pub(crate) fn register(
&self, &self,

View File

@ -224,7 +224,17 @@ impl CompletionStatus {
#[inline] #[inline]
fn duration_millis(dur: Option<Duration>) -> u32 { fn duration_millis(dur: Option<Duration>) -> u32 {
if let Some(dur) = dur { 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 { } else {
u32::MAX 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) # 1.19.2 (June 6, 2022)
This release fixes another bug in `Notified::enable`. ([#4751]) 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 [#4729]: https://github.com/tokio-rs/tokio/pull/4729
[#4739]: https://github.com/tokio-rs/tokio/pull/4739 [#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) # 1.18.2 (May 5, 2022)
Add missing features for the `winapi` dependency. ([#4663]) Add missing features for the `winapi` dependency. ([#4663])
@ -1235,7 +1631,7 @@ Biggest changes are:
- Feature flags are simplified - Feature flags are simplified
- `rt-core` and `rt-util` are combined to `rt` - `rt-core` and `rt-util` are combined to `rt`
- `rt-threaded` is renamed to `rt-multi-thread` to match builder API - `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` - `parking_lot` is included with `full`
### Changes ### Changes
@ -1733,7 +2129,7 @@ Biggest changes are:
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` ([#1870]). - `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` ([#1870]).
- `process::Child` fields are made public to match `std` ([#2014]). - `process::Child` fields are made public to match `std` ([#2014]).
- impl `Stream` for `sync::broadcast::Receiver` ([#2012]). - 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]). - `runtime::Handle::current` returns the handle for the current runtime ([#2040]).
- `StreamExt::filter` filters stream values according to a predicate ([#2001]). - `StreamExt::filter` filters stream values according to a predicate ([#2001]).
- `StreamExt::filter_map` simultaneously filter and map stream values ([#2001]). - `StreamExt::filter_map` simultaneously filter and map stream values ([#2001]).
@ -1842,7 +2238,7 @@ Biggest changes are:
### Fixes ### Fixes
- calling `spawn_blocking` after runtime shutdown ([#1875]). - calling `spawn_blocking` after runtime shutdown ([#1875]).
- `LocalSet` drop inifinite loop ([#1892]). - `LocalSet` drop infinite loop ([#1892]).
- `LocalSet` hang under load ([#1905]). - `LocalSet` hang under load ([#1905]).
- improved documentation ([#1865], [#1866], [#1868], [#1874], [#1876], [#1911]). - improved documentation ([#1865], [#1866], [#1868], [#1874], [#1876], [#1911]).

View File

@ -13,7 +13,7 @@
edition = "2018" edition = "2018"
rust-version = "1.49" rust-version = "1.49"
name = "tokio" name = "tokio"
version = "1.19.2" version = "1.24.1"
authors = ["Tokio Contributors <team@tokio.rs>"] authors = ["Tokio Contributors <team@tokio.rs>"]
description = """ description = """
An event-driven, non-blocking I/O platform for writing asynchronous I/O An event-driven, non-blocking I/O platform for writing asynchronous I/O
@ -62,17 +62,13 @@ version = "2.2"
optional = true optional = true
[dependencies.mio] [dependencies.mio]
version = "0.8.1" version = "0.8.4"
optional = true optional = true
[dependencies.num_cpus] [dependencies.num_cpus]
version = "1.8.0" version = "1.8.0"
optional = true optional = true
[dependencies.once_cell]
version = "1.5.2"
optional = true
[dependencies.parking_lot] [dependencies.parking_lot]
version = "0.12.0" version = "0.12.0"
optional = true optional = true
@ -80,11 +76,6 @@ optional = true
[dependencies.pin-project-lite] [dependencies.pin-project-lite]
version = "0.2.0" version = "0.2.0"
[dependencies.socket2]
version = "0.4.4"
features = ["all"]
optional = true
[dependencies.tokio-macros] [dependencies.tokio-macros]
version = "1.7.0" version = "1.7.0"
optional = true optional = true
@ -108,6 +99,9 @@ version = "0.1"
[dev-dependencies.tokio-test] [dev-dependencies.tokio-test]
version = "0.4.0" version = "0.4.0"
[build-dependencies.autocfg]
version = "1.1"
[features] [features]
default = [] default = []
fs = [] fs = []
@ -137,31 +131,36 @@ net = [
"mio/os-ext", "mio/os-ext",
"mio/net", "mio/net",
"socket2", "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 = [ process = [
"bytes", "bytes",
"once_cell",
"libc", "libc",
"mio/os-poll", "mio/os-poll",
"mio/os-ext", "mio/os-ext",
"mio/net", "mio/net",
"signal-hook-registry", "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 = [ rt-multi-thread = [
"num_cpus", "num_cpus",
"rt", "rt",
] ]
signal = [ signal = [
"once_cell",
"libc", "libc",
"mio/os-poll", "mio/os-poll",
"mio/net", "mio/net",
"mio/os-ext", "mio/os-ext",
"signal-hook-registry", "signal-hook-registry",
"winapi/consoleapi", "windows-sys/Win32_Foundation",
"windows-sys/Win32_System_Console",
] ]
stats = [] stats = []
sync = [] sync = []
@ -172,6 +171,16 @@ test-util = [
] ]
time = [] 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] [target."cfg(loom)".dev-dependencies.loom]
version = "0.5.2" version = "0.5.2"
features = [ features = [
@ -179,20 +188,22 @@ features = [
"checkpoint", "checkpoint",
] ]
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.proptest] [target."cfg(not(all(any(target_arch = \"wasm32\", target_arch = \"wasm64\"), target_os = \"unknown\")))".dev-dependencies.rand]
version = "1"
[target."cfg(not(target_arch = \"wasm32\"))".dev-dependencies.rand]
version = "0.8.0" 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" 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] [target."cfg(target_os = \"freebsd\")".dev-dependencies.mio-aio]
version = "0.6.0" version = "0.7.0"
features = ["tokio"] features = ["tokio"]
[target."cfg(tokio_unstable)".dependencies.tracing] [target."cfg(tokio_unstable)".dependencies.tracing]
@ -220,18 +231,9 @@ features = [
] ]
default-features = false default-features = false
[target."cfg(windows)".dependencies.winapi] [target."cfg(windows)".dependencies.windows-sys]
version = "0.3.8" version = "0.42.0"
features = [
"std",
"winsock2",
"mswsock",
"handleapi",
"ws2ipdef",
"ws2tcpip",
]
optional = true optional = true
default-features = false
[target."cfg(windows)".dev-dependencies.ntapi] [target."cfg(windows)".dev-dependencies.ntapi]
version = "0.3.6" 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 ```toml
[dependencies] [dependencies]
tokio = { version = "1.19.2", features = ["full"] } tokio = { version = "1.24.1", features = ["full"] }
``` ```
Then, on your main.rs: Then, on your main.rs:
@ -161,6 +161,16 @@ several other libraries, including:
[`mio`]: https://github.com/tokio-rs/mio [`mio`]: https://github.com/tokio-rs/mio
[`bytes`]: https://github.com/tokio-rs/bytes [`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 ## 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 released as a new patch release for each LTS minor version. Our current LTS
releases are: releases are:
* `1.14.x` - LTS release until June 2022. * `1.18.x` - LTS release until June 2023
* `1.18.x` - LTS release until January 2023 * `1.20.x` - LTS release until September 2023.
Each LTS release will continue to receive backported fixes for at least half a Each LTS release will continue to receive backported fixes for at least a year.
year. If you wish to use a fixed minor release in your project, we recommend If you wish to use a fixed minor release in your project, we recommend that you
that you use an LTS release. use an LTS release.
To use a fixed minor version, you can specify the version with a tilde. For 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: can use the following dependency specification:
```text ```text
tokio = { version = "~1.14", features = [...] } tokio = { version = "~1.18", features = [...] }
``` ```
## License ## 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: The `ScheduledIo` readiness `AtomicUsize` is structured as:
``` ```
| reserved | generation | driver tick | readinesss | | shutdown | generation | driver tick | readiness |
|----------+------------+--------------+------------| |----------+------------+--------------+-----------|
| 1 bit | 7 bits + 8 bits + 16 bits | | 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 The `readiness()` function returns a `ReadyEvent` value. This value includes the
`tick` component read with the resource's readiness value. When `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 enum NotDefinedHere {}
pub mod os; 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 me = self.get_mut();
let inner = me.inner.get_mut(); let inner = me.inner.get_mut();
loop { match inner.state {
match inner.state { Busy(_) => Err(io::Error::new(
Busy(_) => panic!("must wait for poll_complete before calling start_seek"), io::ErrorKind::Other,
Idle(ref mut buf_cell) => { "other file operation is pending, call poll_complete before start_seek",
let mut buf = buf_cell.take().unwrap(); )),
Idle(ref mut buf_cell) => {
let mut buf = buf_cell.take().unwrap();
// Factor in any unread data from the buf // Factor in any unread data from the buf
if !buf.is_empty() { if !buf.is_empty() {
let n = buf.discard_read(); let n = buf.discard_read();
if let SeekFrom::Current(ref mut offset) = pos { if let SeekFrom::Current(ref mut offset) = pos {
*offset += n; *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!(n, FOO.len());
assert_eq!(&buf[..n], FOO); 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()) 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 //! `std::io::ErrorKind::WouldBlock` if a *worker* thread can not be converted
//! to a *backup* thread immediately. //! 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 //! [`AsyncRead`]: trait@crate::io::AsyncRead
mod canonicalize; mod canonicalize;

View File

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

View File

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

View File

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

View File

@ -1,15 +1,22 @@
use std::future::Future; use std::future::Future;
cfg_rt! { cfg_rt! {
#[track_caller]
pub(crate) fn block_on<F: Future>(f: F) -> F::Output { 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() e.block_on(f).unwrap()
} }
} }
cfg_not_rt! { cfg_not_rt! {
#[track_caller]
pub(crate) fn block_on<F: Future>(f: F) -> F::Output { 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() park.block_on(f).unwrap()
} }
} }

View File

@ -7,13 +7,23 @@ use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; 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. /// Future for the [`poll_fn`] function.
pub struct PollFn<F> { pub struct PollFn<F> {
f: F, f: F,
} }
impl<F> Unpin for PollFn<F> {}
/// Creates a new future wrapping around a function returning [`Poll`]. /// Creates a new future wrapping around a function returning [`Poll`].
pub fn poll_fn<T, F>(f: F) -> PollFn<F> pub fn poll_fn<T, F>(f: F) -> PollFn<F>
where where
@ -34,7 +44,17 @@ where
{ {
type Output = T; type Output = T;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
(self.f)(cx) // 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 mio::unix::SourceFd;
use std::io; use std::io;
@ -167,12 +169,18 @@ pub struct AsyncFdReadyMutGuard<'a, T: AsRawFd> {
const ALL_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE); const ALL_INTEREST: Interest = Interest::READABLE.add(Interest::WRITABLE);
impl<T: AsRawFd> AsyncFd<T> { impl<T: AsRawFd> AsyncFd<T> {
#[inline]
/// Creates an AsyncFd backed by (and taking ownership of) an object /// Creates an AsyncFd backed by (and taking ownership of) an object
/// implementing [`AsRawFd`]. The backing file descriptor is cached at the /// implementing [`AsRawFd`]. The backing file descriptor is cached at the
/// time of creation. /// time of creation.
/// ///
/// This method must be called in the context of a tokio runtime. /// 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> pub fn new(inner: T) -> io::Result<Self>
where where
T: AsRawFd, T: AsRawFd,
@ -180,19 +188,26 @@ impl<T: AsRawFd> AsyncFd<T> {
Self::with_interest(inner, ALL_INTEREST) Self::with_interest(inner, ALL_INTEREST)
} }
#[inline]
/// Creates new instance as `new` with additional ability to customize interest, /// 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. /// 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> pub fn with_interest(inner: T, interest: Interest) -> io::Result<Self>
where where
T: AsRawFd, 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( pub(crate) fn new_with_handle_and_interest(
inner: T, inner: T,
handle: Handle, handle: scheduler::Handle,
interest: Interest, interest: Interest,
) -> io::Result<Self> { ) -> io::Result<Self> {
let fd = inner.as_raw_fd(); let fd = inner.as_raw_fd();

View File

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

View File

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

View File

@ -1,6 +1,6 @@
#![cfg_attr(not(feature = "net"), allow(dead_code, unreachable_pub))] #![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::fmt;
use std::ops; use std::ops;
@ -100,7 +100,7 @@ impl Interest {
self.0 self.0
} }
pub(super) fn mask(self) -> Ready { pub(crate) fn mask(self) -> Ready {
match self { match self {
Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED, Interest::READABLE => Ready::READABLE | Ready::READ_CLOSED,
Interest::WRITABLE => Ready::WRITABLE | Ready::WRITE_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. //! Traits, helpers, and type definitions for asynchronous I/O functionality.
//! //!
//! This module is the asynchronous version of `std::io`. Primarily, it //! 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 //! [`Sink`]: https://docs.rs/futures/0.3/futures/sink/trait.Sink.html
//! [`Stream`]: https://docs.rs/futures/0.3/futures/stream/trait.Stream.html //! [`Stream`]: https://docs.rs/futures/0.3/futures/stream/trait.Stream.html
//! [`Write`]: std::io::Write //! [`Write`]: std::io::Write
#![cfg_attr(
not(all(feature = "rt", feature = "net")),
allow(dead_code, unused_imports)
)]
cfg_io_blocking! { cfg_io_blocking! {
pub(crate) mod blocking; pub(crate) mod blocking;
} }
@ -205,15 +209,19 @@ pub use self::read_buf::ReadBuf;
pub use std::io::{Error, ErrorKind, Result, SeekFrom}; pub use std::io::{Error, ErrorKind, Result, SeekFrom};
cfg_io_driver_impl! { cfg_io_driver_impl! {
pub(crate) mod driver; pub(crate) mod interest;
pub(crate) mod ready;
cfg_net! { 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; mod poll_evented;
#[cfg(not(loom))] #[cfg(not(loom))]
#[cfg_attr(tokio_wasi, allow(unused_imports))]
pub(crate) use poll_evented::PollEvented; 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 mio::event::Source;
use std::fmt; use std::fmt;
@ -11,7 +13,7 @@ cfg_io_driver! {
/// [`std::io::Write`] traits with the reactor that drives it. /// [`std::io::Write`] traits with the reactor that drives it.
/// ///
/// `PollEvented` uses [`Registration`] internally to take a type that /// `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. /// [`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 /// 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. /// [`poll_read_ready`] again will also indicate read readiness.
/// ///
/// When the operation is attempted and is unable to succeed due to the I/O /// 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 clears the readiness state until a new readiness event is received.
/// ///
/// This allows the caller to implement additional functions. For example, /// This allows the caller to implement additional functions. For example,
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and /// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
/// `clear_read_ready`. /// [`clear_readiness`].
/// ///
/// ## Platform-specific events /// ## Platform-specific events
/// ///
@ -57,6 +59,7 @@ cfg_io_driver! {
/// [`AsyncRead`]: crate::io::AsyncRead /// [`AsyncRead`]: crate::io::AsyncRead
/// [`AsyncWrite`]: crate::io::AsyncWrite /// [`AsyncWrite`]: crate::io::AsyncWrite
/// [`TcpListener`]: crate::net::TcpListener /// [`TcpListener`]: crate::net::TcpListener
/// [`clear_readiness`]: Registration::clear_readiness
/// [`poll_read_ready`]: Registration::poll_read_ready /// [`poll_read_ready`]: Registration::poll_read_ready
/// [`poll_write_ready`]: Registration::poll_write_ready /// [`poll_write_ready`]: Registration::poll_write_ready
pub(crate) struct PollEvented<E: Source> { 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 /// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set /// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
#[cfg_attr(feature = "signal", allow(unused))] #[cfg_attr(feature = "signal", allow(unused))]
pub(crate) fn new(io: E) -> io::Result<Self> { pub(crate) fn new(io: E) -> io::Result<Self> {
PollEvented::new_with_interest(io, Interest::READABLE | Interest::WRITABLE) 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 /// a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter)
/// function. /// function.
#[track_caller]
#[cfg_attr(feature = "signal", allow(unused))] #[cfg_attr(feature = "signal", allow(unused))]
pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> { 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( pub(crate) fn new_with_interest_and_handle(
mut io: E, mut io: E,
interest: Interest, interest: Interest,
handle: Handle, handle: scheduler::Handle,
) -> io::Result<Self> { ) -> io::Result<Self> {
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?; let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
Ok(Self { Ok(Self {
@ -115,11 +121,7 @@ impl<E: Source> PollEvented<E> {
} }
/// Returns a reference to the registration. /// Returns a reference to the registration.
#[cfg(any( #[cfg(any(feature = "net"))]
feature = "net",
all(unix, feature = "process"),
all(unix, feature = "signal"),
))]
pub(crate) fn registration(&self) -> &Registration { pub(crate) fn registration(&self) -> &Registration {
&self.registration &self.registration
} }
@ -134,7 +136,7 @@ impl<E: Source> PollEvented<E> {
} }
feature! { feature! {
#![any(feature = "net", feature = "process")] #![any(feature = "net", all(unix, feature = "process"))]
use crate::io::ReadBuf; use crate::io::ReadBuf;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
@ -151,16 +153,32 @@ feature! {
{ {
use std::io::Read; use std::io::Read;
let n = ready!(self.registration.poll_read_io(cx, || { loop {
let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]); let evt = ready!(self.registration.poll_read_ready(cx))?;
self.io.as_ref().unwrap().read(b)
}))?;
// Safety: We trust `TcpStream::read` to have filled up `n` bytes in the let b = &mut *(buf.unfilled_mut() as *mut [std::mem::MaybeUninit<u8>] as *mut [u8]);
// buffer. let len = b.len();
buf.assume_init(n);
buf.advance(n); match self.io.as_ref().unwrap().read(b) {
Poll::Ready(Ok(())) 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>> 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, &'a E: io::Write + 'a,
{ {
use std::io::Write; 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>( pub(crate) fn poll_write_vectored<'a>(
&'a self, &'a self,
cx: &mut Context<'_>, cx: &mut Context<'_>,

View File

@ -152,6 +152,7 @@ impl<'a> ReadBuf<'a> {
/// ///
/// Panics if `self.remaining()` is less than `n`. /// Panics if `self.remaining()` is less than `n`.
#[inline] #[inline]
#[track_caller]
pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] { pub fn initialize_unfilled_to(&mut self, n: usize) -> &mut [u8] {
assert!(self.remaining() >= n, "n overflows remaining"); 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. /// Panics if the filled region of the buffer would become larger than the initialized region.
#[inline] #[inline]
#[track_caller]
pub fn advance(&mut self, n: usize) { pub fn advance(&mut self, n: usize) {
let new = self.filled.checked_add(n).expect("filled overflow"); let new = self.filled.checked_add(n).expect("filled overflow");
self.set_filled(new); 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. /// Panics if the filled region of the buffer would become larger than the initialized region.
#[inline] #[inline]
#[track_caller]
pub fn set_filled(&mut self, n: usize) { pub fn set_filled(&mut self, n: usize) {
assert!( assert!(
n <= self.initialized, n <= self.initialized,
@ -241,6 +244,7 @@ impl<'a> ReadBuf<'a> {
/// ///
/// Panics if `self.remaining()` is less than `buf.len()`. /// Panics if `self.remaining()` is less than `buf.len()`.
#[inline] #[inline]
#[track_caller]
pub fn put_slice(&mut self, buf: &[u8]) { pub fn put_slice(&mut self, buf: &[u8]) {
assert!( assert!(
self.remaining() >= buf.len(), 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. /// `Ready` tracks which operation an I/O resource is ready to perform.
#[cfg_attr(docsrs, doc(cfg(feature = "net")))] #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
#[derive(Clone, Copy, PartialEq, PartialOrd)] #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Ready(usize); pub struct Ready(usize);
impl Ready { impl Ready {

View File

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

View File

@ -69,7 +69,7 @@ cfg_io_util! {
/// Creates a future which will rewind to the beginning of the stream. /// 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> fn rewind(&mut self) -> Seek<'_, Self>
where where
Self: Unpin, Self: Unpin,

View File

@ -406,7 +406,7 @@ cfg_io_util! {
/// ``` /// ```
fn write_u8(&mut self, n: u8) -> WriteU8; 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: /// Equivalent to:
/// ///
@ -425,7 +425,7 @@ cfg_io_util! {
/// ///
/// # Examples /// # Examples
/// ///
/// Write unsigned 8 bit integers to a `AsyncWrite`: /// Write signed 8 bit integers to a `AsyncWrite`:
/// ///
/// ```rust /// ```rust
/// use tokio::io::{self, AsyncWriteExt}; /// use tokio::io::{self, AsyncWriteExt};
@ -434,10 +434,10 @@ cfg_io_util! {
/// async fn main() -> io::Result<()> { /// async fn main() -> io::Result<()> {
/// let mut writer = Vec::new(); /// let mut writer = Vec::new();
/// ///
/// writer.write_u8(2).await?; /// writer.write_i8(-2).await?;
/// writer.write_u8(5).await?; /// writer.write_i8(126).await?;
/// ///
/// assert_eq!(writer, b"\x02\x05"); /// assert_eq!(writer, b"\xFE\x7E");
/// Ok(()) /// 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>( pub(super) fn poll_copy<R, W>(
&mut self, &mut self,
cx: &mut Context<'_>, cx: &mut Context<'_>,
@ -41,10 +86,10 @@ impl CopyBuffer {
// If our buffer is empty, then we need to read some data to // If our buffer is empty, then we need to read some data to
// continue. // continue.
if self.pos == self.cap && !self.read_done { if self.pos == self.cap && !self.read_done {
let me = &mut *self; self.pos = 0;
let mut buf = ReadBuf::new(&mut me.buf); 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(Ok(_)) => (),
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)), Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
Poll::Pending => { Poll::Pending => {
@ -58,20 +103,11 @@ impl CopyBuffer {
return Poll::Pending; 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! // If our buffer has some data, let's write it out!
while self.pos < self.cap { while self.pos < self.cap {
let me = &mut *self; let i = ready!(self.poll_write_buf(cx, reader.as_mut(), writer.as_mut()))?;
let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?;
if i == 0 { if i == 0 {
return Poll::Ready(Err(io::Error::new( return Poll::Ready(Err(io::Error::new(
io::ErrorKind::WriteZero, io::ErrorKind::WriteZero,

View File

@ -77,7 +77,7 @@ impl fmt::Debug for Empty {
cfg_coop! { cfg_coop! {
fn poll_proceed_and_make_progress(cx: &mut Context<'_>) -> Poll<()> { 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(); coop.made_progress();
Poll::Ready(()) Poll::Ready(())
} }

View File

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

View File

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

View File

@ -1,7 +1,9 @@
#![allow( #![allow(
clippy::cognitive_complexity, clippy::cognitive_complexity,
clippy::large_enum_variant, clippy::large_enum_variant,
clippy::needless_doctest_main clippy::module_inception,
clippy::needless_doctest_main,
clippy::declare_interior_mutable_const
)] )]
#![warn( #![warn(
missing_debug_implementations, missing_debug_implementations,
@ -16,6 +18,7 @@
))] ))]
#![cfg_attr(docsrs, feature(doc_cfg))] #![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(docsrs, allow(unused_attributes))] #![cfg_attr(docsrs, allow(unused_attributes))]
#![cfg_attr(loom, allow(dead_code, unreachable_pub))]
//! A runtime for writing reliable network applications without compromising speed. //! A runtime for writing reliable network applications without compromising speed.
//! //!
@ -152,7 +155,7 @@
//! provide the functionality you need. //! provide the functionality you need.
//! //!
//! Using the runtime requires the "rt" or "rt-multi-thread" feature flags, to //! 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 //! scheduler][rt-multi-thread], respectively. See the [`runtime` module
//! documentation][rt-features] for details. In addition, the "macros" feature //! documentation][rt-features] for details. In addition, the "macros" feature
//! flag enables the `#[tokio::main]` and `#[tokio::test]` attributes. //! 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 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 //! 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 //! without reaching an `.await` will prevent other tasks from running. To
//! combat this, Tokio provides two kinds of threads: Core threads and blocking //! combat this, Tokio provides two kinds of threads: Core threads and blocking threads.
//! 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 //! The core threads are where all asynchronous code runs, and Tokio will by default
//! spawned on demand, can be used to run blocking code that would otherwise //! spawn one for each CPU core. You can use the environment variable `TOKIO_WORKER_THREADS`
//! block other tasks from running and are kept alive when not used for a certain //! to override the default value.
//! amount of time which can be configured with [`thread_keep_alive`]. //!
//! 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 //! 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 //! 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`]. //! threads is very large. These limits can be configured on the [`Builder`].
@ -309,7 +315,7 @@
//! need. //! need.
//! //!
//! - `full`: Enables all features listed below except `test-util` and `tracing`. //! - `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. //! and non-scheduler utilities.
//! - `rt-multi-thread`: Enables the heavier, multi-threaded, work-stealing scheduler. //! - `rt-multi-thread`: Enables the heavier, multi-threaded, work-stealing scheduler.
//! - `io-util`: Enables the IO based `Ext` traits. //! - `io-util`: Enables the IO based `Ext` traits.
@ -325,20 +331,15 @@
//! - `signal`: Enables all `tokio::signal` types. //! - `signal`: Enables all `tokio::signal` types.
//! - `fs`: Enables `tokio::fs` types. //! - `fs`: Enables `tokio::fs` types.
//! - `test-util`: Enables testing based infrastructure for the Tokio runtime. //! - `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 //! _Note: `AsyncRead` and `AsyncWrite` traits do not require any features and are
//! always available._ //! 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 //! ### Unstable features
//! //!
//! Some feature flags are only available when specifying the `tokio_unstable` flag: //! 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: //! Likewise, some parts of the API are only available with the same flag:
//! //!
//! - [`task::JoinSet`]
//! - [`task::Builder`] //! - [`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 //! This flag enables **unstable** features. The public API of these features
//! may break in 1.x releases. To enable these features, the `--cfg //! may break in 1.x releases. To enable these features, the `--cfg
//! tokio_unstable` argument must be passed to `rustc` when compiling. This //! 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 //! [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 //! [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 // 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. // 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" "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. // Includes re-exports used by macros.
// //
// This module is not intended to be part of the public API. In general, any // 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; pub mod net;
mod loom; mod loom;
mod park;
cfg_process! { cfg_process! {
pub mod 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; mod blocking;
cfg_rt! { cfg_rt! {
pub mod runtime; pub mod runtime;
} }
cfg_not_rt! {
pub(crate) mod coop; pub(crate) mod runtime;
}
cfg_signal! { cfg_signal! {
pub mod signal; pub mod signal;
@ -508,14 +581,6 @@ pub(crate) use self::doc::os;
#[allow(unused)] #[allow(unused)]
pub(crate) use std::os; 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! { cfg_macros! {
/// Implementation detail of the `select!` macro. This macro is **not** /// Implementation detail of the `select!` macro. This macro is **not**
/// intended to be used as part of the public API and is permitted to /// 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) 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 { pub(crate) mod rand {
@ -38,3 +45,8 @@ pub(crate) mod sys {
2 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::fmt;
use std::ops::Deref; use std::ops::Deref;
/// `AtomicU16` providing an additional `load_unsync` function. /// `AtomicU16` providing an additional `unsync_load` function.
pub(crate) struct AtomicU16 { pub(crate) struct AtomicU16 {
inner: UnsafeCell<std::sync::atomic::AtomicU16>, inner: UnsafeCell<std::sync::atomic::AtomicU16>,
} }
@ -23,7 +23,7 @@ impl AtomicU16 {
/// All mutations must have happened before the unsynchronized load. /// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations. /// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> u16 { 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::fmt;
use std::ops::Deref; use std::ops::Deref;
/// `AtomicU32` providing an additional `load_unsync` function. /// `AtomicU32` providing an additional `unsync_load` function.
pub(crate) struct AtomicU32 { pub(crate) struct AtomicU32 {
inner: UnsafeCell<std::sync::atomic::AtomicU32>, inner: UnsafeCell<std::sync::atomic::AtomicU32>,
} }
@ -15,6 +15,16 @@ impl AtomicU32 {
let inner = UnsafeCell::new(std::sync::atomic::AtomicU32::new(val)); let inner = UnsafeCell::new(std::sync::atomic::AtomicU32::new(val));
AtomicU32 { inner } 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 { impl Deref for AtomicU32 {

View File

@ -7,80 +7,13 @@
// `#[cfg(target_has_atomic = "64")]`. // `#[cfg(target_has_atomic = "64")]`.
// Refs: https://github.com/rust-lang/rust/tree/master/src/librustc_target // Refs: https://github.com/rust-lang/rust/tree/master/src/librustc_target
cfg_has_atomic_u64! { cfg_has_atomic_u64! {
pub(crate) use std::sync::atomic::AtomicU64; #[path = "atomic_u64_native.rs"]
mod imp;
} }
cfg_not_has_atomic_u64! { cfg_not_has_atomic_u64! {
use crate::loom::sync::Mutex; #[path = "atomic_u64_as_mutex.rs"]
use std::sync::atomic::Ordering; mod imp;
#[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),
}
}
}
} }
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::fmt;
use std::ops; use std::ops;
/// `AtomicUsize` providing an additional `load_unsync` function. /// `AtomicUsize` providing an additional `unsync_load` function.
pub(crate) struct AtomicUsize { pub(crate) struct AtomicUsize {
inner: UnsafeCell<std::sync::atomic::AtomicUsize>, inner: UnsafeCell<std::sync::atomic::AtomicUsize>,
} }
@ -23,7 +23,7 @@ impl AtomicUsize {
/// All mutations must have happened before the unsynchronized load. /// All mutations must have happened before the unsynchronized load.
/// Additionally, there must be no concurrent mutations. /// Additionally, there must be no concurrent mutations.
pub(crate) unsafe fn unsync_load(&self) -> usize { 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 { 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))] #![cfg_attr(any(not(feature = "full"), loom), allow(unused_imports, dead_code))]
mod atomic_ptr;
mod atomic_u16; mod atomic_u16;
mod atomic_u32; mod atomic_u32;
mod atomic_u64; mod atomic_u64;
mod atomic_u8;
mod atomic_usize; mod atomic_usize;
mod mutex; mod mutex;
#[cfg(feature = "parking_lot")] #[cfg(feature = "parking_lot")]
@ -71,21 +69,39 @@ pub(crate) mod sync {
pub(crate) use crate::loom::std::mutex::Mutex; pub(crate) use crate::loom::std::mutex::Mutex;
pub(crate) mod atomic { 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_u16::AtomicU16;
pub(crate) use crate::loom::std::atomic_u32::AtomicU32; 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_u64::{AtomicU64, StaticAtomicU64};
pub(crate) use crate::loom::std::atomic_u8::AtomicU8;
pub(crate) use crate::loom::std::atomic_usize::AtomicUsize; 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 { pub(crate) mod sys {
#[cfg(feature = "rt-multi-thread")] #[cfg(feature = "rt-multi-thread")]
pub(crate) fn num_cpus() -> usize { 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"))] #[cfg(not(feature = "rt-multi-thread"))]
@ -102,7 +118,7 @@ pub(crate) mod thread {
#[allow(unused_imports)] #[allow(unused_imports)]
pub(crate) use std::thread::{ pub(crate) use std::thread::{
current, panicking, park, park_timeout, sleep, spawn, Builder, JoinHandle, LocalKey, current, panicking, park, park_timeout, sleep, spawn, AccessError, Builder, JoinHandle,
Result, Thread, ThreadId, LocalKey, Result, Thread, ThreadId,
}; };
} }

View File

@ -12,6 +12,12 @@ impl<T> Mutex<T> {
Mutex(sync::Mutex::new(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] #[inline]
pub(crate) fn lock(&self) -> MutexGuard<'_, T> { pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
match self.0.lock() { match self.0.lock() {

View File

@ -52,7 +52,7 @@ impl<T> Mutex<T> {
} }
#[inline] #[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",))))] #[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))]
pub(crate) const fn const_new(t: T) -> Mutex<T> { pub(crate) const fn const_new(t: T) -> Mutex<T> {
Mutex(PhantomData, parking_lot::const_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)*) => { ($($item:item)*) => {
$( $(
#[cfg(feature = "fs")] #[cfg(feature = "fs")]
#[cfg(not(tokio_wasi))]
#[cfg_attr(docsrs, doc(cfg(feature = "fs")))] #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
$item $item
)* )*
@ -69,7 +70,11 @@ macro_rules! cfg_fs {
macro_rules! cfg_io_blocking { macro_rules! cfg_io_blocking {
($($item:item)*) => { ($($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( #[cfg(any(
feature = "net", feature = "net",
feature = "process", all(unix, feature = "process"),
all(unix, feature = "signal"), all(unix, feature = "signal"),
))] ))]
#[cfg_attr(docsrs, doc(cfg(any( #[cfg_attr(docsrs, doc(cfg(any(
feature = "net", feature = "net",
feature = "process", all(unix, feature = "process"),
all(unix, feature = "signal"), all(unix, feature = "signal"),
))))] ))))]
$item $item
@ -96,7 +101,7 @@ macro_rules! cfg_io_driver_impl {
$( $(
#[cfg(any( #[cfg(any(
feature = "net", feature = "net",
feature = "process", all(unix, feature = "process"),
all(unix, feature = "signal"), all(unix, feature = "signal"),
))] ))]
$item $item
@ -109,7 +114,7 @@ macro_rules! cfg_not_io_driver {
$( $(
#[cfg(not(any( #[cfg(not(any(
feature = "net", feature = "net",
feature = "process", all(unix, feature = "process"),
all(unix, feature = "signal"), all(unix, feature = "signal"),
)))] )))]
$item $item
@ -247,6 +252,7 @@ macro_rules! cfg_process {
#[cfg(feature = "process")] #[cfg(feature = "process")]
#[cfg_attr(docsrs, doc(cfg(feature = "process")))] #[cfg_attr(docsrs, doc(cfg(feature = "process")))]
#[cfg(not(loom))] #[cfg(not(loom))]
#[cfg(not(tokio_wasi))]
$item $item
)* )*
} }
@ -275,6 +281,7 @@ macro_rules! cfg_signal {
#[cfg(feature = "signal")] #[cfg(feature = "signal")]
#[cfg_attr(docsrs, doc(cfg(feature = "signal")))] #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
#[cfg(not(loom))] #[cfg(not(loom))]
#[cfg(not(tokio_wasi))]
$item $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 { macro_rules! cfg_not_signal_internal {
($($item:item)*) => { ($($item:item)*) => {
$( $(
@ -334,7 +348,7 @@ macro_rules! cfg_not_rt {
macro_rules! cfg_rt_multi_thread { macro_rules! cfg_rt_multi_thread {
($($item:item)*) => { ($($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")))] #[cfg_attr(docsrs, doc(cfg(feature = "rt-multi-thread")))]
$item $item
)* )*
@ -447,12 +461,14 @@ macro_rules! cfg_not_coop {
macro_rules! cfg_has_atomic_u64 { macro_rules! cfg_has_atomic_u64 {
($($item:item)*) => { ($($item:item)*) => {
$( $(
#[cfg(not(any( #[cfg_attr(
target_arch = "arm", not(tokio_no_target_has_atomic),
target_arch = "mips", cfg(all(target_has_atomic = "64", not(tokio_no_atomic_u64))
target_arch = "powerpc", ))]
target_arch = "riscv32" #[cfg_attr(
)))] tokio_no_target_has_atomic,
cfg(not(tokio_no_atomic_u64))
)]
$item $item
)* )*
} }
@ -461,12 +477,62 @@ macro_rules! cfg_has_atomic_u64 {
macro_rules! cfg_not_has_atomic_u64 { macro_rules! cfg_not_has_atomic_u64 {
($($item:item)*) => { ($($item:item)*) => {
$( $(
#[cfg(any( #[cfg_attr(
target_arch = "arm", not(tokio_no_target_has_atomic),
target_arch = "mips", cfg(any(not(target_has_atomic = "64"), tokio_no_atomic_u64)
target_arch = "powerpc", ))]
target_arch = "riscv32" #[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 $item
)* )*
} }

View File

@ -72,8 +72,18 @@ macro_rules! join {
// Safety: nothing must be moved out of `futures`. This is to satisfy // Safety: nothing must be moved out of `futures`. This is to satisfy
// the requirement of `Pin::new_unchecked` called below. // 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), )* ); 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 // 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 // to ensure every future passed to join! gets a chance to make progress even if
// one of the futures consumes the whole budget. // one of the futures consumes the whole budget.
@ -106,7 +116,7 @@ macro_rules! join {
to_run -= 1; to_run -= 1;
// Extract the future for this branch from the tuple. // 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 // Safety: future is stored on the stack above
// and never moved. // and never moved.

View File

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

View File

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

View File

@ -460,8 +460,18 @@ macro_rules! select {
let mut output = { let mut output = {
// Safety: Nothing must be moved out of `futures`. This is to // Safety: Nothing must be moved out of `futures`. This is to
// satisfy the requirement of `Pin::new_unchecked` called below. // 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 , )+ ); 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| { $crate::macros::support::poll_fn(|cx| {
// Track if any branch returns pending. If no branch completes // Track if any branch returns pending. If no branch completes
// **or** returns pending, this implies that all branches are // **or** returns pending, this implies that all branches are
@ -497,7 +507,7 @@ macro_rules! select {
// Extract the future for this branch from the // Extract the future for this branch from the
// tuple // tuple
let ( $($skip,)* fut, .. ) = &mut futures; let ( $($skip,)* fut, .. ) = &mut *futures;
// Safety: future is stored on the stack above // Safety: future is stored on the stack above
// and never moved. // and never moved.

View File

@ -1,7 +1,11 @@
cfg_macros! { cfg_macros! {
pub use crate::future::poll_fn; pub use crate::future::poll_fn;
pub use crate::future::maybe_done::maybe_done; 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; pub use std::future::Future;

View File

@ -1,4 +1,32 @@
#[cfg(all(loom, test))] #[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)+ } } ($($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 // Safety: nothing must be moved out of `futures`. This is to satisfy
// the requirement of `Pin::new_unchecked` called below. // 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), )* ); 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 // 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 // to ensure every future passed to join! gets a chance to make progress even if
// one of the futures consumes the whole budget. // one of the futures consumes the whole budget.
@ -152,7 +162,7 @@ macro_rules! try_join {
to_run -= 1; to_run -= 1;
// Extract the future for this branch from the tuple. // 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 // Safety: future is stored on the stack above
// and never moved. // and never moved.

View File

@ -244,7 +244,7 @@ cfg_net! {
type Future = <str as sealed::ToSocketAddrsPriv>::Future; type Future = <str as sealed::ToSocketAddrsPriv>::Future;
fn to_socket_addrs(&self, _: sealed::Internal) -> Self::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 //! [`UnixDatagram`]: UnixDatagram
mod addr; mod addr;
#[cfg(feature = "net")] cfg_not_wasi! {
pub(crate) use addr::to_socket_addrs; #[cfg(feature = "net")]
pub(crate) use addr::to_socket_addrs;
}
pub use addr::ToSocketAddrs; pub use addr::ToSocketAddrs;
cfg_net! { cfg_net! {
@ -33,11 +35,13 @@ cfg_net! {
pub mod tcp; pub mod tcp;
pub use tcp::listener::TcpListener; pub use tcp::listener::TcpListener;
pub use tcp::socket::TcpSocket;
pub use tcp::stream::TcpStream; pub use tcp::stream::TcpStream;
cfg_not_wasi! {
pub use tcp::socket::TcpSocket;
mod udp; mod udp;
pub use udp::UdpSocket; pub use udp::UdpSocket;
}
} }
cfg_net_unix! { cfg_net_unix! {

View File

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

View File

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

View File

@ -398,6 +398,89 @@ impl TcpSocket {
self.inner.linger() 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. /// Gets the local address of this socket.
/// ///
/// Will fail on windows if called before `bind`. /// 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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`]. /// This function is equivalent to [`TcpStream::ready`].
/// ///
/// # Cancel safety /// # Cancel safety
@ -190,8 +196,12 @@ impl ReadHalf<'_> {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the stream is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> { pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.try_read(buf) 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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`]. /// This function is equivalent to [`TcpStream::ready`].
/// ///
/// # Cancel safety /// # Cancel safety

View File

@ -200,6 +200,12 @@ impl OwnedReadHalf {
/// can be used to concurrently read / write to the same socket on a single /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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`]. /// This function is equivalent to [`TcpStream::ready`].
/// ///
/// # Cancel safety /// # Cancel safety
@ -245,8 +251,12 @@ impl OwnedReadHalf {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the stream is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> { pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.try_read(buf) 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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`]. /// This function is equivalent to [`TcpStream::ready`].
/// ///
/// # Cancel safety /// # Cancel safety
@ -474,12 +490,12 @@ impl AsyncWrite for OwnedWriteHalf {
impl AsRef<TcpStream> for OwnedReadHalf { impl AsRef<TcpStream> for OwnedReadHalf {
fn as_ref(&self) -> &TcpStream { fn as_ref(&self) -> &TcpStream {
&*self.inner &self.inner
} }
} }
impl AsRef<TcpStream> for OwnedWriteHalf { impl AsRef<TcpStream> for OwnedWriteHalf {
fn as_ref(&self) -> &TcpStream { 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::io::{AsyncRead, AsyncWrite, Interest, PollEvented, ReadBuf, Ready};
use crate::net::tcp::split::{split, ReadHalf, WriteHalf}; use crate::net::tcp::split::{split, ReadHalf, WriteHalf};
use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf}; use crate::net::tcp::split_owned::{split_owned, OwnedReadHalf, OwnedWriteHalf};
use crate::net::{to_socket_addrs, ToSocketAddrs};
use std::convert::TryFrom; use std::convert::TryFrom;
use std::fmt; use std::fmt;
@ -10,7 +14,6 @@ use std::io;
use std::net::{Shutdown, SocketAddr}; use std::net::{Shutdown, SocketAddr};
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Duration;
cfg_io_util! { cfg_io_util! {
use bytes::BufMut; use bytes::BufMut;
@ -70,86 +73,88 @@ cfg_net! {
} }
impl TcpStream { impl TcpStream {
/// Opens a TCP connection to a remote host. 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` /// `addr` is an address of the remote host. Anything which implements the
/// yields multiple addresses, connect will be attempted with each of the /// [`ToSocketAddrs`] trait can be supplied as the address. If `addr`
/// addresses until a connection is successful. If none of the addresses /// yields multiple addresses, connect will be attempted with each of the
/// result in a successful connection, the error returned from the last /// addresses until a connection is successful. If none of the addresses
/// connection attempt (the last address) is returned. /// 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. /// To configure the socket before connecting, you can use the [`TcpSocket`]
/// /// type.
/// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs ///
/// [`TcpSocket`]: struct@crate::net::TcpSocket /// [`ToSocketAddrs`]: trait@crate::net::ToSocketAddrs
/// /// [`TcpSocket`]: struct@crate::net::TcpSocket
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// use tokio::net::TcpStream; /// ```no_run
/// use tokio::io::AsyncWriteExt; /// use tokio::net::TcpStream;
/// use std::error::Error; /// use tokio::io::AsyncWriteExt;
/// /// use std::error::Error;
/// #[tokio::main] ///
/// async fn main() -> Result<(), Box<dyn Error>> { /// #[tokio::main]
/// // Connect to a peer /// async fn main() -> Result<(), Box<dyn Error>> {
/// let mut stream = TcpStream::connect("127.0.0.1:8080").await?; /// // 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?; /// // Write some data.
/// /// stream.write_all(b"hello world!").await?;
/// Ok(()) ///
/// } /// Ok(())
/// ``` /// }
/// /// ```
/// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait. ///
/// /// The [`write_all`] method is defined on the [`AsyncWriteExt`] trait.
/// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all ///
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt /// [`write_all`]: fn@crate::io::AsyncWriteExt::write_all
pub async fn connect<A: ToSocketAddrs>(addr: A) -> io::Result<TcpStream> { /// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
let addrs = to_socket_addrs(addr).await?; 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 { for addr in addrs {
match TcpStream::connect_addr(addr).await { match TcpStream::connect_addr(addr).await {
Ok(stream) => return Ok(stream), Ok(stream) => return Ok(stream),
Err(e) => last_err = Some(e), 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(|| { /// Establishes a connection to the specified `addr`.
io::Error::new( async fn connect_addr(addr: SocketAddr) -> io::Result<TcpStream> {
io::ErrorKind::InvalidInput, let sys = mio::net::TcpStream::connect(addr)?;
"could not resolve to any address", TcpStream::connect_mio(sys).await
)
}))
}
/// 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);
} }
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> { pub(crate) fn new(connected: mio::net::TcpStream) -> io::Result<TcpStream> {
@ -181,11 +186,13 @@ impl TcpStream {
/// ///
/// # Panics /// # 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 /// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set /// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> { pub fn from_std(stream: std::net::TcpStream) -> io::Result<TcpStream> {
let io = mio::net::TcpStream::from_std(stream); let io = mio::net::TcpStream::from_std(stream);
let io = PollEvented::new(io)?; let io = PollEvented::new(io)?;
@ -244,6 +251,15 @@ impl TcpStream {
.map(|io| io.into_raw_socket()) .map(|io| io.into_raw_socket())
.map(|raw_socket| unsafe { std::net::TcpStream::from_raw_socket(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. /// 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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 /// # Cancel safety
/// ///
/// This method is cancel safe. Once a readiness event occurs, the method /// This method is cancel safe. Once a readiness event occurs, the method
@ -531,8 +553,12 @@ impl TcpStream {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the stream is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
/// ///
/// # Examples /// # Examples
@ -944,7 +970,7 @@ impl TcpStream {
/// Tries to read or write from the socket using a user-provided IO operation. /// 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 /// 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 /// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a /// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value /// `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 /// defined on the Tokio `TcpStream` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly. /// 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. /// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
/// ///
/// [`readable()`]: TcpStream::readable() /// [`readable()`]: TcpStream::readable()
@ -1077,52 +1108,54 @@ impl TcpStream {
self.io.set_nodelay(nodelay) self.io.set_nodelay(nodelay)
} }
/// Reads the linger duration for this socket by getting the `SO_LINGER` cfg_not_wasi! {
/// option. /// Reads the linger duration for this socket by getting the `SO_LINGER`
/// /// option.
/// For more information about this option, see [`set_linger`]. ///
/// /// For more information about this option, see [`set_linger`].
/// [`set_linger`]: TcpStream::set_linger ///
/// /// [`set_linger`]: TcpStream::set_linger
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// use tokio::net::TcpStream; /// ```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?; /// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// /// let stream = TcpStream::connect("127.0.0.1:8080").await?;
/// println!("{:?}", stream.linger()?); ///
/// # Ok(()) /// println!("{:?}", stream.linger()?);
/// # } /// # Ok(())
/// ``` /// # }
pub fn linger(&self) -> io::Result<Option<Duration>> { /// ```
socket2::SockRef::from(self).linger() 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. /// 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 /// 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 /// closed. If SO_LINGER is set, the system shall block the process until it can transmit the
/// data or until the time expires. /// data or until the time expires.
/// ///
/// If SO_LINGER is not specified, and the stream is closed, the system handles the call in a /// 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. /// way that allows the process to continue as quickly as possible.
/// ///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
/// use tokio::net::TcpStream; /// use tokio::net::TcpStream;
/// ///
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> { /// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
/// let stream = TcpStream::connect("127.0.0.1:8080").await?; /// let stream = TcpStream::connect("127.0.0.1:8080").await?;
/// ///
/// stream.set_linger(None)?; /// stream.set_linger(None)?;
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> { pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
socket2::SockRef::from(self).set_linger(dur) socket2::SockRef::from(self).set_linger(dur)
}
} }
/// Gets the value of the `IP_TTL` option for this socket. /// 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) UdpSocket::new(sys)
} }
#[track_caller]
fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> { fn new(socket: mio::net::UdpSocket) -> io::Result<UdpSocket> {
let io = PollEvented::new(socket)?; let io = PollEvented::new(socket)?;
Ok(UdpSocket { io }) Ok(UdpSocket { io })
@ -210,6 +211,7 @@ impl UdpSocket {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[track_caller]
pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> { pub fn from_std(socket: net::UdpSocket) -> io::Result<UdpSocket> {
let io = mio::net::UdpSocket::from_std(socket); let io = mio::net::UdpSocket::from_std(socket);
UdpSocket::new(io) 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. /// Returns the local address that this socket is bound to.
/// ///
/// # Example /// # Example
@ -351,7 +357,9 @@ impl UdpSocket {
/// ///
/// The function may complete without the socket being ready. This is a /// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with /// 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 /// # Cancel safety
/// ///
@ -734,7 +742,7 @@ impl UdpSocket {
/// ///
/// # Cancel safety /// # 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 /// [`tokio::select!`](crate::select) statement and some other branch
/// completes first, it is guaranteed that no messages were received on this /// completes first, it is guaranteed that no messages were received on this
/// socket. /// socket.
@ -919,7 +927,7 @@ impl UdpSocket {
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the // Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
// buffer. // buffer.
let n = (&*self.io).recv(dst)?; let n = (*self.io).recv(dst)?;
unsafe { unsafe {
buf.advance_mut(n); buf.advance_mut(n);
@ -983,7 +991,7 @@ impl UdpSocket {
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the // Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
// buffer. // buffer.
let (n, addr) = (&*self.io).recv_from(dst)?; let (n, addr) = (*self.io).recv_from(dst)?;
unsafe { unsafe {
buf.advance_mut(n); buf.advance_mut(n);
@ -1265,7 +1273,7 @@ impl UdpSocket {
/// Tries to read or write from the socket using a user-provided IO operation. /// 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 /// 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 /// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a /// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value /// `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 /// defined on the Tokio `UdpSocket` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly. /// 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. /// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
/// ///
/// [`readable()`]: UdpSocket::readable() /// [`readable()`]: UdpSocket::readable()
@ -1508,6 +1521,89 @@ impl UdpSocket {
self.io.set_ttl(ttl) 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. /// Executes an operation of the `IP_ADD_MEMBERSHIP` type.
/// ///
/// This function specifies a new multicast group for this socket to join. /// 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 /// The function may complete without the socket being ready. This is a
/// false-positive and attempting an operation will return with /// 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 /// # Cancel safety
/// ///
@ -430,7 +432,8 @@ impl UnixDatagram {
/// ///
/// # Panics /// # 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 /// The runtime is usually set implicitly when this function is called
/// from a future driven by a Tokio runtime, otherwise runtime can be set /// from a future driven by a Tokio runtime, otherwise runtime can be set
@ -457,6 +460,7 @@ impl UnixDatagram {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[track_caller]
pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> { pub fn from_std(datagram: net::UnixDatagram) -> io::Result<UnixDatagram> {
let socket = mio::net::UnixDatagram::from_std(datagram); let socket = mio::net::UnixDatagram::from_std(datagram);
let io = PollEvented::new(socket)?; 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 // Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
// buffer. // buffer.
let (n, addr) = (&*self.io).recv_from(dst)?; let (n, addr) = (*self.io).recv_from(dst)?;
unsafe { unsafe {
buf.advance_mut(n); buf.advance_mut(n);
@ -907,7 +911,7 @@ impl UnixDatagram {
// Safety: We trust `UnixDatagram::recv` to have filled up `n` bytes in the // Safety: We trust `UnixDatagram::recv` to have filled up `n` bytes in the
// buffer. // buffer.
let n = (&*self.io).recv(dst)?; let n = (*self.io).recv(dst)?;
unsafe { unsafe {
buf.advance_mut(n); buf.advance_mut(n);
@ -1212,7 +1216,7 @@ impl UnixDatagram {
/// Tries to read or write from the socket using a user-provided IO operation. /// 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 /// 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 /// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a /// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value /// `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 /// defined on the Tokio `UnixDatagram` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly. /// 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. /// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
/// ///
/// [`readable()`]: UnixDatagram::readable() /// [`readable()`]: UnixDatagram::readable()

View File

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

View File

@ -1,5 +1,4 @@
//! Unix domain socket utility types. //! Unix domain socket utility types.
// This module does not currently provide any public API, but it was // This module does not currently provide any public API, but it was
// unintentionally defined as a public module. Hide it from the documentation // unintentionally defined as a public module. Hide it from the documentation
// instead of changing it to a private module to avoid breakage. // instead of changing it to a private module to avoid breakage.
@ -22,3 +21,15 @@ pub(crate) use stream::UnixStream;
mod ucred; mod ucred;
pub use ucred::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 /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the stream is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> { pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.0.try_read(buf) 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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 /// # Cancel safety
/// ///
/// This method is cancel safe. Once a readiness event occurs, the method /// 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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 /// # Cancel safety
/// ///
/// This method is cancel safe. Once a readiness event occurs, the method /// This method is cancel safe. Once a readiness event occurs, the method
@ -155,8 +161,12 @@ impl OwnedReadHalf {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the stream is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> { pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
self.inner.try_read(buf) 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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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 /// # Cancel safety
/// ///
/// This method is cancel safe. Once a readiness event occurs, the method /// 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 { impl AsRef<UnixStream> for OwnedReadHalf {
fn as_ref(&self) -> &UnixStream { fn as_ref(&self) -> &UnixStream {
&*self.inner &self.inner
} }
} }
impl AsRef<UnixStream> for OwnedWriteHalf { impl AsRef<UnixStream> for OwnedWriteHalf {
fn as_ref(&self) -> &UnixStream { fn as_ref(&self) -> &UnixStream {
&*self.inner &self.inner
} }
} }

View File

@ -22,8 +22,8 @@ cfg_io_util! {
cfg_net_unix! { cfg_net_unix! {
/// A structure representing a connected Unix socket. /// A structure representing a connected Unix socket.
/// ///
/// This socket can be connected directly with `UnixStream::connect` or accepted /// This socket can be connected directly with [`UnixStream::connect`] or accepted
/// from a listener with `UnixListener::incoming`. Additionally, a pair of /// from a listener with [`UnixListener::accept`]. Additionally, a pair of
/// anonymous Unix sockets can be created with `UnixStream::pair`. /// anonymous Unix sockets can be created with `UnixStream::pair`.
/// ///
/// To shut down the stream in the write direction, you can call the /// 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. /// the stream in one direction.
/// ///
/// [`shutdown()`]: fn@crate::io::AsyncWriteExt::shutdown /// [`shutdown()`]: fn@crate::io::AsyncWriteExt::shutdown
/// [`UnixListener::accept`]: crate::net::UnixListener::accept
pub struct UnixStream { pub struct UnixStream {
io: PollEvented<mio::net::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 /// can be used to concurrently read / write to the same socket on a single
/// task without splitting the socket. /// 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 /// # Cancel safety
/// ///
/// This method is cancel safe. Once a readiness event occurs, the method /// This method is cancel safe. Once a readiness event occurs, the method
@ -239,8 +246,12 @@ impl UnixStream {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the stream is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
/// ///
/// # Examples /// # Examples
@ -656,7 +667,7 @@ impl UnixStream {
/// Tries to read or write from the socket using a user-provided IO operation. /// 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 /// 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 /// calling the appropriate syscall. If the operation fails because the
/// socket is not actually ready, then the closure should return a /// socket is not actually ready, then the closure should return a
/// `WouldBlock` error and the readiness flag is cleared. The return value /// `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 /// defined on the Tokio `UnixStream` type, as this will mess with the
/// readiness flag and can cause the socket to behave incorrectly. /// 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. /// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
/// ///
/// [`readable()`]: UnixStream::readable() /// [`readable()`]: UnixStream::readable()
@ -699,11 +715,13 @@ impl UnixStream {
/// ///
/// # Panics /// # 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 /// The runtime is usually set implicitly when this function is called
/// from a future driven by a tokio runtime, otherwise runtime can be set /// from a future driven by a tokio runtime, otherwise runtime can be set
/// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function. /// explicitly with [`Runtime::enter`](crate::runtime::Runtime::enter) function.
#[track_caller]
pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> { pub fn from_std(stream: net::UnixStream) -> io::Result<UnixStream> {
let stream = mio::net::UnixStream::from_std(stream); let stream = mio::net::UnixStream::from_std(stream);
let io = PollEvented::new(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. /// Credentials of a process.
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub struct UCred { pub struct UCred {
/// PID (process ID) of the process. /// PID (process ID) of the process.
pid: Option<pid_t>, pid: Option<unix::pid_t>,
/// UID (user ID) of the process. /// UID (user ID) of the process.
uid: uid_t, uid: unix::uid_t,
/// GID (group ID) of the process. /// GID (group ID) of the process.
gid: gid_t, gid: unix::gid_t,
} }
impl UCred { impl UCred {
/// Gets UID (user ID) of the process. /// Gets UID (user ID) of the process.
pub fn uid(&self) -> uid_t { pub fn uid(&self) -> unix::uid_t {
self.uid self.uid
} }
/// Gets GID (group ID) of the process. /// Gets GID (group ID) of the process.
pub fn gid(&self) -> gid_t { pub fn gid(&self) -> unix::gid_t {
self.gid self.gid
} }
@ -26,7 +26,7 @@ impl UCred {
/// ///
/// This is only implemented under Linux, Android, iOS, macOS, Solaris and /// This is only implemented under Linux, Android, iOS, macOS, Solaris and
/// Illumos. On other platforms this will always return `None`. /// 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 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"))] #[cfg(any(target_os = "linux", target_os = "android", target_os = "openbsd"))]
pub(crate) mod impl_linux { 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 libc::{c_void, getsockopt, socklen_t, SOL_SOCKET, SO_PEERCRED};
use std::{io, mem}; use std::{io, mem};
@ -87,9 +87,9 @@ pub(crate) mod impl_linux {
); );
if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() { if ret == 0 && ucred_size as usize == mem::size_of::<ucred>() {
Ok(super::UCred { Ok(super::UCred {
uid: ucred.uid, uid: ucred.uid as unix::uid_t,
gid: ucred.gid, gid: ucred.gid as unix::gid_t,
pid: Some(ucred.pid), pid: Some(ucred.pid as unix::pid_t),
}) })
} else { } else {
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())
@ -100,7 +100,7 @@ pub(crate) mod impl_linux {
#[cfg(any(target_os = "netbsd"))] #[cfg(any(target_os = "netbsd"))]
pub(crate) mod impl_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 libc::{c_void, getsockopt, socklen_t, unpcbid, LOCAL_PEEREID, SOL_SOCKET};
use std::io; use std::io;
@ -129,9 +129,9 @@ pub(crate) mod impl_netbsd {
); );
if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() { if ret == 0 && unpcbid_size as usize == size_of::<unpcbid>() {
Ok(super::UCred { Ok(super::UCred {
uid: unpcbid.unp_euid, uid: unpcbid.unp_euid as unix::uid_t,
gid: unpcbid.unp_egid, gid: unpcbid.unp_egid as unix::gid_t,
pid: Some(unpcbid.unp_pid), pid: Some(unpcbid.unp_pid as unix::pid_t),
}) })
} else { } else {
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())
@ -142,7 +142,7 @@ pub(crate) mod impl_netbsd {
#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
pub(crate) mod impl_bsd { pub(crate) mod impl_bsd {
use crate::net::unix::UnixStream; use crate::net::unix::{self, UnixStream};
use libc::getpeereid; use libc::getpeereid;
use std::io; use std::io;
@ -160,8 +160,8 @@ pub(crate) mod impl_bsd {
if ret == 0 { if ret == 0 {
Ok(super::UCred { Ok(super::UCred {
uid: uid.assume_init(), uid: uid.assume_init() as unix::uid_t,
gid: gid.assume_init(), gid: gid.assume_init() as unix::gid_t,
pid: None, pid: None,
}) })
} else { } else {
@ -173,7 +173,7 @@ pub(crate) mod impl_bsd {
#[cfg(any(target_os = "macos", target_os = "ios"))] #[cfg(any(target_os = "macos", target_os = "ios"))]
pub(crate) mod impl_macos { 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 libc::{c_void, getpeereid, getsockopt, pid_t, LOCAL_PEEREPID, SOL_LOCAL};
use std::io; use std::io;
@ -207,9 +207,9 @@ pub(crate) mod impl_macos {
if ret == 0 { if ret == 0 {
Ok(super::UCred { Ok(super::UCred {
uid: uid.assume_init(), uid: uid.assume_init() as unix::uid_t,
gid: gid.assume_init(), gid: gid.assume_init() as unix::gid_t,
pid: Some(pid.assume_init()), pid: Some(pid.assume_init() as unix::pid_t),
}) })
} else { } else {
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())
@ -220,7 +220,7 @@ pub(crate) mod impl_macos {
#[cfg(any(target_os = "solaris", target_os = "illumos"))] #[cfg(any(target_os = "solaris", target_os = "illumos"))]
pub(crate) mod impl_solaris { pub(crate) mod impl_solaris {
use crate::net::unix::UnixStream; use crate::net::unix::{self, UnixStream};
use std::io; use std::io;
use std::os::unix::io::AsRawFd; use std::os::unix::io::AsRawFd;
use std::ptr; use std::ptr;
@ -240,9 +240,9 @@ pub(crate) mod impl_solaris {
libc::ucred_free(cred); libc::ucred_free(cred);
Ok(super::UCred { Ok(super::UCred {
uid, uid: uid as unix::uid_t,
gid, gid: gid as unix::gid_t,
pid: Some(pid), pid: Some(pid as unix::pid_t),
}) })
} else { } else {
Err(io::Error::last_os_error()) Err(io::Error::last_os_error())

View File

@ -20,21 +20,18 @@ cfg_io_util! {
#[cfg(not(docsrs))] #[cfg(not(docsrs))]
mod doc { mod doc {
pub(super) use crate::os::windows::ffi::OsStrExt; pub(super) use crate::os::windows::ffi::OsStrExt;
pub(super) use crate::winapi::shared::minwindef::{DWORD, FALSE}; pub(super) mod windows_sys {
pub(super) use crate::winapi::um::fileapi; pub(crate) use windows_sys::{
pub(super) use crate::winapi::um::handleapi; Win32::Foundation::*, Win32::Storage::FileSystem::*, Win32::System::Pipes::*,
pub(super) use crate::winapi::um::namedpipeapi; Win32::System::SystemServices::*,
pub(super) use crate::winapi::um::winbase; };
pub(super) use crate::winapi::um::winnt; }
pub(super) use mio::windows as mio_windows; pub(super) use mio::windows as mio_windows;
} }
// NB: none of these shows up in public API, so don't document them. // NB: none of these shows up in public API, so don't document them.
#[cfg(docsrs)] #[cfg(docsrs)]
mod doc { mod doc {
pub type DWORD = crate::doc::NotDefinedHere;
pub(super) mod mio_windows { pub(super) mod mio_windows {
pub type NamedPipe = crate::doc::NotDefinedHere; pub type NamedPipe = crate::doc::NotDefinedHere;
} }
@ -101,7 +98,6 @@ use self::doc::*;
/// # Ok(()) } /// # 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 /// [Windows named pipe]: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
#[derive(Debug)] #[derive(Debug)]
pub struct NamedPipeServer { pub struct NamedPipeServer {
@ -192,17 +188,15 @@ impl NamedPipeServer {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
pub async fn connect(&self) -> io::Result<()> { pub async fn connect(&self) -> io::Result<()> {
loop { match self.io.connect() {
match self.io.connect() { Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
Ok(()) => break, self.io
Err(e) if e.kind() == io::ErrorKind::WouldBlock => { .registration()
self.io.registration().readiness(Interest::WRITABLE).await?; .async_io(Interest::WRITABLE, || self.io.connect())
} .await
Err(e) => return Err(e),
} }
x => x,
} }
Ok(())
} }
/// Disconnects the server end of a named pipe instance from a client /// Disconnects the server end of a named pipe instance from a client
@ -211,7 +205,7 @@ impl NamedPipeServer {
/// ``` /// ```
/// use tokio::io::AsyncWriteExt; /// use tokio::io::AsyncWriteExt;
/// use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions}; /// 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"; /// 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 /// // Write fails with an OS-specific error after client has been
/// // disconnected. /// // disconnected.
/// let e = client.write(b"ping").await.unwrap_err(); /// 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(()) } /// # Ok(()) }
/// ``` /// ```
pub fn disconnect(&self) -> io::Result<()> { 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 /// can be used to concurrently read / write to the same pipe on a single
/// task without splitting the pipe. /// 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 /// # Examples
/// ///
/// Concurrently read and write to the pipe on the same task without /// Concurrently read and write to the pipe on the same task without
@ -403,8 +403,12 @@ impl NamedPipeServer {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the pipe is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
/// ///
/// # Examples /// # Examples
@ -536,7 +540,7 @@ impl NamedPipeServer {
/// Tries to read data from the stream into the provided buffer, advancing the /// Tries to read data from the stream into the provided buffer, advancing the
/// buffer's internal cursor, returning how many bytes were read. /// 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 /// 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 /// `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. /// 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)?; /// let server = named_pipe::ServerOptions::new().create(PIPE_NAME)?;
/// ///
/// loop { /// loop {
/// // Wait for the socket to be readable /// // Wait for the pipe to be readable
/// server.readable().await?; /// server.readable().await?;
/// ///
/// let mut buf = Vec::with_capacity(4096); /// let mut buf = Vec::with_capacity(4096);
@ -808,27 +812,32 @@ impl NamedPipeServer {
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf)) .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 /// If the pipe 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 from the pipe by manually
/// calling the appropriate syscall. If the operation fails because the /// 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 /// `WouldBlock` error and the readiness flag is cleared. The return value
/// of the closure is then returned by `try_io`. /// 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. /// and a `WouldBlock` error is returned.
/// ///
/// The closure should only return a `WouldBlock` error if it has performed /// 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 /// 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. /// behave incorrectly.
/// ///
/// The closure should not perform the IO operation using any of the /// The closure should not perform the IO operation using any of the
/// methods defined on the Tokio `NamedPipeServer` type, as this will mess with /// 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. /// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
/// ///
@ -903,7 +912,7 @@ impl AsRawHandle for NamedPipeServer {
/// use std::time::Duration; /// use std::time::Duration;
/// use tokio::net::windows::named_pipe::ClientOptions; /// use tokio::net::windows::named_pipe::ClientOptions;
/// use tokio::time; /// 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"; /// const PIPE_NAME: &str = r"\\.\pipe\named-pipe-idiomatic-client";
/// ///
@ -911,7 +920,7 @@ impl AsRawHandle for NamedPipeServer {
/// let client = loop { /// let client = loop {
/// match ClientOptions::new().open(PIPE_NAME) { /// match ClientOptions::new().open(PIPE_NAME) {
/// Ok(client) => break client, /// 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), /// Err(e) => return Err(e),
/// } /// }
/// ///
@ -922,7 +931,7 @@ impl AsRawHandle for NamedPipeServer {
/// # Ok(()) } /// # 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 /// [Windows named pipe]: https://docs.microsoft.com/en-us/windows/win32/ipc/named-pipes
#[derive(Debug)] #[derive(Debug)]
pub struct NamedPipeClient { pub struct NamedPipeClient {
@ -986,6 +995,12 @@ impl NamedPipeClient {
/// can be used to concurrently read / write to the same pipe on a single /// can be used to concurrently read / write to the same pipe on a single
/// task without splitting the pipe. /// 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 /// # Examples
/// ///
/// Concurrently read and write to the pipe on the same task without /// Concurrently read and write to the pipe on the same task without
@ -1143,8 +1158,12 @@ impl NamedPipeClient {
/// # Return /// # Return
/// ///
/// If data is successfully read, `Ok(n)` is returned, where `n` is the /// 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 /// number of bytes read. If `n` is `0`, then it can indicate one of two scenarios:
/// and will no longer yield data. If the pipe is not ready to read data ///
/// 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. /// `Err(io::ErrorKind::WouldBlock)` is returned.
/// ///
/// # Examples /// # Examples
@ -1274,7 +1293,7 @@ impl NamedPipeClient {
/// Tries to read data from the stream into the provided buffer, advancing the /// Tries to read data from the stream into the provided buffer, advancing the
/// buffer's internal cursor, returning how many bytes were read. /// 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 /// 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 /// `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. /// 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)?; /// let client = named_pipe::ClientOptions::new().open(PIPE_NAME)?;
/// ///
/// loop { /// loop {
/// // Wait for the socket to be readable /// // Wait for the pipe to be readable
/// client.readable().await?; /// client.readable().await?;
/// ///
/// let mut buf = Vec::with_capacity(4096); /// let mut buf = Vec::with_capacity(4096);
@ -1543,27 +1562,32 @@ impl NamedPipeClient {
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf)) .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 /// If the pipe 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 from the pipe by manually
/// calling the appropriate syscall. If the operation fails because the /// 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 /// `WouldBlock` error and the readiness flag is cleared. The return value
/// of the closure is then returned by `try_io`. /// 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. /// and a `WouldBlock` error is returned.
/// ///
/// The closure should only return a `WouldBlock` error if it has performed /// 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 /// 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. /// behave incorrectly.
/// ///
/// The closure should not perform the IO operation using any of the methods /// 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 /// 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. /// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
/// ///
@ -1641,12 +1665,12 @@ macro_rules! bool_flag {
/// See [`ServerOptions::create`]. /// See [`ServerOptions::create`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ServerOptions { pub struct ServerOptions {
open_mode: DWORD, open_mode: u32,
pipe_mode: DWORD, pipe_mode: u32,
max_instances: DWORD, max_instances: u32,
out_buffer_size: DWORD, out_buffer_size: u32,
in_buffer_size: DWORD, in_buffer_size: u32,
default_timeout: DWORD, default_timeout: u32,
} }
impl ServerOptions { impl ServerOptions {
@ -1663,9 +1687,9 @@ impl ServerOptions {
/// ``` /// ```
pub fn new() -> ServerOptions { pub fn new() -> ServerOptions {
ServerOptions { ServerOptions {
open_mode: winbase::PIPE_ACCESS_DUPLEX | winbase::FILE_FLAG_OVERLAPPED, open_mode: windows_sys::PIPE_ACCESS_DUPLEX | windows_sys::FILE_FLAG_OVERLAPPED,
pipe_mode: winbase::PIPE_TYPE_BYTE | winbase::PIPE_REJECT_REMOTE_CLIENTS, pipe_mode: windows_sys::PIPE_TYPE_BYTE | windows_sys::PIPE_REJECT_REMOTE_CLIENTS,
max_instances: winbase::PIPE_UNLIMITED_INSTANCES, max_instances: windows_sys::PIPE_UNLIMITED_INSTANCES,
out_buffer_size: 65536, out_buffer_size: 65536,
in_buffer_size: 65536, in_buffer_size: 65536,
default_timeout: 0, default_timeout: 0,
@ -1681,11 +1705,10 @@ impl ServerOptions {
/// ///
/// [`dwPipeMode`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea /// [`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 { pub fn pipe_mode(&mut self, pipe_mode: PipeMode) -> &mut Self {
self.pipe_mode = match pipe_mode { let is_msg = matches!(pipe_mode, PipeMode::Message);
PipeMode::Byte => winbase::PIPE_TYPE_BYTE, // Pipe mode is implemented as a bit flag 0x4. Set is message and unset
PipeMode::Message => winbase::PIPE_TYPE_MESSAGE, // is byte.
}; bool_flag!(self.pipe_mode, is_msg, windows_sys::PIPE_TYPE_MESSAGE);
self self
} }
@ -1781,7 +1804,7 @@ impl ServerOptions {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
pub fn access_inbound(&mut self, allowed: bool) -> &mut Self { 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 self
} }
@ -1879,7 +1902,7 @@ impl ServerOptions {
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
pub fn access_outbound(&mut self, allowed: bool) -> &mut Self { 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 self
} }
@ -1950,7 +1973,113 @@ impl ServerOptions {
bool_flag!( bool_flag!(
self.open_mode, self.open_mode,
first, 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 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 /// [`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 { 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 self
} }
@ -1984,7 +2117,7 @@ impl ServerOptions {
/// ``` /// ```
/// use std::io; /// use std::io;
/// use tokio::net::windows::named_pipe::{ServerOptions, ClientOptions}; /// 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"; /// const PIPE_NAME: &str = r"\\.\pipe\tokio-named-pipe-max-instances";
/// ///
@ -2000,11 +2133,11 @@ impl ServerOptions {
/// ///
/// // Too many servers! /// // Too many servers!
/// let e = server.create(PIPE_NAME).unwrap_err(); /// 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! /// // Still too many servers even if we specify a higher value!
/// let e = server.max_instances(100).create(PIPE_NAME).unwrap_err(); /// 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(()) } /// # Ok(()) }
/// ``` /// ```
/// ///
@ -2020,9 +2153,10 @@ impl ServerOptions {
/// let builder = ServerOptions::new().max_instances(255); /// let builder = ServerOptions::new().max_instances(255);
/// # Ok(()) } /// # Ok(()) }
/// ``` /// ```
#[track_caller]
pub fn max_instances(&mut self, instances: usize) -> &mut Self { pub fn max_instances(&mut self, instances: usize) -> &mut Self {
assert!(instances < 255, "cannot specify more than 254 instances"); assert!(instances < 255, "cannot specify more than 254 instances");
self.max_instances = instances as DWORD; self.max_instances = instances as u32;
self self
} }
@ -2032,7 +2166,7 @@ impl ServerOptions {
/// ///
/// [`nOutBufferSize`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea /// [`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 { pub fn out_buffer_size(&mut self, buffer: u32) -> &mut Self {
self.out_buffer_size = buffer as DWORD; self.out_buffer_size = buffer;
self self
} }
@ -2042,7 +2176,7 @@ impl ServerOptions {
/// ///
/// [`nInBufferSize`]: https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createnamedpipea /// [`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 { pub fn in_buffer_size(&mut self, buffer: u32) -> &mut Self {
self.in_buffer_size = buffer as DWORD; self.in_buffer_size = buffer;
self self
} }
@ -2099,7 +2233,7 @@ impl ServerOptions {
/// ///
/// [`create`]: ServerOptions::create /// [`create`]: ServerOptions::create
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew /// [`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( pub unsafe fn create_with_security_attributes_raw(
&self, &self,
addr: impl AsRef<OsStr>, addr: impl AsRef<OsStr>,
@ -2107,7 +2241,7 @@ impl ServerOptions {
) -> io::Result<NamedPipeServer> { ) -> io::Result<NamedPipeServer> {
let addr = encode_addr(addr); let addr = encode_addr(addr);
let h = namedpipeapi::CreateNamedPipeW( let h = windows_sys::CreateNamedPipeW(
addr.as_ptr(), addr.as_ptr(),
self.open_mode, self.open_mode,
self.pipe_mode, self.pipe_mode,
@ -2118,11 +2252,11 @@ impl ServerOptions {
attrs as *mut _, attrs as *mut _,
); );
if h == handleapi::INVALID_HANDLE_VALUE { if h == windows_sys::INVALID_HANDLE_VALUE {
return Err(io::Error::last_os_error()); 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`]. /// See [`ClientOptions::open`].
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ClientOptions { pub struct ClientOptions {
desired_access: DWORD, desired_access: u32,
security_qos_flags: DWORD, security_qos_flags: u32,
} }
impl ClientOptions { impl ClientOptions {
@ -2152,8 +2286,9 @@ impl ClientOptions {
/// ``` /// ```
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
desired_access: winnt::GENERIC_READ | winnt::GENERIC_WRITE, desired_access: windows_sys::GENERIC_READ | windows_sys::GENERIC_WRITE,
security_qos_flags: winbase::SECURITY_IDENTIFICATION | winbase::SECURITY_SQOS_PRESENT, 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 /// [`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 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
pub fn read(&mut self, allowed: bool) -> &mut Self { 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 self
} }
@ -2175,7 +2310,7 @@ impl ClientOptions {
/// [`GENERIC_WRITE`]: https://docs.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights /// [`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 /// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew
pub fn write(&mut self, allowed: bool) -> &mut Self { 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 self
} }
@ -2198,11 +2333,11 @@ impl ClientOptions {
/// automatically when using this method. /// automatically when using this method.
/// ///
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea /// [`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 /// [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 { pub fn security_qos_flags(&mut self, flags: u32) -> &mut Self {
// See: https://github.com/rust-lang/rust/pull/58216 // 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 self
} }
@ -2227,8 +2362,7 @@ impl ClientOptions {
/// but the server is not currently waiting for a connection. Please see the /// but the server is not currently waiting for a connection. Please see the
/// examples for how to check for this error. /// examples for how to check for this error.
/// ///
/// [`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
/// [`winapi`]: crate::winapi
/// [enabled I/O]: crate::runtime::Builder::enable_io /// [enabled I/O]: crate::runtime::Builder::enable_io
/// [Tokio Runtime]: crate::runtime::Runtime /// [Tokio Runtime]: crate::runtime::Runtime
/// ///
@ -2239,7 +2373,7 @@ impl ClientOptions {
/// use std::time::Duration; /// use std::time::Duration;
/// use tokio::net::windows::named_pipe::ClientOptions; /// use tokio::net::windows::named_pipe::ClientOptions;
/// use tokio::time; /// use tokio::time;
/// use winapi::shared::winerror; /// use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
/// ///
/// const PIPE_NAME: &str = r"\\.\pipe\mynamedpipe"; /// const PIPE_NAME: &str = r"\\.\pipe\mynamedpipe";
/// ///
@ -2247,7 +2381,7 @@ impl ClientOptions {
/// let client = loop { /// let client = loop {
/// match ClientOptions::new().open(PIPE_NAME) { /// match ClientOptions::new().open(PIPE_NAME) {
/// Ok(client) => break client, /// 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), /// Err(e) => return Err(e),
/// } /// }
/// ///
@ -2277,7 +2411,7 @@ impl ClientOptions {
/// ///
/// [`open`]: ClientOptions::open /// [`open`]: ClientOptions::open
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew /// [`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( pub unsafe fn open_with_security_attributes_raw(
&self, &self,
addr: impl AsRef<OsStr>, addr: impl AsRef<OsStr>,
@ -2286,28 +2420,28 @@ impl ClientOptions {
let addr = encode_addr(addr); let addr = encode_addr(addr);
// NB: We could use a platform specialized `OpenOptions` here, but since // 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 // `CreateFile` explicitly since it allows the use of our already
// well-structured wide `addr` to pass into CreateFileW. // well-structured wide `addr` to pass into CreateFileW.
let h = fileapi::CreateFileW( let h = windows_sys::CreateFileW(
addr.as_ptr(), addr.as_ptr(),
self.desired_access, self.desired_access,
0, 0,
attrs as *mut _, attrs as *mut _,
fileapi::OPEN_EXISTING, windows_sys::OPEN_EXISTING,
self.get_flags(), 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()); return Err(io::Error::last_os_error());
} }
NamedPipeClient::from_raw_handle(h) NamedPipeClient::from_raw_handle(h as _)
} }
fn get_flags(&self) -> u32 { 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 /// Data is written to the pipe as a stream of bytes. The pipe does not
/// distinguish bytes written during different write operations. /// 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, Byte,
/// Data is written to the pipe as a stream of messages. The pipe treats the /// 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 /// 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 /// on a named pipe returns [`ERROR_MORE_DATA`] when a message is not read
/// completely. /// 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, Message,
} }
@ -2339,11 +2476,15 @@ pub enum PipeMode {
pub enum PipeEnd { pub enum PipeEnd {
/// The named pipe refers to the client end of a named pipe instance. /// 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, Client,
/// The named pipe refers to the server end of a named pipe instance. /// 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, Server,
} }
@ -2381,26 +2522,26 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
let mut in_buffer_size = 0; let mut in_buffer_size = 0;
let mut max_instances = 0; let mut max_instances = 0;
let result = namedpipeapi::GetNamedPipeInfo( let result = windows_sys::GetNamedPipeInfo(
handle, handle as _,
&mut flags, &mut flags,
&mut out_buffer_size, &mut out_buffer_size,
&mut in_buffer_size, &mut in_buffer_size,
&mut max_instances, &mut max_instances,
); );
if result == FALSE { if result == 0 {
return Err(io::Error::last_os_error()); return Err(io::Error::last_os_error());
} }
let mut end = PipeEnd::Client; let mut end = PipeEnd::Client;
let mut mode = PipeMode::Byte; let mut mode = PipeMode::Byte;
if flags & winbase::PIPE_SERVER_END != 0 { if flags & windows_sys::PIPE_SERVER_END != 0 {
end = PipeEnd::Server; end = PipeEnd::Server;
} }
if flags & winbase::PIPE_TYPE_MESSAGE != 0 { if flags & windows_sys::PIPE_TYPE_MESSAGE != 0 {
mode = PipeMode::Message; mode = PipeMode::Message;
} }
@ -2412,3 +2553,48 @@ unsafe fn named_pipe_info(handle: RawHandle) -> io::Result<PipeInfo> {
max_instances, 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 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. /// Executes the command as a child process, returning a handle to it.
/// ///
/// By default, stdin, stdout and stderr are inherited from the parent. /// 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> { fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// Keep track of task budget // 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); let ret = Pin::new(&mut self.inner).poll(cx);
@ -1285,41 +1315,51 @@ impl ChildStderr {
impl AsyncWrite for ChildStdin { impl AsyncWrite for ChildStdin {
fn poll_write( fn poll_write(
self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> 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<()>> { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) Pin::new(&mut self.inner).poll_flush(cx)
} }
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_shutdown(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) 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 { impl AsyncRead for ChildStdout {
fn poll_read( fn poll_read(
self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>, buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> { ) -> Poll<io::Result<()>> {
// Safety: pipes support reading into uninitialized memory Pin::new(&mut self.inner).poll_read(cx, buf)
unsafe { self.inner.poll_read(cx, buf) }
} }
} }
impl AsyncRead for ChildStderr { impl AsyncRead for ChildStderr {
fn poll_read( fn poll_read(
self: Pin<&mut Self>, mut self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut ReadBuf<'_>, buf: &mut ReadBuf<'_>,
) -> Poll<io::Result<()>> { ) -> Poll<io::Result<()>> {
// Safety: pipes support reading into uninitialized memory Pin::new(&mut self.inner).poll_read(cx, buf)
unsafe { 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 //! processes in general aren't scalable (e.g. millions) so it shouldn't be that
//! bad in theory... //! bad in theory...
pub(crate) mod driver;
pub(crate) mod orphan; pub(crate) mod orphan;
use orphan::{OrphanQueue, OrphanQueueImpl, Wait}; use orphan::{OrphanQueue, OrphanQueueImpl, Wait};
mod reap; mod reap;
use reap::Reaper; use reap::Reaper;
use crate::io::PollEvented; use crate::io::{AsyncRead, AsyncWrite, PollEvented, ReadBuf};
use crate::process::kill::Kill; use crate::process::kill::Kill;
use crate::process::SpawnedChild; 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 crate::signal::unix::{signal, Signal, SignalKind};
use mio::event::Source; use mio::event::Source;
use mio::unix::SourceFd; use mio::unix::SourceFd;
use once_cell::sync::Lazy;
use std::fmt; use std::fmt;
use std::fs::File; use std::fs::File;
use std::future::Future; 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; pub(crate) struct GlobalOrphanQueue;
impl fmt::Debug for GlobalOrphanQueue { impl fmt::Debug for GlobalOrphanQueue {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
ORPHAN_QUEUE.fmt(fmt) get_orphan_queue().fmt(fmt)
} }
} }
impl GlobalOrphanQueue { impl GlobalOrphanQueue {
fn reap_orphans(handle: &SignalHandle) { pub(crate) fn reap_orphans(handle: &SignalHandle) {
ORPHAN_QUEUE.reap_orphans(handle) get_orphan_queue().reap_orphans(handle)
} }
} }
impl OrphanQueue<StdChild> for GlobalOrphanQueue { impl OrphanQueue<StdChild> for GlobalOrphanQueue {
fn push_orphan(&self, orphan: StdChild) { 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)] #[derive(Debug)]
pub(crate) struct Pipe { 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`. // close on drop. This is a similar trick as `mio`.
fd: File, fd: File,
} }
@ -169,6 +182,10 @@ impl<'a> io::Write for &'a Pipe {
fn flush(&mut self) -> io::Result<()> { fn flush(&mut self) -> io::Result<()> {
(&self.fd).flush() (&self.fd).flush()
} }
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
(&self.fd).write_vectored(bufs)
}
} }
impl AsRawFd for Pipe { impl AsRawFd for Pipe {
@ -177,8 +194,8 @@ impl AsRawFd for Pipe {
} }
} }
pub(crate) fn convert_to_stdio(io: PollEvented<Pipe>) -> io::Result<Stdio> { pub(crate) fn convert_to_stdio(io: ChildStdio) -> io::Result<Stdio> {
let mut fd = io.into_inner()?.fd; let mut fd = io.inner.into_inner()?.fd;
// Ensure that the fd to be inherited is set to *blocking* mode, as this // 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 // 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<()> { fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()> {
unsafe { unsafe {
@ -238,7 +310,7 @@ fn set_nonblocking<T: AsRawFd>(fd: &mut T, nonblocking: bool) -> io::Result<()>
Ok(()) Ok(())
} }
pub(super) fn stdio<T>(io: T) -> io::Result<PollEvented<Pipe>> pub(super) fn stdio<T>(io: T) -> io::Result<ChildStdio>
where where
T: IntoRawFd, T: IntoRawFd,
{ {
@ -246,5 +318,5 @@ where
let mut pipe = Pipe::from(io); let mut pipe = Pipe::from(io);
set_nonblocking(&mut pipe, true)?; 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::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::signal::unix::{signal_with_handle, SignalKind};
use crate::sync::watch; use crate::sync::watch;
use std::io; use std::io;
@ -43,10 +43,21 @@ pub(crate) struct OrphanQueueImpl<T> {
} }
impl<T> OrphanQueueImpl<T> { impl<T> OrphanQueueImpl<T> {
pub(crate) fn new() -> Self { cfg_not_has_const_mutex_new! {
Self { pub(crate) fn new() -> Self {
sigchild: Mutex::new(None), Self {
queue: Mutex::new(Vec::new()), 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)))] #[cfg(all(test, not(loom)))]
pub(crate) mod test { pub(crate) mod test {
use super::*; use super::*;
use crate::io::driver::Driver as IoDriver; use crate::runtime::io::Driver as IoDriver;
use crate::signal::unix::driver::{Driver as SignalDriver, Handle as SignalHandle}; use crate::runtime::signal::{Driver as SignalDriver, Handle as SignalHandle};
use crate::sync::watch; use crate::sync::watch;
use std::cell::{Cell, RefCell}; use std::cell::{Cell, RefCell};
use std::io; use std::io;
@ -283,7 +294,8 @@ pub(crate) mod test {
#[cfg_attr(miri, ignore)] // Miri does not support epoll. #[cfg_attr(miri, ignore)] // Miri does not support epoll.
#[test] #[test]
fn does_not_register_signal_if_queue_empty() { 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 handle = signal_driver.handle();
let orphanage = OrphanQueueImpl::new(); let orphanage = OrphanQueueImpl::new();

View File

@ -15,29 +15,31 @@
//! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot //! `RegisterWaitForSingleObject` and then wait on the other end of the oneshot
//! from then on out. //! from then on out.
use crate::io::PollEvented; use crate::io::{blocking::Blocking, AsyncRead, AsyncWrite, ReadBuf};
use crate::process::kill::Kill; use crate::process::kill::Kill;
use crate::process::SpawnedChild; use crate::process::SpawnedChild;
use crate::sync::oneshot; use crate::sync::oneshot;
use mio::windows::NamedPipe;
use std::fmt; use std::fmt;
use std::fs::File as StdFile;
use std::future::Future; use std::future::Future;
use std::io; 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::pin::Pin;
use std::process::Stdio; use std::process::Stdio;
use std::process::{Child as StdChild, Command as StdCommand, ExitStatus}; use std::process::{Child as StdChild, Command as StdCommand, ExitStatus};
use std::ptr; use std::sync::Arc;
use std::task::Context; use std::task::{Context, Poll};
use std::task::Poll;
use winapi::shared::minwindef::{DWORD, FALSE}; use windows_sys::{
use winapi::um::handleapi::{DuplicateHandle, INVALID_HANDLE_VALUE}; Win32::Foundation::{
use winapi::um::processthreadsapi::GetCurrentProcess; DuplicateHandle, BOOLEAN, DUPLICATE_SAME_ACCESS, HANDLE, INVALID_HANDLE_VALUE,
use winapi::um::threadpoollegacyapiset::UnregisterWaitEx; },
use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE}; Win32::System::Threading::{
use winapi::um::winnt::{ GetCurrentProcess, RegisterWaitForSingleObject, UnregisterWaitEx, WT_EXECUTEINWAITTHREAD,
BOOLEAN, DUPLICATE_SAME_ACCESS, HANDLE, PVOID, WT_EXECUTEINWAITTHREAD, WT_EXECUTEONLYONCE, WT_EXECUTEONLYONCE,
},
Win32::System::WindowsProgramming::INFINITE,
}; };
#[must_use = "futures do nothing unless polled"] #[must_use = "futures do nothing unless polled"]
@ -119,11 +121,11 @@ impl Future for Child {
} }
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let ptr = Box::into_raw(Box::new(Some(tx))); let ptr = Box::into_raw(Box::new(Some(tx)));
let mut wait_object = ptr::null_mut(); let mut wait_object = 0;
let rc = unsafe { let rc = unsafe {
RegisterWaitForSingleObject( RegisterWaitForSingleObject(
&mut wait_object, &mut wait_object,
inner.child.as_raw_handle(), inner.child.as_raw_handle() as _,
Some(callback), Some(callback),
ptr as *mut _, ptr as *mut _,
INFINITE, 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 = &mut *(ptr as *mut Option<oneshot::Sender<()>>);
let _ = complete.take().unwrap().send(()); 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 where
T: IntoRawHandle, T: IntoRawHandle,
{ {
let pipe = unsafe { NamedPipe::from_raw_handle(io.into_raw_handle()) }; use std::os::windows::prelude::FromRawHandle;
PollEvented::new(pipe)
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> { pub(crate) fn convert_to_stdio(child_stdio: ChildStdio) -> io::Result<Stdio> {
let named_pipe = io.into_inner()?; 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 { unsafe {
let mut dup_handle = INVALID_HANDLE_VALUE; let mut dup_handle = INVALID_HANDLE_VALUE;
let cur_proc = GetCurrentProcess(); let cur_proc = GetCurrentProcess();
let status = DuplicateHandle( let status = DuplicateHandle(
cur_proc, cur_proc,
named_pipe.as_raw_handle(), io.as_raw_handle() as _,
cur_proc, cur_proc,
&mut dup_handle, &mut dup_handle,
0 as DWORD, 0,
FALSE, 0,
DUPLICATE_SAME_ACCESS, 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()); 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. //! compilation.
mod pool; mod pool;
pub(crate) use pool::{spawn_blocking, BlockingPool, Mandatory, Spawner, Task}; pub(crate) use pool::{spawn_blocking, BlockingPool, Spawner};
cfg_fs! { cfg_fs! {
pub(crate) use pool::spawn_mandatory_blocking; pub(crate) use pool::spawn_mandatory_blocking;
} }
cfg_trace! {
pub(crate) use pool::Mandatory;
}
mod schedule; mod schedule;
mod shutdown; mod shutdown;
mod task; mod task;
pub(crate) use schedule::NoopSchedule;
pub(crate) use task::BlockingTask; pub(crate) use task::BlockingTask;
use crate::runtime::Builder; use crate::runtime::Builder;

View File

@ -2,15 +2,16 @@
use crate::loom::sync::{Arc, Condvar, Mutex}; use crate::loom::sync::{Arc, Condvar, Mutex};
use crate::loom::thread; use crate::loom::thread;
use crate::runtime::blocking::schedule::NoopSchedule; use crate::runtime::blocking::schedule::BlockingSchedule;
use crate::runtime::blocking::shutdown; use crate::runtime::blocking::{shutdown, BlockingTask};
use crate::runtime::builder::ThreadNameFn; use crate::runtime::builder::ThreadNameFn;
use crate::runtime::context;
use crate::runtime::task::{self, JoinHandle}; 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::collections::{HashMap, VecDeque};
use std::fmt; use std::fmt;
use std::io;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::time::Duration; use std::time::Duration;
pub(crate) struct BlockingPool { pub(crate) struct BlockingPool {
@ -23,6 +24,53 @@ pub(crate) struct Spawner {
inner: Arc<Inner>, 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 { struct Inner {
/// State shared between worker threads. /// State shared between worker threads.
shared: Mutex<Shared>, shared: Mutex<Shared>,
@ -47,12 +95,13 @@ struct Inner {
// Customizable wait timeout. // Customizable wait timeout.
keep_alive: Duration, keep_alive: Duration,
// Metrics about the pool.
metrics: SpawnerMetrics,
} }
struct Shared { struct Shared {
queue: VecDeque<Task>, queue: VecDeque<Task>,
num_th: usize,
num_idle: u32,
num_notify: u32, num_notify: u32,
shutdown: bool, shutdown: bool,
shutdown_tx: Option<shutdown::Sender>, shutdown_tx: Option<shutdown::Sender>,
@ -71,7 +120,7 @@ struct Shared {
} }
pub(crate) struct Task { pub(crate) struct Task {
task: task::UnownedTask<NoopSchedule>, task: task::UnownedTask<BlockingSchedule>,
mandatory: Mandatory, mandatory: Mandatory,
} }
@ -82,8 +131,27 @@ pub(crate) enum Mandatory {
NonMandatory, 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 { 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 } 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 /// Tasks will be scheduled as non-mandatory, meaning they may not get executed
/// in case of runtime shutdown. /// in case of runtime shutdown.
#[track_caller] #[track_caller]
#[cfg_attr(tokio_wasi, allow(dead_code))]
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R> pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
where where
F: FnOnce() -> R + Send + 'static, F: FnOnce() -> R + Send + 'static,
R: Send + 'static, R: Send + 'static,
{ {
let rt = context::current(); let rt = Handle::current();
rt.spawn_blocking(func) rt.spawn_blocking(func)
} }
@ -128,8 +197,8 @@ cfg_fs! {
F: FnOnce() -> R + Send + 'static, F: FnOnce() -> R + Send + 'static,
R: Send + 'static, R: Send + 'static,
{ {
let rt = context::current(); let rt = Handle::current();
rt.as_inner().spawn_mandatory_blocking(&rt, func) rt.inner.blocking_spawner().spawn_mandatory_blocking(&rt, func)
} }
} }
@ -145,8 +214,6 @@ impl BlockingPool {
inner: Arc::new(Inner { inner: Arc::new(Inner {
shared: Mutex::new(Shared { shared: Mutex::new(Shared {
queue: VecDeque::new(), queue: VecDeque::new(),
num_th: 0,
num_idle: 0,
num_notify: 0, num_notify: 0,
shutdown: false, shutdown: false,
shutdown_tx: Some(shutdown_tx), shutdown_tx: Some(shutdown_tx),
@ -161,6 +228,7 @@ impl BlockingPool {
before_stop: builder.before_stop.clone(), before_stop: builder.before_stop.clone(),
thread_cap, thread_cap,
keep_alive, keep_alive,
metrics: Default::default(),
}), }),
}, },
shutdown_rx, shutdown_rx,
@ -220,7 +288,104 @@ impl fmt::Debug for BlockingPool {
// ===== impl Spawner ===== // ===== impl Spawner =====
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(); let mut shared = self.inner.shared.lock();
if shared.shutdown { if shared.shutdown {
@ -230,15 +395,16 @@ impl Spawner {
task.task.shutdown(); task.task.shutdown();
// no need to even push this task; it would never get picked up // no need to even push this task; it would never get picked up
return Err(()); return Err(SpawnError::ShuttingDown);
} }
shared.queue.push_back(task); 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. // 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 // At max number of threads
} else { } else {
assert!(shared.shutdown_tx.is_some()); assert!(shared.shutdown_tx.is_some());
@ -249,11 +415,14 @@ impl Spawner {
match self.spawn_thread(shutdown_tx, rt, id) { match self.spawn_thread(shutdown_tx, rt, id) {
Ok(handle) => { Ok(handle) => {
shared.num_th += 1; self.inner.metrics.inc_num_threads();
shared.worker_thread_index += 1; shared.worker_thread_index += 1;
shared.worker_threads.insert(id, handle); 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. // OS temporarily failed to spawn a new thread.
// The task will be picked up eventually by a currently // The task will be picked up eventually by a currently
// busy thread. // busy thread.
@ -261,7 +430,7 @@ impl Spawner {
Err(e) => { Err(e) => {
// The OS refused to spawn the thread and there is no thread // 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. // 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 // exactly. Thread libraries may generate spurious
// wakeups, this counter is used to keep us in a // wakeups, this counter is used to keep us in a
// consistent state. // consistent state.
shared.num_idle -= 1; self.inner.metrics.dec_num_idle_threads();
shared.num_notify += 1; shared.num_notify += 1;
self.inner.condvar.notify_one(); self.inner.condvar.notify_one();
} }
@ -283,7 +452,7 @@ impl Spawner {
fn spawn_thread( fn spawn_thread(
&self, &self,
shutdown_tx: shutdown::Sender, shutdown_tx: shutdown::Sender,
rt: &dyn ToHandle, rt: &Handle,
id: usize, id: usize,
) -> std::io::Result<thread::JoinHandle<()>> { ) -> std::io::Result<thread::JoinHandle<()>> {
let mut builder = thread::Builder::new().name((self.inner.thread_name)()); let mut builder = thread::Builder::new().name((self.inner.thread_name)());
@ -292,17 +461,33 @@ impl Spawner {
builder = builder.stack_size(stack_size); builder = builder.stack_size(stack_size);
} }
let rt = rt.to_handle(); let rt = rt.clone();
builder.spawn(move || { builder.spawn(move || {
// Only the reference should be moved into the closure // Only the reference should be moved into the closure
let _enter = crate::runtime::context::enter(rt.clone()); let _enter = rt.enter();
rt.as_inner().blocking_spawner.inner.run(id); rt.inner.blocking_spawner().inner.run(id);
drop(shutdown_tx); 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. // Tells whether the error when spawning a thread is temporary.
#[inline] #[inline]
fn is_temporary_os_thread_error(error: &std::io::Error) -> bool { fn is_temporary_os_thread_error(error: &std::io::Error) -> bool {
@ -321,6 +506,7 @@ impl Inner {
'main: loop { 'main: loop {
// BUSY // BUSY
while let Some(task) = shared.queue.pop_front() { while let Some(task) = shared.queue.pop_front() {
self.metrics.dec_queue_depth();
drop(shared); drop(shared);
task.run(); task.run();
@ -328,7 +514,7 @@ impl Inner {
} }
// IDLE // IDLE
shared.num_idle += 1; self.metrics.inc_num_idle_threads();
while !shared.shutdown { while !shared.shutdown {
let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap(); let lock_result = self.condvar.wait_timeout(shared, self.keep_alive).unwrap();
@ -362,6 +548,7 @@ impl Inner {
if shared.shutdown { if shared.shutdown {
// Drain the queue // Drain the queue
while let Some(task) = shared.queue.pop_front() { while let Some(task) = shared.queue.pop_front() {
self.metrics.dec_queue_depth();
drop(shared); drop(shared);
task.shutdown_or_run_if_mandatory(); task.shutdown_or_run_if_mandatory();
@ -372,7 +559,7 @@ impl Inner {
// Work was produced, and we "took" it (by decrementing num_notify). // Work was produced, and we "took" it (by decrementing num_notify).
// This means that num_idle was decremented once for our wakeup. // 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. // 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, // NOTE: Technically we should also do num_notify++ and notify again,
// but since we're shutting down anyway, that won't be necessary. // but since we're shutting down anyway, that won't be necessary.
break; break;
@ -380,17 +567,17 @@ impl Inner {
} }
// Thread exit // Thread exit
shared.num_th -= 1; self.metrics.dec_num_threads();
// num_idle should now be tracked exactly, panic // num_idle should now be tracked exactly, panic
// with a descriptive message if it is not the // with a descriptive message if it is not the
// case. // case.
shared.num_idle = shared let prev_idle = self.metrics.dec_num_idle_threads();
.num_idle if prev_idle < self.metrics.num_idle_threads() {
.checked_sub(1) panic!("num_idle_threads underflowed on thread exit")
.expect("num_idle underflowed on thread exit"); }
if shared.shutdown && shared.num_th == 0 { if shared.shutdown && self.metrics.num_threads() == 0 {
self.condvar.notify_one(); 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::task::{self, Task};
use crate::runtime::Handle;
/// `task::Schedule` implementation that does nothing. This is unique to the /// `task::Schedule` implementation that does nothing (except some bookkeeping
/// blocking scheduler as tasks scheduled are not really futures but blocking /// in test-util builds). This is unique to the blocking scheduler as tasks
/// operations. /// scheduled are not really futures but blocking operations.
/// ///
/// We avoid storing the task by forgetting it in `bind` and re-materializing it /// We avoid storing the task by forgetting it in `bind` and re-materializing it
/// in `release. /// in `release`.
pub(crate) struct NoopSchedule; 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>> { 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 None
} }

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