mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
93
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75fef53d0a | ||
|
|
ae9d011213 | ||
|
|
eb4988dc2e | ||
|
|
91d3b4c0bc | ||
|
|
a46338401b | ||
|
|
be689a35f5 | ||
|
|
50f76c71ec | ||
|
|
f61fccad3c | ||
|
|
efdba5fcf0 | ||
|
|
b0ba02e755 | ||
|
|
7bcd2d343d | ||
|
|
f84b209126 | ||
|
|
eacb98e189 | ||
|
|
5e16ee00fa | ||
|
|
88212ab64a | ||
|
|
9cae638de6 | ||
|
|
315a320e96 | ||
|
|
dac81bf8c8 | ||
|
|
145f124d98 | ||
|
|
6e8475dd60 | ||
|
|
b6f30cae9a | ||
|
|
4638b65f9c | ||
|
|
fe258f5e6d | ||
|
|
33f46a5395 | ||
|
|
9c465e2f42 | ||
|
|
d5950a8890 | ||
|
|
c793a631f7 | ||
|
|
bfd4ddf597 | ||
|
|
cd3bcca32b | ||
|
|
c4c6265a07 | ||
|
|
bb2815ae23 | ||
|
|
e06f16259d | ||
|
|
962420a4f3 | ||
|
|
c637f6e73d | ||
|
|
dd683aba3f | ||
|
|
9fe3c5619d | ||
|
|
448d1227a1 | ||
|
|
98104c36f1 | ||
|
|
61aeb33f51 | ||
|
|
7b354d22a9 | ||
|
|
4bbd2f4d7c | ||
|
|
930ca7436b | ||
|
|
46c830117b | ||
|
|
8f37021c16 | ||
|
|
cdaa78fc37 | ||
|
|
dba1f20585 | ||
|
|
95b8895da8 | ||
|
|
73e8937cc5 | ||
|
|
630ec12a4f | ||
|
|
060f66c665 | ||
|
|
a59f9a0a94 | ||
|
|
aee321206b | ||
|
|
5f52f113d4 | ||
|
|
f59aae423e | ||
|
|
dc3a883b99 | ||
|
|
66e29121b3 | ||
|
|
daa653d94f | ||
|
|
7892f6020d | ||
|
|
da044f27d7 | ||
|
|
ecb5125a67 | ||
|
|
bde8967853 | ||
|
|
21e4a2a282 | ||
|
|
2e7930fe58 | ||
|
|
67637b348a | ||
|
|
778e9d97d9 | ||
|
|
71362aa609 | ||
|
|
2de86e557c | ||
|
|
326bd2cc44 | ||
|
|
32312ae0d6 | ||
|
|
37ced33efd | ||
|
|
923e72345c | ||
|
|
f619fc0587 | ||
|
|
82fe082ef1 | ||
|
|
1fe1b0e727 | ||
|
|
c6af672353 | ||
|
|
2a05f364b7 | ||
|
|
c6d58ce7e7 | ||
|
|
de360cfc7e | ||
|
|
7d3b0ad192 | ||
|
|
0121120b6d | ||
|
|
bdcea6b2cd | ||
|
|
ee0dc90926 | ||
|
|
78594a7497 | ||
|
|
d87569164f | ||
|
|
e1aebb031c | ||
|
|
fd63094ee0 | ||
|
|
067f229371 | ||
|
|
8c600d0fd2 | ||
|
|
11bfc1345b | ||
|
|
f085b6211b | ||
|
|
30d25ccb8b | ||
|
|
9fccf5339d | ||
|
|
ebf61b45b5 |
@@ -15,6 +15,8 @@ R-loom-time-driver:
|
||||
- any-glob-to-any-file:
|
||||
- tokio/src/runtime/time/*
|
||||
- tokio/src/runtime/time/**/*
|
||||
- tokio/src/runtime/time_alt/*
|
||||
- tokio/src/runtime/time_alt/**/*
|
||||
|
||||
R-loom-current-thread:
|
||||
- changed-files:
|
||||
|
||||
@@ -20,5 +20,5 @@ jobs:
|
||||
issues: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
|
||||
+74
-48
@@ -18,7 +18,7 @@ env:
|
||||
rust_stable: stable
|
||||
rust_nightly: nightly-2025-10-12
|
||||
# Pin a specific miri version
|
||||
rust_miri_nightly: nightly-2026-04-27
|
||||
rust_miri_nightly: nightly-2026-06-29
|
||||
rust_clippy: '1.88'
|
||||
# When updating this, also update:
|
||||
# - README.md
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -75,6 +75,9 @@ jobs:
|
||||
tool: cargo-nextest
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
|
||||
# Run `tokio` with stable features. This excludes testing utilities which
|
||||
# can alter the runtime behavior of Tokio.
|
||||
@@ -96,7 +99,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -107,6 +110,9 @@ jobs:
|
||||
tool: cargo-nextest
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
|
||||
- name: test --features ${{ env.TOKIO_STABLE_FEATURES }}
|
||||
run: |
|
||||
@@ -135,7 +141,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -146,6 +152,9 @@ jobs:
|
||||
tool: cargo-nextest
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
|
||||
- name: test --features ${{ env.TOKIO_STABLE_FEATURES }} panic=abort
|
||||
run: |
|
||||
@@ -168,7 +177,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -179,6 +188,9 @@ jobs:
|
||||
tool: cargo-hack
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
|
||||
# Run integration tests for each feature
|
||||
- name: test tests-integration --each-feature
|
||||
@@ -208,7 +220,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -234,7 +246,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -275,7 +287,7 @@ jobs:
|
||||
- { os: ubuntu-latest, extra_features: io-uring }
|
||||
- { os: macos-latest, extra_features: "" }
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -287,6 +299,9 @@ jobs:
|
||||
tool: cargo-nextest
|
||||
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
# Run `tokio` with "unstable" cfg flag.
|
||||
- name: test tokio full --cfg unstable
|
||||
run: |
|
||||
@@ -309,7 +324,7 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -343,7 +358,7 @@ jobs:
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -372,7 +387,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_miri_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -395,7 +410,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_miri_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -418,7 +433,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_miri_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -437,7 +452,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install llvm
|
||||
# Required to resolve symbols in sanitizer output
|
||||
run: sudo apt-get install -y llvm
|
||||
@@ -459,7 +474,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Check `tokio` semver
|
||||
uses: obi1kenobi/cargo-semver-checks-action@v2
|
||||
with:
|
||||
@@ -488,7 +503,7 @@ jobs:
|
||||
- powerpc64-unknown-linux-gnu
|
||||
- arm-linux-androideabi
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -513,7 +528,7 @@ jobs:
|
||||
# - name: armv7-sony-vita-newlibeabihf
|
||||
# exclude_features: "process,signal,rt-process-signal,full,taskdump"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
@@ -547,7 +562,7 @@ jobs:
|
||||
- target: aarch64-pc-windows-msvc
|
||||
os: windows-11-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -597,7 +612,7 @@ jobs:
|
||||
- target: aarch64-pc-windows-msvc
|
||||
os: windows-11-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -638,7 +653,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -670,7 +685,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -686,8 +701,9 @@ jobs:
|
||||
# https://github.com/tokio-rs/tokio/pull/5356
|
||||
# https://github.com/tokio-rs/tokio/issues/5373
|
||||
- name: Check
|
||||
# We use `--skip io-uring` since io-uring crate doesn't provide a binding for the i686 target.
|
||||
run: cargo hack check -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --feature-powerset --skip io-uring --depth 2 --keep-going
|
||||
# We use `--skip io-uring,schedule-latency` since io-uring crate doesn't provide a binding for the i686 target
|
||||
# and schedule latency tracking is only supported on 64-bit targets.
|
||||
run: cargo hack check -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --feature-powerset --skip io-uring,schedule-latency --depth 2 --keep-going
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
|
||||
@@ -700,15 +716,15 @@ jobs:
|
||||
include:
|
||||
- name: ""
|
||||
rustflags: ""
|
||||
exclude_features: "io-uring,taskdump"
|
||||
exclude_features: "io-uring,taskdump,schedule-latency"
|
||||
- name: "--unstable"
|
||||
rustflags: "--cfg tokio_unstable -Dwarnings"
|
||||
exclude_features: "io-uring,taskdump"
|
||||
- name: "--unstable io-uring,taskdump"
|
||||
exclude_features: "io-uring,taskdump,schedule-latency"
|
||||
- name: "--unstable io-uring,taskdump,schedule-latency"
|
||||
rustflags: "--cfg tokio_unstable -Dwarnings"
|
||||
exclude_features: ""
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -727,7 +743,7 @@ jobs:
|
||||
name: minrust
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_min }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -756,7 +772,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -789,7 +805,7 @@ jobs:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -809,7 +825,7 @@ jobs:
|
||||
name: clippy
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_clippy }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -849,7 +865,7 @@ jobs:
|
||||
extra_features: "tracing,io-uring,taskdump"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -866,7 +882,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -882,7 +898,7 @@ jobs:
|
||||
name: Check README
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Verify that both READMEs are identical
|
||||
run: diff README.md tokio/README.md
|
||||
|
||||
@@ -901,7 +917,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -937,6 +953,8 @@ jobs:
|
||||
# relative to the `$workspace` and defaults to "target" if not explicitly given.
|
||||
# default: ". -> target"
|
||||
workspaces: "./hyper"
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
|
||||
- name: Test hyper
|
||||
run: cargo test --features full
|
||||
@@ -953,7 +971,7 @@ jobs:
|
||||
- ubuntu-latest
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -985,6 +1003,8 @@ jobs:
|
||||
# relative to the `$workspace` and defaults to "target" if not explicitly given.
|
||||
# default: ". -> target"
|
||||
workspaces: "./quinn"
|
||||
# FIXME: temporary workaround, see: https://github.com/Swatinem/rust-cache/issues/341
|
||||
cache-bin: ${{ matrix.os != 'macos-latest' }}
|
||||
|
||||
- name: Test Quinn
|
||||
working-directory: quinn
|
||||
@@ -997,7 +1017,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1014,7 +1034,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -1038,7 +1058,7 @@ jobs:
|
||||
- name: macros sync time rt
|
||||
features: "macros sync time rt"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1061,7 +1081,7 @@ jobs:
|
||||
- wasm32-wasip1
|
||||
- wasm32-wasip1-threads
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1122,7 +1142,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1157,7 +1177,7 @@ jobs:
|
||||
# includes all unstable features.
|
||||
extra_features: "tracing,io-uring,taskdump"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ matrix.rust }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1181,7 +1201,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1201,7 +1221,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -1210,7 +1230,7 @@ jobs:
|
||||
uses: taiki-e/install-action@v2
|
||||
with:
|
||||
tool: cargo-spellcheck
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Make sure dictionary words are sorted and unique
|
||||
run: |
|
||||
FILE="spellcheck.dic"
|
||||
@@ -1298,12 +1318,14 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Test in FreeBSD
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
release: '14.4'
|
||||
envs: "TOKIO_STABLE_FEATURES RUSTFLAGS"
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: |
|
||||
pkg install -y curl
|
||||
curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
@@ -1322,7 +1344,7 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Build docs in FreeBSD
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
env:
|
||||
@@ -1331,6 +1353,8 @@ jobs:
|
||||
with:
|
||||
release: '14.4'
|
||||
envs: "TOKIO_STABLE_FEATURES RUSTDOCFLAGS RUSTFLAGS"
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: |
|
||||
pkg install -y curl
|
||||
curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
@@ -1348,12 +1372,14 @@ jobs:
|
||||
needs: basics
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Test in FreeBSD
|
||||
uses: vmactions/freebsd-vm@v1
|
||||
with:
|
||||
release: '14.4'
|
||||
envs: "TOKIO_STABLE_FEATURES RUSTFLAGS"
|
||||
sync: rsync
|
||||
copyback: false
|
||||
prepare: |
|
||||
pkg install -y curl
|
||||
curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-blocking') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-sync') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-time-driver') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -77,7 +77,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-current-thread') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -100,7 +100,7 @@ jobs:
|
||||
- scope: loom_multi_thread::group_c
|
||||
- scope: loom_multi_thread::group_d
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
if: github.repository_owner == 'tokio-rs' && (contains(github.event.pull_request.labels.*.name, 'R-loom-util') || (github.base_ref == null))
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
|
||||
@@ -19,5 +19,5 @@ jobs:
|
||||
cargo-deny:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- uses: EmbarkStudios/cargo-deny-action@v2
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
stress-test:
|
||||
- simple_echo_tcp
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
|
||||
@@ -14,7 +14,7 @@ jobs:
|
||||
env:
|
||||
KERNEL_VERSION: ${{ inputs.kernel_version }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
|
||||
- name: Install system dependencies
|
||||
run: |
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
- name: Cache Linux source
|
||||
id: cache-kernel
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: linux-${{ env.KERNEL_VERSION }}
|
||||
key: kernel-${{ env.KERNEL_VERSION }}
|
||||
@@ -40,6 +40,8 @@ jobs:
|
||||
make defconfig
|
||||
make -j$(nproc)
|
||||
|
||||
# We are running tests also on a Kernel version that does not support io_uring
|
||||
# to check if the fallback mechanism works
|
||||
- name: Generate test binaries with io_uring enabled
|
||||
run: |
|
||||
# Build both integration (tokio/tests/) and unit (e.g., tokio/src/fs/file/tests.rs) tests with io_uring enabled
|
||||
@@ -87,16 +89,31 @@ jobs:
|
||||
| cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz)
|
||||
|
||||
- name: Run tests in QEMU
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
set +e
|
||||
qemu-system-x86_64 \
|
||||
-kernel linux-${{ env.KERNEL_VERSION }}/arch/x86/boot/bzImage \
|
||||
-initrd initramfs.cpio.gz \
|
||||
-append "console=ttyS0 rootfstype=ramfs panic=1" \
|
||||
-nographic -no-reboot -m 1024 -action panic=exit-failure 2>&1 | tee qemu-output.log
|
||||
qemu_status=${PIPESTATUS[0]} tee_status=${PIPESTATUS[1]}
|
||||
set -e
|
||||
|
||||
if [ "$qemu_status" -ne 0 ]; then
|
||||
echo "QEMU exited with status $qemu_status"
|
||||
exit "$qemu_status"
|
||||
fi
|
||||
|
||||
if [ "$tee_status" -ne 0 ]; then
|
||||
echo "tee exited with status $tee_status"
|
||||
exit "$tee_status"
|
||||
fi
|
||||
|
||||
# qemu always exits with 0, so we check if the tests passed by using grep.
|
||||
if grep -q "test result: FAILED" qemu-output.log; then
|
||||
echo "tests failed (QEMU exited abnormally)"
|
||||
echo "tests reported failures"
|
||||
exit 1
|
||||
else
|
||||
echo "all tests passed"
|
||||
|
||||
@@ -60,7 +60,7 @@ Make sure you enable the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.52.2", features = ["full"] }
|
||||
tokio = { version = "1.53.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
+4
-4
@@ -73,7 +73,7 @@ impl SlowHddWriter {
|
||||
fn write_bytes(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
writeable: usize,
|
||||
writable: usize,
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
let service_res = self.as_mut().service_write(cx);
|
||||
|
||||
@@ -86,7 +86,7 @@ impl SlowHddWriter {
|
||||
assert!(service_res.is_pending());
|
||||
Poll::Pending
|
||||
} else {
|
||||
let written = available.min(writeable);
|
||||
let written = available.min(writable);
|
||||
self.buffer_used += written;
|
||||
Poll::Ready(Ok(written))
|
||||
}
|
||||
@@ -123,8 +123,8 @@ impl AsyncWrite for SlowHddWriter {
|
||||
cx: &mut std::task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
let writeable = bufs.iter().fold(0, |acc, buf| acc + buf.len());
|
||||
self.write_bytes(cx, writeable)
|
||||
let writable = bufs.iter().fold(0, |acc, buf| acc + buf.len());
|
||||
self.write_bytes(cx, writable)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
|
||||
@@ -8,7 +8,8 @@ It should be considered a map to help you navigate the process.
|
||||
If you are unsure where to begin, use the following guides:
|
||||
|
||||
- Want to report or triage a bug? Start with [Contributing in Issues](contributing-in-issues.md).
|
||||
- Looking for something to work on? Filter issues by [`E-help-wanted`](https://github.com/tokio-rs/tokio/labels/E-help-wanted).
|
||||
- Looking for something to work on? Check the open [`issues`](https://github.com/tokio-rs/tokio/issues)
|
||||
(some might be in progress or still under discussion, so leave a comment before starting the work).
|
||||
- Planning to submit a PR? Read [Pull Requests](pull-requests.md) for the full workflow and required checks.
|
||||
- Want to understand what the labels on issues mean? See [Keeping track of issues and PRs](keeping-track-of-issues-and-prs.md).
|
||||
- Interested in code review? See [Reviewing Pull Requests](reviewing-pull-requests.md).
|
||||
@@ -22,7 +23,7 @@ If you are unsure where to begin, use the following guides:
|
||||
- [Resolving a Bug Report](contributing-in-issues.md#resolving-a-bug-report)
|
||||
- [Pull Requests](pull-requests.md)
|
||||
- [Cargo Commands](pull-requests.md#cargo-commands)
|
||||
- [Performing spellcheck on tokio codebase](pull-requests.md#performing-spellcheck-on-tokio-codebase)
|
||||
- [Performing spellcheck on Tokio codebase](pull-requests.md#performing-spellcheck-on-tokio-codebase)
|
||||
- [Tests](pull-requests.md#tests)
|
||||
- [Integration tests](pull-requests.md#integration-tests)
|
||||
- [Fuzz tests](pull-requests.md#fuzz-tests)
|
||||
|
||||
@@ -81,8 +81,10 @@ The module label provides a more fine grained categorization than **Area**.
|
||||
Some extra information.
|
||||
|
||||
- **T-docs** This is about documentation.
|
||||
- **T-io-uring** This is about io-uring (Linux).
|
||||
- **T-performance** This is about performance.
|
||||
- **T-v0.1.x** This is about old Tokio.
|
||||
- **T-wasm** This is about Web Assembly.
|
||||
|
||||
Any label not listed here is not in active use.
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ Pull Requests are the way concrete changes are made to the code, documentation,
|
||||
and dependencies in the Tokio repository.
|
||||
|
||||
Even tiny pull requests (e.g., one-character pull request fixing a typo in API
|
||||
documentation) are greatly appreciated. Before making a large change, it is
|
||||
usually a good idea to first open an issue describing the change to solicit
|
||||
feedback and guidance. This will increase the likelihood of the PR getting
|
||||
merged.
|
||||
documentation) are greatly appreciated.
|
||||
|
||||
> [!NOTE]
|
||||
> Before making a large change, it is usually a good idea to first open an
|
||||
> issue describing the change to solicit feedback and guidance.
|
||||
> This will increase the likelihood of the PR getting merged.
|
||||
|
||||
### Cargo Commands
|
||||
|
||||
@@ -23,8 +25,10 @@ cargo check --all-features
|
||||
cargo test --all-features
|
||||
```
|
||||
|
||||
**NOTE**: there are some features that are not supported in every system, so you might
|
||||
need to specify which features you want to pass to cargo (e.g., `cargo check --features=full,io-uring`)
|
||||
> [!NOTE]
|
||||
> There are some features that are not available in some systems (e.g., `io-uring`
|
||||
> which is Linux only). In that case you cannot use the `--all-features` and you
|
||||
> should specify only the features that are supported (e.g., `--features=full`).
|
||||
|
||||
Ideally, you should use the same version of clippy as the one used in CI
|
||||
(defined by `env.rust_clippy` in [ci.yml][ci.yml]), because newer versions
|
||||
@@ -99,7 +103,7 @@ MIRIFLAGS="-Zmiri-disable-isolation -Zmiri-strict-provenance" \
|
||||
cargo +nightly miri test --features full --lib --tests
|
||||
```
|
||||
|
||||
### Performing spellcheck on tokio codebase
|
||||
### Performing spellcheck on Tokio codebase
|
||||
|
||||
You can perform a spell-check on the Tokio codebase. For details of how to use the spellcheck tool, feel free to visit
|
||||
https://github.com/drahnr/cargo-spellcheck
|
||||
|
||||
+12
-2
@@ -5,7 +5,12 @@
|
||||
#[cfg(all(
|
||||
tokio_unstable,
|
||||
target_os = "linux",
|
||||
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "s390x"
|
||||
)
|
||||
))]
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@@ -82,7 +87,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[cfg(not(all(
|
||||
tokio_unstable,
|
||||
target_os = "linux",
|
||||
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "s390x"
|
||||
)
|
||||
)))]
|
||||
fn main() {
|
||||
println!("task dumps are not available")
|
||||
|
||||
@@ -9,6 +9,28 @@
|
||||
//! cargo run --example connect-udp 127.0.0.1:8080
|
||||
//!
|
||||
//! Each line you type in to the `connect-udp` terminal should be echo'd back to you!
|
||||
//!
|
||||
//! # Binding to all interfaces
|
||||
//!
|
||||
//! By default this example binds to `127.0.0.1` so it is only reachable
|
||||
//! from the local machine.
|
||||
//!
|
||||
//! To listen on all interfaces instead:
|
||||
//!
|
||||
//! ```sh
|
||||
//! cargo run --example echo-udp -- 0.0.0.0:8080
|
||||
//! ```
|
||||
//!
|
||||
//! Binding to `0.0.0.0` exposes the server on all network interfaces.
|
||||
//! Only do this in trusted network environments.
|
||||
//!
|
||||
//! On multi-homed systems, a UDP socket bound to a wildcard address
|
||||
//! (`0.0.0.0` or `::`) cannot always send replies from the same local IP
|
||||
//! that received the packet. Replies may therefore originate from a
|
||||
//! different address than the client targeted. See [this Cloudflare blog
|
||||
//! post][udp-blog] for more details.
|
||||
//!
|
||||
//! [udp-blog]: https://blog.cloudflare.com/everything-you-ever-wanted-to-know-about-udp-sockets-but-were-afraid-to-ask-part-1
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
|
||||
+12
-9
@@ -25,7 +25,6 @@
|
||||
use tokio::io::copy_bidirectional;
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
|
||||
use futures::FutureExt;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
|
||||
@@ -44,16 +43,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let listener = TcpListener::bind(listen_addr).await?;
|
||||
|
||||
while let Ok((mut inbound, _)) = listener.accept().await {
|
||||
let mut outbound = TcpStream::connect(server_addr.clone()).await?;
|
||||
let server_addr = server_addr.clone();
|
||||
|
||||
tokio::spawn(async move {
|
||||
copy_bidirectional(&mut inbound, &mut outbound)
|
||||
.map(|r| {
|
||||
if let Err(e) = r {
|
||||
println!("Failed to transfer; error={e}");
|
||||
}
|
||||
})
|
||||
.await
|
||||
let mut outbound = match TcpStream::connect(server_addr).await {
|
||||
Ok(outbound) => outbound,
|
||||
Err(e) => {
|
||||
println!("Failed to connect; error={e}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = copy_bidirectional(&mut inbound, &mut outbound).await {
|
||||
println!("Failed to transfer; error={e}");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
323
|
||||
324
|
||||
&
|
||||
+
|
||||
<
|
||||
@@ -89,6 +89,7 @@ decrementing
|
||||
demangled
|
||||
dequeued
|
||||
dereferenced
|
||||
derefs
|
||||
deregister
|
||||
deregistered
|
||||
deregistering
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# 2.7.1 (July 17th, 2026)
|
||||
|
||||
- macros: clarify `tokio::main` expansion ([#8193])
|
||||
|
||||
[#8193]: https://github.com/tokio-rs/tokio/pull/8193
|
||||
|
||||
# 2.7.0 (April 3rd, 2026)
|
||||
|
||||
- macros: stabilize `LocalRuntime` ([#7557])
|
||||
|
||||
@@ -4,7 +4,7 @@ name = "tokio-macros"
|
||||
# - Remove path dependencies (if any)
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-x.y.z" git tag.
|
||||
version = "2.7.0"
|
||||
version = "2.7.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.71"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
|
||||
@@ -29,10 +29,11 @@ use proc_macro::TokenStream;
|
||||
/// powerful interface.
|
||||
///
|
||||
/// Note: This macro can be used on any function and not just the `main`
|
||||
/// function. Using it on a non-main function makes the function behave as if it
|
||||
/// was synchronous by starting a new runtime each time it is called. If the
|
||||
/// function is called often, it is preferable to create the runtime using the
|
||||
/// runtime builder so the runtime can be reused across calls.
|
||||
/// function. Although the function is written with `async fn`, this macro
|
||||
/// expands it to a synchronous function that starts a runtime each time it is
|
||||
/// called. If the function is called often, it is preferable to create the
|
||||
/// runtime using the runtime builder so the runtime can be reused across calls.
|
||||
/// For details on the expansion, see [Bridging with sync code][bridging].
|
||||
///
|
||||
/// # Non-worker async function
|
||||
///
|
||||
@@ -308,6 +309,7 @@ use proc_macro::TokenStream;
|
||||
/// [`Builder::unhandled_panic`]: ../tokio/runtime/struct.Builder.html#method.unhandled_panic
|
||||
/// [unstable]: ../tokio/index.html#unstable-features
|
||||
/// [local runtime]: ../tokio/runtime/struct.LocalRuntime.html
|
||||
/// [bridging]: https://tokio.rs/tokio/topics/bridging#what-tokiomain-expands-to
|
||||
#[proc_macro_attribute]
|
||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
entry::main(args.into(), item.into(), true).into()
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
|
||||
### Documented
|
||||
|
||||
- stream: improve the the docs of `TcpListenerStream` ([#7578])
|
||||
- stream: improve the docs of `TcpListenerStream` ([#7578])
|
||||
|
||||
[#7024]: https://github.com/tokio-rs/tokio/pull/7024
|
||||
[#7492]: https://github.com/tokio-rs/tokio/pull/7492
|
||||
|
||||
@@ -24,6 +24,7 @@ full = [
|
||||
"net",
|
||||
"io-util",
|
||||
"fs",
|
||||
"rt",
|
||||
"sync",
|
||||
"signal"
|
||||
]
|
||||
@@ -32,6 +33,7 @@ time = ["tokio/time"]
|
||||
net = ["tokio/net"]
|
||||
io-util = ["tokio/io-util"]
|
||||
fs = ["tokio/fs"]
|
||||
rt = ["tokio/rt"]
|
||||
sync = ["tokio/sync", "tokio-util"]
|
||||
signal = ["tokio/signal"]
|
||||
|
||||
|
||||
@@ -40,7 +40,15 @@ pub const fn empty<T>() -> Empty<T> {
|
||||
impl<T> Stream for Empty<T> {
|
||||
type Item = T;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
#[cfg(feature = "rt")]
|
||||
{
|
||||
use tokio::task::coop;
|
||||
|
||||
let coop = std::task::ready!(coop::poll_proceed(_cx));
|
||||
coop.made_progress();
|
||||
}
|
||||
|
||||
Poll::Ready(None)
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ use core::task::{Context, Poll};
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
pub struct Iter<I> {
|
||||
iter: I,
|
||||
#[cfg(not(feature = "rt"))]
|
||||
yield_amt: usize,
|
||||
}
|
||||
|
||||
@@ -36,6 +37,7 @@ where
|
||||
{
|
||||
Iter {
|
||||
iter: i.into_iter(),
|
||||
#[cfg(not(feature = "rt"))]
|
||||
yield_amt: 0,
|
||||
}
|
||||
}
|
||||
@@ -47,17 +49,33 @@ where
|
||||
type Item = I::Item;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<I::Item>> {
|
||||
// TODO: add coop back
|
||||
if self.yield_amt >= 32 {
|
||||
self.yield_amt = 0;
|
||||
#[cfg(feature = "rt")]
|
||||
{
|
||||
use tokio::task::coop;
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
let coop = std::task::ready!(coop::poll_proceed(cx));
|
||||
let item = self.iter.next();
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
self.yield_amt += 1;
|
||||
coop.made_progress();
|
||||
|
||||
Poll::Ready(self.iter.next())
|
||||
Poll::Ready(item)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "rt"))]
|
||||
{
|
||||
if self.yield_amt >= 32 {
|
||||
self.yield_amt = 0;
|
||||
|
||||
cx.waker().wake_by_ref();
|
||||
|
||||
Poll::Pending
|
||||
} else {
|
||||
let item = self.iter.next();
|
||||
|
||||
self.yield_amt += 1;
|
||||
|
||||
Poll::Ready(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,3 +57,13 @@ macro_rules! cfg_signal {
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_rt {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "rt")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "rt")))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::{Iter, Stream};
|
||||
use crate::Stream;
|
||||
|
||||
use core::option;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
|
||||
@@ -8,7 +7,7 @@ use core::task::{Context, Poll};
|
||||
#[derive(Debug)]
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
pub struct Once<T> {
|
||||
iter: Iter<option::IntoIter<T>>,
|
||||
value: Option<T>,
|
||||
}
|
||||
|
||||
impl<I> Unpin for Once<I> {}
|
||||
@@ -34,19 +33,30 @@ impl<I> Unpin for Once<I> {}
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn once<T>(value: T) -> Once<T> {
|
||||
Once {
|
||||
iter: crate::iter(Some(value)),
|
||||
}
|
||||
Once { value: Some(value) }
|
||||
}
|
||||
|
||||
impl<T> Stream for Once<T> {
|
||||
type Item = T;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
Pin::new(&mut self.iter).poll_next(cx)
|
||||
fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
#[cfg(feature = "rt")]
|
||||
{
|
||||
use tokio::task::coop;
|
||||
|
||||
let coop = std::task::ready!(coop::poll_proceed(_cx));
|
||||
|
||||
coop.made_progress();
|
||||
}
|
||||
|
||||
Poll::Ready(self.value.take())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
if self.value.is_some() {
|
||||
(1, Some(1))
|
||||
} else {
|
||||
(0, Some(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::Stream;
|
||||
use core::fmt;
|
||||
use core::pin::Pin;
|
||||
use core::task::{Context, Poll};
|
||||
use futures_core::FusedStream;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
pin_project! {
|
||||
@@ -12,6 +13,7 @@ pin_project! {
|
||||
#[pin]
|
||||
stream: St,
|
||||
f: F,
|
||||
done: bool,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +24,18 @@ where
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MapWhile")
|
||||
.field("stream", &self.stream)
|
||||
.field("done", &self.done)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<St, F> MapWhile<St, F> {
|
||||
pub(super) fn new(stream: St, f: F) -> Self {
|
||||
MapWhile { stream, f }
|
||||
MapWhile {
|
||||
stream,
|
||||
f,
|
||||
done: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,12 +48,36 @@ where
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<T>> {
|
||||
let me = self.project();
|
||||
if *me.done {
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
let f = me.f;
|
||||
me.stream.poll_next(cx).map(|opt| opt.and_then(f))
|
||||
let done = me.done;
|
||||
me.stream.poll_next(cx).map(|opt| {
|
||||
let mapped = opt.and_then(f);
|
||||
if mapped.is_none() {
|
||||
*done = true;
|
||||
}
|
||||
mapped
|
||||
})
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
if self.done {
|
||||
return (0, Some(0));
|
||||
}
|
||||
|
||||
let (_, upper) = self.stream.size_hint();
|
||||
(0, upper)
|
||||
}
|
||||
}
|
||||
|
||||
impl<St, F> FusedStream for MapWhile<St, F>
|
||||
where
|
||||
Self: Stream,
|
||||
{
|
||||
fn is_terminated(&self) -> bool {
|
||||
self.done
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,4 +47,12 @@ impl<T: Stream> Stream for Peekable<T> {
|
||||
this.stream.poll_next(cx)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let peek_len = if self.peek.is_some() { 1 } else { 0 };
|
||||
let (lo, hi) = self.stream.size_hint();
|
||||
let lo = lo.saturating_add(peek_len);
|
||||
let hi = hi.and_then(|x| x.checked_add(peek_len));
|
||||
(lo, hi)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +49,7 @@ where
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
if !*self.as_mut().project().done {
|
||||
self.as_mut().project().stream.poll_next(cx).map(|ready| {
|
||||
let ready = ready.and_then(|item| {
|
||||
if !(self.as_mut().project().predicate)(&item) {
|
||||
None
|
||||
} else {
|
||||
Some(item)
|
||||
}
|
||||
});
|
||||
let ready = ready.filter(self.as_mut().project().predicate);
|
||||
|
||||
if ready.is_none() {
|
||||
*self.as_mut().project().done = true;
|
||||
|
||||
@@ -632,6 +632,10 @@ where
|
||||
should_loop = true;
|
||||
|
||||
idx = idx.wrapping_add(1) % self.entries.len();
|
||||
|
||||
if added == limit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
// Remove the entry
|
||||
@@ -685,15 +689,15 @@ where
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let mut ret = (0, Some(0));
|
||||
let mut ret: (usize, Option<usize>) = (0, Some(0));
|
||||
|
||||
for (_, stream) in &self.entries {
|
||||
let hint = stream.size_hint();
|
||||
|
||||
ret.0 += hint.0;
|
||||
ret.0 = ret.0.saturating_add(hint.0);
|
||||
|
||||
match (ret.1, hint.1) {
|
||||
(Some(a), Some(b)) => ret.1 = Some(a + b),
|
||||
(Some(a), Some(b)) => ret.1 = a.checked_add(b),
|
||||
(Some(_), None) => ret.1 = None,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ pub use mpsc_bounded::ReceiverStream;
|
||||
mod mpsc_unbounded;
|
||||
pub use mpsc_unbounded::UnboundedReceiverStream;
|
||||
|
||||
cfg_rt! {
|
||||
mod task;
|
||||
pub use task::JoinSetStream;
|
||||
}
|
||||
|
||||
cfg_sync! {
|
||||
mod broadcast;
|
||||
pub use broadcast::BroadcastStream;
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
use crate::Stream;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::task::{JoinError, JoinSet};
|
||||
|
||||
/// A wrapper around [`tokio::task::JoinSet`] that implements [`Stream`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::task::JoinSet;
|
||||
/// use tokio_stream::wrappers::JoinSetStream;
|
||||
/// use tokio_stream::StreamExt;
|
||||
///
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() -> Result<(), tokio::task::JoinError> {
|
||||
/// let set: JoinSet<_> = (0..2).map(|i| async move { i }).collect();
|
||||
///
|
||||
/// let mut stream = JoinSetStream::new(set);
|
||||
/// assert_eq!(stream.next().await.transpose()?, Some(0));
|
||||
/// assert_eq!(stream.next().await.transpose()?, Some(1));
|
||||
/// assert_eq!(stream.next().await.transpose()?, None);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`tokio::task::JoinSet`]: struct@tokio::task::JoinSet
|
||||
/// [`Stream`]: trait@crate::Stream
|
||||
#[derive(Debug)]
|
||||
pub struct JoinSetStream<T> {
|
||||
inner: JoinSet<T>,
|
||||
}
|
||||
|
||||
impl<T> JoinSetStream<T> {
|
||||
/// Create a new `JoinSetStream`.
|
||||
pub fn new(join_set: JoinSet<T>) -> Self {
|
||||
Self { inner: join_set }
|
||||
}
|
||||
|
||||
/// Get back the inner `JoinSet`.
|
||||
pub fn into_inner(self) -> JoinSet<T> {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> Stream for JoinSetStream<T> {
|
||||
type Item = Result<T, JoinError>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
self.inner.poll_join_next(cx)
|
||||
}
|
||||
|
||||
/// Returns the bounds of the stream based on the underlying `JoinSet`.
|
||||
///
|
||||
/// It returns `(set.len(), Some(set.len()))`.
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
let size = self.inner.len();
|
||||
(size, Some(size))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<JoinSet<T>> for JoinSetStream<T> {
|
||||
fn as_ref(&self) -> &JoinSet<T> {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<JoinSet<T>> for JoinSetStream<T> {
|
||||
fn as_mut(&mut self) -> &mut JoinSet<T> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<JoinSet<T>> for JoinSetStream<T> {
|
||||
fn from(join_set: JoinSet<T>) -> Self {
|
||||
Self::new(join_set)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#![cfg(feature = "rt")]
|
||||
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::collections::HashSet;
|
||||
use tokio::task::JoinSet;
|
||||
use tokio_stream::wrappers::JoinSetStream;
|
||||
|
||||
#[tokio::test]
|
||||
async fn size_hint_stream() {
|
||||
let set: JoinSet<_> = (0..2).map(|i| async move { i }).collect();
|
||||
let mut stream = JoinSetStream::new(set);
|
||||
|
||||
assert_eq!(stream.size_hint(), (2, Some(2)));
|
||||
stream.next().await;
|
||||
assert_eq!(stream.size_hint(), (1, Some(1)));
|
||||
stream.next().await;
|
||||
assert_eq!(stream.size_hint(), (0, Some(0)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn join_set_as_stream() {
|
||||
let set: JoinSet<_> = (0..2).map(|i| async move { i }).collect();
|
||||
let stream = JoinSetStream::new(set);
|
||||
|
||||
let values: HashSet<_> = stream.map(|result| result.unwrap()).collect().await;
|
||||
assert_eq!(values, HashSet::from([0, 1]));
|
||||
}
|
||||
|
||||
// Cannot run this test when “unwind” is disabled
|
||||
// since `JoinSet` use it to catch futures that panics.
|
||||
#[cfg(panic = "unwind")]
|
||||
#[tokio::test]
|
||||
async fn join_set_as_stream_panics_with_error() {
|
||||
let set: JoinSet<_> = std::iter::once(async move { panic!("boom!") }).collect();
|
||||
let mut stream = JoinSetStream::new(set);
|
||||
|
||||
let result = stream.next().await.transpose();
|
||||
assert!(matches!(result, Err(e) if e.is_panic()));
|
||||
}
|
||||
@@ -129,6 +129,26 @@ async fn take_while_terminated_after_predicate_fails() {
|
||||
assert!(stream.is_terminated());
|
||||
}
|
||||
|
||||
// ── map_while ─────────────────────────────────────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
async fn map_while_not_terminated_before_closure_returns_none() {
|
||||
let stream =
|
||||
tokio_stream::iter(vec![1, 2, 3]).map_while(|x| if x < 10 { Some(x) } else { None });
|
||||
assert!(!stream.is_terminated());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn map_while_terminated_after_closure_returns_none() {
|
||||
let mut stream =
|
||||
tokio_stream::iter(vec![1, 5, 2]).map_while(|x| if x < 3 { Some(x) } else { None });
|
||||
assert_eq!(stream.next().await, Some(1));
|
||||
assert!(!stream.is_terminated());
|
||||
// closure returns `None` on 5 → done flag set
|
||||
assert_eq!(stream.next().await, None);
|
||||
assert!(stream.is_terminated());
|
||||
}
|
||||
|
||||
// ── then ─────────────────────────────────────────────────────────────────────
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use tokio_stream as stream;
|
||||
use tokio_test::task;
|
||||
|
||||
use std::iter;
|
||||
use tokio_stream::{self as stream, Stream};
|
||||
use tokio_test::{assert_pending, assert_ready, task};
|
||||
|
||||
#[tokio::test]
|
||||
async fn coop() {
|
||||
@@ -9,6 +8,7 @@ async fn coop() {
|
||||
|
||||
for _ in 0..10_000 {
|
||||
if stream.poll_next().is_pending() {
|
||||
tokio::task::yield_now().await;
|
||||
assert!(stream.is_woken());
|
||||
return;
|
||||
}
|
||||
@@ -16,3 +16,37 @@ async fn coop() {
|
||||
|
||||
panic!("did not yield");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_iter_coop_budget() {
|
||||
let mut stream = task::spawn(stream::iter(iter::repeat(1)));
|
||||
|
||||
// Tokio's default budget is 128.
|
||||
// Fallback yield_amt is 32.
|
||||
let limit = if cfg!(feature = "rt") { 128 } else { 32 };
|
||||
|
||||
for i in 0..limit {
|
||||
let res = stream.poll_next();
|
||||
assert!(res.is_ready(), "Should be ready at index {i}");
|
||||
}
|
||||
|
||||
// Next poll should be pending
|
||||
assert_pending!(stream.poll_next());
|
||||
|
||||
tokio::task::yield_now().await;
|
||||
assert!(stream.is_woken());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_iter_size_hint() {
|
||||
let stream = stream::iter(vec![1, 2, 3]);
|
||||
assert_eq!(stream.size_hint(), (3, Some(3)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_iter_eof_behavior() {
|
||||
let mut stream = task::spawn(stream::iter(vec![1]));
|
||||
|
||||
assert_ready!(stream.poll_next());
|
||||
assert_ready!(stream.poll_next()); // EOF should be ready None
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn map_while_yields_until_closure_returns_none() {
|
||||
let mut stream =
|
||||
tokio_stream::iter(1..=10).map_while(|x| if x < 4 { Some(x + 3) } else { None });
|
||||
assert_eq!(stream.next().await, Some(4));
|
||||
assert_eq!(stream.next().await, Some(5));
|
||||
assert_eq!(stream.next().await, Some(6));
|
||||
assert_eq!(stream.next().await, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn map_while_does_not_poll_after_closure_returns_none() {
|
||||
// Once the closure returns `None`, the underlying stream must not be polled
|
||||
// again, so the trailing `2` is never yielded.
|
||||
let mut stream =
|
||||
tokio_stream::iter(vec![1, 5, 2]).map_while(|x| if x < 3 { Some(x) } else { None });
|
||||
assert_eq!(stream.next().await, Some(1));
|
||||
assert_eq!(stream.next().await, None);
|
||||
assert_eq!(stream.next().await, None);
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
use tokio_stream::{self as stream, Stream, StreamExt};
|
||||
|
||||
#[tokio::test]
|
||||
async fn size_hint_without_peek() {
|
||||
let mut s = stream::iter(vec![1, 2, 3]).peekable();
|
||||
assert_eq!(s.size_hint(), (3, Some(3)));
|
||||
s.next().await;
|
||||
assert_eq!(s.size_hint(), (2, Some(2)));
|
||||
s.next().await;
|
||||
assert_eq!(s.size_hint(), (1, Some(1)));
|
||||
s.next().await;
|
||||
assert_eq!(s.size_hint(), (0, Some(0)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn size_hint_with_peek() {
|
||||
let mut s = stream::iter(vec![1, 2, 3]).peekable();
|
||||
|
||||
// before peek: all items are in inner stream
|
||||
assert_eq!(s.size_hint(), (3, Some(3)));
|
||||
|
||||
// after peek: one item moves into self.peek buffer — total must still be 3
|
||||
let _ = s.peek().await;
|
||||
assert_eq!(s.size_hint(), (3, Some(3)));
|
||||
|
||||
// consume the peeked item via next()
|
||||
assert_eq!(s.next().await, Some(1));
|
||||
assert_eq!(s.size_hint(), (2, Some(2)));
|
||||
|
||||
// peek again
|
||||
let _ = s.peek().await;
|
||||
assert_eq!(s.size_hint(), (2, Some(2)));
|
||||
|
||||
s.next().await;
|
||||
assert_eq!(s.size_hint(), (1, Some(1)));
|
||||
|
||||
// peek the last item
|
||||
let _ = s.peek().await;
|
||||
assert_eq!(s.size_hint(), (1, Some(1)));
|
||||
|
||||
s.next().await;
|
||||
assert_eq!(s.size_hint(), (0, Some(0)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn size_hint_empty_stream() {
|
||||
let mut s = stream::iter(Vec::<i32>::new()).peekable();
|
||||
assert_eq!(s.size_hint(), (0, Some(0)));
|
||||
assert_eq!(s.peek().await, None);
|
||||
assert_eq!(s.size_hint(), (0, Some(0)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn peek_returns_correct_item() {
|
||||
let mut s = stream::iter(vec![10, 20, 30]).peekable();
|
||||
|
||||
assert_eq!(s.peek().await, Some(&10));
|
||||
assert_eq!(s.peek().await, Some(&10)); // second peek returns same item
|
||||
assert_eq!(s.next().await, Some(10)); // next() gives same item
|
||||
assert_eq!(s.next().await, Some(20));
|
||||
assert_eq!(s.next().await, Some(30));
|
||||
assert_eq!(s.next().await, None);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn size_hint_overflow() {
|
||||
// When inner stream reports usize::MAX upper bound and an item is peeked,
|
||||
// checked_add must return None rather than wrapping.
|
||||
struct MaxHint(bool); // bool = whether to return one item
|
||||
|
||||
impl Stream for MaxHint {
|
||||
type Item = ();
|
||||
fn poll_next(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<()>> {
|
||||
if self.0 {
|
||||
self.0 = false;
|
||||
std::task::Poll::Ready(Some(()))
|
||||
} else {
|
||||
std::task::Poll::Ready(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
let mut s = MaxHint(true).peekable();
|
||||
|
||||
// before peek: delegates directly to inner
|
||||
assert_eq!(s.size_hint(), (usize::MAX, Some(usize::MAX)));
|
||||
|
||||
// after peek: peek_len=1, checked_add(1) on usize::MAX must give None not panic
|
||||
let _ = s.peek().await;
|
||||
assert_eq!(s.size_hint(), (usize::MAX, None));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn size_hint_unbounded_upper() {
|
||||
// A stream that reports unknown upper bound
|
||||
struct Unbounded;
|
||||
|
||||
impl Stream for Unbounded {
|
||||
type Item = u32;
|
||||
fn poll_next(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
_cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Option<u32>> {
|
||||
std::task::Poll::Ready(Some(42))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(1, None)
|
||||
}
|
||||
}
|
||||
|
||||
let mut s = Unbounded.peekable();
|
||||
assert_eq!(s.size_hint(), (1, None));
|
||||
let _ = s.peek().await;
|
||||
assert_eq!(s.size_hint(), (2, None)); // still unbounded after peek
|
||||
}
|
||||
@@ -225,6 +225,30 @@ fn size_hint_without_upper() {
|
||||
assert_eq!(size_hint, (3, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn size_hint_overflow() {
|
||||
struct Monster;
|
||||
|
||||
impl Stream for Monster {
|
||||
type Item = ();
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, _cx: &mut std::task::Context<'_>) -> Poll<Option<()>> {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
let mut map = StreamMap::new();
|
||||
|
||||
map.insert("a", Monster);
|
||||
map.insert("b", Monster);
|
||||
|
||||
assert_eq!(map.size_hint(), (usize::MAX, None));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_capacity_zero() {
|
||||
let map = StreamMap::<&str, stream::Pending<()>>::new();
|
||||
@@ -405,6 +429,27 @@ async fn poll_next_many_enough() {
|
||||
assert!(buffer.contains(&(1, 1)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn poll_next_many_does_not_exceed_limit() {
|
||||
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
|
||||
|
||||
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
|
||||
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
|
||||
|
||||
let mut buffer = vec![];
|
||||
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 1)).await;
|
||||
|
||||
assert_eq!(n, 1);
|
||||
assert_eq!(buffer.len(), 1);
|
||||
|
||||
let n = poll_fn(|cx| stream_map.poll_next_many(cx, &mut buffer, 1)).await;
|
||||
|
||||
assert_eq!(n, 1);
|
||||
assert_eq!(buffer.len(), 2);
|
||||
assert!(buffer.contains(&(0, 0)));
|
||||
assert!(buffer.contains(&(1, 1)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn poll_next_many_correctly_loops_around() {
|
||||
for _ in 0..10 {
|
||||
@@ -519,6 +564,27 @@ async fn next_many_enough() {
|
||||
assert!(buffer.contains(&(1, 1)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn next_many_does_not_exceed_limit() {
|
||||
let mut stream_map: StreamMap<usize, UsizeStream> = StreamMap::new();
|
||||
|
||||
stream_map.insert(0, Box::pin(iter([0usize].into_iter())) as UsizeStream);
|
||||
stream_map.insert(1, Box::pin(iter([1usize].into_iter())) as UsizeStream);
|
||||
|
||||
let mut buffer = vec![];
|
||||
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 1)).poll(cx)).await;
|
||||
|
||||
assert_eq!(n, 1);
|
||||
assert_eq!(buffer.len(), 1);
|
||||
|
||||
let n = poll_fn(|cx| pin!(stream_map.next_many(&mut buffer, 1)).poll(cx)).await;
|
||||
|
||||
assert_eq!(n, 1);
|
||||
assert_eq!(buffer.len(), 2);
|
||||
assert!(buffer.contains(&(0, 0)));
|
||||
assert!(buffer.contains(&(1, 1)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn next_many_correctly_loops_around() {
|
||||
for _ in 0..10 {
|
||||
|
||||
@@ -70,6 +70,9 @@ const IDLE: usize = 0;
|
||||
const WAKE: usize = 1;
|
||||
const SLEEP: usize = 2;
|
||||
|
||||
/// Default maximum number of poll iterations in [`Spawn::poll_until_idle`].
|
||||
const POLL_UNTIL_IDLE_MAX_ITERATIONS: usize = 150;
|
||||
|
||||
impl<T> Spawn<T> {
|
||||
/// Consumes `self` returning the inner value
|
||||
pub fn into_inner(self) -> T
|
||||
@@ -123,6 +126,43 @@ impl<T: Future> Spawn<T> {
|
||||
let fut = self.future.as_mut();
|
||||
self.task.enter(|cx| fut.poll(cx))
|
||||
}
|
||||
|
||||
/// Polls the future until it is idle.
|
||||
///
|
||||
/// A future is considered idle when it either completes, or returns
|
||||
/// [`Poll::Pending`] without a pending wake notification.
|
||||
///
|
||||
/// Unlike [`poll`](Self::poll), this method keeps polling while the future
|
||||
/// returns [`Poll::Pending`] but has received a wake notification, advancing
|
||||
/// the future as far as possible without waiting for external events.
|
||||
///
|
||||
/// Polling is bounded to avoid infinite loops when a future wakes without
|
||||
/// making progress.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the iteration limit is exceeded.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tokio_test::task;
|
||||
///
|
||||
/// let mut task = task::spawn(async { 42 });
|
||||
///
|
||||
/// assert!(task.poll_until_idle().is_ready());
|
||||
/// ```
|
||||
pub fn poll_until_idle(&mut self) -> Poll<T::Output> {
|
||||
for _ in 0..POLL_UNTIL_IDLE_MAX_ITERATIONS {
|
||||
let result = self.poll();
|
||||
if result.is_ready() || !self.is_woken() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
panic!(
|
||||
"poll_until_idle exceeded {POLL_UNTIL_IDLE_MAX_ITERATIONS} iterations; future may be waking without making progress"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Stream> Spawn<T> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use std::future::{pending, Future};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio_stream::Stream;
|
||||
@@ -23,3 +24,81 @@ fn test_spawn_stream_size_hint() {
|
||||
let spawn = task::spawn(SizedStream);
|
||||
assert_eq!(spawn.size_hint(), (100, Some(200)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poll_until_idle_ready() {
|
||||
let mut task = task::spawn(async { 42 });
|
||||
assert_eq!(task.poll_until_idle(), Poll::Ready(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poll_until_idle_pending_not_woken() {
|
||||
let mut task = task::spawn(pending::<()>());
|
||||
assert!(task.poll_until_idle().is_pending());
|
||||
}
|
||||
|
||||
struct WakeThenReady {
|
||||
step: u8,
|
||||
}
|
||||
|
||||
impl Future for WakeThenReady {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
match self.step {
|
||||
0 => {
|
||||
self.step = 1;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
_ => Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poll_until_idle_advances_on_wake() {
|
||||
let mut task = task::spawn(WakeThenReady { step: 0 });
|
||||
assert!(task.poll_until_idle().is_ready());
|
||||
}
|
||||
|
||||
struct WakeNTimes {
|
||||
remaining: u8,
|
||||
}
|
||||
|
||||
impl Future for WakeNTimes {
|
||||
type Output = u8;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<u8> {
|
||||
if self.remaining == 0 {
|
||||
return Poll::Ready(0);
|
||||
}
|
||||
self.remaining -= 1;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn poll_until_idle_multiple_wakes() {
|
||||
let mut task = task::spawn(WakeNTimes { remaining: 3 });
|
||||
assert_eq!(task.poll_until_idle(), Poll::Ready(0));
|
||||
}
|
||||
|
||||
struct WakeForever;
|
||||
|
||||
impl Future for WakeForever {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "poll_until_idle exceeded 150 iterations")]
|
||||
fn poll_until_idle_panics_on_infinite_wake() {
|
||||
let mut task = task::spawn(WakeForever);
|
||||
let _ = task.poll_until_idle();
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ full = ["codec", "compat", "io-util", "time", "net", "rt", "join-map"]
|
||||
|
||||
net = ["tokio/net"]
|
||||
compat = ["futures-io"]
|
||||
codec = []
|
||||
codec = ["libc"]
|
||||
time = ["tokio/time", "slab"]
|
||||
io = []
|
||||
io-util = ["io", "tokio/rt", "tokio/io-util"]
|
||||
@@ -46,6 +46,9 @@ slab = { version = "0.4.4", optional = true } # Backs `DelayQueue`
|
||||
tracing = { version = "0.1.29", default-features = false, features = ["std"], optional = true }
|
||||
hashbrown = { version = "0.15.0", default-features = false, optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { version = "0.2.168", optional = true } # Backs the LinesCodec delimiter scan via libc::memchr
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.0.0", features = ["full"] }
|
||||
tokio-test = "0.4.0"
|
||||
|
||||
@@ -47,7 +47,7 @@ pub trait Decoder {
|
||||
/// implementation of `decode_eof` to yield an `io::Error` when the decoder
|
||||
/// fails to consume all available data.
|
||||
///
|
||||
/// Note that implementors of this trait can simply indicate `type Error =
|
||||
/// Note that implementers of this trait can simply indicate `type Error =
|
||||
/// io::Error` to use I/O errors as this type.
|
||||
///
|
||||
/// [`FramedRead`]: crate::codec::FramedRead
|
||||
|
||||
@@ -115,9 +115,7 @@ impl Decoder for LinesCodec {
|
||||
// there's no max_length set, we'll read to the end of the buffer.
|
||||
let read_to = cmp::min(self.max_length.saturating_add(1), buf.len());
|
||||
|
||||
let newline_offset = buf[self.next_index..read_to]
|
||||
.iter()
|
||||
.position(|b| *b == b'\n');
|
||||
let newline_offset = crate::util::memchr::memchr(b'\n', &buf[self.next_index..read_to]);
|
||||
|
||||
match (self.is_discarding, newline_offset) {
|
||||
(true, Some(offset)) => {
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! The following example demonstrates how to use a codec such as [`LinesCodec`] to
|
||||
//! write framed data. [`FramedWrite`] can be used to achieve this. Data sent to
|
||||
//! [`FramedWrite`] are first framed according to a specific codec, and then sent to
|
||||
//! an implementor of [`AsyncWrite`].
|
||||
//! an implementer of [`AsyncWrite`].
|
||||
//!
|
||||
//! ```
|
||||
//! use futures::sink::SinkExt;
|
||||
@@ -43,7 +43,7 @@
|
||||
//! # Example decoding using `LinesCodec`
|
||||
//! The following example demonstrates how to use a codec such as [`LinesCodec`] to
|
||||
//! read a stream of framed data. [`FramedRead`] can be used to achieve this. [`FramedRead`]
|
||||
//! will keep reading from an [`AsyncRead`] implementor until a whole frame, according to a codec,
|
||||
//! will keep reading from an [`AsyncRead`] implementer until a whole frame, according to a codec,
|
||||
//! can be parsed.
|
||||
//!
|
||||
//!```
|
||||
|
||||
@@ -147,8 +147,8 @@ fn advance_slices<'a>(bufs: &mut &mut [IoSlice<'a>], n: usize) {
|
||||
|
||||
*bufs = &mut std::mem::take(bufs)[remove..];
|
||||
if let Some(first) = bufs.first_mut() {
|
||||
let buf = &first[..left];
|
||||
// necessary due to limitating in the borrow checker,
|
||||
let buf = &first[left..];
|
||||
// Necessary due to a limitation in the borrow checker,
|
||||
// when tokio MSRV reaches 1.81.0 this entire function
|
||||
// can be replaced with `IoSlice::advance_slices`
|
||||
//
|
||||
|
||||
@@ -10,6 +10,13 @@ pub struct DropGuard {
|
||||
}
|
||||
|
||||
impl DropGuard {
|
||||
/// Returns a reference to the cancellation token wrapped by this guard.
|
||||
pub fn token(&self) -> &CancellationToken {
|
||||
self.inner
|
||||
.as_ref()
|
||||
.expect("`inner` can only be None in a destructor")
|
||||
}
|
||||
|
||||
/// Returns stored cancellation token and removes this drop guard instance
|
||||
/// (i.e. it will no longer cancel token). Other guards for this token
|
||||
/// are not affected.
|
||||
|
||||
@@ -13,6 +13,13 @@ pub struct DropGuardRef<'a> {
|
||||
}
|
||||
|
||||
impl<'a> DropGuardRef<'a> {
|
||||
/// Returns a reference to the cancellation token wrapped by this guard.
|
||||
pub fn token(&self) -> &CancellationToken {
|
||||
self.inner
|
||||
.as_ref()
|
||||
.expect("`inner` can only be None in a destructor")
|
||||
}
|
||||
|
||||
/// Returns stored cancellation token and removes this drop guard instance
|
||||
/// (i.e. it will no longer cancel token). Other guards for this token
|
||||
/// are not affected.
|
||||
|
||||
@@ -446,7 +446,7 @@ where
|
||||
/// * `Some((key, Ok(value)))` if one of the tasks in this `JoinMap` has
|
||||
/// completed. The `value` is the return value of that ask, and `key` is
|
||||
/// the key associated with the task.
|
||||
/// * `Some((key, Err(err))` if one of the tasks in this `JoinMap` has
|
||||
/// * `Some((key, Err(err)))` if one of the tasks in this `JoinMap` has
|
||||
/// panicked or been aborted. `key` is the key associated with the task
|
||||
/// that panicked or was aborted.
|
||||
/// * `None` if the `JoinMap` is empty.
|
||||
@@ -468,6 +468,60 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to join one of the tasks in the map that has completed and
|
||||
/// returns its output, along with the key corresponding to that task.
|
||||
///
|
||||
/// Returns `None` if there are no completed tasks, or if the map is empty.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// This function returns:
|
||||
///
|
||||
/// * `Some((key, Ok(value)))` if one of the tasks in this `JoinMap` has
|
||||
/// completed. The `value` is the return value of that task, and `key`
|
||||
/// is the key associated with the task.
|
||||
/// * `Some((key, Err(err)))` if one of the tasks in this `JoinMap` has
|
||||
/// panicked or been aborted. `key` is the key associated with the task
|
||||
/// that panicked or was aborted.
|
||||
/// * `None` if there are no completed tasks ready to be joined, or the
|
||||
/// `JoinMap` is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio_util::task::JoinMap;
|
||||
///
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() {
|
||||
/// let mut map = JoinMap::new();
|
||||
/// map.spawn("answer", async { 42 });
|
||||
///
|
||||
/// let (key, res) = loop {
|
||||
/// if let Some(joined) = map.try_join_next() {
|
||||
/// break joined;
|
||||
/// }
|
||||
/// tokio::task::yield_now().await;
|
||||
/// };
|
||||
///
|
||||
/// assert_eq!(key, "answer");
|
||||
/// assert_eq!(res.unwrap(), 42);
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_join_next(&mut self) -> Option<(K, Result<V, JoinError>)> {
|
||||
loop {
|
||||
let (res, id) = match self.tasks.try_join_next_with_id()? {
|
||||
Ok((id, output)) => (Ok(output), id),
|
||||
Err(e) => {
|
||||
let id = e.id();
|
||||
(Err(e), id)
|
||||
}
|
||||
};
|
||||
if let Some(key) = self.remove_by_id(id) {
|
||||
break Some((key, res));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Aborts all tasks and waits for them to finish shutting down.
|
||||
///
|
||||
/// Calling this method is equivalent to calling [`abort_all`] and then calling [`join_next`] in
|
||||
|
||||
@@ -188,6 +188,10 @@ impl<T> JoinQueue<T> {
|
||||
/// Note that on success the handle will panic on subsequent polls
|
||||
/// since it becomes consumed.
|
||||
fn try_poll_handle(jh: &mut AbortOnDropHandle<T>) -> Option<Result<T, JoinError>> {
|
||||
if !jh.is_finished() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let waker = futures_util::task::noop_waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
|
||||
@@ -864,11 +864,19 @@ impl<T> DelayQueue<T> {
|
||||
self.slab[*key].expired = false;
|
||||
|
||||
self.insert_idx(when, *key);
|
||||
let inserted_expired = self.slab[*key].expired;
|
||||
|
||||
let next_deadline = self.next_deadline();
|
||||
if let (Some(ref mut delay), Some(deadline)) = (&mut self.delay, next_deadline) {
|
||||
// This should awaken us if necessary (ie, if already expired)
|
||||
delay.as_mut().reset(deadline);
|
||||
match (next_deadline, &mut self.delay) {
|
||||
(None, _) => self.delay = None,
|
||||
(Some(deadline), Some(delay)) => delay.as_mut().reset(deadline),
|
||||
(Some(deadline), None) => self.delay = Some(Box::pin(sleep_until(deadline))),
|
||||
}
|
||||
|
||||
if inserted_expired {
|
||||
if let Some(waker) = self.waker.take() {
|
||||
waker.wake();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,12 @@ use std::{
|
||||
/// handle encoding and decoding of messages frames. Note that the incoming and
|
||||
/// outgoing frame types may be distinct.
|
||||
///
|
||||
/// A single datagram may decode into multiple frames. `UdpFramed` will keep
|
||||
/// calling [`Decoder::decode_eof`] with the current datagram until it returns
|
||||
/// `Ok(None)`. If a decoder wants to discard a malformed datagram and continue
|
||||
/// receiving later datagrams, it should consume or clear the remaining bytes
|
||||
/// from the buffer before returning `Err`.
|
||||
///
|
||||
/// This function returns a *single* object that is both [`Stream`] and [`Sink`];
|
||||
/// grouping this into a single object is often useful for layering things which
|
||||
/// require both read and write access to the underlying object.
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
//! Search for a byte in a byte array using libc.
|
||||
//!
|
||||
//! When nothing pulls in libc, then just use a trivial implementation. Note
|
||||
//! that we only depend on libc on unix.
|
||||
|
||||
#[cfg(not(all(unix, feature = "libc")))]
|
||||
fn memchr_inner(needle: u8, haystack: &[u8]) -> Option<usize> {
|
||||
haystack.iter().position(|val| needle == *val)
|
||||
}
|
||||
|
||||
#[cfg(all(unix, feature = "libc"))]
|
||||
fn memchr_inner(needle: u8, haystack: &[u8]) -> Option<usize> {
|
||||
let start = haystack.as_ptr();
|
||||
|
||||
// SAFETY: `start` is valid for `haystack.len()` bytes.
|
||||
let ptr = (unsafe { libc::memchr(start.cast(), needle as _, haystack.len()) })
|
||||
.cast::<u8>()
|
||||
.cast_const();
|
||||
|
||||
if ptr.is_null() {
|
||||
None
|
||||
} else {
|
||||
// SAFETY: `ptr` will always be in bounds, since libc guarantees that the ptr will either
|
||||
// be to an element inside the array or the ptr will be null
|
||||
// since the ptr is in bounds the offset must also always be non null
|
||||
// and there can't be more than isize::MAX elements inside an array
|
||||
// as rust guarantees that the maximum number of bytes a allocation
|
||||
// may occupy is isize::MAX
|
||||
unsafe {
|
||||
// TODO(MSRV 1.87): When bumping MSRV, switch to `ptr.byte_offset_from_unsigned(start)`.
|
||||
Some(usize::try_from(ptr.offset_from(start)).unwrap_unchecked())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn memchr(needle: u8, haystack: &[u8]) -> Option<usize> {
|
||||
let index = memchr_inner(needle, haystack)?;
|
||||
|
||||
// SAFETY: `memchr_inner` returns Some(index) and in that case index must point to an element in haystack
|
||||
// or `memchr_inner` None which is guarded by the `?` operator above
|
||||
// therefore the index must **always** point to an element in the array
|
||||
// and so this indexing operation is safe
|
||||
// TODO(MSRV 1.81): When bumping MSRV, switch to `std::hint::assert_unchecked(haystack.get(..=index).is_some());`
|
||||
unsafe {
|
||||
if haystack.get(..=index).is_none() {
|
||||
std::hint::unreachable_unchecked()
|
||||
}
|
||||
}
|
||||
|
||||
Some(index)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::memchr;
|
||||
|
||||
#[test]
|
||||
fn memchr_test() {
|
||||
let haystack = b"123abc456\0\xffabc\n";
|
||||
|
||||
assert_eq!(memchr(b'1', haystack), Some(0));
|
||||
assert_eq!(memchr(b'2', haystack), Some(1));
|
||||
assert_eq!(memchr(b'3', haystack), Some(2));
|
||||
assert_eq!(memchr(b'4', haystack), Some(6));
|
||||
assert_eq!(memchr(b'5', haystack), Some(7));
|
||||
assert_eq!(memchr(b'6', haystack), Some(8));
|
||||
assert_eq!(memchr(b'7', haystack), None);
|
||||
assert_eq!(memchr(b'a', haystack), Some(3));
|
||||
assert_eq!(memchr(b'b', haystack), Some(4));
|
||||
assert_eq!(memchr(b'c', haystack), Some(5));
|
||||
assert_eq!(memchr(b'd', haystack), None);
|
||||
assert_eq!(memchr(b'A', haystack), None);
|
||||
assert_eq!(memchr(0, haystack), Some(9));
|
||||
assert_eq!(memchr(0xff, haystack), Some(10));
|
||||
assert_eq!(memchr(0xfe, haystack), None);
|
||||
assert_eq!(memchr(1, haystack), None);
|
||||
assert_eq!(memchr(b'\n', haystack), Some(14));
|
||||
assert_eq!(memchr(b'\r', haystack), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memchr_all() {
|
||||
let mut arr = Vec::new();
|
||||
for b in 0..=255 {
|
||||
arr.push(b);
|
||||
}
|
||||
for b in 0..=255 {
|
||||
assert_eq!(memchr(b, &arr), Some(b as usize));
|
||||
}
|
||||
arr.reverse();
|
||||
for b in 0..=255 {
|
||||
assert_eq!(memchr(b, &arr), Some(255 - b as usize));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn memchr_empty() {
|
||||
for b in 0..=255 {
|
||||
assert_eq!(memchr(b, b""), None);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
mod maybe_dangling;
|
||||
#[cfg(feature = "codec")]
|
||||
pub(crate) mod memchr;
|
||||
#[cfg(any(feature = "io", feature = "codec"))]
|
||||
mod poll_buf;
|
||||
|
||||
|
||||
@@ -10,6 +10,55 @@ use std::io::IoSlice;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
struct PartialVectoredWriter {
|
||||
buf: BytesMut,
|
||||
max_write: usize,
|
||||
}
|
||||
|
||||
impl AsyncWrite for PartialVectoredWriter {
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
_buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
panic!("shouldn't be called")
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Ok(()).into()
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
bufs: &[io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
let mut remaining = self.max_write;
|
||||
let mut written = 0;
|
||||
|
||||
for buf in bufs {
|
||||
if remaining == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
let n = remaining.min(buf.len());
|
||||
self.buf.extend_from_slice(&buf[..n]);
|
||||
remaining -= n;
|
||||
written += n;
|
||||
}
|
||||
|
||||
Ok(written).into()
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_write_all_vectored() {
|
||||
struct Wr {
|
||||
@@ -140,3 +189,29 @@ async fn write_all_vectored_with_empty_slice() {
|
||||
write_all_vectored(&mut wr, buf).await.unwrap();
|
||||
assert_eq!(&wr.buf[..], b"hello");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn write_all_vectored_should_continue_with_unwritten_suffix_if_write_stops_inside_buffer() {
|
||||
let mut wr = PartialVectoredWriter {
|
||||
buf: BytesMut::with_capacity(64),
|
||||
max_write: 3,
|
||||
};
|
||||
|
||||
let buf = &mut [IoSlice::new(b"hello"), IoSlice::new(b"world")];
|
||||
|
||||
write_all_vectored(&mut wr, buf).await.unwrap();
|
||||
assert_eq!(&wr.buf[..], b"helloworld");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn write_all_vectored_should_continue_with_next_buffer_if_write_ends_on_boundary() {
|
||||
let mut wr = PartialVectoredWriter {
|
||||
buf: BytesMut::with_capacity(64),
|
||||
max_write: 2,
|
||||
};
|
||||
|
||||
let buf = &mut [IoSlice::new(b"ab"), IoSlice::new(b"cd")];
|
||||
|
||||
write_all_vectored(&mut wr, buf).await.unwrap();
|
||||
assert_eq!(&wr.buf[..], b"abcd");
|
||||
}
|
||||
|
||||
@@ -358,6 +358,131 @@ async fn abort_all() {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn try_join_next_empty() {
|
||||
let mut map: JoinMap<usize, ()> = JoinMap::new();
|
||||
assert!(map.try_join_next().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn try_join_next_no_ready_task() {
|
||||
let mut map = JoinMap::new();
|
||||
let (_tx, rx) = oneshot::channel::<()>();
|
||||
map.spawn("pending", async move {
|
||||
let _ = rx.await;
|
||||
});
|
||||
|
||||
// Task is not yet ready.
|
||||
assert!(map.try_join_next().is_none());
|
||||
assert_eq!(map.len(), 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn try_join_next_completed_task() {
|
||||
let mut map = JoinMap::new();
|
||||
map.spawn("hello", async { 42 });
|
||||
|
||||
let mut got = None;
|
||||
while got.is_none() {
|
||||
got = map.try_join_next();
|
||||
if got.is_none() {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
let (key, res) = got.unwrap();
|
||||
assert_eq!(key, "hello");
|
||||
assert_eq!(res.unwrap(), 42);
|
||||
assert!(map.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn try_join_next_aborted_task() {
|
||||
let mut map = JoinMap::new();
|
||||
map.spawn("forever", async {
|
||||
futures::future::pending::<()>().await;
|
||||
});
|
||||
|
||||
assert!(map.abort("forever"));
|
||||
|
||||
let mut got = None;
|
||||
while got.is_none() {
|
||||
got = map.try_join_next();
|
||||
if got.is_none() {
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
}
|
||||
let (key, res) = got.unwrap();
|
||||
assert_eq!(key, "forever");
|
||||
assert!(res.unwrap_err().is_cancelled());
|
||||
assert!(map.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn try_join_next_advances_through_multiple() {
|
||||
const N: u32 = 8;
|
||||
|
||||
static SEM: tokio::sync::Semaphore = tokio::sync::Semaphore::const_new(0);
|
||||
|
||||
let mut map = JoinMap::new();
|
||||
for i in 0..N {
|
||||
map.spawn(i, async move {
|
||||
SEM.add_permits(1);
|
||||
i
|
||||
});
|
||||
}
|
||||
|
||||
// Wait until all tasks have signalled completion. On the current_thread
|
||||
// runtime this means they have actually finished.
|
||||
let _ = SEM.acquire_many(N).await.unwrap();
|
||||
|
||||
let mut seen = vec![false; N as usize];
|
||||
let mut count = 0;
|
||||
loop {
|
||||
match map.try_join_next() {
|
||||
Some((key, res)) => {
|
||||
let v = res.expect("task should have completed successfully");
|
||||
assert_eq!(key, v);
|
||||
seen[v as usize] = true;
|
||||
count += 1;
|
||||
}
|
||||
None if map.is_empty() => break,
|
||||
None => tokio::task::yield_now().await,
|
||||
}
|
||||
}
|
||||
|
||||
assert_eq!(count, N);
|
||||
assert!(seen.into_iter().all(|b| b));
|
||||
assert!(map.try_join_next().is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn try_join_next_skips_replaced_task() {
|
||||
let mut map = JoinMap::new();
|
||||
|
||||
let (tx1, rx1) = oneshot::channel::<()>();
|
||||
map.spawn(1, async {
|
||||
let _ = rx1.await;
|
||||
11
|
||||
});
|
||||
tx1.send(()).unwrap();
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
let (tx2, rx2) = oneshot::channel::<()>();
|
||||
map.spawn(1, async {
|
||||
let _ = rx2.await;
|
||||
22
|
||||
});
|
||||
tx2.send(()).unwrap();
|
||||
tokio::task::yield_now().await;
|
||||
|
||||
let (key, res) = map.try_join_next().unwrap();
|
||||
assert_eq!(key, 1);
|
||||
assert_eq!(res.unwrap(), 22);
|
||||
|
||||
assert!(map.try_join_next().is_none());
|
||||
assert!(map.is_empty());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn duplicate_keys() {
|
||||
let mut map = JoinMap::new();
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use std::task::Context;
|
||||
|
||||
use futures_test::task::new_count_waker;
|
||||
use tokio::sync::oneshot;
|
||||
use tokio::task::yield_now;
|
||||
use tokio::time::Duration;
|
||||
@@ -276,6 +279,60 @@ async fn test_join_queue_try_join_next() {
|
||||
check_try_join_next_is_noop(&mut queue);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_join_queue_try_join_next_does_not_replace_waker() {
|
||||
let (send, recv) = oneshot::channel();
|
||||
let mut queue = JoinQueue::new();
|
||||
queue.spawn(async move {
|
||||
recv.await.unwrap();
|
||||
42
|
||||
});
|
||||
|
||||
let (waker, wake_count) = new_count_waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
assert_pending!(queue.poll_join_next(&mut cx));
|
||||
assert_eq!(wake_count, 0);
|
||||
assert!(queue.try_join_next().is_none());
|
||||
|
||||
send.send(()).unwrap();
|
||||
yield_now().await;
|
||||
|
||||
assert_eq!(wake_count, 1);
|
||||
assert_eq!(
|
||||
assert_ready!(queue.poll_join_next(&mut cx))
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
42
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_join_queue_try_join_next_with_id_does_not_replace_waker() {
|
||||
let (send, recv) = oneshot::channel();
|
||||
let mut queue = JoinQueue::new();
|
||||
queue.spawn(async move {
|
||||
recv.await.unwrap();
|
||||
42
|
||||
});
|
||||
|
||||
let (waker, wake_count) = new_count_waker();
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
|
||||
assert_pending!(queue.poll_join_next_with_id(&mut cx));
|
||||
assert_eq!(wake_count, 0);
|
||||
assert!(queue.try_join_next_with_id().is_none());
|
||||
|
||||
send.send(()).unwrap();
|
||||
yield_now().await;
|
||||
|
||||
assert_eq!(wake_count, 1);
|
||||
let (_, output) = assert_ready!(queue.poll_join_next_with_id(&mut cx))
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(output, 42);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_join_queue_try_join_next_disabled_coop() {
|
||||
// This number is large enough to trigger coop. Without using `tokio::task::coop::unconstrained`
|
||||
|
||||
@@ -202,6 +202,27 @@ async fn reset_entry() {
|
||||
assert!(entry.is_none())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn reset_to_past_wakes_pending_queue() {
|
||||
time::pause();
|
||||
|
||||
let mut queue = task::spawn(DelayQueue::new());
|
||||
let key = queue.insert("foo", ms(10_000));
|
||||
|
||||
assert_pending!(poll!(queue));
|
||||
assert!(!queue.is_woken());
|
||||
|
||||
queue.reset_at(&key, Instant::now() - ms(100));
|
||||
|
||||
assert!(queue.is_woken());
|
||||
|
||||
let entry = assert_ready_some!(poll!(queue));
|
||||
assert_eq!(*entry.get_ref(), "foo");
|
||||
|
||||
let entry = assert_ready!(poll!(queue));
|
||||
assert!(entry.is_none());
|
||||
}
|
||||
|
||||
// Reproduces tokio-rs/tokio#849.
|
||||
#[tokio::test]
|
||||
async fn reset_much_later() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(not(target_os = "wasi"))] // Wasi doesn't support UDP
|
||||
#![cfg(not(miri))] // No `socket` in Miri.
|
||||
#![cfg(not(miri))] // No UDP sockets in Miri.
|
||||
#![cfg(not(loom))] // No udp / UdpFramed in loom
|
||||
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
@@ -1,3 +1,128 @@
|
||||
# 1.53.1 (July 20th, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
- signal: restore MSRV by removing `OnceLock::wait` from the Windows handler ([#8300])
|
||||
|
||||
### Fixed (unstable)
|
||||
|
||||
- time: fix alt timer cancellation and insertion race ([#8252])
|
||||
|
||||
### Documented
|
||||
|
||||
- runtime: remove dead link definition in Runtime::block_on ([#8301])
|
||||
|
||||
[#8252]: https://github.com/tokio-rs/tokio/pull/8252
|
||||
[#8300]: https://github.com/tokio-rs/tokio/pull/8300
|
||||
[#8301]: https://github.com/tokio-rs/tokio/pull/8301
|
||||
|
||||
# 1.53.0 (July 17th, 2026)
|
||||
|
||||
### Added
|
||||
|
||||
- fs: implement `From<OwnedFd>` and `From<OwnedHandle>` for `File` ([#8266])
|
||||
- metrics: add task schedule latency metric ([#7986])
|
||||
- net: add `SocketAddr` methods to Unix sockets ([#8144])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: add `#[inline]` to IO trait impls for in-memory types ([#8242])
|
||||
- net: implement UCred::pid on FreeBSD ([#8086])
|
||||
- net: support Nuttx target os ([#8259])
|
||||
- signal: refactor global variables on Windows ([#8231])
|
||||
- sync: `mpsc::{Receiver,UnboundedReceiver}` now drops waker on drop, even if there are still senders ([#8095])
|
||||
- taskdump: support taskdumps on s390x ([#8192])
|
||||
- time: add `#[track_caller]` to `timeout_at()` ([#8077])
|
||||
- time: consolidate mutex locks on spurious poll ([#8124])
|
||||
- time: defer waker clone on spurious poll ([#8107])
|
||||
- time: move lazy-registration state into `Sleep` ([#8132])
|
||||
- tracing: remove unnecessary span clone ([#8126])
|
||||
|
||||
### Fixed
|
||||
|
||||
- io: do not treat zero-length reads as EOF in `Chain` ([#8251])
|
||||
- net: use getpeereid for QNX peer credentials ([#8270])
|
||||
- runtime: avoid illegal state in `FastRand` ([#8078])
|
||||
- sync: wake mpsc receiver when a queued `reserve[_many]` returns permits ([#8260])
|
||||
- taskdump: skip double wake on `Trace::capture`/`Trace::trace_with` ([#8043])
|
||||
- time: avoid stack overflow in runtime constructor ([#8093])
|
||||
- time (alt timer): ensure timers stay in the same runtime after `.reset()` ([#8169])
|
||||
|
||||
### IO uring (unstable)
|
||||
|
||||
- fs: use io-uring for `fs::try_exists` ([#8080])
|
||||
- fs: use io-uring for renaming files ([#7800])
|
||||
- rt: flush io-uring CQE in case of CQE overflow ([#8277])
|
||||
|
||||
### Documented
|
||||
|
||||
- docs: clarify cancel safety wording ([#8181])
|
||||
- fs: clarify `create_dir_all` succeeds if path exists ([#8149])
|
||||
- io: add warning about stdout reordering with multiple handles ([#8276])
|
||||
- net: document pipe `try_read*`/`try_write*` readiness behavior ([#8032])
|
||||
- runtime: document interaction with fork() ([#8202])
|
||||
- sync: clarify broadcast lagging semantics ([#8239])
|
||||
- sync: document memory ordering guarantees for Semaphore ([#8119])
|
||||
- task: explain why `yield_now` defers its waker ([#8254])
|
||||
- time: add panic docs to `timeout_at()` ([#8077])
|
||||
- time: fix reversed poll order in timeout doc ([#8214])
|
||||
|
||||
[#7800]: https://github.com/tokio-rs/tokio/pull/7800
|
||||
[#7986]: https://github.com/tokio-rs/tokio/pull/7986
|
||||
[#8032]: https://github.com/tokio-rs/tokio/pull/8032
|
||||
[#8043]: https://github.com/tokio-rs/tokio/pull/8043
|
||||
[#8077]: https://github.com/tokio-rs/tokio/pull/8077
|
||||
[#8078]: https://github.com/tokio-rs/tokio/pull/8078
|
||||
[#8080]: https://github.com/tokio-rs/tokio/pull/8080
|
||||
[#8086]: https://github.com/tokio-rs/tokio/pull/8086
|
||||
[#8093]: https://github.com/tokio-rs/tokio/pull/8093
|
||||
[#8095]: https://github.com/tokio-rs/tokio/pull/8095
|
||||
[#8107]: https://github.com/tokio-rs/tokio/pull/8107
|
||||
[#8119]: https://github.com/tokio-rs/tokio/pull/8119
|
||||
[#8124]: https://github.com/tokio-rs/tokio/pull/8124
|
||||
[#8126]: https://github.com/tokio-rs/tokio/pull/8126
|
||||
[#8132]: https://github.com/tokio-rs/tokio/pull/8132
|
||||
[#8144]: https://github.com/tokio-rs/tokio/pull/8144
|
||||
[#8149]: https://github.com/tokio-rs/tokio/pull/8149
|
||||
[#8169]: https://github.com/tokio-rs/tokio/pull/8169
|
||||
[#8181]: https://github.com/tokio-rs/tokio/pull/8181
|
||||
[#8192]: https://github.com/tokio-rs/tokio/pull/8192
|
||||
[#8193]: https://github.com/tokio-rs/tokio/pull/8193
|
||||
[#8202]: https://github.com/tokio-rs/tokio/pull/8202
|
||||
[#8214]: https://github.com/tokio-rs/tokio/pull/8214
|
||||
[#8231]: https://github.com/tokio-rs/tokio/pull/8231
|
||||
[#8239]: https://github.com/tokio-rs/tokio/pull/8239
|
||||
[#8242]: https://github.com/tokio-rs/tokio/pull/8242
|
||||
[#8251]: https://github.com/tokio-rs/tokio/pull/8251
|
||||
[#8254]: https://github.com/tokio-rs/tokio/pull/8254
|
||||
[#8259]: https://github.com/tokio-rs/tokio/pull/8259
|
||||
[#8260]: https://github.com/tokio-rs/tokio/pull/8260
|
||||
[#8266]: https://github.com/tokio-rs/tokio/pull/8266
|
||||
[#8270]: https://github.com/tokio-rs/tokio/pull/8270
|
||||
[#8276]: https://github.com/tokio-rs/tokio/pull/8276
|
||||
[#8277]: https://github.com/tokio-rs/tokio/pull/8277
|
||||
|
||||
# 1.52.4 (July 16th, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: don't skip the driver when `before_park` schedules work ([#8222])
|
||||
|
||||
### Fixed (unstable)
|
||||
|
||||
- taskdump: remove crate disambiguators from output ([#8264])
|
||||
|
||||
[#8264]: https://github.com/tokio-rs/tokio/pull/8264
|
||||
|
||||
# 1.52.3 (May 8th, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
* sync: fix underflow in mpsc channel `len()` ([#8062])
|
||||
* sync: notify receivers in mpsc `OwnedPermit::release()` method ([#8075])
|
||||
* sync: require that an `RwLock` has `max_readers != 0` ([#8076])
|
||||
* sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#8074])
|
||||
|
||||
# 1.52.2 (May 4th, 2026)
|
||||
|
||||
This release reverts the LIFO slot stealing change introduced in 1.51.0
|
||||
@@ -55,6 +180,23 @@ This release reverts the LIFO slot stealing change introduced in 1.51.0
|
||||
[#8035]: https://github.com/tokio-rs/tokio/pull/8035
|
||||
[#8040]: https://github.com/tokio-rs/tokio/pull/8040
|
||||
|
||||
# 1.51.4 (July 16th, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: don't skip the driver when `before_park` schedules work ([#8222])
|
||||
|
||||
[#8222]: https://github.com/tokio-rs/tokio/pull/8222
|
||||
|
||||
# 1.51.3 (May 8th, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
* sync: fix underflow in mpsc channel `len()` ([#8062])
|
||||
* sync: notify receivers in mpsc `OwnedPermit::release()` method ([#8075])
|
||||
* sync: require that an `RwLock` has `max_readers != 0` ([#8076])
|
||||
* sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#8074])
|
||||
|
||||
# 1.51.2 (May 4th, 2026)
|
||||
|
||||
This release reverts the LIFO slot stealing change introduced in 1.51.0
|
||||
@@ -387,6 +529,20 @@ The MSRV is increased to 1.71.
|
||||
[#7672]: https://github.com/tokio-rs/tokio/pull/7672
|
||||
[#7675]: https://github.com/tokio-rs/tokio/pull/7675
|
||||
|
||||
# 1.47.5 (May 7th, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
* sync: fix underflow in mpsc channel `len()` ([#8062])
|
||||
* sync: notify receivers in mpsc `OwnedPermit::release()` method ([#8075])
|
||||
* sync: require that an `RwLock` has `max_readers != 0` ([#8076])
|
||||
* sync: return `Empty` from `try_recv()` when mpsc is closed with outstanding permits ([#8074])
|
||||
|
||||
[#8062]: https://github.com/tokio-rs/tokio/pull/8062
|
||||
[#8074]: https://github.com/tokio-rs/tokio/pull/8074
|
||||
[#8075]: https://github.com/tokio-rs/tokio/pull/8075
|
||||
[#8076]: https://github.com/tokio-rs/tokio/pull/8076
|
||||
|
||||
# 1.47.4 (April 2nd, 2026)
|
||||
|
||||
### Fixed
|
||||
|
||||
+3
-1
@@ -6,7 +6,7 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.x.y" git tag.
|
||||
version = "1.52.2"
|
||||
version = "1.53.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.71"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
@@ -88,6 +88,8 @@ time = []
|
||||
io-uring = ["dep:io-uring", "libc", "mio/os-poll", "mio/os-ext", "dep:slab"]
|
||||
# Unstable feature. Requires `--cfg tokio_unstable` to enable.
|
||||
taskdump = ["dep:backtrace"]
|
||||
# Unstable feature. Requires `--cfg tokio_unstable` to enable.
|
||||
schedule-latency = []
|
||||
|
||||
[dependencies]
|
||||
tokio-macros = { version = "~2.7.0", optional = true }
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ Make sure you enable the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.52.2", features = ["full"] }
|
||||
tokio = { version = "1.53.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ impl Registration {
|
||||
|
||||
/// Clears resource level readiness represented by the specified `ReadyEvent`
|
||||
async fn clear_readiness(&self, ready_event: ReadyEvent);
|
||||
}
|
||||
```
|
||||
|
||||
A new registration is created for a `T: mio::Evented` and a `interest`. This
|
||||
|
||||
@@ -26,6 +26,9 @@ use std::path::Path;
|
||||
/// when a directory is being created (after it is determined to not exist) are
|
||||
/// outlined by [`fs::create_dir`].
|
||||
///
|
||||
/// If the final component at `path` already exists and is a directory, this
|
||||
/// function will also return successfully without error.
|
||||
///
|
||||
/// Notable exception is made for situations where any of the directories
|
||||
/// specified in the `path` could not be created as it was being created concurrently.
|
||||
/// Such cases are considered to be successful. That is, calling `create_dir_all`
|
||||
|
||||
+21
-8
@@ -599,7 +599,7 @@ impl AsyncRead for File {
|
||||
cx: &mut Context<'_>,
|
||||
dst: &mut ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
|
||||
let me = self.get_mut();
|
||||
let inner = me.inner.get_mut();
|
||||
@@ -694,7 +694,7 @@ impl AsyncSeek for File {
|
||||
}
|
||||
|
||||
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let inner = self.inner.get_mut();
|
||||
|
||||
loop {
|
||||
@@ -730,7 +730,7 @@ impl AsyncWrite for File {
|
||||
cx: &mut Context<'_>,
|
||||
src: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let me = self.get_mut();
|
||||
let inner = me.inner.get_mut();
|
||||
|
||||
@@ -801,7 +801,7 @@ impl AsyncWrite for File {
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let me = self.get_mut();
|
||||
let inner = me.inner.get_mut();
|
||||
|
||||
@@ -872,13 +872,13 @@ impl AsyncWrite for File {
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let inner = self.inner.get_mut();
|
||||
inner.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
@@ -897,6 +897,13 @@ impl fmt::Debug for File {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl From<std::os::fd::OwnedFd> for File {
|
||||
fn from(fd: std::os::fd::OwnedFd) -> Self {
|
||||
Self::from_std(StdFile::from(fd))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for File {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
@@ -923,7 +930,13 @@ impl std::os::unix::io::FromRawFd for File {
|
||||
}
|
||||
|
||||
cfg_windows! {
|
||||
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle};
|
||||
use crate::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle, OwnedHandle};
|
||||
|
||||
impl From<OwnedHandle> for File {
|
||||
fn from(handle: OwnedHandle) -> Self {
|
||||
Self::from_std(StdFile::from(handle))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> RawHandle {
|
||||
@@ -1077,7 +1090,7 @@ impl Inner {
|
||||
}
|
||||
|
||||
fn poll_complete_inflight(&mut self, cx: &mut Context<'_>) -> Poll<()> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
match self.poll_flush(cx) {
|
||||
Poll::Ready(Err(e)) => {
|
||||
self.last_write_err = Some(e.kind());
|
||||
|
||||
@@ -38,6 +38,10 @@ mock! {
|
||||
pub fn try_clone(&self) -> io::Result<Self>;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl From<std::os::windows::io::OwnedHandle> for File {
|
||||
fn from(handle: std::os::windows::io::OwnedHandle) -> Self;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle;
|
||||
}
|
||||
@@ -106,6 +110,15 @@ impl From<MockFile> for OwnedFd {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
impl From<OwnedFd> for MockFile {
|
||||
#[inline]
|
||||
fn from(file: OwnedFd) -> MockFile {
|
||||
use std::os::fd::IntoRawFd;
|
||||
unsafe { MockFile::from_raw_fd(IntoRawFd::into_raw_fd(file)) }
|
||||
}
|
||||
}
|
||||
|
||||
tokio_thread_local! {
|
||||
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
|
||||
}
|
||||
|
||||
@@ -518,6 +518,10 @@ impl OpenOptions {
|
||||
/// [`Other`]: std::io::ErrorKind::Other
|
||||
/// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied
|
||||
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
|
||||
self.open_inner(path.as_ref()).await
|
||||
}
|
||||
|
||||
async fn open_inner(&self, path: &Path) -> io::Result<File> {
|
||||
match &self.inner {
|
||||
Kind::Std(opts) => Self::std_open(opts, path).await,
|
||||
#[cfg(all(
|
||||
@@ -535,7 +539,7 @@ impl OpenOptions {
|
||||
.check_and_init(io_uring::opcode::OpenAt::CODE)
|
||||
.await?
|
||||
{
|
||||
Op::open(path.as_ref(), opts)?.await
|
||||
Op::open(path, opts)?.await
|
||||
} else {
|
||||
let opts = opts.clone().into();
|
||||
Self::std_open(&opts, path).await
|
||||
@@ -544,12 +548,11 @@ impl OpenOptions {
|
||||
}
|
||||
}
|
||||
|
||||
async fn std_open(opts: &StdOpenOptions, path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
async fn std_open(opts: &StdOpenOptions, path: &Path) -> io::Result<File> {
|
||||
let path = path.to_owned();
|
||||
let opts = opts.clone();
|
||||
|
||||
let std = asyncify(move || opts.open(path)).await?;
|
||||
Ok(File::from_std(std))
|
||||
Ok(asyncify(move || opts.open(path)).await?.into())
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
||||
+17
-3
@@ -54,14 +54,23 @@ use std::{io, path::Path};
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let path = path.as_ref();
|
||||
|
||||
#[cfg(all(
|
||||
tokio_unstable,
|
||||
feature = "io-uring",
|
||||
feature = "rt",
|
||||
feature = "fs",
|
||||
target_os = "linux"
|
||||
// libc::statx is only supported on these platforms
|
||||
// FIXME: Add musl target env when our minimum supported
|
||||
// rust version is 1.93. To clarify, statx support is
|
||||
// introduced to musl in 1.25 as mentioned officially here:
|
||||
// https://musl.libc.org/releases.html.
|
||||
// However, rustup target_env building for *-linux-musl
|
||||
// uses 1.25 musl on all *-linux-musl platforms starting
|
||||
// in 1.93 stable rust version.
|
||||
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
|
||||
any(target_env = "gnu", target_os = "android")
|
||||
))]
|
||||
{
|
||||
use crate::fs::read_uring;
|
||||
@@ -72,9 +81,14 @@ pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
|
||||
.check_and_init(io_uring::opcode::Read::CODE)
|
||||
.await?
|
||||
{
|
||||
return read_uring(&path).await;
|
||||
return read_uring(path).await;
|
||||
}
|
||||
}
|
||||
|
||||
read_spawn_blocking(path).await
|
||||
}
|
||||
|
||||
async fn read_spawn_blocking(path: &Path) -> io::Result<Vec<u8>> {
|
||||
let path = path.to_owned();
|
||||
asyncify(move || std::fs::read(path)).await
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ impl ReadDir {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe.
|
||||
/// This method is cancel safe.
|
||||
pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
|
||||
use std::future::poll_fn;
|
||||
poll_fn(|cx| self.poll_next_entry(cx)).await
|
||||
|
||||
@@ -19,9 +19,26 @@ const MAX_READ_SIZE: usize = 64 * 1024 * 1024;
|
||||
pub(crate) async fn read_uring(path: &Path) -> io::Result<Vec<u8>> {
|
||||
let file = OpenOptions::new().read(true).open(path).await?;
|
||||
|
||||
// TODO: use io uring in the future to obtain metadata
|
||||
#[cfg(not(any(target_env = "gnu", target_os = "android")))]
|
||||
let size_hint: Option<usize> = file.metadata().await.map(|m| m.len() as usize).ok();
|
||||
|
||||
#[cfg(
|
||||
// libc::statx is only supported on these platforms
|
||||
// FIXME: Add musl target env when our minimum supported
|
||||
// rust version is 1.93. To clarify, statx support is
|
||||
// introduced to musl in 1.25 as mentioned officially here:
|
||||
// https://musl.libc.org/releases.html.
|
||||
// However, rustup target_env building for *-linux-musl
|
||||
// uses 1.25 musl on all *-linux-musl platforms starting
|
||||
// in 1.93 stable rust version.
|
||||
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
|
||||
any(target_env = "gnu", target_os = "android")
|
||||
)]
|
||||
let size_hint = Op::file_metadata(&file)?
|
||||
.await
|
||||
.map(|m| m.len() as usize)
|
||||
.ok();
|
||||
|
||||
let fd: OwnedFd = file
|
||||
.try_into_std()
|
||||
.expect("unexpected in-flight operation detected")
|
||||
|
||||
+31
-2
@@ -10,8 +10,37 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::fs::rename`].
|
||||
pub async fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> io::Result<()> {
|
||||
let from = from.as_ref().to_owned();
|
||||
let to = to.as_ref().to_owned();
|
||||
let from = from.as_ref();
|
||||
let to = to.as_ref();
|
||||
|
||||
#[cfg(all(
|
||||
tokio_unstable,
|
||||
feature = "io-uring",
|
||||
feature = "rt",
|
||||
feature = "fs",
|
||||
target_os = "linux",
|
||||
))]
|
||||
{
|
||||
use crate::io::uring::rename::Rename;
|
||||
use crate::runtime::driver::op::Op;
|
||||
|
||||
let handle = crate::runtime::Handle::current();
|
||||
let driver_handle = handle.inner.driver().io();
|
||||
|
||||
type RenameOp = Op<Rename>;
|
||||
|
||||
if driver_handle
|
||||
.check_and_init(io_uring::opcode::RenameAt::CODE)
|
||||
.await?
|
||||
{
|
||||
return RenameOp::rename(from, to)?.await;
|
||||
}
|
||||
}
|
||||
|
||||
rename_blocking(from, to).await
|
||||
}
|
||||
|
||||
async fn rename_blocking(from: &Path, to: &Path) -> io::Result<()> {
|
||||
let [from, to] = [from, to].map(Path::to_owned);
|
||||
asyncify(move || std::fs::rename(from, to)).await
|
||||
}
|
||||
|
||||
@@ -23,6 +23,67 @@ use std::path::Path;
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn try_exists(path: impl AsRef<Path>) -> io::Result<bool> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let path = path.as_ref();
|
||||
|
||||
#[cfg(all(
|
||||
tokio_unstable,
|
||||
feature = "io-uring",
|
||||
feature = "rt",
|
||||
feature = "fs",
|
||||
// libc::statx is only supported on these platforms
|
||||
// FIXME: Add musl target env when our minimum supported
|
||||
// rust version is 1.93. To clarify, statx support is
|
||||
// introduced to musl in 1.25 as mentioned officially here:
|
||||
// https://musl.libc.org/releases.html.
|
||||
// However, rustup target_env building for *-linux-musl
|
||||
// uses 1.25 musl on all *-linux-musl platforms starting
|
||||
// in 1.93 stable rust version.
|
||||
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
|
||||
any(target_env = "gnu", target_os = "android")
|
||||
))]
|
||||
{
|
||||
let handle = crate::runtime::Handle::current();
|
||||
let driver_handle = handle.inner.driver().io();
|
||||
if driver_handle
|
||||
.check_and_init(io_uring::opcode::Statx::CODE)
|
||||
.await?
|
||||
{
|
||||
return try_exists_uring(path).await;
|
||||
}
|
||||
}
|
||||
|
||||
try_exists_spawn_blocking(path).await
|
||||
}
|
||||
|
||||
cfg_io_uring! {
|
||||
#[inline]
|
||||
#[cfg(
|
||||
// libc::statx is only supported on these platforms
|
||||
// FIXME: Add musl target env when our minimum supported
|
||||
// rust version is 1.93. To clarify, statx support is
|
||||
// introduced to musl in 1.25 as mentioned officially here:
|
||||
// https://musl.libc.org/releases.html.
|
||||
// However, rustup target_env building for *-linux-musl
|
||||
// uses 1.25 musl on all *-linux-musl platforms starting
|
||||
// in 1.93 stable rust version.
|
||||
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
|
||||
any(target_env = "gnu", target_os = "android")
|
||||
)]
|
||||
async fn try_exists_uring(path: &Path) -> io::Result<bool> {
|
||||
use crate::runtime::driver::op::Op;
|
||||
|
||||
match Op::metadata(path)?.await {
|
||||
Ok(_) => Ok(true),
|
||||
Err(error) if error.kind() == io::ErrorKind::NotFound => Ok(false),
|
||||
Err(error) => Err(error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn try_exists_spawn_blocking(path: &Path) -> io::Result<bool> {
|
||||
let path = path.to_owned();
|
||||
// FIXME: When MSRV is 1.81, change this to
|
||||
// std::fs::exists() to be consistent with
|
||||
// all other tokio::fs operations
|
||||
asyncify(move || path.try_exists()).await
|
||||
}
|
||||
|
||||
@@ -97,20 +97,24 @@ where
|
||||
}
|
||||
|
||||
impl AsyncBufRead for &[u8] {
|
||||
#[inline]
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
Poll::Ready(Ok(*self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(mut self: Pin<&mut Self>, amt: usize) {
|
||||
*self = &self[amt..];
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + Unpin> AsyncBufRead for io::Cursor<T> {
|
||||
#[inline]
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
Poll::Ready(io::BufRead::fill_buf(self.get_mut()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
||||
io::BufRead::consume(self.get_mut(), amt);
|
||||
}
|
||||
|
||||
@@ -94,6 +94,7 @@ where
|
||||
}
|
||||
|
||||
impl AsyncRead for &[u8] {
|
||||
#[inline]
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
@@ -108,6 +109,7 @@ impl AsyncRead for &[u8] {
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + Unpin> AsyncRead for io::Cursor<T> {
|
||||
#[inline]
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
|
||||
@@ -87,9 +87,11 @@ where
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + Unpin> AsyncSeek for io::Cursor<T> {
|
||||
#[inline]
|
||||
fn start_seek(mut self: Pin<&mut Self>, pos: SeekFrom) -> io::Result<()> {
|
||||
io::Seek::seek(&mut *self, pos).map(drop)
|
||||
}
|
||||
#[inline]
|
||||
fn poll_complete(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
Poll::Ready(Ok(self.get_mut().position()))
|
||||
}
|
||||
|
||||
@@ -251,6 +251,7 @@ where
|
||||
}
|
||||
|
||||
impl AsyncWrite for Vec<u8> {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
@@ -260,6 +261,7 @@ impl AsyncWrite for Vec<u8> {
|
||||
Poll::Ready(Ok(buf.len()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -268,20 +270,24 @@ impl AsyncWrite for Vec<u8> {
|
||||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for io::Cursor<&mut [u8]> {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -290,6 +296,7 @@ impl AsyncWrite for io::Cursor<&mut [u8]> {
|
||||
Poll::Ready(io::Write::write(&mut *self, buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -298,20 +305,24 @@ impl AsyncWrite for io::Cursor<&mut [u8]> {
|
||||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(io::Write::flush(&mut *self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for io::Cursor<&mut Vec<u8>> {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -320,6 +331,7 @@ impl AsyncWrite for io::Cursor<&mut Vec<u8>> {
|
||||
Poll::Ready(io::Write::write(&mut *self, buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -328,20 +340,24 @@ impl AsyncWrite for io::Cursor<&mut Vec<u8>> {
|
||||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(io::Write::flush(&mut *self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for io::Cursor<Vec<u8>> {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -350,6 +366,7 @@ impl AsyncWrite for io::Cursor<Vec<u8>> {
|
||||
Poll::Ready(io::Write::write(&mut *self, buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -358,20 +375,24 @@ impl AsyncWrite for io::Cursor<Vec<u8>> {
|
||||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(io::Write::flush(&mut *self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for io::Cursor<Box<[u8]>> {
|
||||
#[inline]
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -380,6 +401,7 @@ impl AsyncWrite for io::Cursor<Box<[u8]>> {
|
||||
Poll::Ready(io::Write::write(&mut *self, buf))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
@@ -388,14 +410,17 @@ impl AsyncWrite for io::Cursor<Box<[u8]>> {
|
||||
Poll::Ready(io::Write::write_vectored(&mut *self, bufs))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(io::Write::flush(&mut *self))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.poll_flush(cx)
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ impl Buf {
|
||||
self.pos += n;
|
||||
|
||||
if self.pos == self.buf.len() {
|
||||
self.buf.truncate(0);
|
||||
self.buf.clear();
|
||||
self.pos = 0;
|
||||
}
|
||||
|
||||
@@ -315,7 +315,7 @@ cfg_fs! {
|
||||
pub(crate) fn discard_read(&mut self) -> i64 {
|
||||
let ret = -(self.bytes().len() as i64);
|
||||
self.pos = 0;
|
||||
self.buf.truncate(0);
|
||||
self.buf.clear();
|
||||
ret
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ use std::task::{ready, Context, Poll};
|
||||
|
||||
/// Like [`mio::event::Source`], but for POSIX AIO only.
|
||||
///
|
||||
/// Tokio's consumer must pass an implementor of this trait to create a
|
||||
/// [`Aio`] object. Implementors must implement at least one of [`AioSource::register`] and
|
||||
/// Tokio's consumer must pass an implementer of this trait to create a
|
||||
/// [`Aio`] object. Implementers must implement at least one of [`AioSource::register`] and
|
||||
/// [`AioSource::register_borrowed`].
|
||||
pub trait AioSource {
|
||||
/// Registers this AIO event source with Tokio's reactor.
|
||||
@@ -27,7 +27,7 @@ pub trait AioSource {
|
||||
/// source may end up notifying the wrong file.
|
||||
#[deprecated(since = "1.52.0", note = "use register_borrowed instead")]
|
||||
fn register(&mut self, _kq: RawFd, _token: usize) {
|
||||
// This default implementation exists so new AioSource implementors that implement the
|
||||
// This default implementation exists so new AioSource implementers that implement the
|
||||
// register_borrowed method can compile without the need to implement register.
|
||||
unimplemented!("Use AioSource::register_borrowed instead")
|
||||
}
|
||||
@@ -35,7 +35,7 @@ pub trait AioSource {
|
||||
/// Registers this AIO event source with Tokio's reactor.
|
||||
fn register_borrowed(&mut self, kq: BorrowedFd<'_>, token: usize) {
|
||||
// This default implementation serves to provide backwards compatibility with AioSource
|
||||
// implementors written before 1.52.0 that only implemented the unsafe `register` method.
|
||||
// implementers written before 1.52.0 that only implemented the unsafe `register` method.
|
||||
#[allow(deprecated)]
|
||||
self.register(kq.as_raw_fd(), token)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,45 @@ cfg_io_std! {
|
||||
/// to occur as a single write, so multiple threads writing data with
|
||||
/// [`write_all`] may result in interleaved output.
|
||||
///
|
||||
/// # Warning
|
||||
///
|
||||
/// Each call to [`stdout()`] creates a **new** handle with its own
|
||||
/// internal state. Writes through different handles are not
|
||||
/// coordinated, so creating a new handle in a loop can cause output
|
||||
/// to appear out of order:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use tokio::io::{self, AsyncWriteExt};
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
/// // WRONG: creates a new handle each iteration
|
||||
/// for i in 0..10 {
|
||||
/// let mut out = io::stdout();
|
||||
/// out.write_all(b"data").await?;
|
||||
/// out.write_all(b"\n").await?;
|
||||
/// // out is dropped here; its last write may still be
|
||||
/// // running when the next iteration starts
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// To preserve order, create one handle outside the loop and
|
||||
/// reuse it:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use tokio::io::{self, AsyncWriteExt};
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
/// let mut out = io::stdout();
|
||||
/// for i in 0..10 {
|
||||
/// out.write_all(b"data").await?;
|
||||
/// out.write_all(b"\n").await?;
|
||||
/// }
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Created by the [`stdout`] function.
|
||||
///
|
||||
/// [`stdout`]: stdout()
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
pub(crate) mod open;
|
||||
pub(crate) mod read;
|
||||
pub(crate) mod rename;
|
||||
pub(crate) mod statx;
|
||||
pub(crate) mod utils;
|
||||
pub(crate) mod write;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
use super::utils::cstr;
|
||||
|
||||
use crate::runtime::driver::op::{CancelData, Cancellable, Completable, CqeResult, Op};
|
||||
|
||||
use io_uring::{opcode, types};
|
||||
use std::ffi::CString;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Rename {
|
||||
/// This field will be read by the kernel during the operation, so we
|
||||
/// need to ensure it is valid for the entire duration of the operation.
|
||||
_from: CString,
|
||||
_to: CString,
|
||||
}
|
||||
|
||||
impl Completable for Rename {
|
||||
type Output = io::Result<()>;
|
||||
|
||||
fn complete(self, cqe: CqeResult) -> Self::Output {
|
||||
cqe.result.map(drop)
|
||||
}
|
||||
|
||||
fn complete_with_error(self, error: io::Error) -> Self::Output {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Cancellable for Rename {
|
||||
fn cancel(self) -> CancelData {
|
||||
CancelData::Rename(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Op<Rename> {
|
||||
pub(crate) fn rename(from: &Path, to: &Path) -> io::Result<Op<Rename>> {
|
||||
let from = cstr(from)?;
|
||||
let to = cstr(to)?;
|
||||
|
||||
let rename_op = opcode::RenameAt::new(
|
||||
types::Fd(libc::AT_FDCWD),
|
||||
from.as_ptr(),
|
||||
types::Fd(libc::AT_FDCWD),
|
||||
to.as_ptr(),
|
||||
)
|
||||
.build();
|
||||
|
||||
// SAFETY: Parameters are valid for the entire duration of the operation
|
||||
Ok(unsafe {
|
||||
Op::new(
|
||||
rename_op,
|
||||
Rename {
|
||||
_from: from,
|
||||
_to: to,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
#![cfg(all(
|
||||
tokio_unstable,
|
||||
feature = "io-uring",
|
||||
feature = "rt",
|
||||
feature = "fs",
|
||||
// libc::statx is only supported on these platforms
|
||||
// FIXME: Add musl target env when our minimum supported
|
||||
// rust version is 1.93. To clarify, statx support is
|
||||
// introduced to musl in 1.25 as mentioned officially here:
|
||||
// https://musl.libc.org/releases.html.
|
||||
// However, rustup target_env building for *-linux-musl
|
||||
// uses 1.25 musl on all *-linux-musl platforms starting
|
||||
// in 1.93 stable rust version.
|
||||
// https://blog.rust-lang.org/2025/12/05/Updating-musl-1.2.5/
|
||||
any(target_env = "gnu", target_os = "android")
|
||||
))]
|
||||
|
||||
use crate::fs::File;
|
||||
use crate::io::uring::utils::cstr;
|
||||
use crate::runtime::driver::op::{CancelData, Cancellable, Completable, CqeResult, Op};
|
||||
use io_uring::{opcode, types};
|
||||
use libc::statx;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt::{Debug, Formatter};
|
||||
use std::io;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::fd::AsRawFd;
|
||||
use std::path::Path;
|
||||
|
||||
pub(crate) struct Metadata(statx);
|
||||
|
||||
impl Metadata {
|
||||
/// Returns the size of the file, in bytes, this metadata is for.
|
||||
pub(crate) fn len(&self) -> u64 {
|
||||
self.0.stx_size
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Metadata {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
let mut debug = f.debug_struct("Metadata");
|
||||
debug.field("len", &self.len());
|
||||
debug.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Statx {
|
||||
/// This field will be read by the kernel during the operation, so we
|
||||
/// need to ensure it is valid for the entire duration of the operation.
|
||||
_path: CString,
|
||||
buffer: Box<MaybeUninit<statx>>,
|
||||
}
|
||||
|
||||
impl Completable for Statx {
|
||||
type Output = io::Result<Metadata>;
|
||||
|
||||
fn complete(self, cqe: CqeResult) -> Self::Output {
|
||||
// SAFETY: On success, we always receive 0, which should guarantee
|
||||
// that the information about a file is stored inside the
|
||||
// statx buffer. On failure, we'll receive an Error value.
|
||||
// Refer to man page description and return value:
|
||||
// https://man7.org/linux/man-pages/man2/statx.2.html
|
||||
cqe.result
|
||||
.map(|_| Metadata(unsafe { *self.buffer.as_ptr() }))
|
||||
}
|
||||
|
||||
fn complete_with_error(self, error: io::Error) -> Self::Output {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
|
||||
impl Cancellable for Statx {
|
||||
fn cancel(self) -> CancelData {
|
||||
CancelData::Statx(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Op<Statx> {
|
||||
/// Submit a request to retrieve a file's status.
|
||||
#[inline]
|
||||
fn statx(path: &Path, flags: i32) -> io::Result<Op<Statx>> {
|
||||
let path = cstr(path)?;
|
||||
let mut buffer = Box::new(MaybeUninit::<statx>::uninit());
|
||||
|
||||
let statx_op = opcode::Statx::new(
|
||||
types::Fd(libc::AT_FDCWD),
|
||||
path.as_ptr(),
|
||||
buffer.as_mut_ptr().cast(),
|
||||
)
|
||||
.flags(flags)
|
||||
.mask(libc::STATX_BASIC_STATS | libc::STATX_BTIME)
|
||||
.build();
|
||||
|
||||
// SAFETY: Parameters are valid for the entire duration of the operation
|
||||
Ok(unsafe {
|
||||
Op::new(
|
||||
statx_op,
|
||||
Statx {
|
||||
_path: path,
|
||||
buffer,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieves the metadata information of the given path, following symlinks
|
||||
/// if the path provided points to a symlink location.
|
||||
#[inline]
|
||||
pub(crate) fn metadata(path: &Path) -> io::Result<Op<Statx>> {
|
||||
Op::statx(path, libc::AT_STATX_SYNC_AS_STAT)
|
||||
}
|
||||
|
||||
/// Retrieves the metadata information of the given file
|
||||
pub(crate) fn file_metadata(file: &File) -> io::Result<Op<Statx>> {
|
||||
let mut buffer = Box::new(MaybeUninit::<statx>::uninit());
|
||||
let empty_path: &'static CStr = c"";
|
||||
|
||||
// io-uring was introduced in linux 5.1
|
||||
// pass in an empty path instead of null to target the file descriptor
|
||||
// status as specified by man:
|
||||
// https://man7.org/linux/man-pages/man2/statx.2.html
|
||||
let statx_op = opcode::Statx::new(
|
||||
types::Fd(file.as_raw_fd()),
|
||||
// it should be fine to pass in `empty_path` whose lifetime
|
||||
// does not exceed the `file_metadata()` function as a ptr here
|
||||
// because we want to stat the dirfd not this pathname
|
||||
empty_path.as_ptr(),
|
||||
buffer.as_mut_ptr().cast(),
|
||||
)
|
||||
.flags(libc::AT_STATX_SYNC_AS_STAT | libc::AT_EMPTY_PATH)
|
||||
.mask(libc::STATX_BASIC_STATS | libc::STATX_BTIME)
|
||||
.build();
|
||||
|
||||
// SAFETY: Parameters are valid for the entire duration of the operation
|
||||
Ok(unsafe {
|
||||
Op::new(
|
||||
statx_op,
|
||||
Statx {
|
||||
_path: empty_path.into(),
|
||||
buffer,
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: Once `Metadata::from_statx` is stabilized, we can use use this function
|
||||
// to enable io-uring support on `tokio::fs::symlink_metadata`.
|
||||
// See this PR for more detail: https://github.com/tokio-rs/tokio/pull/8080
|
||||
// See `Metadata::from_statx` tracking issue to see progress:
|
||||
// https://github.com/rust-lang/rust/issues/156268
|
||||
/// Retrieves the metadata information of the given path without following symlinks.
|
||||
#[inline]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn symlink_metadata(path: &Path) -> io::Result<Op<Statx>> {
|
||||
Op::statx(
|
||||
path,
|
||||
libc::AT_STATX_SYNC_AS_STAT | libc::AT_SYMLINK_NOFOLLOW,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// If the method is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// If used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then some data may have been partially read. Any
|
||||
/// partially read bytes are appended to `buf`, and the method can be
|
||||
/// called again to continue reading until `byte`.
|
||||
@@ -129,9 +129,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may have been partially
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may have been partially
|
||||
/// read, and this data is lost. There are no guarantees regarding the
|
||||
/// contents of `buf` when the call is cancelled. The current
|
||||
/// implementation replaces `buf` with the empty string, but this may
|
||||
@@ -269,8 +269,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// [`consume`]: crate::io::AsyncBufReadExt::consume
|
||||
|
||||
@@ -147,8 +147,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If you use it as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -215,8 +215,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If you use it as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -291,9 +291,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may already have been
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may already have been
|
||||
/// read into `buf`.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -347,8 +347,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If this method is used as an event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If this method is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, it is guaranteed that no data were read.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -391,8 +391,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If this method is used as an event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If this method is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, it is guaranteed that no data were read.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -436,9 +436,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -480,9 +480,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -524,9 +524,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -568,9 +568,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -611,9 +611,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -656,9 +656,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -699,9 +699,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -745,9 +745,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -791,9 +791,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -834,9 +834,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -879,9 +879,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -923,9 +923,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -967,9 +967,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1011,9 +1011,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1054,9 +1054,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1099,9 +1099,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1142,9 +1142,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1188,9 +1188,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1234,9 +1234,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1277,9 +1277,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may be lost.
|
||||
/// This method is not cancel safe. If the method is used as a
|
||||
/// branch in [`tokio::select!`](crate::select) and another
|
||||
/// branch completes first, then some data may be lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -103,9 +103,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// This method is cancel safe. If it is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch completes
|
||||
/// first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -146,9 +146,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// This method is cancel safe. If it is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch completes
|
||||
/// first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -223,9 +223,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// This method is cancel safe. If it is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch completes
|
||||
/// first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -294,8 +294,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// If `write_all_buf` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// If `write_all_buf` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then the data in the provided buffer may have been
|
||||
/// partially written. However, it is guaranteed that the provided
|
||||
/// buffer has been [advanced] by the amount of bytes that have been
|
||||
@@ -354,9 +354,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If it is used as the event
|
||||
/// in a [`tokio::select!`](crate::select) statement and some other
|
||||
/// branch completes first, then the provided buffer may have been
|
||||
/// This method is not cancel safe. If it is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch completes
|
||||
/// first, then the provided buffer may have been
|
||||
/// partially written, but future calls to `write_all` will start over
|
||||
/// from the beginning of the buffer.
|
||||
///
|
||||
@@ -1253,9 +1253,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// This method is cancel safe.
|
||||
///
|
||||
/// If `flush` is used as the event in a [`tokio::select!`](crate::select)
|
||||
/// statement and some other branch completes first, then the data in the
|
||||
/// buffered data in this `AsyncWrite` may have been partially flushed.
|
||||
/// If `flush` is used as a branch in [`tokio::select!`](crate::select)
|
||||
/// and another branch completes first, then the buffered data in this
|
||||
/// `AsyncWrite` may have been partially flushed.
|
||||
/// However, it is guaranteed that the buffer is advanced by the amount of
|
||||
/// bytes that have been partially flushed.
|
||||
///
|
||||
|
||||
@@ -94,7 +94,9 @@ where
|
||||
if !*me.done_first {
|
||||
let rem = buf.remaining();
|
||||
ready!(me.first.poll_read(cx, buf))?;
|
||||
if buf.remaining() == rem {
|
||||
// A read that fills nothing only indicates EOF if the buffer
|
||||
// had capacity to begin with.
|
||||
if buf.remaining() == rem && rem != 0 {
|
||||
*me.done_first = true;
|
||||
} else {
|
||||
return Poll::Ready(Ok(()));
|
||||
|
||||
@@ -82,7 +82,7 @@ impl CopyBuffer {
|
||||
R: AsyncRead + ?Sized,
|
||||
W: AsyncWrite + ?Sized,
|
||||
{
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
#[cfg(any(
|
||||
feature = "fs",
|
||||
feature = "io-std",
|
||||
|
||||
@@ -71,7 +71,7 @@ impl AsyncRead for Empty {
|
||||
cx: &mut Context<'_>,
|
||||
_: &mut ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
@@ -80,7 +80,7 @@ impl AsyncRead for Empty {
|
||||
impl AsyncBufRead for Empty {
|
||||
#[inline]
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(&[]))
|
||||
}
|
||||
@@ -96,21 +96,21 @@ impl AsyncWrite for Empty {
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(buf.len()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
@@ -126,7 +126,7 @@ impl AsyncWrite for Empty {
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
let num_bytes = bufs.iter().map(|b| b.len()).sum();
|
||||
Poll::Ready(Ok(num_bytes))
|
||||
@@ -141,7 +141,7 @@ impl AsyncSeek for Empty {
|
||||
|
||||
#[inline]
|
||||
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(0))
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ where
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe.
|
||||
/// This method is cancel safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
||||
@@ -185,7 +185,7 @@ impl Drop for DuplexStream {
|
||||
/// Creates unidirectional buffer that acts like in memory pipe.
|
||||
///
|
||||
/// The `max_buf_size` argument is the maximum amount of bytes that can be
|
||||
/// written to a buffer before the it returns `Poll::Pending`.
|
||||
/// written to a buffer before it returns `Poll::Pending`.
|
||||
///
|
||||
/// # Unify reader and writer
|
||||
///
|
||||
@@ -217,7 +217,7 @@ impl SimplexStream {
|
||||
/// version with separate reader and writer you can use [`simplex`] function.
|
||||
///
|
||||
/// The `max_buf_size` argument is the maximum amount of bytes that can be
|
||||
/// written to a buffer before the it returns `Poll::Pending`.
|
||||
/// written to a buffer before it returns `Poll::Pending`.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
|
||||
pub fn new_unsplit(max_buf_size: usize) -> SimplexStream {
|
||||
SimplexStream {
|
||||
@@ -331,7 +331,7 @@ impl AsyncRead for SimplexStream {
|
||||
cx: &mut task::Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let coop = ready!(crate::task::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_read_internal(cx, buf);
|
||||
@@ -348,7 +348,7 @@ impl AsyncRead for SimplexStream {
|
||||
cx: &mut task::Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
self.poll_read_internal(cx, buf)
|
||||
}
|
||||
}
|
||||
@@ -361,7 +361,7 @@ impl AsyncWrite for SimplexStream {
|
||||
cx: &mut task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let coop = ready!(crate::task::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_write_internal(cx, buf);
|
||||
@@ -378,7 +378,7 @@ impl AsyncWrite for SimplexStream {
|
||||
cx: &mut task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
self.poll_write_internal(cx, buf)
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,7 @@ impl AsyncWrite for SimplexStream {
|
||||
cx: &mut task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
let coop = ready!(crate::task::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_write_vectored_internal(cx, bufs);
|
||||
@@ -406,7 +406,7 @@ impl AsyncWrite for SimplexStream {
|
||||
cx: &mut task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
self.poll_write_vectored_internal(cx, bufs)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ cfg_io_util! {
|
||||
|
||||
|
||||
// used by `BufReader` and `BufWriter`
|
||||
// https://github.com/rust-lang/rust/blob/master/library/std/src/sys_common/io.rs#L1
|
||||
// https://github.com/rust-lang/rust/blob/main/library/std/src/sys/io/mod.rs#L72
|
||||
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
|
||||
|
||||
cfg_coop! {
|
||||
|
||||
@@ -56,7 +56,7 @@ impl AsyncRead for Repeat {
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<io::Result<()>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
buf.put_bytes(self.byte, buf.remaining());
|
||||
Poll::Ready(Ok(()))
|
||||
|
||||
@@ -57,21 +57,21 @@ impl AsyncWrite for Sink {
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(buf.len()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
ready!(crate::trace::trace_leaf(cx));
|
||||
ready!(crate::trace::trace_leaf());
|
||||
ready!(poll_proceed_and_make_progress(cx));
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::mem::MaybeUninit;
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The implementor must guarantee that the vector returned by the
|
||||
/// The implementer must guarantee that the vector returned by the
|
||||
/// `as_mut` and `as_mut` methods do not change from one call to
|
||||
/// another.
|
||||
pub(crate) unsafe trait VecU8: AsRef<Vec<u8>> + AsMut<Vec<u8>> {}
|
||||
|
||||
+19
-4
@@ -354,6 +354,7 @@
|
||||
//! Some feature flags are only available when specifying the `tokio_unstable` flag:
|
||||
//!
|
||||
//! - `tracing`: Enables tracing events.
|
||||
//! - `schedule-latency`: Allows measurement of task scheduling latencies.
|
||||
//! - `io-uring`: Enables `io-uring` (Linux only).
|
||||
//! - `taskdump`: Enables `taskdump` (Linux only).
|
||||
//!
|
||||
@@ -488,14 +489,28 @@ compile_error!("The `taskdump` feature requires `--cfg tokio_unstable`.");
|
||||
not(doc),
|
||||
not(all(
|
||||
target_os = "linux",
|
||||
any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64")
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64",
|
||||
target_arch = "s390x"
|
||||
)
|
||||
))
|
||||
))]
|
||||
compile_error!(
|
||||
"The `taskdump` feature is only currently supported on \
|
||||
linux, on `aarch64`, `x86` and `x86_64`."
|
||||
linux, on `aarch64`, `x86`, `x86_64` and `s390x`."
|
||||
);
|
||||
|
||||
#[cfg(all(not(tokio_unstable), feature = "schedule-latency"))]
|
||||
compile_error!("The `schedule-latency` feature requires `--cfg tokio_unstable`.");
|
||||
|
||||
#[cfg(all(
|
||||
feature = "schedule-latency",
|
||||
not(all(target_pointer_width = "64", target_has_atomic = "64"))
|
||||
))]
|
||||
compile_error!("The `schedule-latency` feature is only currently supported on 64-bit targets.");
|
||||
|
||||
// Includes re-exports used by macros.
|
||||
//
|
||||
// This module is not intended to be part of the public API. In general, any
|
||||
@@ -574,14 +589,14 @@ mod trace {
|
||||
cfg_not_taskdump! {
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn trace_leaf(_: &mut std::task::Context<'_>) -> std::task::Poll<()> {
|
||||
pub(crate) fn trace_leaf() -> std::task::Poll<()> {
|
||||
std::task::Poll::Ready(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "sync"), allow(dead_code))]
|
||||
pub(crate) async fn async_trace_leaf() {
|
||||
std::future::poll_fn(trace_leaf).await
|
||||
std::future::poll_fn(|_cx| trace_leaf()).await
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+25
-3
@@ -517,7 +517,8 @@ macro_rules! cfg_taskdump {
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64"
|
||||
target_arch = "x86_64",
|
||||
target_arch = "s390x"
|
||||
)
|
||||
))]
|
||||
#[cfg_attr(
|
||||
@@ -530,7 +531,8 @@ macro_rules! cfg_taskdump {
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64"
|
||||
target_arch = "x86_64",
|
||||
target_arch = "s390x"
|
||||
)
|
||||
)))
|
||||
)]
|
||||
@@ -550,7 +552,8 @@ macro_rules! cfg_not_taskdump {
|
||||
any(
|
||||
target_arch = "aarch64",
|
||||
target_arch = "x86",
|
||||
target_arch = "x86_64"
|
||||
target_arch = "x86_64",
|
||||
target_arch = "s390x"
|
||||
)
|
||||
)))]
|
||||
$item
|
||||
@@ -746,3 +749,22 @@ macro_rules! cfg_io_uring {
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cfg_schedule_latency {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "schedule-latency")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "schedule-latency")))]
|
||||
$item
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_schedule_latency {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(feature = "schedule-latency"))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,6 +95,11 @@ macro_rules! doc {
|
||||
/// describes whether they are cancel safe. The lists in this section are not
|
||||
/// exhaustive.
|
||||
///
|
||||
/// Cancellation safety describes what happens when a future is dropped
|
||||
/// before it completes. Whether something is cancellation safe depends on
|
||||
/// the behavior of the future passed to `select!`, which may come from an
|
||||
/// async method, an async expression, or another future-producing operation.
|
||||
///
|
||||
/// The following methods are cancellation safe:
|
||||
///
|
||||
/// * [`tokio::sync::mpsc::Receiver::recv`](crate::sync::mpsc::Receiver::recv)
|
||||
|
||||
@@ -69,7 +69,7 @@ impl TcpListener {
|
||||
/// 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.
|
||||
/// The address type can be any implementer of the [`ToSocketAddrs`] trait.
|
||||
/// If `addr` yields multiple addresses, bind will be attempted with each of
|
||||
/// the addresses until one succeeds and returns the listener. If none of
|
||||
/// the addresses succeed in creating a listener, the error returned from
|
||||
@@ -91,7 +91,6 @@ impl TcpListener {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// let listener = TcpListener::bind("127.0.0.1:2345").await?;
|
||||
///
|
||||
/// // use the listener
|
||||
@@ -134,8 +133,8 @@ impl TcpListener {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If the method is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If the method is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that no new connections were
|
||||
/// accepted by this method.
|
||||
///
|
||||
|
||||
@@ -286,6 +286,7 @@ impl TcpSocket {
|
||||
not(target_os = "solaris"),
|
||||
not(target_os = "illumos"),
|
||||
not(target_os = "cygwin"),
|
||||
not(target_os = "nuttx"),
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
@@ -294,6 +295,7 @@ impl TcpSocket {
|
||||
not(target_os = "solaris"),
|
||||
not(target_os = "illumos"),
|
||||
not(target_os = "cygwin"),
|
||||
not(target_os = "nuttx"),
|
||||
)))
|
||||
)]
|
||||
pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> {
|
||||
@@ -331,6 +333,7 @@ impl TcpSocket {
|
||||
not(target_os = "solaris"),
|
||||
not(target_os = "illumos"),
|
||||
not(target_os = "cygwin"),
|
||||
not(target_os = "nuttx"),
|
||||
))]
|
||||
#[cfg_attr(
|
||||
docsrs,
|
||||
@@ -339,6 +342,7 @@ impl TcpSocket {
|
||||
not(target_os = "solaris"),
|
||||
not(target_os = "illumos"),
|
||||
not(target_os = "cygwin"),
|
||||
not(target_os = "nuttx"),
|
||||
)))
|
||||
)]
|
||||
pub fn reuseport(&self) -> io::Result<bool> {
|
||||
@@ -945,7 +949,6 @@ impl TcpSocket {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> std::io::Result<()> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// let socket2_socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
|
||||
/// socket2_socket.set_nonblocking(true)?;
|
||||
///
|
||||
|
||||
@@ -231,7 +231,6 @@ impl TcpStream {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// let mut data = [0u8; 12];
|
||||
/// # if false {
|
||||
/// let listener = TcpListener::bind("127.0.0.1:34254").await?;
|
||||
@@ -1075,8 +1074,8 @@ impl TcpStream {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If the method is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If the method is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that no peek was performed, and
|
||||
/// that `buf` has not been modified.
|
||||
///
|
||||
|
||||
+12
-12
@@ -140,7 +140,7 @@ impl UdpSocket {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No UDP sockets in miri.
|
||||
/// let sock = UdpSocket::bind("0.0.0.0:8080").await?;
|
||||
/// // use `sock`
|
||||
/// # let _ = sock;
|
||||
@@ -308,7 +308,7 @@ impl UdpSocket {
|
||||
/// # use std::{io, net::SocketAddr};
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> io::Result<()> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No UDP sockets in miri.
|
||||
/// let addr = "0.0.0.0:8080".parse::<SocketAddr>().unwrap();
|
||||
/// let peer = "127.0.0.1:11100".parse::<SocketAddr>().unwrap();
|
||||
/// let sock = UdpSocket::bind(addr).await?;
|
||||
@@ -545,8 +545,8 @@ impl UdpSocket {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `send` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `send` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that the message was not sent.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -762,8 +762,8 @@ impl UdpSocket {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `recv` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `recv` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, it is guaranteed that no messages were received on this
|
||||
/// socket.
|
||||
///
|
||||
@@ -1149,7 +1149,7 @@ impl UdpSocket {
|
||||
/// Sends data on the socket to the given address. On success, returns the
|
||||
/// number of bytes written.
|
||||
///
|
||||
/// Address type can be any implementor of [`ToSocketAddrs`] trait. See its
|
||||
/// Address type can be any implementer of [`ToSocketAddrs`] trait. See its
|
||||
/// documentation for concrete examples.
|
||||
///
|
||||
/// It is possible for `addr` to yield multiple addresses, but `send_to`
|
||||
@@ -1162,8 +1162,8 @@ impl UdpSocket {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `send_to` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `send_to` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that the message was not sent.
|
||||
///
|
||||
/// # Example
|
||||
@@ -1291,8 +1291,8 @@ impl UdpSocket {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `recv_from` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `recv_from` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, it is guaranteed that no messages were received on this
|
||||
/// socket.
|
||||
///
|
||||
@@ -2288,7 +2288,7 @@ impl UdpSocket {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No UDP sockets in miri.
|
||||
/// // Create a socket
|
||||
/// let socket = UdpSocket::bind("0.0.0.0:8080").await?;
|
||||
///
|
||||
|
||||
@@ -39,7 +39,7 @@ cfg_net_unix! {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -377,7 +377,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -469,7 +469,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use std::os::unix::net::UnixDatagram as StdUDS;
|
||||
/// use tempfile::tempdir;
|
||||
@@ -536,7 +536,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -576,7 +576,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -612,8 +612,8 @@ impl UnixDatagram {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `send` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `send` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that the message was not sent.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -742,8 +742,8 @@ impl UnixDatagram {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `recv` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `recv` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, it is guaranteed that no messages were received on this
|
||||
/// socket.
|
||||
///
|
||||
@@ -903,7 +903,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -1062,8 +1062,8 @@ impl UnixDatagram {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `send_to` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `send_to` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, then it is guaranteed that the message was not sent.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -1071,7 +1071,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -1112,8 +1112,8 @@ impl UnixDatagram {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `recv_from` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// This method is cancel safe. If `recv_from` is used as a branch in
|
||||
/// [`tokio::select!`](crate::select) and another branch
|
||||
/// completes first, it is guaranteed that no messages were received on this
|
||||
/// socket.
|
||||
///
|
||||
@@ -1122,7 +1122,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -1439,7 +1439,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -1462,7 +1462,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
///
|
||||
/// // Create an unbound socket
|
||||
@@ -1487,7 +1487,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
/// use tempfile::tempdir;
|
||||
///
|
||||
@@ -1535,7 +1535,7 @@ impl UnixDatagram {
|
||||
/// # use std::error::Error;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// # if cfg!(miri) { return Ok(()); } // No `socket` in miri.
|
||||
/// # if cfg!(miri) { return Ok(()); } // No Unix domain sockets in miri.
|
||||
/// use tokio::net::UnixDatagram;
|
||||
///
|
||||
/// // Create an unbound socket
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user