mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
84
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4077a5c27 | ||
|
|
5f022559f3 | ||
|
|
3735958023 | ||
|
|
586934e5d9 | ||
|
|
551da8f577 | ||
|
|
884a9a4b18 | ||
|
|
e0be45e49b | ||
|
|
26f0938bf3 | ||
|
|
4b78ed4d68 | ||
|
|
79d7a625d0 | ||
|
|
ccf855ec24 | ||
|
|
579f61106d | ||
|
|
f45320a9c0 | ||
|
|
1f8105588c | ||
|
|
94ee305741 | ||
|
|
669bc4476e | ||
|
|
1265d0c5dc | ||
|
|
09b770c5db | ||
|
|
d1a400912e | ||
|
|
aa03622cf3 | ||
|
|
ac89d8926d | ||
|
|
e184205421 | ||
|
|
44a1aad8df | ||
|
|
75c07770bf | ||
|
|
268ed5e73e | ||
|
|
0c68b89452 | ||
|
|
827694a9e3 | ||
|
|
d15e5fad16 | ||
|
|
9cb495cdb8 | ||
|
|
e7d3e0c93c | ||
|
|
e04b5be1f5 | ||
|
|
2734fa9a85 | ||
|
|
83aeae8610 | ||
|
|
cf550b2183 | ||
|
|
e1c8b5a159 | ||
|
|
eb7a615c96 | ||
|
|
44e9013f64 | ||
|
|
03969cdae7 | ||
|
|
095012b03b | ||
|
|
b5c1fb4012 | ||
|
|
d4848a9e2e | ||
|
|
bb6a292d0a | ||
|
|
eb2106f87e | ||
|
|
fadd0190da | ||
|
|
f0cb360d70 | ||
|
|
d047584e86 | ||
|
|
c5d37204dc | ||
|
|
7f26ad85c2 | ||
|
|
d39c9ed9dc | ||
|
|
44cfe10ee5 | ||
|
|
1073f6e8be | ||
|
|
dee3236c97 | ||
|
|
d9ca1517c6 | ||
|
|
6c1a1d9b07 | ||
|
|
d32acd97eb | ||
|
|
7875f26586 | ||
|
|
7ce8f05cff | ||
|
|
ea19606bc4 | ||
|
|
b9b59e4f15 | ||
|
|
cdc46a9ded | ||
|
|
b9834f6d8b | ||
|
|
1ed89aa5cf | ||
|
|
d9b2dc81ca | ||
|
|
8e54145c8b | ||
|
|
279e8b001a | ||
|
|
e9f6faee67 | ||
|
|
f1b89675eb | ||
|
|
ddd33f2b05 | ||
|
|
8e92f05795 | ||
|
|
957ed3eac0 | ||
|
|
ab34805849 | ||
|
|
8b298d9ed4 | ||
|
|
57563e218b | ||
|
|
33f0a1fd2e | ||
|
|
4c9b469562 | ||
|
|
7af0f32751 | ||
|
|
3fe1662e4b | ||
|
|
a73428252b | ||
|
|
b99eedc2ea | ||
|
|
6ebd0575e4 | ||
|
|
7e51b44a20 | ||
|
|
bd1e4aaea6 | ||
|
|
fd22164f5d | ||
|
|
98e78a6f7b |
@@ -0,0 +1,7 @@
|
||||
# See https://github.com/rustsec/rustsec/blob/59e1d2ad0b9cbc6892c26de233d4925074b4b97b/cargo-audit/audit.toml.example for example.
|
||||
|
||||
[advisories]
|
||||
ignore = [
|
||||
# https://github.com/tokio-rs/tokio/issues/4177
|
||||
"RUSTSEC-2020-0159",
|
||||
]
|
||||
+38
-11
@@ -1,19 +1,51 @@
|
||||
freebsd_instance:
|
||||
image: freebsd-12-2-release-amd64
|
||||
env:
|
||||
RUST_STABLE: 1.56.0
|
||||
RUST_NIGHTLY: nightly-2021-10-25
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
|
||||
# same VM. The binary will be built in 32-bit mode, but will execute on a
|
||||
# 64-bit kernel and in a 64-bit environment. Our tests don't execute any of
|
||||
# the system's binaries, so the environment shouldn't matter.
|
||||
task:
|
||||
name: FreeBSD
|
||||
env:
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
RUSTFLAGS: -Dwarnings
|
||||
name: FreeBSD 64-bit
|
||||
setup_script:
|
||||
- pkg install -y bash curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain stable
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --all-features
|
||||
|
||||
task:
|
||||
name: FreeBSD docs
|
||||
env:
|
||||
RUSTFLAGS: --cfg docsrs
|
||||
RUSTDOCFLAGS: --cfg docsrs -Dwarnings
|
||||
setup_script:
|
||||
- pkg install -y bash curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_NIGHTLY
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
echo "~~~~ rustc --version ~~~~"
|
||||
rustc --version
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo doc --lib --no-deps --all-features --document-private-items
|
||||
|
||||
task:
|
||||
name: FreeBSD 32-bit
|
||||
setup_script:
|
||||
- pkg install -y bash curl
|
||||
- curl https://sh.rustup.rs -sSf --output rustup.sh
|
||||
- sh rustup.sh -y --profile minimal --default-toolchain $RUST_STABLE
|
||||
- . $HOME/.cargo/env
|
||||
- rustup target add i686-unknown-freebsd
|
||||
- |
|
||||
@@ -21,9 +53,4 @@ task:
|
||||
rustc --version
|
||||
test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- cargo test --all --all-features
|
||||
- cargo doc --all --no-deps
|
||||
i686_test_script:
|
||||
- . $HOME/.cargo/env
|
||||
- |
|
||||
cargo test --all --all-features --target i686-unknown-freebsd
|
||||
- cargo test --all --all-features --target i686-unknown-freebsd
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
R-loom:
|
||||
- ./tokio/src/sync/*
|
||||
- ./tokio/src/sync/**/*
|
||||
- ./tokio-util/src/sync/*
|
||||
- ./tokio-util/src/sync/**/*
|
||||
- ./tokio/src/runtime/*
|
||||
- ./tokio/src/runtime/**/*
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
name: Benchmark
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
benchmark:
|
||||
name: Benchmark
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
bench:
|
||||
- rt_multi_threaded
|
||||
- sync_mpsc
|
||||
- sync_rwlock
|
||||
- sync_semaphore
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
# Run benchmark with `go test -bench` and stores the output to a file
|
||||
- name: Run benchmark
|
||||
run: cargo bench --bench ${{ matrix.bench }} | tee ../output.txt
|
||||
working-directory: benches
|
||||
|
||||
# Download previous benchmark result from cache (if exists)
|
||||
- name: Download previous benchmark data
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ./cache
|
||||
key: ${{ runner.os }}-benchmark
|
||||
|
||||
# Run `github-action-benchmark` action
|
||||
- name: Store benchmark result
|
||||
uses: rhysd/github-action-benchmark@v1
|
||||
with:
|
||||
name: ${{ matrix.bench }}
|
||||
# What benchmark tool the output.txt came from
|
||||
tool: 'cargo'
|
||||
# Where the output from the benchmark tool is stored
|
||||
output-file-path: output.txt
|
||||
# # Where the previous data file is stored
|
||||
# external-data-json-path: ./cache/benchmark-data.json
|
||||
# Workflow will fail when an alert happens
|
||||
fail-on-alert: true
|
||||
# GitHub API token to make a commit comment
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Enable alert commit comment
|
||||
comment-on-alert: true
|
||||
alert-comment-cc-users: '@tokio-rs/maintainers'
|
||||
auto-push: true
|
||||
|
||||
# Upload the updated cache file for the next job by actions/cache
|
||||
+99
-63
@@ -9,8 +9,16 @@ name: CI
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
nightly: nightly-2021-07-09
|
||||
minrust: 1.45.2
|
||||
nightly: nightly-2021-10-25
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: 1.56.0
|
||||
rust_nightly: nightly-2021-10-25
|
||||
rust_clippy: 1.52.0
|
||||
rust_min: 1.45.2
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
jobs:
|
||||
# Depends on all action sthat are required for a "successful" CI run.
|
||||
@@ -27,8 +35,8 @@ jobs:
|
||||
- fmt
|
||||
- clippy
|
||||
- docs
|
||||
- loom
|
||||
- valgrind
|
||||
- loom-compile
|
||||
- check-readme
|
||||
steps:
|
||||
- run: exit 0
|
||||
@@ -44,8 +52,14 @@ jobs:
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
|
||||
@@ -81,8 +95,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
|
||||
- name: Install Valgrind
|
||||
run: |
|
||||
@@ -118,9 +136,12 @@ jobs:
|
||||
- macos-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
# Run `tokio` with "unstable" cfg flag.
|
||||
- name: test tokio full --cfg unstable
|
||||
run: cargo test --all-features
|
||||
@@ -133,29 +154,31 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
components: miri
|
||||
override: true
|
||||
- name: Install Miri
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: miri
|
||||
run: |
|
||||
set -e
|
||||
rustup component add miri
|
||||
cargo miri setup
|
||||
rm -rf tokio/tests
|
||||
|
||||
- name: miri
|
||||
run: cargo miri test --features rt,rt-multi-thread,sync task
|
||||
rm -rf tests
|
||||
cargo miri test --features rt,rt-multi-thread,sync task
|
||||
working-directory: tokio
|
||||
|
||||
san:
|
||||
name: san
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: asan
|
||||
run: cargo test --all-features --target x86_64-unknown-linux-gnu --lib -- --test-threads 1
|
||||
working-directory: tokio
|
||||
@@ -176,11 +199,13 @@ jobs:
|
||||
- arm-linux-androideabi
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
@@ -192,16 +217,17 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
|
||||
- name: check --each-feature
|
||||
run: cargo hack check --all --each-feature -Z avoid-dev-deps
|
||||
|
||||
# Try with unstable feature flags
|
||||
- name: check --each-feature --unstable
|
||||
run: cargo hack check --all --each-feature -Z avoid-dev-deps
|
||||
@@ -213,11 +239,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_min }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.minrust }}
|
||||
toolchain: ${{ env.rust_min }}
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "test --workspace --all-features"
|
||||
run: cargo check --workspace --all-features
|
||||
|
||||
@@ -226,10 +253,12 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
- name: "check --all-features -Z minimal-versions"
|
||||
@@ -240,23 +269,35 @@ jobs:
|
||||
# Update Cargo.lock to minimal version dependencies.
|
||||
cargo update -Z minimal-versions
|
||||
cargo hack check --all-features --ignore-private
|
||||
- name: "check --all-features --unstable -Z minimal-versions"
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
run: |
|
||||
# Remove dev-dependencies from Cargo.toml to prevent the next `cargo update`
|
||||
# from determining minimal versions based on dev-dependencies.
|
||||
cargo hack --remove-dev-deps --workspace
|
||||
# Update Cargo.lock to minimal version dependencies.
|
||||
cargo update -Z minimal-versions
|
||||
cargo hack check --all-features --ignore-private
|
||||
|
||||
fmt:
|
||||
name: fmt
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- name: Install rustfmt
|
||||
run: rustup component add rustfmt
|
||||
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
components: rustfmt
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
# Check fmt
|
||||
- name: "rustfmt --check"
|
||||
# Workaround for rust-lang/cargo#7732
|
||||
run: |
|
||||
if ! rustfmt --check --edition 2018 $(find . -name '*.rs' -print); then
|
||||
printf "Please run \`rustfmt --edition 2018 \$(find . -name '*.rs' -print)\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
|
||||
if ! rustfmt --check --edition 2018 $(git ls-files '*.rs'); then
|
||||
printf "Please run \`rustfmt --edition 2018 \$(git ls-files '*.rs')\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -265,11 +306,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update 1.52.1 && rustup default 1.52.1
|
||||
- name: Install clippy
|
||||
run: rustup component add clippy
|
||||
|
||||
- name: Install Rust ${{ env.rust_clippy }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_clippy }}
|
||||
override: true
|
||||
components: clippy
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
# Run clippy
|
||||
- name: "clippy --all"
|
||||
run: cargo clippy --all --tests --all-features
|
||||
@@ -279,41 +322,34 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "doc --lib --all-features"
|
||||
run: cargo doc --lib --no-deps --all-features --document-private-items
|
||||
env:
|
||||
RUSTFLAGS: --cfg docsrs
|
||||
RUSTDOCFLAGS: --cfg docsrs -Dwarnings
|
||||
|
||||
loom:
|
||||
name: loom
|
||||
loom-compile:
|
||||
name: build loom tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
scope:
|
||||
- --skip loom_pool
|
||||
- loom_pool::group_a
|
||||
- loom_pool::group_b
|
||||
- loom_pool::group_c
|
||||
- loom_pool::group_d
|
||||
- time::driver
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
- name: loom ${{ matrix.scope }}
|
||||
run: cargo test --lib --release --features full -- --nocapture $SCOPE
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: build --cfg loom
|
||||
run: cargo test --no-run --lib --features full
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
SCOPE: ${{ matrix.scope }}
|
||||
|
||||
check-readme:
|
||||
name: Check README
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
name: "Pull Request Labeler"
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
# See .github/labeler.yml file
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/labeler@v3
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
@@ -0,0 +1,45 @@
|
||||
on:
|
||||
push:
|
||||
branches: ["master", "tokio-*.x"]
|
||||
pull_request:
|
||||
types: [labeled, opened, synchronize, reopened]
|
||||
branches: ["master", "tokio-*.x"]
|
||||
|
||||
name: Loom
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: 1.56.0
|
||||
|
||||
jobs:
|
||||
loom:
|
||||
name: loom
|
||||
# base_ref is null when it's not a pull request
|
||||
if: contains(github.event.pull_request.labels.*.name, 'R-loom') || (github.base_ref == null)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
scope:
|
||||
- --skip loom_pool
|
||||
- loom_pool::group_a
|
||||
- loom_pool::group_b
|
||||
- loom_pool::group_c
|
||||
- loom_pool::group_d
|
||||
- time::driver
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: loom ${{ matrix.scope }}
|
||||
run: cargo test --lib --release --features full -- --nocapture $SCOPE
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
SCOPE: ${{ matrix.scope }}
|
||||
@@ -5,6 +5,12 @@ on:
|
||||
branches:
|
||||
- master
|
||||
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: 1.56.0
|
||||
|
||||
jobs:
|
||||
stess-test:
|
||||
name: Stress Test
|
||||
@@ -15,9 +21,12 @@ jobs:
|
||||
- simple_echo_tcp
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install Valgrind
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ command below instead:
|
||||
|
||||
```
|
||||
# Mac or Linux
|
||||
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
|
||||
rustfmt --check --edition 2018 $(git ls-files '*.rs')
|
||||
|
||||
# Powershell
|
||||
Get-ChildItem . -Filter "*.rs" -Recurse | foreach { rustfmt --check --edition 2018 $_.FullName }
|
||||
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.11.0", features = ["full"] }
|
||||
tokio = { version = "1.14.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ serde = "1.0"
|
||||
serde_derive = "1.0"
|
||||
serde_json = "1.0"
|
||||
httparse = "1.0"
|
||||
time = "0.1"
|
||||
httpdate = "1.0"
|
||||
once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
|
||||
|
||||
+14
-10
@@ -221,8 +221,9 @@ mod date {
|
||||
use std::cell::RefCell;
|
||||
use std::fmt::{self, Write};
|
||||
use std::str;
|
||||
use std::time::SystemTime;
|
||||
|
||||
use time::{self, Duration};
|
||||
use httpdate::HttpDate;
|
||||
|
||||
pub struct Now(());
|
||||
|
||||
@@ -252,22 +253,26 @@ mod date {
|
||||
struct LastRenderedNow {
|
||||
bytes: [u8; 128],
|
||||
amt: usize,
|
||||
next_update: time::Timespec,
|
||||
unix_date: u64,
|
||||
}
|
||||
|
||||
thread_local!(static LAST: RefCell<LastRenderedNow> = RefCell::new(LastRenderedNow {
|
||||
bytes: [0; 128],
|
||||
amt: 0,
|
||||
next_update: time::Timespec::new(0, 0),
|
||||
unix_date: 0,
|
||||
}));
|
||||
|
||||
impl fmt::Display for Now {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
LAST.with(|cache| {
|
||||
let mut cache = cache.borrow_mut();
|
||||
let now = time::get_time();
|
||||
if now >= cache.next_update {
|
||||
cache.update(now);
|
||||
let now = SystemTime::now();
|
||||
let now_unix = now
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map(|since_epoch| since_epoch.as_secs())
|
||||
.unwrap_or(0);
|
||||
if cache.unix_date != now_unix {
|
||||
cache.update(now, now_unix);
|
||||
}
|
||||
f.write_str(cache.buffer())
|
||||
})
|
||||
@@ -279,11 +284,10 @@ mod date {
|
||||
str::from_utf8(&self.bytes[..self.amt]).unwrap()
|
||||
}
|
||||
|
||||
fn update(&mut self, now: time::Timespec) {
|
||||
fn update(&mut self, now: SystemTime, now_unix: u64) {
|
||||
self.amt = 0;
|
||||
write!(LocalBuffer(self), "{}", time::at(now).rfc822()).unwrap();
|
||||
self.next_update = now + Duration::seconds(1);
|
||||
self.next_update.nsec = 0;
|
||||
self.unix_date = now_unix;
|
||||
write!(LocalBuffer(self), "{}", HttpDate::from(now)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,14 @@ async fn missing_return_type() {
|
||||
|
||||
#[tokio::main]
|
||||
async fn extra_semicolon() -> Result<(), ()> {
|
||||
/* TODO(taiki-e): help message still wrong
|
||||
help: try using a variant of the expected enum
|
||||
|
|
||||
23 | Ok(Ok(());)
|
||||
|
|
||||
23 | Err(Ok(());)
|
||||
|
|
||||
*/
|
||||
Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,11 @@ error[E0308]: mismatched types
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
5 | Ok(());
|
||||
| ^
|
||||
| +
|
||||
help: try adding a return type
|
||||
|
|
||||
4 | async fn missing_semicolon_or_return_type() -> Result<(), _> {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
| ++++++++++++++++
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:10:5
|
||||
@@ -27,12 +27,19 @@ error[E0308]: mismatched types
|
||||
found enum `Result<(), _>`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:14:31
|
||||
--> $DIR/macros_type_mismatch.rs:23:5
|
||||
|
|
||||
14 | async fn extra_semicolon() -> Result<(), ()> {
|
||||
| --------------- ^^^^^^^^^^^^^^ expected enum `Result`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
| -------------- expected `Result<(), ()>` because of return type
|
||||
...
|
||||
23 | Ok(());
|
||||
| ^^^^^^^ expected enum `Result`, found `()`
|
||||
|
|
||||
= note: expected enum `Result<(), ()>`
|
||||
found unit type `()`
|
||||
help: try using a variant of the expected enum
|
||||
|
|
||||
23 | Ok(Ok(());)
|
||||
|
|
||||
23 | Err(Ok(());)
|
||||
|
|
||||
|
||||
@@ -1,3 +1,36 @@
|
||||
# 1.5.1 (October 29th, 2021)
|
||||
|
||||
- macros: fix type resolution error in `#[tokio::main]` ([#4176])
|
||||
|
||||
[#4176]: https://github.com/tokio-rs/tokio/pull/4176
|
||||
|
||||
# 1.5.0 (October 13th, 2021)
|
||||
|
||||
- macros: make tokio-macros attributes more IDE friendly ([#4162])
|
||||
|
||||
[#4162]: https://github.com/tokio-rs/tokio/pull/4162
|
||||
|
||||
# 1.4.1 (September 30th, 2021)
|
||||
|
||||
Reverted: run `current_thread` inside `LocalSet` ([#4027])
|
||||
|
||||
# 1.4.0 (September 29th, 2021)
|
||||
|
||||
(yanked)
|
||||
|
||||
### Changed
|
||||
|
||||
- macros: run `current_thread` inside `LocalSet` ([#4027])
|
||||
- macros: explicitly relaxed clippy lint for `.expect()` in runtime entry macro ([#4030])
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: fix invalid error messages in functions wrapped with `#[main]` or `#[test]` ([#4067])
|
||||
|
||||
[#4027]: https://github.com/tokio-rs/tokio/pull/4027
|
||||
[#4030]: https://github.com/tokio-rs/tokio/pull/4030
|
||||
[#4067]: https://github.com/tokio-rs/tokio/pull/4067
|
||||
|
||||
# 1.3.0 (July 7, 2021)
|
||||
|
||||
- macros: don't trigger `clippy::unwrap_used` ([#3926])
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-macros"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.0.x" git tag.
|
||||
version = "1.3.0"
|
||||
version = "1.5.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-macros/1.3.0/tokio_macros"
|
||||
documentation = "https://docs.rs/tokio-macros/1.5.1/tokio_macros"
|
||||
description = """
|
||||
Tokio's proc macros.
|
||||
"""
|
||||
|
||||
+73
-34
@@ -1,6 +1,10 @@
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro2::Span;
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use syn::parse::Parser;
|
||||
|
||||
// syn::AttributeArgs does not implement syn::Parse
|
||||
type AttributeArgs = syn::punctuated::Punctuated<syn::NestedMeta, syn::Token![,]>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum RuntimeFlavor {
|
||||
@@ -27,6 +31,13 @@ struct FinalConfig {
|
||||
start_paused: Option<bool>,
|
||||
}
|
||||
|
||||
/// Config used in case of the attribute not being able to build a valid config
|
||||
const DEFAULT_ERROR_CONFIG: FinalConfig = FinalConfig {
|
||||
flavor: RuntimeFlavor::CurrentThread,
|
||||
worker_threads: None,
|
||||
start_paused: None,
|
||||
};
|
||||
|
||||
struct Configuration {
|
||||
rt_multi_thread_available: bool,
|
||||
default_flavor: RuntimeFlavor,
|
||||
@@ -184,13 +195,13 @@ fn parse_bool(bool: syn::Lit, span: Span, field: &str) -> Result<bool, syn::Erro
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_knobs(
|
||||
mut input: syn::ItemFn,
|
||||
args: syn::AttributeArgs,
|
||||
fn build_config(
|
||||
input: syn::ItemFn,
|
||||
args: AttributeArgs,
|
||||
is_test: bool,
|
||||
rt_multi_thread: bool,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
if input.sig.asyncness.take().is_none() {
|
||||
) -> Result<FinalConfig, syn::Error> {
|
||||
if input.sig.asyncness.is_none() {
|
||||
let msg = "the `async` keyword is missing from the function declaration";
|
||||
return Err(syn::Error::new_spanned(input.sig.fn_token, msg));
|
||||
}
|
||||
@@ -278,7 +289,11 @@ fn parse_knobs(
|
||||
}
|
||||
}
|
||||
|
||||
let config = config.build()?;
|
||||
config.build()
|
||||
}
|
||||
|
||||
fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> TokenStream {
|
||||
input.sig.asyncness = None;
|
||||
|
||||
// If type mismatch occurs, the current rustc points to the last statement.
|
||||
let (last_stmt_start_span, last_stmt_end_span) = {
|
||||
@@ -324,15 +339,17 @@ fn parse_knobs(
|
||||
let body = &input.block;
|
||||
let brace_token = input.block.brace_token;
|
||||
let (tail_return, tail_semicolon) = match body.stmts.last() {
|
||||
Some(syn::Stmt::Semi(expr, _)) => (
|
||||
match expr {
|
||||
syn::Expr::Return(_) => quote! { return },
|
||||
_ => quote! {},
|
||||
Some(syn::Stmt::Semi(expr, _)) => match expr {
|
||||
syn::Expr::Return(_) => (quote! { return }, quote! { ; }),
|
||||
_ => match &input.sig.output {
|
||||
syn::ReturnType::Type(_, ty) if matches!(&**ty, syn::Type::Tuple(ty) if ty.elems.is_empty()) =>
|
||||
{
|
||||
(quote! {}, quote! { ; }) // unit
|
||||
}
|
||||
syn::ReturnType::Default => (quote! {}, quote! { ; }), // unit
|
||||
syn::ReturnType::Type(..) => (quote! {}, quote! {}), // ! or another
|
||||
},
|
||||
quote! {
|
||||
;
|
||||
},
|
||||
),
|
||||
},
|
||||
_ => (quote! {}, quote! {}),
|
||||
};
|
||||
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
|
||||
@@ -354,36 +371,58 @@ fn parse_knobs(
|
||||
#input
|
||||
};
|
||||
|
||||
Ok(result.into())
|
||||
result.into()
|
||||
}
|
||||
|
||||
fn token_stream_with_error(mut tokens: TokenStream, error: syn::Error) -> TokenStream {
|
||||
tokens.extend(TokenStream::from(error.into_compile_error()));
|
||||
tokens
|
||||
}
|
||||
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub(crate) fn main(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||
// If any of the steps for this macro fail, we still want to expand to an item that is as close
|
||||
// to the expected output as possible. This helps out IDEs such that completions and other
|
||||
// related features keep working.
|
||||
let input: syn::ItemFn = match syn::parse(item.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(item, e),
|
||||
};
|
||||
|
||||
if input.sig.ident == "main" && !input.sig.inputs.is_empty() {
|
||||
let config = if input.sig.ident == "main" && !input.sig.inputs.is_empty() {
|
||||
let msg = "the main function cannot accept arguments";
|
||||
return syn::Error::new_spanned(&input.sig.ident, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
Err(syn::Error::new_spanned(&input.sig.ident, msg))
|
||||
} else {
|
||||
AttributeArgs::parse_terminated
|
||||
.parse(args)
|
||||
.and_then(|args| build_config(input.clone(), args, false, rt_multi_thread))
|
||||
};
|
||||
|
||||
parse_knobs(input, args, false, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
|
||||
match config {
|
||||
Ok(config) => parse_knobs(input, false, config),
|
||||
Err(e) => token_stream_with_error(parse_knobs(input, false, DEFAULT_ERROR_CONFIG), e),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||
// If any of the steps for this macro fail, we still want to expand to an item that is as close
|
||||
// to the expected output as possible. This helps out IDEs such that completions and other
|
||||
// related features keep working.
|
||||
let input: syn::ItemFn = match syn::parse(item.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(e) => return token_stream_with_error(item, e),
|
||||
};
|
||||
let config = if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) {
|
||||
let msg = "second test attribute is supplied";
|
||||
Err(syn::Error::new_spanned(&attr, msg))
|
||||
} else {
|
||||
AttributeArgs::parse_terminated
|
||||
.parse(args)
|
||||
.and_then(|args| build_config(input.clone(), args, true, rt_multi_thread))
|
||||
};
|
||||
|
||||
for attr in &input.attrs {
|
||||
if attr.path.is_ident("test") {
|
||||
let msg = "second test attribute is supplied";
|
||||
return syn::Error::new_spanned(&attr, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
match config {
|
||||
Ok(config) => parse_knobs(input, true, config),
|
||||
Err(e) => token_stream_with_error(parse_knobs(input, true, DEFAULT_ERROR_CONFIG), e),
|
||||
}
|
||||
|
||||
parse_knobs(input, args, true, rt_multi_thread).unwrap_or_else(|e| e.to_compile_error().into())
|
||||
}
|
||||
|
||||
@@ -329,3 +329,11 @@ pub fn test_fail(_args: TokenStream, _item: TokenStream) -> TokenStream {
|
||||
pub fn select_priv_declare_output_enum(input: TokenStream) -> TokenStream {
|
||||
select::declare_output_enum(input)
|
||||
}
|
||||
|
||||
/// Implementation detail of the `select!` macro. This macro is **not** intended
|
||||
/// to be used as part of the public API and is permitted to change.
|
||||
#[proc_macro]
|
||||
#[doc(hidden)]
|
||||
pub fn select_priv_clean_pattern(input: TokenStream) -> TokenStream {
|
||||
select::clean_pattern_macro(input)
|
||||
}
|
||||
|
||||
@@ -41,3 +41,70 @@ pub(crate) fn declare_output_enum(input: TokenStream) -> TokenStream {
|
||||
pub(super) type Mask = #mask;
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn clean_pattern_macro(input: TokenStream) -> TokenStream {
|
||||
// If this isn't a pattern, we return the token stream as-is. The select!
|
||||
// macro is using it in a location requiring a pattern, so an error will be
|
||||
// emitted there.
|
||||
let mut input: syn::Pat = match syn::parse(input.clone()) {
|
||||
Ok(it) => it,
|
||||
Err(_) => return input,
|
||||
};
|
||||
|
||||
clean_pattern(&mut input);
|
||||
quote::ToTokens::into_token_stream(input).into()
|
||||
}
|
||||
|
||||
// Removes any occurrences of ref or mut in the provided pattern.
|
||||
fn clean_pattern(pat: &mut syn::Pat) {
|
||||
match pat {
|
||||
syn::Pat::Box(_box) => {}
|
||||
syn::Pat::Lit(_literal) => {}
|
||||
syn::Pat::Macro(_macro) => {}
|
||||
syn::Pat::Path(_path) => {}
|
||||
syn::Pat::Range(_range) => {}
|
||||
syn::Pat::Rest(_rest) => {}
|
||||
syn::Pat::Verbatim(_tokens) => {}
|
||||
syn::Pat::Wild(_underscore) => {}
|
||||
syn::Pat::Ident(ident) => {
|
||||
ident.by_ref = None;
|
||||
ident.mutability = None;
|
||||
if let Some((_at, pat)) = &mut ident.subpat {
|
||||
clean_pattern(&mut *pat);
|
||||
}
|
||||
}
|
||||
syn::Pat::Or(or) => {
|
||||
for case in or.cases.iter_mut() {
|
||||
clean_pattern(case);
|
||||
}
|
||||
}
|
||||
syn::Pat::Slice(slice) => {
|
||||
for elem in slice.elems.iter_mut() {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
syn::Pat::Struct(struct_pat) => {
|
||||
for field in struct_pat.fields.iter_mut() {
|
||||
clean_pattern(&mut field.pat);
|
||||
}
|
||||
}
|
||||
syn::Pat::Tuple(tuple) => {
|
||||
for elem in tuple.elems.iter_mut() {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
syn::Pat::TupleStruct(tuple) => {
|
||||
for elem in tuple.pat.elems.iter_mut() {
|
||||
clean_pattern(elem);
|
||||
}
|
||||
}
|
||||
syn::Pat::Reference(reference) => {
|
||||
reference.mutability = None;
|
||||
clean_pattern(&mut *reference.pat);
|
||||
}
|
||||
syn::Pat::Type(type_pat) => {
|
||||
clean_pattern(&mut *type_pat.pat);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# 0.1.8 (October 29, 2021)
|
||||
|
||||
- stream: add `From<Receiver<T>>` impl for receiver streams ([#4080])
|
||||
- stream: impl `FromIterator` for `StreamMap` ([#4052])
|
||||
- signal: make windows docs for signal module show up on unix builds ([#3770])
|
||||
|
||||
[#3770]: https://github.com/tokio-rs/tokio/pull/3770
|
||||
[#4052]: https://github.com/tokio-rs/tokio/pull/4052
|
||||
[#4080]: https://github.com/tokio-rs/tokio/pull/4080
|
||||
|
||||
# 0.1.7 (July 7, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-stream"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-stream-0.1.x" git tag.
|
||||
version = "0.1.7"
|
||||
version = "0.1.8"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-stream/0.1.7/tokio_stream"
|
||||
documentation = "https://docs.rs/tokio-stream/0.1.8/tokio_stream"
|
||||
description = """
|
||||
Utilities to work with `Stream` and `tokio`.
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg(feature = "sync")]
|
||||
|
||||
use tokio::sync::watch;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
# 0.6.9 (October 29, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- codec: implement `Clone` for `LengthDelimitedCodec` ([#4089])
|
||||
- io: add `SyncIoBridge` ([#4146])
|
||||
|
||||
### Fixed
|
||||
|
||||
- time: update deadline on removal in `DelayQueue` ([#4178])
|
||||
- codec: Update stream impl for Framed to return None after Err ([#4166])
|
||||
|
||||
[#4089]: https://github.com/tokio-rs/tokio/pull/4089
|
||||
[#4146]: https://github.com/tokio-rs/tokio/pull/4146
|
||||
[#4166]: https://github.com/tokio-rs/tokio/pull/4166
|
||||
[#4178]: https://github.com/tokio-rs/tokio/pull/4178
|
||||
|
||||
# 0.6.8 (September 3, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-util"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-util-0.6.x" git tag.
|
||||
version = "0.6.8"
|
||||
version = "0.6.9"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-util/0.6.8/tokio_util"
|
||||
documentation = "https://docs.rs/tokio-util/0.6.9/tokio_util"
|
||||
description = """
|
||||
Additional utilities for working with Tokio.
|
||||
"""
|
||||
@@ -23,13 +23,14 @@ categories = ["asynchronous"]
|
||||
default = []
|
||||
|
||||
# Shorthand for enabling everything
|
||||
full = ["codec", "compat", "io", "time", "net", "rt"]
|
||||
full = ["codec", "compat", "io-util", "time", "net", "rt"]
|
||||
|
||||
net = ["tokio/net"]
|
||||
compat = ["futures-io",]
|
||||
codec = []
|
||||
time = ["tokio/time","slab"]
|
||||
io = []
|
||||
io-util = ["io", "tokio/rt", "tokio/io-util"]
|
||||
rt = ["tokio/rt"]
|
||||
|
||||
__docs_rs = ["futures-util"]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# tokio-util
|
||||
|
||||
Utilities for encoding and decoding frames.
|
||||
Utilities for working with Tokio.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -38,6 +38,18 @@ macro_rules! cfg_io {
|
||||
}
|
||||
}
|
||||
|
||||
cfg_io! {
|
||||
macro_rules! cfg_io_util {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(feature = "io-util")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "io-util")))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_rt {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
|
||||
@@ -106,6 +106,7 @@ where
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(capacity),
|
||||
has_errored: false,
|
||||
},
|
||||
write: WriteFrame::default(),
|
||||
},
|
||||
|
||||
@@ -27,10 +27,12 @@ pin_project! {
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReadFrame {
|
||||
pub(crate) eof: bool,
|
||||
pub(crate) is_readable: bool,
|
||||
pub(crate) buffer: BytesMut,
|
||||
pub(crate) has_errored: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct WriteFrame {
|
||||
@@ -49,6 +51,7 @@ impl Default for ReadFrame {
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
has_errored: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +75,7 @@ impl From<BytesMut> for ReadFrame {
|
||||
buffer,
|
||||
is_readable: size > 0,
|
||||
eof: false,
|
||||
has_errored: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,30 +130,42 @@ where
|
||||
//
|
||||
// The initial state is `reading`.
|
||||
//
|
||||
// | state | eof | is_readable |
|
||||
// |---------|-------|-------------|
|
||||
// | reading | false | false |
|
||||
// | framing | false | true |
|
||||
// | pausing | true | true |
|
||||
// | paused | true | false |
|
||||
//
|
||||
// `decode_eof`
|
||||
// returns `Some` read 0 bytes
|
||||
// │ │ │ │
|
||||
// │ ▼ │ ▼
|
||||
// ┌───────┐ `decode_eof` ┌──────┐
|
||||
// ┌──read 0 bytes──▶│pausing│─returns `None`─▶│paused│──┐
|
||||
// │ └───────┘ └──────┘ │
|
||||
// pending read┐ │ ┌──────┐ │ ▲ │
|
||||
// │ │ │ │ │ │ │ │
|
||||
// │ ▼ │ │ `decode` returns `Some`│ pending read
|
||||
// │ ╔═══════╗ ┌───────┐◀─┘ │
|
||||
// └──║reading║─read n>0 bytes─▶│framing│ │
|
||||
// ╚═══════╝ └───────┘◀──────read n>0 bytes┘
|
||||
// ▲ │
|
||||
// │ │
|
||||
// └─`decode` returns `None`─┘
|
||||
// | state | eof | is_readable | has_errored |
|
||||
// |---------|-------|-------------|-------------|
|
||||
// | reading | false | false | false |
|
||||
// | framing | false | true | false |
|
||||
// | pausing | true | true | false |
|
||||
// | paused | true | false | false |
|
||||
// | errored | <any> | <any> | true |
|
||||
// `decode_eof` returns Err
|
||||
// ┌────────────────────────────────────────────────────────┐
|
||||
// `decode_eof` returns │ │
|
||||
// `Ok(Some)` │ │
|
||||
// ┌─────┐ │ `decode_eof` returns After returning │
|
||||
// Read 0 bytes ├─────▼──┴┐ `Ok(None)` ┌────────┐ ◄───┐ `None` ┌───▼─────┐
|
||||
// ┌────────────────►│ Pausing ├───────────────────────►│ Paused ├─┐ └───────────┤ Errored │
|
||||
// │ └─────────┘ └─┬──▲───┘ │ └───▲───▲─┘
|
||||
// Pending read │ │ │ │ │ │
|
||||
// ┌──────┐ │ `decode` returns `Some` │ └─────┘ │ │
|
||||
// │ │ │ ┌──────┐ │ Pending │ │
|
||||
// │ ┌────▼──┴─┐ Read n>0 bytes ┌┴──────▼─┐ read n>0 bytes │ read │ │
|
||||
// └─┤ Reading ├───────────────►│ Framing │◄────────────────────────┘ │ │
|
||||
// └──┬─▲────┘ └─────┬──┬┘ │ │
|
||||
// │ │ │ │ `decode` returns Err │ │
|
||||
// │ └───decode` returns `None`──┘ └───────────────────────────────────────────────────────┘ │
|
||||
// │ read returns Err │
|
||||
// └────────────────────────────────────────────────────────────────────────────────────────────┘
|
||||
loop {
|
||||
// Return `None` if we have encountered an error from the underlying decoder
|
||||
// See: https://github.com/tokio-rs/tokio/issues/3976
|
||||
if state.has_errored {
|
||||
// preparing has_errored -> paused
|
||||
trace!("Returning None and setting paused");
|
||||
state.is_readable = false;
|
||||
state.has_errored = false;
|
||||
return Poll::Ready(None);
|
||||
}
|
||||
|
||||
// Repeatedly call `decode` or `decode_eof` while the buffer is "readable",
|
||||
// i.e. it _might_ contain data consumable as a frame or closing frame.
|
||||
// Both signal that there is no such data by returning `None`.
|
||||
@@ -165,7 +181,11 @@ where
|
||||
// pausing or framing
|
||||
if state.eof {
|
||||
// pausing
|
||||
let frame = pinned.codec.decode_eof(&mut state.buffer)?;
|
||||
let frame = pinned.codec.decode_eof(&mut state.buffer).map_err(|err| {
|
||||
trace!("Got an error, going to errored state");
|
||||
state.has_errored = true;
|
||||
err
|
||||
})?;
|
||||
if frame.is_none() {
|
||||
state.is_readable = false; // prepare pausing -> paused
|
||||
}
|
||||
@@ -176,7 +196,11 @@ where
|
||||
// framing
|
||||
trace!("attempting to decode a frame");
|
||||
|
||||
if let Some(frame) = pinned.codec.decode(&mut state.buffer)? {
|
||||
if let Some(frame) = pinned.codec.decode(&mut state.buffer).map_err(|op| {
|
||||
trace!("Got an error, going to errored state");
|
||||
state.has_errored = true;
|
||||
op
|
||||
})? {
|
||||
trace!("frame decoded from buffer");
|
||||
// implicit framing -> framing
|
||||
return Poll::Ready(Some(Ok(frame)));
|
||||
@@ -190,7 +214,13 @@ where
|
||||
// Make sure we've got room for at least one byte to read to ensure
|
||||
// that we don't get a spurious 0 that looks like EOF.
|
||||
state.buffer.reserve(1);
|
||||
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer)? {
|
||||
let bytect = match poll_read_buf(pinned.inner.as_mut(), cx, &mut state.buffer).map_err(
|
||||
|err| {
|
||||
trace!("Got an error, going to errored state");
|
||||
state.has_errored = true;
|
||||
err
|
||||
},
|
||||
)? {
|
||||
Poll::Ready(ct) => ct,
|
||||
// implicit reading -> reading or implicit paused -> paused
|
||||
Poll::Pending => return Poll::Pending,
|
||||
|
||||
@@ -51,6 +51,7 @@ where
|
||||
eof: false,
|
||||
is_readable: false,
|
||||
buffer: BytesMut::with_capacity(capacity),
|
||||
has_errored: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -421,7 +421,7 @@ pub struct LengthDelimitedCodecError {
|
||||
/// See [module level] documentation for more detail.
|
||||
///
|
||||
/// [module level]: index.html
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LengthDelimitedCodec {
|
||||
// Configuration values
|
||||
builder: Builder,
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
//! Helpers for IO related tasks.
|
||||
//!
|
||||
//! These types are often used in combination with hyper or reqwest, as they
|
||||
//! The stream types are often used in combination with hyper or reqwest, as they
|
||||
//! allow converting between a hyper [`Body`] and [`AsyncRead`].
|
||||
//!
|
||||
//! The [`SyncIoBridge`] type converts from the world of async I/O
|
||||
//! to synchronous I/O; this may often come up when using synchronous APIs
|
||||
//! inside [`tokio::task::spawn_blocking`].
|
||||
//!
|
||||
//! [`Body`]: https://docs.rs/hyper/0.13/hyper/struct.Body.html
|
||||
//! [`AsyncRead`]: tokio::io::AsyncRead
|
||||
|
||||
mod read_buf;
|
||||
mod reader_stream;
|
||||
mod stream_reader;
|
||||
cfg_io_util! {
|
||||
mod sync_bridge;
|
||||
pub use self::sync_bridge::SyncIoBridge;
|
||||
}
|
||||
|
||||
pub use self::read_buf::read_buf;
|
||||
pub use self::reader_stream::ReaderStream;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
use std::io::{Read, Write};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
#[derive(Debug)]
|
||||
pub struct SyncIoBridge<T> {
|
||||
src: T,
|
||||
rt: tokio::runtime::Handle,
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + Unpin> Read for SyncIoBridge<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(AsyncReadExt::read(src, buf))
|
||||
}
|
||||
|
||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.read_to_end(buf))
|
||||
}
|
||||
|
||||
fn read_to_string(&mut self, buf: &mut String) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.read_to_string(buf))
|
||||
}
|
||||
|
||||
fn read_exact(&mut self, buf: &mut [u8]) -> std::io::Result<()> {
|
||||
let src = &mut self.src;
|
||||
// The AsyncRead trait returns the count, synchronous doesn't.
|
||||
let _n = self.rt.block_on(src.read_exact(buf))?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncWrite + Unpin> Write for SyncIoBridge<T> {
|
||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.write(buf))
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> std::io::Result<()> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.flush())
|
||||
}
|
||||
|
||||
fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.write_all(buf))
|
||||
}
|
||||
|
||||
fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(src.write_vectored(bufs))
|
||||
}
|
||||
}
|
||||
|
||||
// Because https://doc.rust-lang.org/std/io/trait.Write.html#method.is_write_vectored is at the time
|
||||
// of this writing still unstable, we expose this as part of a standalone method.
|
||||
impl<T: AsyncWrite> SyncIoBridge<T> {
|
||||
/// Determines if the underlying [`tokio::io::AsyncWrite`] target supports efficient vectored writes.
|
||||
///
|
||||
/// See [`tokio::io::AsyncWrite::is_write_vectored`].
|
||||
pub fn is_write_vectored(&self) -> bool {
|
||||
self.src.is_write_vectored()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Unpin> SyncIoBridge<T> {
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
///
|
||||
/// When this struct is created, it captures a handle to the current thread's runtime with [`tokio::runtime::Handle::current`].
|
||||
/// It is hence OK to move this struct into a separate thread outside the runtime, as created
|
||||
/// by e.g. [`tokio::task::spawn_blocking`].
|
||||
///
|
||||
/// Stated even more strongly: to make use of this bridge, you *must* move
|
||||
/// it into a separate thread outside the runtime. The synchronous I/O will use the
|
||||
/// underlying handle to block on the backing asynchronous source, via
|
||||
/// [`tokio::runtime::Handle::block_on`]. As noted in the documentation for that
|
||||
/// function, an attempt to `block_on` from an asynchronous execution context
|
||||
/// will panic.
|
||||
///
|
||||
/// # Wrapping `!Unpin` types
|
||||
///
|
||||
/// Use e.g. `SyncIoBridge::new(Box::pin(src))`.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// This will panic if called outside the context of a Tokio runtime.
|
||||
pub fn new(src: T) -> Self {
|
||||
Self::new_with_handle(src, tokio::runtime::Handle::current())
|
||||
}
|
||||
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
///
|
||||
/// This is the same as [`SyncIoBridge::new`], but allows passing an arbitrary handle and hence may
|
||||
/// be initially invoked outside of an asynchronous context.
|
||||
pub fn new_with_handle(src: T, rt: tokio::runtime::Handle) -> Self {
|
||||
Self { src, rt }
|
||||
}
|
||||
}
|
||||
@@ -498,9 +498,20 @@ impl<T> DelayQueue<T> {
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn remove(&mut self, key: &Key) -> Expired<T> {
|
||||
let prev_deadline = self.next_deadline();
|
||||
|
||||
self.remove_key(key);
|
||||
let data = self.slab.remove(key.index);
|
||||
|
||||
let next_deadline = self.next_deadline();
|
||||
if prev_deadline != next_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))),
|
||||
}
|
||||
}
|
||||
|
||||
Expired {
|
||||
key: Key::new(key.index),
|
||||
data: data.inner,
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
use futures_core::stream::Stream;
|
||||
use std::{io, pin::Pin};
|
||||
use tokio_test::{assert_ready, io::Builder, task};
|
||||
use tokio_util::codec::{BytesCodec, FramedRead};
|
||||
|
||||
macro_rules! pin {
|
||||
($id:ident) => {
|
||||
Pin::new(&mut $id)
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_read {
|
||||
($e:expr, $n:expr) => {{
|
||||
let val = assert_ready!($e);
|
||||
assert_eq!(val.unwrap().unwrap(), $n);
|
||||
}};
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn return_none_after_error() {
|
||||
let mut io = FramedRead::new(
|
||||
Builder::new()
|
||||
.read(b"abcdef")
|
||||
.read_error(io::Error::new(io::ErrorKind::Other, "Resource errored out"))
|
||||
.read(b"more data")
|
||||
.build(),
|
||||
BytesCodec::new(),
|
||||
);
|
||||
|
||||
let mut task = task::spawn(());
|
||||
|
||||
task.enter(|cx, _| {
|
||||
assert_read!(pin!(io).poll_next(cx), b"abcdef".to_vec());
|
||||
assert!(assert_ready!(pin!(io).poll_next(cx)).unwrap().is_err());
|
||||
assert!(assert_ready!(pin!(io).poll_next(cx)).is_none());
|
||||
assert_read!(pin!(io).poll_next(cx), b"more data".to_vec());
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#![cfg(feature = "io-util")]
|
||||
|
||||
use std::error::Error;
|
||||
use std::io::{Cursor, Read, Result as IoResult};
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio_util::io::SyncIoBridge;
|
||||
|
||||
async fn test_reader_len(
|
||||
r: impl AsyncRead + Unpin + Send + 'static,
|
||||
expected_len: usize,
|
||||
) -> IoResult<()> {
|
||||
let mut r = SyncIoBridge::new(r);
|
||||
let res = tokio::task::spawn_blocking(move || {
|
||||
let mut buf = Vec::new();
|
||||
r.read_to_end(&mut buf)?;
|
||||
Ok::<_, std::io::Error>(buf)
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(res?.len(), expected_len);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_read_to_sync() -> Result<(), Box<dyn Error>> {
|
||||
test_reader_len(tokio::io::empty(), 0).await?;
|
||||
let buf = b"hello world";
|
||||
test_reader_len(Cursor::new(buf), buf.len()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_async_write_to_sync() -> Result<(), Box<dyn Error>> {
|
||||
let mut dest = Vec::new();
|
||||
let src = b"hello world";
|
||||
let dest = tokio::task::spawn_blocking(move || -> Result<_, String> {
|
||||
let mut w = SyncIoBridge::new(Cursor::new(&mut dest));
|
||||
std::io::copy(&mut Cursor::new(src), &mut w).map_err(|e| e.to_string())?;
|
||||
Ok(dest)
|
||||
})
|
||||
.await??;
|
||||
assert_eq!(dest.as_slice(), src);
|
||||
Ok(())
|
||||
}
|
||||
@@ -630,6 +630,16 @@ async fn insert_in_past_after_poll_fires_immediately() {
|
||||
assert_eq!(entry, "bar");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delay_queue_poll_expired_when_empty() {
|
||||
let mut delay_queue = task::spawn(DelayQueue::new());
|
||||
let key = delay_queue.insert(0, std::time::Duration::from_secs(10));
|
||||
assert_pending!(poll!(delay_queue));
|
||||
|
||||
delay_queue.remove(&key);
|
||||
assert!(assert_ready!(poll!(delay_queue)).is_none());
|
||||
}
|
||||
|
||||
fn ms(n: u64) -> Duration {
|
||||
Duration::from_millis(n)
|
||||
}
|
||||
|
||||
@@ -1,3 +1,171 @@
|
||||
# 1.14.1 (January 30, 2022)
|
||||
|
||||
This release backports a bug fix from 1.16.1
|
||||
|
||||
Fixes a soundness bug in `io::Take` ([#4428]). The unsoundness is exposed when
|
||||
leaking memory in the given `AsyncRead` implementation and then overwriting the
|
||||
supplied buffer:
|
||||
|
||||
```rust
|
||||
impl AsyncRead for Buggy {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>
|
||||
) -> Poll<Result<()>> {
|
||||
let new_buf = vec![0; 5].leak();
|
||||
*buf = ReadBuf::new(new_buf);
|
||||
buf.put_slice(b"hello");
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Fixed
|
||||
|
||||
- io: **soundness** don't expose uninitialized memory when using `io::Take` in edge case ([#4428])
|
||||
|
||||
[#4428]: https://github.com/tokio-rs/tokio/pull/4428
|
||||
|
||||
# 1.14.0 (November 15, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: fix compiler errors when using `mut` patterns in `select!` ([#4211])
|
||||
- sync: fix a data race between `oneshot::Sender::send` and awaiting a
|
||||
`oneshot::Receiver` when the oneshot has been closed ([#4226])
|
||||
- sync: make `AtomicWaker` panic safe ([#3689])
|
||||
- runtime: fix basic scheduler dropping tasks outside a runtime context
|
||||
([#4213])
|
||||
|
||||
### Added
|
||||
|
||||
- stats: add `RuntimeStats::busy_duration_total` ([#4179], [#4223])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: updated `copy` buffer size to match `std::io::copy` ([#4209])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: rename buffer to file in doc-test ([#4230])
|
||||
- sync: fix Notify example ([#4212])
|
||||
|
||||
[#4211]: https://github.com/tokio-rs/tokio/pull/4211
|
||||
[#4226]: https://github.com/tokio-rs/tokio/pull/4226
|
||||
[#3689]: https://github.com/tokio-rs/tokio/pull/3689
|
||||
[#4213]: https://github.com/tokio-rs/tokio/pull/4213
|
||||
[#4179]: https://github.com/tokio-rs/tokio/pull/4179
|
||||
[#4223]: https://github.com/tokio-rs/tokio/pull/4223
|
||||
[#4209]: https://github.com/tokio-rs/tokio/pull/4209
|
||||
[#4230]: https://github.com/tokio-rs/tokio/pull/4230
|
||||
[#4212]: https://github.com/tokio-rs/tokio/pull/4212
|
||||
|
||||
# 1.13.1 (November 15, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: fix a data race between `oneshot::Sender::send` and awaiting a
|
||||
`oneshot::Receiver` when the oneshot has been closed ([#4226])
|
||||
|
||||
[#4226]: https://github.com/tokio-rs/tokio/pull/4226
|
||||
|
||||
# 1.13.0 (October 29, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: fix `Notify` to clone the waker before locking its waiter list ([#4129])
|
||||
- tokio: add riscv32 to non atomic64 architectures ([#4185])
|
||||
|
||||
### Added
|
||||
|
||||
- net: add `poll_{recv,send}_ready` methods to `udp` and `uds_datagram` ([#4131])
|
||||
- net: add `try_*`, `readable`, `writable`, `ready`, and `peer_addr` methods to split halves ([#4120])
|
||||
- sync: add `blocking_lock` to `Mutex` ([#4130])
|
||||
- sync: add `watch::Sender::send_replace` ([#3962], [#4195])
|
||||
- sync: expand `Debug` for `Mutex<T>` impl to unsized `T` ([#4134])
|
||||
- tracing: instrument time::Sleep ([#4072])
|
||||
- tracing: use structured location fields for spawned tasks ([#4128])
|
||||
|
||||
### Changed
|
||||
|
||||
- io: add assert in `copy_bidirectional` that `poll_write` is sensible ([#4125])
|
||||
- macros: use qualified syntax when polling in `select!` ([#4192])
|
||||
- runtime: handle `block_on` wakeups better ([#4157])
|
||||
- task: allocate callback on heap immediately in debug mode ([#4203])
|
||||
- tokio: assert platform-minimum requirements at build time ([#3797])
|
||||
|
||||
### Documented
|
||||
|
||||
- docs: conversion of doc comments to indicative mood ([#4174])
|
||||
- docs: add returning on the first error example for `try_join!` ([#4133])
|
||||
- docs: fixing broken links in `tokio/src/lib.rs` ([#4132])
|
||||
- signal: add example with background listener ([#4171])
|
||||
- sync: add more oneshot examples ([#4153])
|
||||
- time: document `Interval::tick` cancel safety ([#4152])
|
||||
|
||||
[#3797]: https://github.com/tokio-rs/tokio/pull/3797
|
||||
[#3962]: https://github.com/tokio-rs/tokio/pull/3962
|
||||
[#4072]: https://github.com/tokio-rs/tokio/pull/4072
|
||||
[#4120]: https://github.com/tokio-rs/tokio/pull/4120
|
||||
[#4125]: https://github.com/tokio-rs/tokio/pull/4125
|
||||
[#4128]: https://github.com/tokio-rs/tokio/pull/4128
|
||||
[#4129]: https://github.com/tokio-rs/tokio/pull/4129
|
||||
[#4130]: https://github.com/tokio-rs/tokio/pull/4130
|
||||
[#4131]: https://github.com/tokio-rs/tokio/pull/4131
|
||||
[#4132]: https://github.com/tokio-rs/tokio/pull/4132
|
||||
[#4133]: https://github.com/tokio-rs/tokio/pull/4133
|
||||
[#4134]: https://github.com/tokio-rs/tokio/pull/4134
|
||||
[#4152]: https://github.com/tokio-rs/tokio/pull/4152
|
||||
[#4153]: https://github.com/tokio-rs/tokio/pull/4153
|
||||
[#4157]: https://github.com/tokio-rs/tokio/pull/4157
|
||||
[#4171]: https://github.com/tokio-rs/tokio/pull/4171
|
||||
[#4174]: https://github.com/tokio-rs/tokio/pull/4174
|
||||
[#4185]: https://github.com/tokio-rs/tokio/pull/4185
|
||||
[#4192]: https://github.com/tokio-rs/tokio/pull/4192
|
||||
[#4195]: https://github.com/tokio-rs/tokio/pull/4195
|
||||
[#4203]: https://github.com/tokio-rs/tokio/pull/4203
|
||||
|
||||
# 1.12.0 (September 21, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- mpsc: ensure `try_reserve` error is consistent with `try_send` ([#4119])
|
||||
- mpsc: use `spin_loop_hint` instead of `yield_now` ([#4115])
|
||||
- sync: make `SendError` field public ([#4097])
|
||||
|
||||
### Added
|
||||
|
||||
- io: add POSIX AIO on FreeBSD ([#4054])
|
||||
- io: add convenience method `AsyncSeekExt::rewind` ([#4107])
|
||||
- runtime: add tracing span for `block_on` futures ([#4094])
|
||||
- runtime: callback when a worker parks and unparks ([#4070])
|
||||
- sync: implement `try_recv` for mpsc channels ([#4113])
|
||||
|
||||
### Documented
|
||||
|
||||
- docs: clarify CPU-bound tasks on Tokio ([#4105])
|
||||
- mpsc: document spurious failures on `poll_recv` ([#4117])
|
||||
- mpsc: document that `PollSender` impls `Sink` ([#4110])
|
||||
- task: document non-guarantees of `yield_now` ([#4091])
|
||||
- time: document paused time details better ([#4061], [#4103])
|
||||
|
||||
[#4027]: https://github.com/tokio-rs/tokio/pull/4027
|
||||
[#4054]: https://github.com/tokio-rs/tokio/pull/4054
|
||||
[#4061]: https://github.com/tokio-rs/tokio/pull/4061
|
||||
[#4070]: https://github.com/tokio-rs/tokio/pull/4070
|
||||
[#4091]: https://github.com/tokio-rs/tokio/pull/4091
|
||||
[#4094]: https://github.com/tokio-rs/tokio/pull/4094
|
||||
[#4097]: https://github.com/tokio-rs/tokio/pull/4097
|
||||
[#4103]: https://github.com/tokio-rs/tokio/pull/4103
|
||||
[#4105]: https://github.com/tokio-rs/tokio/pull/4105
|
||||
[#4107]: https://github.com/tokio-rs/tokio/pull/4107
|
||||
[#4110]: https://github.com/tokio-rs/tokio/pull/4110
|
||||
[#4113]: https://github.com/tokio-rs/tokio/pull/4113
|
||||
[#4115]: https://github.com/tokio-rs/tokio/pull/4115
|
||||
[#4117]: https://github.com/tokio-rs/tokio/pull/4117
|
||||
[#4119]: https://github.com/tokio-rs/tokio/pull/4119
|
||||
|
||||
# 1.11.0 (August 31, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
+7
-4
@@ -7,12 +7,12 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.0.x" git tag.
|
||||
version = "1.11.0"
|
||||
version = "1.14.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/1.11.0/tokio/"
|
||||
documentation = "https://docs.rs/tokio/1.14.1/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -87,7 +87,7 @@ test-util = ["rt", "sync", "time"]
|
||||
time = []
|
||||
|
||||
[dependencies]
|
||||
tokio-macros = { version = "1.1.0", path = "../tokio-macros", optional = true }
|
||||
tokio-macros = { version = "1.6.0", optional = true }
|
||||
|
||||
pin-project-lite = "0.2.0"
|
||||
|
||||
@@ -102,7 +102,7 @@ parking_lot = { version = "0.11.0", optional = true }
|
||||
# Currently unstable. The API exposed by these features may be broken at any time.
|
||||
# Requires `--cfg tokio_unstable` to enable.
|
||||
[target.'cfg(tokio_unstable)'.dependencies]
|
||||
tracing = { version = "0.1.21", default-features = false, features = ["std"], optional = true } # Not in full
|
||||
tracing = { version = "0.1.25", default-features = false, features = ["std"], optional = true } # Not in full
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = { version = "0.2.42", optional = true }
|
||||
@@ -131,6 +131,9 @@ tempfile = "3.1.0"
|
||||
async-stream = "0.3"
|
||||
socket2 = "0.4"
|
||||
|
||||
[target.'cfg(target_os = "freebsd")'.dev-dependencies]
|
||||
mio-aio = { version = "0.6.0", features = ["tokio"] }
|
||||
|
||||
[target.'cfg(loom)'.dev-dependencies]
|
||||
loom = { version = "0.5", features = ["futures", "checkpoint"] }
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2021 Tokio Contributors
|
||||
Copyright (c) 2022 Tokio Contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.11.0", features = ["full"] }
|
||||
tokio = { version = "1.14.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
+5
-5
@@ -69,14 +69,14 @@ cfg_rt_multi_thread! {
|
||||
}
|
||||
}
|
||||
|
||||
/// Run the given closure with a cooperative task budget. When the function
|
||||
/// Runs the given closure with a cooperative task budget. When the function
|
||||
/// returns, the budget is reset to the value prior to calling the function.
|
||||
#[inline(always)]
|
||||
pub(crate) fn budget<R>(f: impl FnOnce() -> R) -> R {
|
||||
with_budget(Budget::initial(), f)
|
||||
}
|
||||
|
||||
/// Run the given closure with an unconstrained task budget. When the function returns, the budget
|
||||
/// Runs the given closure with an unconstrained task budget. When the function returns, the budget
|
||||
/// is reset to the value prior to calling the function.
|
||||
#[inline(always)]
|
||||
pub(crate) fn with_unconstrained<R>(f: impl FnOnce() -> R) -> R {
|
||||
@@ -108,7 +108,7 @@ fn with_budget<R>(budget: Budget, f: impl FnOnce() -> R) -> R {
|
||||
}
|
||||
|
||||
cfg_rt_multi_thread! {
|
||||
/// Set the current task's budget
|
||||
/// Sets the current task's budget.
|
||||
pub(crate) fn set(budget: Budget) {
|
||||
CURRENT.with(|cell| cell.set(budget))
|
||||
}
|
||||
@@ -120,7 +120,7 @@ cfg_rt_multi_thread! {
|
||||
}
|
||||
|
||||
cfg_rt! {
|
||||
/// Forcibly remove the budgeting constraints early.
|
||||
/// Forcibly removes the budgeting constraints early.
|
||||
///
|
||||
/// Returns the remaining budget
|
||||
pub(crate) fn stop() -> Budget {
|
||||
@@ -186,7 +186,7 @@ cfg_coop! {
|
||||
}
|
||||
|
||||
impl Budget {
|
||||
/// Decrement the budget. Returns `true` if successful. Decrementing fails
|
||||
/// Decrements the budget. Returns `true` if successful. Decrementing fails
|
||||
/// when there is not enough remaining budget.
|
||||
fn decrement(&mut self) -> bool {
|
||||
if let Some(num) = &mut self.0 {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
/// will ever accidentally use it.
|
||||
///
|
||||
/// [`never` type]: https://doc.rust-lang.org/std/primitive.never.html
|
||||
#[derive(Debug)]
|
||||
pub enum NotDefinedHere {}
|
||||
|
||||
pub mod os;
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::fs::asyncify;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Creates a new, empty directory at the provided path
|
||||
/// Creates a new, empty directory at the provided path.
|
||||
///
|
||||
/// This is an async version of [`std::fs::create_dir`][std]
|
||||
///
|
||||
|
||||
@@ -14,7 +14,7 @@ pub struct DirBuilder {
|
||||
/// Indicates whether to create parent directories if they are missing.
|
||||
recursive: bool,
|
||||
|
||||
/// Set the Unix mode for newly created directories.
|
||||
/// Sets the Unix mode for newly created directories.
|
||||
#[cfg(unix)]
|
||||
pub(super) mode: Option<u32>,
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ use std::fs::File as StdFile;
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Read the contents of a file into a buffer
|
||||
/// Read the contents of a file into a buffer:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::File;
|
||||
@@ -383,7 +383,7 @@ impl File {
|
||||
asyncify(move || std.metadata()).await
|
||||
}
|
||||
|
||||
/// Create a new `File` instance that shares the same underlying file handle
|
||||
/// Creates a new `File` instance that shares the same underlying file handle
|
||||
/// as the existing `File` instance. Reads, writes, and seeks will affect both
|
||||
/// File instances simultaneously.
|
||||
///
|
||||
|
||||
@@ -430,7 +430,7 @@ feature! {
|
||||
self
|
||||
}
|
||||
|
||||
/// Pass custom flags to the `flags` argument of `open`.
|
||||
/// Passes custom flags to the `flags` argument of `open`.
|
||||
///
|
||||
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
|
||||
/// ensure they do not interfere with the access mode set by Rusts options.
|
||||
|
||||
@@ -34,7 +34,7 @@ pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
Ok(ReadDir(State::Idle(Some(std))))
|
||||
}
|
||||
|
||||
/// Read the the entries in a directory.
|
||||
/// Reads the the entries in a directory.
|
||||
///
|
||||
/// This struct is returned from the [`read_dir`] function of this module and
|
||||
/// will yield instances of [`DirEntry`]. Through a [`DirEntry`] information
|
||||
@@ -287,7 +287,7 @@ impl DirEntry {
|
||||
asyncify(move || std.file_type()).await
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying `std::fs::DirEntry`
|
||||
/// Returns a reference to the underlying `std::fs::DirEntry`.
|
||||
#[cfg(unix)]
|
||||
pub(super) fn as_inner(&self) -> &std::fs::DirEntry {
|
||||
&self.0
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Definition of the MaybeDone combinator
|
||||
//! Definition of the MaybeDone combinator.
|
||||
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
@@ -8,9 +8,9 @@ use std::task::{Context, Poll};
|
||||
/// A future that may have completed.
|
||||
#[derive(Debug)]
|
||||
pub enum MaybeDone<Fut: Future> {
|
||||
/// A not-yet-completed future
|
||||
/// A not-yet-completed future.
|
||||
Future(Fut),
|
||||
/// The output of the completed future
|
||||
/// The output of the completed future.
|
||||
Done(Fut::Output),
|
||||
/// The empty variant after the result of a [`MaybeDone`] has been
|
||||
/// taken using the [`take_output`](MaybeDone::take_output) method.
|
||||
@@ -20,7 +20,7 @@ pub enum MaybeDone<Fut: Future> {
|
||||
// Safe because we never generate `Pin<&mut Fut::Output>`
|
||||
impl<Fut: Future + Unpin> Unpin for MaybeDone<Fut> {}
|
||||
|
||||
/// Wraps a future into a `MaybeDone`
|
||||
/// Wraps a future into a `MaybeDone`.
|
||||
pub fn maybe_done<Fut: Future>(future: Fut) -> MaybeDone<Fut> {
|
||||
MaybeDone::Future(future)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
//! Definition of the `PollFn` adapter combinator
|
||||
//! Definition of the `PollFn` adapter combinator.
|
||||
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
|
||||
@@ -205,13 +205,13 @@ impl<T: AsRawFd> AsyncFd<T> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the backing object of this [`AsyncFd`]
|
||||
/// Returns a shared reference to the backing object of this [`AsyncFd`].
|
||||
#[inline]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
self.inner.as_ref().unwrap()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the backing object of this [`AsyncFd`]
|
||||
/// Returns a mutable reference to the backing object of this [`AsyncFd`].
|
||||
#[inline]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
self.inner.as_mut().unwrap()
|
||||
|
||||
@@ -16,7 +16,7 @@ use self::State::*;
|
||||
pub(crate) struct Blocking<T> {
|
||||
inner: Option<T>,
|
||||
state: State<T>,
|
||||
/// `true` if the lower IO layer needs flushing
|
||||
/// `true` if the lower IO layer needs flushing.
|
||||
need_flush: bool,
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Repeats operations that are interrupted
|
||||
/// Repeats operations that are interrupted.
|
||||
macro_rules! uninterruptibly {
|
||||
($e:expr) => {{
|
||||
loop {
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
//! Use POSIX AIO futures with Tokio.
|
||||
|
||||
use crate::io::driver::{Handle, Interest, ReadyEvent, Registration};
|
||||
use mio::event::Source;
|
||||
use mio::Registry;
|
||||
use mio::Token;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::os::unix::prelude::RawFd;
|
||||
use std::task::{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.
|
||||
pub trait AioSource {
|
||||
/// Registers this AIO event source with Tokio's reactor.
|
||||
fn register(&mut self, kq: RawFd, token: usize);
|
||||
|
||||
/// Deregisters this AIO event source with Tokio's reactor.
|
||||
fn deregister(&mut self);
|
||||
}
|
||||
|
||||
/// Wraps the user's AioSource in order to implement mio::event::Source, which
|
||||
/// is what the rest of the crate wants.
|
||||
struct MioSource<T>(T);
|
||||
|
||||
impl<T: AioSource> Source for MioSource<T> {
|
||||
fn register(
|
||||
&mut self,
|
||||
registry: &Registry,
|
||||
token: Token,
|
||||
interests: mio::Interest,
|
||||
) -> io::Result<()> {
|
||||
assert!(interests.is_aio() || interests.is_lio());
|
||||
self.0.register(registry.as_raw_fd(), usize::from(token));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deregister(&mut self, _registry: &Registry) -> io::Result<()> {
|
||||
self.0.deregister();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
registry: &Registry,
|
||||
token: Token,
|
||||
interests: mio::Interest,
|
||||
) -> io::Result<()> {
|
||||
assert!(interests.is_aio() || interests.is_lio());
|
||||
self.0.register(registry.as_raw_fd(), usize::from(token));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Associates a POSIX AIO control block with the reactor that drives it.
|
||||
///
|
||||
/// `Aio`'s wrapped type must implement [`AioSource`] to be driven
|
||||
/// by the reactor.
|
||||
///
|
||||
/// The wrapped source may be accessed through the `Aio` via the `Deref` and
|
||||
/// `DerefMut` traits.
|
||||
///
|
||||
/// ## Clearing readiness
|
||||
///
|
||||
/// If [`Aio::poll_ready`] returns ready, but the consumer determines that the
|
||||
/// Source is not completely ready and must return to the Pending state,
|
||||
/// [`Aio::clear_ready`] may be used. This can be useful with
|
||||
/// [`lio_listio`], which may generate a kevent when only a portion of the
|
||||
/// operations have completed.
|
||||
///
|
||||
/// ## Platforms
|
||||
///
|
||||
/// Only FreeBSD implements POSIX AIO with kqueue notification, so
|
||||
/// `Aio` is only available for that operating system.
|
||||
///
|
||||
/// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
|
||||
// Note: Unlike every other kqueue event source, POSIX AIO registers events not
|
||||
// via kevent(2) but when the aiocb is submitted to the kernel via aio_read,
|
||||
// aio_write, etc. It needs the kqueue's file descriptor to do that. So
|
||||
// AsyncFd can't be used for POSIX AIO.
|
||||
//
|
||||
// Note that Aio doesn't implement Drop. There's no need. Unlike other
|
||||
// kqueue sources, simply dropping the object effectively deregisters it.
|
||||
pub struct Aio<E> {
|
||||
io: MioSource<E>,
|
||||
registration: Registration,
|
||||
}
|
||||
|
||||
// ===== impl Aio =====
|
||||
|
||||
impl<E: AioSource> Aio<E> {
|
||||
/// Creates a new `Aio` suitable for use with POSIX AIO functions.
|
||||
///
|
||||
/// It will be associated with the default reactor. The runtime is usually
|
||||
/// set implicitly when this function is called from a future driven by a
|
||||
/// Tokio runtime, otherwise runtime can be set explicitly with
|
||||
/// [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
pub fn new_for_aio(io: E) -> io::Result<Self> {
|
||||
Self::new_with_interest(io, Interest::AIO)
|
||||
}
|
||||
|
||||
/// Creates a new `Aio` suitable for use with [`lio_listio`].
|
||||
///
|
||||
/// It will be associated with the default reactor. The runtime is usually
|
||||
/// set implicitly when this function is called from a future driven by a
|
||||
/// Tokio runtime, otherwise runtime can be set explicitly with
|
||||
/// [`Runtime::enter`](crate::runtime::Runtime::enter) function.
|
||||
///
|
||||
/// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
|
||||
pub fn new_for_lio(io: E) -> io::Result<Self> {
|
||||
Self::new_with_interest(io, Interest::LIO)
|
||||
}
|
||||
|
||||
fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
||||
let mut io = MioSource(io);
|
||||
let handle = Handle::current();
|
||||
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
|
||||
Ok(Self { io, registration })
|
||||
}
|
||||
|
||||
/// Indicates to Tokio that the source is no longer ready. The internal
|
||||
/// readiness flag will be cleared, and tokio will wait for the next
|
||||
/// edge-triggered readiness notification from the OS.
|
||||
///
|
||||
/// It is critical that this method not be called unless your code
|
||||
/// _actually observes_ that the source is _not_ ready. The OS must
|
||||
/// deliver a subsequent notification, or this source will block
|
||||
/// forever. It is equally critical that you `do` call this method if you
|
||||
/// resubmit the same structure to the kernel and poll it again.
|
||||
///
|
||||
/// This method is not very useful with AIO readiness, since each `aiocb`
|
||||
/// structure is typically only used once. It's main use with
|
||||
/// [`lio_listio`], which will sometimes send notification when only a
|
||||
/// portion of its elements are complete. In that case, the caller must
|
||||
/// call `clear_ready` before resubmitting it.
|
||||
///
|
||||
/// [`lio_listio`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lio_listio.html
|
||||
pub fn clear_ready(&self, ev: AioEvent) {
|
||||
self.registration.clear_readiness(ev.0)
|
||||
}
|
||||
|
||||
/// Destroy the [`Aio`] and return its inner source.
|
||||
pub fn into_inner(self) -> E {
|
||||
self.io.0
|
||||
}
|
||||
|
||||
/// Polls for readiness. Either AIO or LIO counts.
|
||||
///
|
||||
/// This method returns:
|
||||
/// * `Poll::Pending` if the underlying operation is not complete, whether
|
||||
/// or not it completed successfully. This will be true if the OS is
|
||||
/// still processing it, or if it has not yet been submitted to the OS.
|
||||
/// * `Poll::Ready(Ok(_))` if the underlying operation is complete.
|
||||
/// * `Poll::Ready(Err(_))` if the reactor has been shutdown. This does
|
||||
/// _not_ indicate that the underlying operation encountered an error.
|
||||
///
|
||||
/// When the method returns `Poll::Pending`, the `Waker` in the provided `Context`
|
||||
/// is scheduled to receive a wakeup when the underlying operation
|
||||
/// completes. Note that on multiple calls to `poll_ready`, only the `Waker` from the
|
||||
/// `Context` passed to the most recent call is scheduled to receive a wakeup.
|
||||
pub fn poll_ready<'a>(&'a self, cx: &mut Context<'_>) -> Poll<io::Result<AioEvent>> {
|
||||
let ev = ready!(self.registration.poll_read_ready(cx))?;
|
||||
Poll::Ready(Ok(AioEvent(ev)))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AioSource> Deref for Aio<E> {
|
||||
type Target = E;
|
||||
|
||||
fn deref(&self) -> &E {
|
||||
&self.io.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AioSource> DerefMut for Aio<E> {
|
||||
fn deref_mut(&mut self) -> &mut E {
|
||||
&mut self.io.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: AioSource + fmt::Debug> fmt::Debug for Aio<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Aio").field("io", &self.io.0).finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque data returned by [`Aio::poll_ready`].
|
||||
///
|
||||
/// It can be fed back to [`Aio::clear_ready`].
|
||||
#[derive(Debug)]
|
||||
pub struct AioEvent(ReadyEvent);
|
||||
@@ -5,7 +5,7 @@ use crate::io::driver::Ready;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
/// Readiness event interest
|
||||
/// Readiness event interest.
|
||||
///
|
||||
/// Specifies the readiness events the caller is interested in when awaiting on
|
||||
/// I/O resource readiness states.
|
||||
@@ -14,12 +14,32 @@ use std::ops;
|
||||
pub struct Interest(mio::Interest);
|
||||
|
||||
impl Interest {
|
||||
// The non-FreeBSD definitions in this block are active only when
|
||||
// building documentation.
|
||||
cfg_aio! {
|
||||
/// Interest for POSIX AIO.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub const AIO: Interest = Interest(mio::Interest::AIO);
|
||||
|
||||
/// Interest for POSIX AIO.
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
pub const AIO: Interest = Interest(mio::Interest::READABLE);
|
||||
|
||||
/// Interest for POSIX AIO lio_listio events.
|
||||
#[cfg(target_os = "freebsd")]
|
||||
pub const LIO: Interest = Interest(mio::Interest::LIO);
|
||||
|
||||
/// Interest for POSIX AIO lio_listio events.
|
||||
#[cfg(not(target_os = "freebsd"))]
|
||||
pub const LIO: Interest = Interest(mio::Interest::READABLE);
|
||||
}
|
||||
|
||||
/// Interest in all readable events.
|
||||
///
|
||||
/// Readable interest includes read-closed events.
|
||||
pub const READABLE: Interest = Interest(mio::Interest::READABLE);
|
||||
|
||||
/// Interest in all writable events
|
||||
/// Interest in all writable events.
|
||||
///
|
||||
/// Writable interest includes write-closed events.
|
||||
pub const WRITABLE: Interest = Interest(mio::Interest::WRITABLE);
|
||||
|
||||
@@ -23,10 +23,10 @@ use std::io;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::time::Duration;
|
||||
|
||||
/// I/O driver, backed by Mio
|
||||
/// I/O driver, backed by Mio.
|
||||
pub(crate) struct Driver {
|
||||
/// Tracks the number of times `turn` is called. It is safe for this to wrap
|
||||
/// as it is mostly used to determine when to call `compact()`
|
||||
/// as it is mostly used to determine when to call `compact()`.
|
||||
tick: u8,
|
||||
|
||||
/// Reuse the `mio::Events` value across calls to poll.
|
||||
@@ -35,22 +35,23 @@ pub(crate) struct Driver {
|
||||
/// Primary slab handle containing the state for each resource registered
|
||||
/// with this driver. During Drop this is moved into the Inner structure, so
|
||||
/// this is an Option to allow it to be vacated (until Drop this is always
|
||||
/// Some)
|
||||
/// Some).
|
||||
resources: Option<Slab<ScheduledIo>>,
|
||||
|
||||
/// The system event queue
|
||||
/// The system event queue.
|
||||
poll: mio::Poll,
|
||||
|
||||
/// State shared between the reactor and the handles.
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
/// A reference to an I/O driver
|
||||
/// A reference to an I/O driver.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Handle {
|
||||
inner: Weak<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReadyEvent {
|
||||
tick: u8,
|
||||
pub(crate) ready: Ready,
|
||||
@@ -65,13 +66,13 @@ pub(super) struct Inner {
|
||||
/// without risking new ones being registered in the meantime.
|
||||
resources: Mutex<Option<Slab<ScheduledIo>>>,
|
||||
|
||||
/// Registers I/O resources
|
||||
/// Registers I/O resources.
|
||||
registry: mio::Registry,
|
||||
|
||||
/// Allocates `ScheduledIo` handles when creating new resources.
|
||||
pub(super) io_dispatch: slab::Allocator<ScheduledIo>,
|
||||
|
||||
/// Used to wake up the reactor from a call to `turn`
|
||||
/// Used to wake up the reactor from a call to `turn`.
|
||||
waker: mio::Waker,
|
||||
}
|
||||
|
||||
@@ -252,7 +253,7 @@ impl fmt::Debug for Driver {
|
||||
|
||||
cfg_rt! {
|
||||
impl Handle {
|
||||
/// Returns a handle to the current reactor
|
||||
/// Returns a handle to the current reactor.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -266,7 +267,7 @@ cfg_rt! {
|
||||
|
||||
cfg_not_rt! {
|
||||
impl Handle {
|
||||
/// Returns a handle to the current reactor
|
||||
/// Returns a handle to the current reactor.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
||||
@@ -38,6 +38,17 @@ impl Ready {
|
||||
pub(crate) fn from_mio(event: &mio::event::Event) -> Ready {
|
||||
let mut ready = Ready::EMPTY;
|
||||
|
||||
#[cfg(all(target_os = "freebsd", feature = "net"))]
|
||||
{
|
||||
if event.is_aio() {
|
||||
ready |= Ready::READABLE;
|
||||
}
|
||||
|
||||
if event.is_lio() {
|
||||
ready |= Ready::READABLE;
|
||||
}
|
||||
}
|
||||
|
||||
if event.is_readable() {
|
||||
ready |= Ready::READABLE;
|
||||
}
|
||||
@@ -57,7 +68,7 @@ impl Ready {
|
||||
ready
|
||||
}
|
||||
|
||||
/// Returns true if `Ready` is the empty set
|
||||
/// Returns true if `Ready` is the empty set.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -71,7 +82,7 @@ impl Ready {
|
||||
self == Ready::EMPTY
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes `readable`
|
||||
/// Returns `true` if the value includes `readable`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -87,7 +98,7 @@ impl Ready {
|
||||
self.contains(Ready::READABLE) || self.is_read_closed()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes writable `readiness`
|
||||
/// Returns `true` if the value includes writable `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -103,7 +114,7 @@ impl Ready {
|
||||
self.contains(Ready::WRITABLE) || self.is_write_closed()
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes read-closed `readiness`
|
||||
/// Returns `true` if the value includes read-closed `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -118,7 +129,7 @@ impl Ready {
|
||||
self.contains(Ready::READ_CLOSED)
|
||||
}
|
||||
|
||||
/// Returns `true` if the value includes write-closed `readiness`
|
||||
/// Returns `true` if the value includes write-closed `readiness`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -143,7 +154,7 @@ impl Ready {
|
||||
(self & other) == other
|
||||
}
|
||||
|
||||
/// Create a `Ready` instance using the given `usize` representation.
|
||||
/// Creates a `Ready` instance using the given `usize` representation.
|
||||
///
|
||||
/// The `usize` representation must have been obtained from a call to
|
||||
/// `Readiness::as_usize`.
|
||||
|
||||
@@ -36,16 +36,16 @@ cfg_io_readiness! {
|
||||
#[derive(Debug, Default)]
|
||||
struct Waiters {
|
||||
#[cfg(feature = "net")]
|
||||
/// List of all current waiters
|
||||
/// List of all current waiters.
|
||||
list: WaitList,
|
||||
|
||||
/// Waker used for AsyncRead
|
||||
/// Waker used for AsyncRead.
|
||||
reader: Option<Waker>,
|
||||
|
||||
/// Waker used for AsyncWrite
|
||||
/// Waker used for AsyncWrite.
|
||||
writer: Option<Waker>,
|
||||
|
||||
/// True if this ScheduledIo has been killed due to IO driver shutdown
|
||||
/// True if this ScheduledIo has been killed due to IO driver shutdown.
|
||||
is_shutdown: bool,
|
||||
}
|
||||
|
||||
@@ -54,19 +54,19 @@ cfg_io_readiness! {
|
||||
struct Waiter {
|
||||
pointers: linked_list::Pointers<Waiter>,
|
||||
|
||||
/// The waker for this task
|
||||
/// The waker for this task.
|
||||
waker: Option<Waker>,
|
||||
|
||||
/// The interest this waiter is waiting on
|
||||
/// The interest this waiter is waiting on.
|
||||
interest: Interest,
|
||||
|
||||
is_ready: bool,
|
||||
|
||||
/// Should never be `!Unpin`
|
||||
/// Should never be `!Unpin`.
|
||||
_p: PhantomPinned,
|
||||
}
|
||||
|
||||
/// Future returned by `readiness()`
|
||||
/// Future returned by `readiness()`.
|
||||
struct Readiness<'a> {
|
||||
scheduled_io: &'a ScheduledIo,
|
||||
|
||||
@@ -276,7 +276,7 @@ impl ScheduledIo {
|
||||
}
|
||||
}
|
||||
|
||||
/// Poll version of checking readiness for a certain direction.
|
||||
/// Polls for readiness events in a given direction.
|
||||
///
|
||||
/// These are to support `AsyncRead` and `AsyncWrite` polling methods,
|
||||
/// which cannot use the `async fn` version. This uses reserved reader
|
||||
@@ -363,7 +363,7 @@ unsafe impl Sync for ScheduledIo {}
|
||||
|
||||
cfg_io_readiness! {
|
||||
impl ScheduledIo {
|
||||
/// An async version of `poll_readiness` which uses a linked list of wakers
|
||||
/// An async version of `poll_readiness` which uses a linked list of wakers.
|
||||
pub(crate) async fn readiness(&self, interest: Interest) -> ReadyEvent {
|
||||
self.readiness_fut(interest).await
|
||||
}
|
||||
|
||||
@@ -217,6 +217,15 @@ cfg_io_driver_impl! {
|
||||
pub(crate) use poll_evented::PollEvented;
|
||||
}
|
||||
|
||||
cfg_aio! {
|
||||
/// BSD-specific I/O types.
|
||||
pub mod bsd {
|
||||
mod poll_aio;
|
||||
|
||||
pub use poll_aio::{Aio, AioEvent, AioSource};
|
||||
}
|
||||
}
|
||||
|
||||
cfg_net_unix! {
|
||||
mod async_fd;
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ impl<E: Source> PollEvented<E> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a reference to the registration
|
||||
/// Returns a reference to the registration.
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
all(unix, feature = "process"),
|
||||
@@ -123,7 +123,7 @@ impl<E: Source> PollEvented<E> {
|
||||
&self.registration
|
||||
}
|
||||
|
||||
/// Deregister the inner io from the registration and returns a Result containing the inner io
|
||||
/// Deregisters the inner io from the registration and returns a Result containing the inner io.
|
||||
#[cfg(any(feature = "net", feature = "process"))]
|
||||
pub(crate) fn into_inner(mut self) -> io::Result<E> {
|
||||
let mut inner = self.io.take().unwrap(); // As io shouldn't ever be None, just unwrap here.
|
||||
|
||||
@@ -90,7 +90,7 @@ impl<T> ReadHalf<T> {
|
||||
}
|
||||
|
||||
impl<T> WriteHalf<T> {
|
||||
/// Check if this `WriteHalf` and some `ReadHalf` were split from the same
|
||||
/// Checks if this `WriteHalf` and some `ReadHalf` were split from the same
|
||||
/// stream.
|
||||
pub fn is_pair_of(&self, other: &ReadHalf<T>) -> bool {
|
||||
Arc::ptr_eq(&self.inner, &other.inner)
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::task::{Context, Poll};
|
||||
/// if buffer contents seems to be utf8. Otherwise it only trims buffer down to MAX_BUF.
|
||||
/// That's why, wrapped writer will always receive well-formed utf-8 bytes.
|
||||
/// # Other platforms
|
||||
/// passes data to `inner` as is
|
||||
/// Passes data to `inner` as is.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SplitByUtf8BoundaryIfWindows<W> {
|
||||
inner: W,
|
||||
|
||||
@@ -67,6 +67,16 @@ cfg_io_util! {
|
||||
seek(self, pos)
|
||||
}
|
||||
|
||||
/// Creates a future which will rewind to the beginning of the stream.
|
||||
///
|
||||
/// This is convenience method, equivalent to to `self.seek(SeekFrom::Start(0))`.
|
||||
fn rewind(&mut self) -> Seek<'_, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
self.seek(SeekFrom::Start(0))
|
||||
}
|
||||
|
||||
/// Creates a future which will return the current seek position from the
|
||||
/// start of the stream.
|
||||
///
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::io::IoSlice;
|
||||
use bytes::Buf;
|
||||
|
||||
cfg_io_util! {
|
||||
/// Defines numeric writer
|
||||
/// Defines numeric writer.
|
||||
macro_rules! write_impl {
|
||||
(
|
||||
$(
|
||||
@@ -256,7 +256,7 @@ cfg_io_util! {
|
||||
write_buf(self, src)
|
||||
}
|
||||
|
||||
/// Attempts to write an entire buffer into this writer
|
||||
/// Attempts to write an entire buffer into this writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
@@ -353,9 +353,9 @@ cfg_io_util! {
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut buffer = File::create("foo.txt").await?;
|
||||
/// let mut file = File::create("foo.txt").await?;
|
||||
///
|
||||
/// buffer.write_all(b"some bytes").await?;
|
||||
/// file.write_all(b"some bytes").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@@ -155,7 +155,7 @@ pub(super) enum SeekState {
|
||||
Pending,
|
||||
}
|
||||
|
||||
/// Seek to an offset, in bytes, in the underlying reader.
|
||||
/// Seeks to an offset, in bytes, in the underlying reader.
|
||||
///
|
||||
/// The position used for seeking with `SeekFrom::Current(_)` is the
|
||||
/// position the underlying reader would be at if the `BufReader` had no
|
||||
|
||||
@@ -23,7 +23,7 @@ impl CopyBuffer {
|
||||
pos: 0,
|
||||
cap: 0,
|
||||
amt: 0,
|
||||
buf: vec![0; 2048].into_boxed_slice(),
|
||||
buf: vec![0; super::DEFAULT_BUF_SIZE].into_boxed_slice(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,6 +84,14 @@ impl CopyBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
// If pos larger than cap, this loop will never stop.
|
||||
// In particular, user's wrong poll_write implementation returning
|
||||
// incorrect written length may lead to thread blocking.
|
||||
debug_assert!(
|
||||
self.pos <= self.cap,
|
||||
"writer returned length larger than input slice"
|
||||
);
|
||||
|
||||
// If we've written all the data and we've seen EOF, flush out the
|
||||
// data and finish the transfer.
|
||||
if self.pos == self.cap && self.read_done {
|
||||
|
||||
@@ -8,7 +8,7 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Read lines from an [`AsyncBufRead`].
|
||||
/// Reads lines from an [`AsyncBufRead`].
|
||||
///
|
||||
/// A `Lines` can be turned into a `Stream` with [`LinesStream`].
|
||||
///
|
||||
@@ -72,12 +72,12 @@ where
|
||||
poll_fn(|cx| Pin::new(&mut *self).poll_next_line(cx)).await
|
||||
}
|
||||
|
||||
/// Obtain a mutable reference to the underlying reader
|
||||
/// Obtains a mutable reference to the underlying reader.
|
||||
pub fn get_mut(&mut self) -> &mut R {
|
||||
&mut self.reader
|
||||
}
|
||||
|
||||
/// Obtain a reference to the underlying reader
|
||||
/// Obtains a reference to the underlying reader.
|
||||
pub fn get_ref(&mut self) -> &R {
|
||||
&self.reader
|
||||
}
|
||||
|
||||
@@ -86,7 +86,11 @@ impl<R: AsyncRead> AsyncRead for Take<R> {
|
||||
|
||||
let me = self.project();
|
||||
let mut b = buf.take(*me.limit_ as usize);
|
||||
|
||||
let buf_ptr = b.filled().as_ptr();
|
||||
ready!(me.inner.poll_read(cx, &mut b))?;
|
||||
assert_eq!(b.filled().as_ptr(), buf_ptr);
|
||||
|
||||
let n = b.filled().len();
|
||||
|
||||
// We need to update the original ReadBuf
|
||||
|
||||
+37
-7
@@ -16,6 +16,10 @@
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg_hide))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(loom)))]
|
||||
#![cfg_attr(docsrs, doc(cfg_hide(not(loom))))]
|
||||
#![cfg_attr(docsrs, allow(unused_attributes))]
|
||||
|
||||
//! A runtime for writing reliable network applications without compromising speed.
|
||||
@@ -161,8 +165,8 @@
|
||||
//! [`tokio::runtime`]: crate::runtime
|
||||
//! [`Builder`]: crate::runtime::Builder
|
||||
//! [`Runtime`]: crate::runtime::Runtime
|
||||
//! [rt]: runtime/index.html#basic-scheduler
|
||||
//! [rt-multi-thread]: runtime/index.html#threaded-scheduler
|
||||
//! [rt]: runtime/index.html#current-thread-scheduler
|
||||
//! [rt-multi-thread]: runtime/index.html#multi-thread-scheduler
|
||||
//! [rt-features]: runtime/index.html#runtime-scheduler
|
||||
//!
|
||||
//! ## CPU-bound tasks and blocking code
|
||||
@@ -205,9 +209,15 @@
|
||||
//! ```
|
||||
//!
|
||||
//! If your code is CPU-bound and you wish to limit the number of threads used
|
||||
//! to run it, you should run it on another thread pool such as [rayon]. You
|
||||
//! can use an [`oneshot`] channel to send the result back to Tokio when the
|
||||
//! rayon task finishes.
|
||||
//! to run it, you should use a separate thread pool dedicated to CPU bound tasks.
|
||||
//! For example, you could consider using the [rayon] library for CPU-bound
|
||||
//! tasks. It is also possible to create an extra Tokio runtime dedicated to
|
||||
//! CPU-bound tasks, but if you do this, you should be careful that the extra
|
||||
//! runtime runs _only_ CPU-bound tasks, as IO-bound tasks on that runtime
|
||||
//! will behave poorly.
|
||||
//!
|
||||
//! Hint: If using rayon, you can use a [`oneshot`] channel to send the result back
|
||||
//! to Tokio when the rayon task finishes.
|
||||
//!
|
||||
//! [rayon]: https://docs.rs/rayon
|
||||
//! [`oneshot`]: crate::sync::oneshot
|
||||
@@ -308,8 +318,9 @@
|
||||
//! - `rt-multi-thread`: Enables the heavier, multi-threaded, work-stealing scheduler.
|
||||
//! - `io-util`: Enables the IO based `Ext` traits.
|
||||
//! - `io-std`: Enable `Stdout`, `Stdin` and `Stderr` types.
|
||||
//! - `net`: Enables `tokio::net` types such as `TcpStream`, `UnixStream` and `UdpSocket`,
|
||||
//! as well as (on Unix-like systems) `AsyncFd`
|
||||
//! - `net`: Enables `tokio::net` types such as `TcpStream`, `UnixStream` and
|
||||
//! `UdpSocket`, as well as (on Unix-like systems) `AsyncFd` and (on
|
||||
//! FreeBSD) `PollAio`.
|
||||
//! - `time`: Enables `tokio::time` types and allows the schedulers to enable
|
||||
//! the built in timer.
|
||||
//! - `process`: Enables `tokio::process` types.
|
||||
@@ -343,6 +354,19 @@
|
||||
//!
|
||||
//! [feature flags]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
|
||||
|
||||
// Test that pointer width is compatible. This asserts that e.g. usize is at
|
||||
// least 32 bits, which a lot of components in Tokio currently assumes.
|
||||
//
|
||||
// TODO: improve once we have MSRV access to const eval to make more flexible.
|
||||
#[cfg(not(any(
|
||||
target_pointer_width = "32",
|
||||
target_pointer_width = "64",
|
||||
target_pointer_width = "128"
|
||||
)))]
|
||||
compile_error! {
|
||||
"Tokio requires the platform pointer width to be 32, 64, or 128 bits"
|
||||
}
|
||||
|
||||
// Includes re-exports used by macros.
|
||||
//
|
||||
// This module is not intended to be part of the public API. In general, any
|
||||
@@ -473,6 +497,12 @@ cfg_macros! {
|
||||
#[doc(hidden)]
|
||||
pub use tokio_macros::select_priv_declare_output_enum;
|
||||
|
||||
/// Implementation detail of the `select!` macro. This macro is **not**
|
||||
/// intended to be used as part of the public API and is permitted to
|
||||
/// change.
|
||||
#[doc(hidden)]
|
||||
pub use tokio_macros::select_priv_clean_pattern;
|
||||
|
||||
cfg_rt! {
|
||||
#[cfg(feature = "rt-multi-thread")]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
|
||||
@@ -93,4 +93,17 @@ pub(crate) mod sys {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) use std::thread;
|
||||
pub(crate) mod thread {
|
||||
#[inline]
|
||||
pub(crate) fn yield_now() {
|
||||
// TODO: once we bump MSRV to 1.49+, use `hint::spin_loop` instead.
|
||||
#[allow(deprecated)]
|
||||
std::sync::atomic::spin_loop_hint();
|
||||
}
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use std::thread::{
|
||||
current, panicking, park, park_timeout, sleep, spawn, Builder, JoinHandle, LocalKey,
|
||||
Result, Thread, ThreadId,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::{self, MutexGuard, TryLockError};
|
||||
|
||||
/// Adapter for `std::Mutex` that removes the poisoning aspects
|
||||
// from its api
|
||||
/// from its api.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Mutex<T: ?Sized>(sync::Mutex<T>);
|
||||
|
||||
|
||||
+19
-4
@@ -13,7 +13,7 @@ macro_rules! feature {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables enter::block_on
|
||||
/// Enables enter::block_on.
|
||||
macro_rules! cfg_block_on {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -28,7 +28,7 @@ macro_rules! cfg_block_on {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enables internal `AtomicWaker` impl
|
||||
/// Enables internal `AtomicWaker` impl.
|
||||
macro_rules! cfg_atomic_waker_impl {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -45,6 +45,18 @@ macro_rules! cfg_atomic_waker_impl {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_aio {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(all(any(docsrs, target_os = "freebsd"), feature = "net"))]
|
||||
#[cfg_attr(docsrs,
|
||||
doc(cfg(all(target_os = "freebsd", feature = "net")))
|
||||
)]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_fs {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -87,6 +99,7 @@ macro_rules! cfg_io_driver_impl {
|
||||
feature = "process",
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all())))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
@@ -410,7 +423,8 @@ macro_rules! cfg_has_atomic_u64 {
|
||||
#[cfg(not(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc"
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32"
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
@@ -423,7 +437,8 @@ macro_rules! cfg_not_has_atomic_u64 {
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc"
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32"
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// Wait on multiple concurrent branches, returning when **all** branches
|
||||
/// Waits on multiple concurrent branches, returning when **all** branches
|
||||
/// complete.
|
||||
///
|
||||
/// The `join!` macro must be used inside of async functions, closures, and
|
||||
|
||||
@@ -15,6 +15,11 @@ mod ready;
|
||||
#[macro_use]
|
||||
mod thread_local;
|
||||
|
||||
cfg_trace! {
|
||||
#[macro_use]
|
||||
mod trace;
|
||||
}
|
||||
|
||||
#[macro_use]
|
||||
#[cfg(feature = "rt")]
|
||||
pub(crate) mod scoped_tls;
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::loom::thread::LocalKey;
|
||||
use std::cell::Cell;
|
||||
use std::marker;
|
||||
|
||||
/// Set a reference as a thread-local
|
||||
/// Sets a reference as a thread-local.
|
||||
macro_rules! scoped_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty) => (
|
||||
$(#[$attrs])*
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// Wait on multiple concurrent branches, returning when the **first** branch
|
||||
/// Waits on multiple concurrent branches, returning when the **first** branch
|
||||
/// completes, cancelling the remaining branches.
|
||||
///
|
||||
/// The `select!` macro must be used inside of async functions, closures, and
|
||||
@@ -502,7 +502,7 @@ macro_rules! select {
|
||||
let mut fut = unsafe { Pin::new_unchecked(fut) };
|
||||
|
||||
// Try polling it
|
||||
let out = match fut.poll(cx) {
|
||||
let out = match Future::poll(fut, cx) {
|
||||
Ready(out) => out,
|
||||
Pending => {
|
||||
// Track that at least one future is
|
||||
@@ -520,7 +520,7 @@ macro_rules! select {
|
||||
#[allow(unused_variables)]
|
||||
#[allow(unused_mut)]
|
||||
match &out {
|
||||
$bind => {}
|
||||
$crate::select_priv_clean_pattern!($bind) => {}
|
||||
_ => continue,
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
cfg_trace! {
|
||||
macro_rules! trace_op {
|
||||
($name:literal, $readiness:literal, $parent:expr) => {
|
||||
tracing::trace!(
|
||||
target: "runtime::resource::poll_op",
|
||||
parent: $parent,
|
||||
op_name = $name,
|
||||
is_ready = $readiness
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! trace_poll_op {
|
||||
($name:literal, $poll:expr, $parent:expr $(,)*) => {
|
||||
match $poll {
|
||||
std::task::Poll::Ready(t) => {
|
||||
trace_op!($name, true, $parent);
|
||||
std::task::Poll::Ready(t)
|
||||
}
|
||||
std::task::Poll::Pending => {
|
||||
trace_op!($name, false, $parent);
|
||||
return std::task::Poll::Pending;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
/// Wait on multiple concurrent branches, returning when **all** branches
|
||||
/// Waits on multiple concurrent branches, returning when **all** branches
|
||||
/// complete with `Ok(_)` or on the first `Err(_)`.
|
||||
///
|
||||
/// The `try_join!` macro must be used inside of async functions, closures, and
|
||||
@@ -59,6 +59,45 @@
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Using `try_join!` with spawned tasks.
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::task::JoinHandle;
|
||||
///
|
||||
/// async fn do_stuff_async() -> Result<(), &'static str> {
|
||||
/// // async work
|
||||
/// # Err("failed")
|
||||
/// }
|
||||
///
|
||||
/// async fn more_async_work() -> Result<(), &'static str> {
|
||||
/// // more here
|
||||
/// # Ok(())
|
||||
/// }
|
||||
///
|
||||
/// async fn flatten<T>(handle: JoinHandle<Result<T, &'static str>>) -> Result<T, &'static str> {
|
||||
/// match handle.await {
|
||||
/// Ok(Ok(result)) => Ok(result),
|
||||
/// Ok(Err(err)) => Err(err),
|
||||
/// Err(err) => Err("handling failed"),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let handle1 = tokio::spawn(do_stuff_async());
|
||||
/// let handle2 = tokio::spawn(more_async_work());
|
||||
/// match tokio::try_join!(flatten(handle1), flatten(handle2)) {
|
||||
/// Ok(val) => {
|
||||
/// // do something with the values
|
||||
/// }
|
||||
/// Err(err) => {
|
||||
/// println!("Failed with {}.", err);
|
||||
/// # assert_eq!(err, "failed");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
macro_rules! try_join {
|
||||
|
||||
@@ -227,7 +227,7 @@ impl TcpListener {
|
||||
Ok(TcpListener { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::TcpListener`] into a [`std::net::TcpListener`].
|
||||
/// Turns a [`tokio::net::TcpListener`] into a [`std::net::TcpListener`].
|
||||
///
|
||||
/// The returned [`std::net::TcpListener`] will have nonblocking mode set as
|
||||
/// `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! TCP utility types
|
||||
//! TCP utility types.
|
||||
|
||||
pub(crate) mod listener;
|
||||
|
||||
|
||||
+10
-10
@@ -87,7 +87,7 @@ cfg_net! {
|
||||
}
|
||||
|
||||
impl TcpSocket {
|
||||
/// Create a new socket configured for IPv4.
|
||||
/// Creates a new socket configured for IPv4.
|
||||
///
|
||||
/// Calls `socket(2)` with `AF_INET` and `SOCK_STREAM`.
|
||||
///
|
||||
@@ -121,7 +121,7 @@ impl TcpSocket {
|
||||
Ok(TcpSocket { inner })
|
||||
}
|
||||
|
||||
/// Create a new socket configured for IPv6.
|
||||
/// Creates a new socket configured for IPv6.
|
||||
///
|
||||
/// Calls `socket(2)` with `AF_INET6` and `SOCK_STREAM`.
|
||||
///
|
||||
@@ -155,7 +155,7 @@ impl TcpSocket {
|
||||
Ok(TcpSocket { inner })
|
||||
}
|
||||
|
||||
/// Allow the socket to bind to an in-use address.
|
||||
/// Allows the socket to bind to an in-use address.
|
||||
///
|
||||
/// Behavior is platform specific. Refer to the target platform's
|
||||
/// documentation for more details.
|
||||
@@ -185,7 +185,7 @@ impl TcpSocket {
|
||||
self.inner.set_reuseaddr(reuseaddr)
|
||||
}
|
||||
|
||||
/// Retrieves the value set for `SO_REUSEADDR` on this socket
|
||||
/// Retrieves the value set for `SO_REUSEADDR` on this socket.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -211,7 +211,7 @@ impl TcpSocket {
|
||||
self.inner.get_reuseaddr()
|
||||
}
|
||||
|
||||
/// Allow the socket to bind to an in-use port. Only available for unix systems
|
||||
/// Allows the socket to bind to an in-use port. Only available for unix systems
|
||||
/// (excluding Solaris & Illumos).
|
||||
///
|
||||
/// Behavior is platform specific. Refer to the target platform's
|
||||
@@ -245,7 +245,7 @@ impl TcpSocket {
|
||||
self.inner.set_reuseport(reuseport)
|
||||
}
|
||||
|
||||
/// Allow the socket to bind to an in-use port. Only available for unix systems
|
||||
/// Allows the socket to bind to an in-use port. Only available for unix systems
|
||||
/// (excluding Solaris & Illumos).
|
||||
///
|
||||
/// Behavior is platform specific. Refer to the target platform's
|
||||
@@ -348,7 +348,7 @@ impl TcpSocket {
|
||||
self.inner.get_recv_buffer_size()
|
||||
}
|
||||
|
||||
/// Get the local address of this socket.
|
||||
/// Gets the local address of this socket.
|
||||
///
|
||||
/// Will fail on windows if called before `bind`.
|
||||
///
|
||||
@@ -374,7 +374,7 @@ impl TcpSocket {
|
||||
self.inner.get_localaddr()
|
||||
}
|
||||
|
||||
/// Bind the socket to the given address.
|
||||
/// Binds the socket to the given address.
|
||||
///
|
||||
/// This calls the `bind(2)` operating-system function. Behavior is
|
||||
/// platform specific. Refer to the target platform's documentation for more
|
||||
@@ -406,7 +406,7 @@ impl TcpSocket {
|
||||
self.inner.bind(addr)
|
||||
}
|
||||
|
||||
/// Establish a TCP connection with a peer at the specified socket address.
|
||||
/// Establishes a TCP connection with a peer at the specified socket address.
|
||||
///
|
||||
/// The `TcpSocket` is consumed. Once the connection is established, a
|
||||
/// connected [`TcpStream`] is returned. If the connection fails, the
|
||||
@@ -443,7 +443,7 @@ impl TcpSocket {
|
||||
TcpStream::connect_mio(mio).await
|
||||
}
|
||||
|
||||
/// Convert the socket into a `TcpListener`.
|
||||
/// Converts the socket into a `TcpListener`.
|
||||
///
|
||||
/// `backlog` defines the maximum number of pending connections are queued
|
||||
/// by the operating system at any given time. Connection are removed from
|
||||
|
||||
+212
-3
@@ -9,14 +9,18 @@
|
||||
//! level.
|
||||
|
||||
use crate::future::poll_fn;
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::TcpStream;
|
||||
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Borrowed read half of a [`TcpStream`], created by [`split`].
|
||||
///
|
||||
/// Reading from a `ReadHalf` is usually done using the convenience methods found on the
|
||||
@@ -49,7 +53,7 @@ pub(crate) fn split(stream: &mut TcpStream) -> (ReadHalf<'_>, WriteHalf<'_>) {
|
||||
}
|
||||
|
||||
impl ReadHalf<'_> {
|
||||
/// Attempt to receive data on the socket, without removing that data from
|
||||
/// Attempts to receive data on the socket, without removing that data from
|
||||
/// the queue, registering the current task for wakeup if data is not yet
|
||||
/// available.
|
||||
///
|
||||
@@ -134,6 +138,211 @@ impl ReadHalf<'_> {
|
||||
let mut buf = ReadBuf::new(buf);
|
||||
poll_fn(|cx| self.poll_peek(cx, &mut buf)).await
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// This function is also equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.0.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.try_read(buf)
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.0.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.0.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.try_write_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for ReadHalf<'_> {
|
||||
|
||||
@@ -9,16 +9,20 @@
|
||||
//! level.
|
||||
|
||||
use crate::future::poll_fn;
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::TcpStream;
|
||||
|
||||
use std::error::Error;
|
||||
use std::net::Shutdown;
|
||||
use std::net::{Shutdown, SocketAddr};
|
||||
use std::pin::Pin;
|
||||
use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, io};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Owned read half of a [`TcpStream`], created by [`into_split`].
|
||||
///
|
||||
/// Reading from an `OwnedReadHalf` is usually done using the convenience methods found
|
||||
@@ -189,6 +193,128 @@ impl OwnedReadHalf {
|
||||
let mut buf = ReadBuf::new(buf);
|
||||
poll_fn(|cx| self.poll_peek(cx, &mut buf)).await
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// This function is also equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.inner.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.try_read(buf)
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.inner.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for OwnedReadHalf {
|
||||
@@ -211,13 +337,94 @@ impl OwnedWriteHalf {
|
||||
reunite(other, self)
|
||||
}
|
||||
|
||||
/// Destroy the write half, but don't close the write half of the stream
|
||||
/// Destroys the write half, but don't close the write half of the stream
|
||||
/// until the read half is dropped. If the read half has already been
|
||||
/// dropped, this closes the stream.
|
||||
pub fn forget(mut self) {
|
||||
self.shutdown_on_drop = false;
|
||||
drop(self);
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.inner.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_write_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the remote address that this stream is connected to.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the local address that this stream is bound to.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedWriteHalf {
|
||||
|
||||
@@ -192,7 +192,7 @@ impl TcpStream {
|
||||
Ok(TcpStream { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::TcpStream`] into a [`std::net::TcpStream`].
|
||||
/// Turns a [`tokio::net::TcpStream`] into a [`std::net::TcpStream`].
|
||||
///
|
||||
/// The returned [`std::net::TcpStream`] will have nonblocking mode set as `true`.
|
||||
/// Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
@@ -350,7 +350,7 @@ impl TcpStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
@@ -422,7 +422,7 @@ impl TcpStream {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -510,7 +510,7 @@ impl TcpStream {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffer, returning how
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
@@ -577,7 +577,7 @@ impl TcpStream {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffers, returning
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -656,7 +656,7 @@ impl TcpStream {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to read data from the stream into the provided buffer, advancing the
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
@@ -734,7 +734,7 @@ impl TcpStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -874,7 +874,7 @@ impl TcpStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the stream, returning how many bytes
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -936,7 +936,7 @@ impl TcpStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(bufs))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
|
||||
+78
-12
@@ -12,7 +12,7 @@ cfg_io_util! {
|
||||
}
|
||||
|
||||
cfg_net! {
|
||||
/// A UDP socket
|
||||
/// A UDP socket.
|
||||
///
|
||||
/// UDP is "connectionless", unlike TCP. Meaning, regardless of what address you've bound to, a `UdpSocket`
|
||||
/// is free to communicate with many different remotes. In tokio there are basically two main ways to use `UdpSocket`:
|
||||
@@ -211,7 +211,7 @@ impl UdpSocket {
|
||||
UdpSocket::new(io)
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UdpSocket`] into a [`std::net::UdpSocket`].
|
||||
/// Turns a [`tokio::net::UdpSocket`] into a [`std::net::UdpSocket`].
|
||||
///
|
||||
/// The returned [`std::net::UdpSocket`] will have nonblocking mode set as
|
||||
/// `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
@@ -317,7 +317,7 @@ impl UdpSocket {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_recv()` or `try_send()`. It
|
||||
/// can be used to concurrently recv / send to the same socket on a single
|
||||
@@ -388,7 +388,7 @@ impl UdpSocket {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is
|
||||
/// usually paired with `try_send()` or `try_send_to()`.
|
||||
@@ -443,6 +443,39 @@ impl UdpSocket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Polls for write/send readiness.
|
||||
///
|
||||
/// If the udp stream is not currently ready for sending, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the udp
|
||||
/// stream becomes ready for sending, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_send_ready` or `poll_send`, only
|
||||
/// the `Waker` from the `Context` passed to the most recent call is
|
||||
/// scheduled to receive a wakeup. (However, `poll_recv_ready` retains a
|
||||
/// second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`writable`] is not feasible. Where possible, using [`writable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the udp stream is not ready for writing.
|
||||
/// * `Poll::Ready(Ok(()))` if the udp stream is ready for writing.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`writable`]: method@Self::writable
|
||||
pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Sends data on the socket to the remote address that the socket is
|
||||
/// connected to.
|
||||
///
|
||||
@@ -516,7 +549,7 @@ impl UdpSocket {
|
||||
.poll_write_io(cx, || self.io.send(buf))
|
||||
}
|
||||
|
||||
/// Try to send data on the socket to the remote address to which it is
|
||||
/// Tries to send data on the socket to the remote address to which it is
|
||||
/// connected.
|
||||
///
|
||||
/// When the socket buffer is full, `Err(io::ErrorKind::WouldBlock)` is
|
||||
@@ -570,7 +603,7 @@ impl UdpSocket {
|
||||
.try_io(Interest::WRITABLE, || self.io.send(buf))
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_recv()`.
|
||||
@@ -630,6 +663,39 @@ impl UdpSocket {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Polls for read/receive readiness.
|
||||
///
|
||||
/// If the udp stream is not currently ready for receiving, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the udp
|
||||
/// socket becomes ready for reading, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_recv_ready`, `poll_recv` or
|
||||
/// `poll_peek`, only the `Waker` from the `Context` passed to the most
|
||||
/// recent call is scheduled to receive a wakeup. (However,
|
||||
/// `poll_send_ready` retains a second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`readable`] is not feasible. Where possible, using [`readable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the udp stream is not ready for reading.
|
||||
/// * `Poll::Ready(Ok(()))` if the udp stream is ready for reading.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`readable`]: method@Self::readable
|
||||
pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Receives a single datagram message on the socket from the remote address
|
||||
/// to which it is connected. On success, returns the number of bytes read.
|
||||
///
|
||||
@@ -715,7 +781,7 @@ impl UdpSocket {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Try to receive a single datagram message on the socket from the remote
|
||||
/// Tries to receive a single datagram message on the socket from the remote
|
||||
/// address to which it is connected. On success, returns the number of
|
||||
/// bytes read.
|
||||
///
|
||||
@@ -772,7 +838,7 @@ impl UdpSocket {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to receive data from the stream into the provided buffer, advancing the
|
||||
/// Tries to receive data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
@@ -837,7 +903,7 @@ impl UdpSocket {
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to receive a single datagram message on the socket. On success,
|
||||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
@@ -978,7 +1044,7 @@ impl UdpSocket {
|
||||
.poll_write_io(cx, || self.io.send_to(buf, target))
|
||||
}
|
||||
|
||||
/// Try to send data on the socket to the given address, but if the send is
|
||||
/// Tries to send data on the socket to the given address, but if the send is
|
||||
/// blocked this will return right away.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
@@ -1116,7 +1182,7 @@ impl UdpSocket {
|
||||
Poll::Ready(Ok(addr))
|
||||
}
|
||||
|
||||
/// Try to receive a single datagram message on the socket. On success,
|
||||
/// Tries to receive a single datagram message on the socket. On success,
|
||||
/// returns the number of bytes read and the origin.
|
||||
///
|
||||
/// The function must be called with valid byte array buf of sufficient size
|
||||
@@ -1170,7 +1236,7 @@ impl UdpSocket {
|
||||
.try_io(Interest::READABLE, || self.io.recv_from(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
|
||||
@@ -96,7 +96,7 @@ cfg_net_unix! {
|
||||
}
|
||||
|
||||
impl UnixDatagram {
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_recv()` or `try_send()`. It
|
||||
/// can be used to concurrently recv / send to the same socket on a single
|
||||
@@ -169,7 +169,7 @@ impl UnixDatagram {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is
|
||||
/// usually paired with `try_send()` or `try_send_to()`.
|
||||
@@ -226,7 +226,40 @@ impl UnixDatagram {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Polls for write/send readiness.
|
||||
///
|
||||
/// If the socket is not currently ready for sending, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the socket
|
||||
/// becomes ready for sending, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_send_ready` or `poll_send`, only
|
||||
/// the `Waker` from the `Context` passed to the most recent call is
|
||||
/// scheduled to receive a wakeup. (However, `poll_recv_ready` retains a
|
||||
/// second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`writable`] is not feasible. Where possible, using [`writable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the socket is not ready for writing.
|
||||
/// * `Poll::Ready(Ok(()))` if the socket is ready for writing.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`writable`]: method@Self::writable
|
||||
pub fn poll_send_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_recv()`.
|
||||
@@ -289,6 +322,39 @@ impl UnixDatagram {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Polls for read/receive readiness.
|
||||
///
|
||||
/// If the socket is not currently ready for receiving, this method will
|
||||
/// store a clone of the `Waker` from the provided `Context`. When the
|
||||
/// socket becomes ready for reading, `Waker::wake` will be called on the
|
||||
/// waker.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_recv_ready`, `poll_recv` or
|
||||
/// `poll_peek`, only the `Waker` from the `Context` passed to the most
|
||||
/// recent call is scheduled to receive a wakeup. (However,
|
||||
/// `poll_send_ready` retains a second, independent waker.)
|
||||
///
|
||||
/// This function is intended for cases where creating and pinning a future
|
||||
/// via [`readable`] is not feasible. Where possible, using [`readable`] is
|
||||
/// preferred, as this supports polling from multiple tasks at once.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// The function returns:
|
||||
///
|
||||
/// * `Poll::Pending` if the socket is not ready for reading.
|
||||
/// * `Poll::Ready(Ok(()))` if the socket is ready for reading.
|
||||
/// * `Poll::Ready(Err(e))` if an error is encountered.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may encounter any standard I/O error except `WouldBlock`.
|
||||
///
|
||||
/// [`readable`]: method@Self::readable
|
||||
pub fn poll_recv_ready(&self, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Creates a new `UnixDatagram` bound to the specified path.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -397,7 +463,7 @@ impl UnixDatagram {
|
||||
Ok(UnixDatagram { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UnixDatagram`] into a [`std::os::unix::net::UnixDatagram`].
|
||||
/// Turns a [`tokio::net::UnixDatagram`] into a [`std::os::unix::net::UnixDatagram`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixDatagram`] will have nonblocking
|
||||
/// mode set as `true`. Use [`set_nonblocking`] to change the blocking mode
|
||||
@@ -548,7 +614,7 @@ impl UnixDatagram {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Try to send a datagram to the peer without waiting.
|
||||
/// Tries to send a datagram to the peer without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -592,7 +658,7 @@ impl UnixDatagram {
|
||||
.try_io(Interest::WRITABLE, || self.io.send(buf))
|
||||
}
|
||||
|
||||
/// Try to send a datagram to the peer without waiting.
|
||||
/// Tries to send a datagram to the peer without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -678,7 +744,7 @@ impl UnixDatagram {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Try to receive a datagram from the peer without waiting.
|
||||
/// Tries to receive a datagram from the peer without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -729,7 +795,7 @@ impl UnixDatagram {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to receive data from the socket without waiting.
|
||||
/// Tries to receive data from the socket without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -790,7 +856,7 @@ impl UnixDatagram {
|
||||
Ok((n, SocketAddr(addr)))
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffer, advancing the
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// # Examples
|
||||
@@ -1091,7 +1157,7 @@ impl UnixDatagram {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
/// Try to receive data from the socket without waiting.
|
||||
/// Tries to receive data from the socket without waiting.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -1143,7 +1209,7 @@ impl UnixDatagram {
|
||||
Ok((n, SocketAddr(addr)))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
|
||||
@@ -88,7 +88,7 @@ impl UnixListener {
|
||||
Ok(UnixListener { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UnixListener`] into a [`std::os::unix::net::UnixListener`].
|
||||
/// Turns a [`tokio::net::UnixListener`] into a [`std::os::unix::net::UnixListener`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixListener`] will have nonblocking mode
|
||||
/// set as `true`. Use [`set_nonblocking`] to change the blocking mode if needed.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Unix domain socket utility types
|
||||
//! Unix domain socket utility types.
|
||||
|
||||
// This module does not currently provide any public API, but it was
|
||||
// unintentionally defined as a public module. Hide it from the documentation
|
||||
|
||||
+206
-1
@@ -8,14 +8,19 @@
|
||||
//! split has no associated overhead and enforces all invariants at the type
|
||||
//! level.
|
||||
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::UnixStream;
|
||||
|
||||
use crate::net::unix::SocketAddr;
|
||||
use std::io;
|
||||
use std::net::Shutdown;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Borrowed read half of a [`UnixStream`], created by [`split`].
|
||||
///
|
||||
/// Reading from a `ReadHalf` is usually done using the convenience methods found on the
|
||||
@@ -47,6 +52,206 @@ pub(crate) fn split(stream: &mut UnixStream) -> (ReadHalf<'_>, WriteHalf<'_>) {
|
||||
(ReadHalf(stream), WriteHalf(stream))
|
||||
}
|
||||
|
||||
impl ReadHalf<'_> {
|
||||
/// Wait for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.0.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.0.try_read(buf)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.0.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.0.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl WriteHalf<'_> {
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.0.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.0.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.0.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, buf: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.0.try_write_vectored(buf)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.0.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for ReadHalf<'_> {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
//! split has no associated overhead and enforces all invariants at the type
|
||||
//! level.
|
||||
|
||||
use crate::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncRead, AsyncWrite, Interest, ReadBuf, Ready};
|
||||
use crate::net::UnixStream;
|
||||
|
||||
use crate::net::unix::SocketAddr;
|
||||
use std::error::Error;
|
||||
use std::net::Shutdown;
|
||||
use std::pin::Pin;
|
||||
@@ -18,6 +19,10 @@ use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{fmt, io};
|
||||
|
||||
cfg_io_util! {
|
||||
use bytes::BufMut;
|
||||
}
|
||||
|
||||
/// Owned read half of a [`UnixStream`], created by [`into_split`].
|
||||
///
|
||||
/// Reading from an `OwnedReadHalf` is usually done using the convenience methods found
|
||||
@@ -102,6 +107,124 @@ impl OwnedReadHalf {
|
||||
pub fn reunite(self, other: OwnedWriteHalf) -> Result<UnixStream, ReuniteError> {
|
||||
reunite(self, other)
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn readable(&self) -> io::Result<()> {
|
||||
self.inner.readable().await
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read(&self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner.try_read(buf)
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_buf()` is non-blocking, the buffer does not have to be stored by
|
||||
/// the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_buf<B: BufMut>(&self, buf: &mut B) -> io::Result<usize> {
|
||||
self.inner.try_read_buf(buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
/// written to possibly being only partially filled. This method behaves
|
||||
/// equivalently to a single call to [`try_read()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
/// to arrive. On success, returns the number of bytes read. Because
|
||||
/// `try_read_vectored()` is non-blocking, the buffer does not have to be
|
||||
/// stored by the async task and can exist entirely on the stack.
|
||||
///
|
||||
/// Usually, [`readable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`try_read()`]: Self::try_read()
|
||||
/// [`readable()`]: Self::readable()
|
||||
/// [`ready()`]: Self::ready()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully read, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes read. `Ok(0)` indicates the stream's read half is closed
|
||||
/// and will no longer yield data. If the stream is not ready to read data
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_read_vectored(&self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_read_vectored(bufs)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for OwnedReadHalf {
|
||||
@@ -124,13 +247,92 @@ impl OwnedWriteHalf {
|
||||
reunite(other, self)
|
||||
}
|
||||
|
||||
/// Destroy the write half, but don't close the write half of the stream
|
||||
/// Destroys the write half, but don't close the write half of the stream
|
||||
/// until the read half is dropped. If the read half has already been
|
||||
/// dropped, this closes the stream.
|
||||
pub fn forget(mut self) {
|
||||
self.shutdown_on_drop = false;
|
||||
drop(self);
|
||||
}
|
||||
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to read or write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn ready(&self, interest: Interest) -> io::Result<Ready> {
|
||||
self.inner.ready(interest).await
|
||||
}
|
||||
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
/// will continue to return immediately until the readiness event is
|
||||
/// consumed by an attempt to write that fails with `WouldBlock` or
|
||||
/// `Poll::Pending`.
|
||||
pub async fn writable(&self) -> io::Result<()> {
|
||||
self.inner.writable().await
|
||||
}
|
||||
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
/// only part of the buffer may be written.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write(&self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.inner.try_write(buf)
|
||||
}
|
||||
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
/// from possible being only partially consumed. This method behaves
|
||||
/// equivalently to a single call to [`try_write()`] with concatenated
|
||||
/// buffers.
|
||||
///
|
||||
/// This function is usually paired with `writable()`.
|
||||
///
|
||||
/// [`try_write()`]: Self::try_write()
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If data is successfully written, `Ok(n)` is returned, where `n` is the
|
||||
/// number of bytes written. If the stream is not ready to write data,
|
||||
/// `Err(io::ErrorKind::WouldBlock)` is returned.
|
||||
pub fn try_write_vectored(&self, buf: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
||||
self.inner.try_write_vectored(buf)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.peer_addr()
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.inner.local_addr()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for OwnedWriteHalf {
|
||||
|
||||
@@ -59,7 +59,7 @@ impl UnixStream {
|
||||
Ok(stream)
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
@@ -133,7 +133,7 @@ impl UnixStream {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the socket to become readable.
|
||||
/// Waits for the socket to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -290,7 +290,7 @@ impl UnixStream {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the stream into the provided buffers, returning
|
||||
/// Tries to read data from the stream into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -369,7 +369,7 @@ impl UnixStream {
|
||||
}
|
||||
|
||||
cfg_io_util! {
|
||||
/// Try to read data from the stream into the provided buffer, advancing the
|
||||
/// Tries to read data from the stream into the provided buffer, advancing the
|
||||
/// buffer's internal cursor, returning how many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the socket but does not wait for new data
|
||||
@@ -449,7 +449,7 @@ impl UnixStream {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wait for the socket to become writable.
|
||||
/// Waits for the socket to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -535,7 +535,7 @@ impl UnixStream {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to write a buffer to the stream, returning how many bytes were
|
||||
/// Tries to write a buffer to the stream, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
@@ -591,7 +591,7 @@ impl UnixStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the stream, returning how many bytes
|
||||
/// Tries to write several buffers to the stream, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -653,7 +653,7 @@ impl UnixStream {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
@@ -709,7 +709,7 @@ impl UnixStream {
|
||||
Ok(UnixStream { io })
|
||||
}
|
||||
|
||||
/// Turn a [`tokio::net::UnixStream`] into a [`std::os::unix::net::UnixStream`].
|
||||
/// Turns a [`tokio::net::UnixStream`] into a [`std::os::unix::net::UnixStream`].
|
||||
///
|
||||
/// The returned [`std::os::unix::net::UnixStream`] will have nonblocking
|
||||
/// mode set as `true`. Use [`set_nonblocking`] to change the blocking
|
||||
@@ -773,11 +773,41 @@ impl UnixStream {
|
||||
}
|
||||
|
||||
/// Returns the socket address of the local half of this connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UnixStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let dir = tempfile::tempdir().unwrap();
|
||||
/// let bind_path = dir.path().join("bind_path");
|
||||
/// let stream = UnixStream::connect(bind_path).await?;
|
||||
///
|
||||
/// println!("{:?}", stream.local_addr()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn local_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.io.local_addr().map(SocketAddr)
|
||||
}
|
||||
|
||||
/// Returns the socket address of the remote half of this connection.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::UnixStream;
|
||||
///
|
||||
/// # async fn dox() -> Result<(), Box<dyn std::error::Error>> {
|
||||
/// let dir = tempfile::tempdir().unwrap();
|
||||
/// let bind_path = dir.path().join("bind_path");
|
||||
/// let stream = UnixStream::connect(bind_path).await?;
|
||||
///
|
||||
/// println!("{:?}", stream.peer_addr()?);
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn peer_addr(&self) -> io::Result<SocketAddr> {
|
||||
self.io.peer_addr().map(SocketAddr)
|
||||
}
|
||||
@@ -804,7 +834,7 @@ impl UnixStream {
|
||||
// These lifetime markers also appear in the generated documentation, and make
|
||||
// it more clear that this is a *borrowed* split.
|
||||
#[allow(clippy::needless_lifetimes)]
|
||||
/// Split a `UnixStream` into a read half and a write half, which can be used
|
||||
/// Splits a `UnixStream` into a read half and a write half, which can be used
|
||||
/// to read and write the stream concurrently.
|
||||
///
|
||||
/// This method is more efficient than [`into_split`], but the halves cannot be
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use libc::{gid_t, pid_t, uid_t};
|
||||
|
||||
/// Credentials of a process
|
||||
/// Credentials of a process.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct UCred {
|
||||
/// PID (process ID) of the process
|
||||
/// PID (process ID) of the process.
|
||||
pid: Option<pid_t>,
|
||||
/// UID (user ID) of the process
|
||||
/// UID (user ID) of the process.
|
||||
uid: uid_t,
|
||||
/// GID (group ID) of the process
|
||||
/// GID (group ID) of the process.
|
||||
gid: gid_t,
|
||||
}
|
||||
|
||||
|
||||
@@ -105,7 +105,7 @@ pub struct NamedPipeServer {
|
||||
}
|
||||
|
||||
impl NamedPipeServer {
|
||||
/// Construct a new named pipe server from the specified raw handle.
|
||||
/// Constructs a new named pipe server from the specified raw handle.
|
||||
///
|
||||
/// This function will consume ownership of the handle given, passing
|
||||
/// responsibility for closing the handle to the returned object.
|
||||
@@ -234,7 +234,7 @@ impl NamedPipeServer {
|
||||
self.io.disconnect()
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same pipe on a single
|
||||
@@ -301,7 +301,7 @@ impl NamedPipeServer {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become readable.
|
||||
/// Waits for the pipe to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -383,7 +383,7 @@ impl NamedPipeServer {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffer, returning how
|
||||
/// Tries to read data from the pipe into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the pipe but does not wait for new data
|
||||
@@ -450,7 +450,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffers, returning
|
||||
/// Tries to read data from the pipe into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -528,7 +528,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read_vectored(bufs))
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become writable.
|
||||
/// Waits for the pipe to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -606,7 +606,7 @@ impl NamedPipeServer {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to write a buffer to the pipe, returning how many bytes were
|
||||
/// Tries to write a buffer to the pipe, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
@@ -662,7 +662,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the pipe, returning how many bytes
|
||||
/// Tries to write several buffers to the pipe, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -724,7 +724,7 @@ impl NamedPipeServer {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
@@ -846,7 +846,7 @@ pub struct NamedPipeClient {
|
||||
}
|
||||
|
||||
impl NamedPipeClient {
|
||||
/// Construct a new named pipe client from the specified raw handle.
|
||||
/// Constructs a new named pipe client from the specified raw handle.
|
||||
///
|
||||
/// This function will consume ownership of the handle given, passing
|
||||
/// responsibility for closing the handle to the returned object.
|
||||
@@ -896,7 +896,7 @@ impl NamedPipeClient {
|
||||
unsafe { named_pipe_info(self.io.as_raw_handle()) }
|
||||
}
|
||||
|
||||
/// Wait for any of the requested ready states.
|
||||
/// Waits for any of the requested ready states.
|
||||
///
|
||||
/// This function is usually paired with `try_read()` or `try_write()`. It
|
||||
/// can be used to concurrently read / write to the same pipe on a single
|
||||
@@ -962,7 +962,7 @@ impl NamedPipeClient {
|
||||
Ok(event.ready)
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become readable.
|
||||
/// Waits for the pipe to become readable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::READABLE)` and is usually
|
||||
/// paired with `try_read()`.
|
||||
@@ -1043,7 +1043,7 @@ impl NamedPipeClient {
|
||||
self.io.registration().poll_read_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffer, returning how
|
||||
/// Tries to read data from the pipe into the provided buffer, returning how
|
||||
/// many bytes were read.
|
||||
///
|
||||
/// Receives any pending data from the pipe but does not wait for new data
|
||||
@@ -1109,7 +1109,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read(buf))
|
||||
}
|
||||
|
||||
/// Try to read data from the pipe into the provided buffers, returning
|
||||
/// Tries to read data from the pipe into the provided buffers, returning
|
||||
/// how many bytes were read.
|
||||
///
|
||||
/// Data is copied to fill each buffer in order, with the final buffer
|
||||
@@ -1186,7 +1186,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::READABLE, || (&*self.io).read_vectored(bufs))
|
||||
}
|
||||
|
||||
/// Wait for the pipe to become writable.
|
||||
/// Waits for the pipe to become writable.
|
||||
///
|
||||
/// This function is equivalent to `ready(Interest::WRITABLE)` and is usually
|
||||
/// paired with `try_write()`.
|
||||
@@ -1263,7 +1263,7 @@ impl NamedPipeClient {
|
||||
self.io.registration().poll_write_ready(cx).map_ok(|_| ())
|
||||
}
|
||||
|
||||
/// Try to write a buffer to the pipe, returning how many bytes were
|
||||
/// Tries to write a buffer to the pipe, returning how many bytes were
|
||||
/// written.
|
||||
///
|
||||
/// The function will attempt to write the entire contents of `buf`, but
|
||||
@@ -1318,7 +1318,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write(buf))
|
||||
}
|
||||
|
||||
/// Try to write several buffers to the pipe, returning how many bytes
|
||||
/// Tries to write several buffers to the pipe, returning how many bytes
|
||||
/// were written.
|
||||
///
|
||||
/// Data is written from each buffer in order, with the final buffer read
|
||||
@@ -1379,7 +1379,7 @@ impl NamedPipeClient {
|
||||
.try_io(Interest::WRITABLE, || (&*self.io).write_vectored(buf))
|
||||
}
|
||||
|
||||
/// Try to read or write from the socket using a user-provided IO operation.
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
@@ -1882,7 +1882,7 @@ impl ServerOptions {
|
||||
self
|
||||
}
|
||||
|
||||
/// Create the named pipe identified by `addr` for use as a server.
|
||||
/// Creates the named pipe identified by `addr` for use as a server.
|
||||
///
|
||||
/// This uses the [`CreateNamedPipe`] function.
|
||||
///
|
||||
@@ -1913,7 +1913,7 @@ impl ServerOptions {
|
||||
unsafe { self.create_with_security_attributes_raw(addr, ptr::null_mut()) }
|
||||
}
|
||||
|
||||
/// Create the named pipe identified by `addr` for use as a server.
|
||||
/// Creates the named pipe identified by `addr` for use as a server.
|
||||
///
|
||||
/// This is the same as [`create`] except that it supports providing the raw
|
||||
/// pointer to a structure of [`SECURITY_ATTRIBUTES`] which will be passed
|
||||
@@ -2042,7 +2042,7 @@ impl ClientOptions {
|
||||
self
|
||||
}
|
||||
|
||||
/// Open the named pipe identified by `addr`.
|
||||
/// Opens the named pipe identified by `addr`.
|
||||
///
|
||||
/// This opens the client using [`CreateFile`] with the
|
||||
/// `dwCreationDisposition` option set to `OPEN_EXISTING`.
|
||||
@@ -2099,7 +2099,7 @@ impl ClientOptions {
|
||||
unsafe { self.open_with_security_attributes_raw(addr, ptr::null_mut()) }
|
||||
}
|
||||
|
||||
/// Open the named pipe identified by `addr`.
|
||||
/// Opens the named pipe identified by `addr`.
|
||||
///
|
||||
/// This is the same as [`open`] except that it supports providing the raw
|
||||
/// pointer to a structure of [`SECURITY_ATTRIBUTES`] which will be passed
|
||||
@@ -2201,7 +2201,7 @@ pub struct PipeInfo {
|
||||
pub in_buffer_size: u32,
|
||||
}
|
||||
|
||||
/// Encode an address so that it is a null-terminated wide string.
|
||||
/// Encodes an address so that it is a null-terminated wide string.
|
||||
fn encode_addr(addr: impl AsRef<OsStr>) -> Box<[u16]> {
|
||||
let len = addr.as_ref().encode_wide().count();
|
||||
let mut vec = Vec::with_capacity(len + 1);
|
||||
|
||||
@@ -45,12 +45,12 @@ use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Block the current thread.
|
||||
/// Blocks the current thread.
|
||||
pub(crate) trait Park {
|
||||
/// Unpark handle type for the `Park` implementation.
|
||||
type Unpark: Unpark;
|
||||
|
||||
/// Error returned by `park`
|
||||
/// Error returned by `park`.
|
||||
type Error: Debug;
|
||||
|
||||
/// Gets a new `Unpark` handle associated with this `Park` instance.
|
||||
@@ -66,7 +66,7 @@ pub(crate) trait Park {
|
||||
///
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
/// `Park` implementation.
|
||||
fn park(&mut self) -> Result<(), Self::Error>;
|
||||
|
||||
/// Parks the current thread for at most `duration`.
|
||||
@@ -82,10 +82,10 @@ pub(crate) trait Park {
|
||||
///
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Park` implementation
|
||||
/// `Park` implementation.
|
||||
fn park_timeout(&mut self, duration: Duration) -> Result<(), Self::Error>;
|
||||
|
||||
/// Release all resources holded by the parker for proper leak-free shutdown
|
||||
/// Releases all resources holded by the parker for proper leak-free shutdown.
|
||||
fn shutdown(&mut self);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ pub(crate) trait Unpark: Sync + Send + 'static {
|
||||
///
|
||||
/// This function **should** not panic, but ultimately, panics are left as
|
||||
/// an implementation detail. Refer to the documentation for the specific
|
||||
/// `Unpark` implementation
|
||||
/// `Unpark` implementation.
|
||||
fn unpark(&self);
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ impl Park for ParkThread {
|
||||
// ==== impl Inner ====
|
||||
|
||||
impl Inner {
|
||||
/// Park the current thread for at most `dur`.
|
||||
/// Parks the current thread for at most `dur`.
|
||||
fn park(&self) {
|
||||
// If we were previously notified then we consume this notification and
|
||||
// return quickly.
|
||||
@@ -227,7 +227,7 @@ pub(crate) struct CachedParkThread {
|
||||
}
|
||||
|
||||
impl CachedParkThread {
|
||||
/// Create a new `ParkThread` handle for the current thread.
|
||||
/// Creates a new `ParkThread` handle for the current thread.
|
||||
///
|
||||
/// This type cannot be moved to other threads, so it should be created on
|
||||
/// the thread that the caller intends to park.
|
||||
@@ -241,7 +241,7 @@ impl CachedParkThread {
|
||||
self.with_current(|park_thread| park_thread.unpark())
|
||||
}
|
||||
|
||||
/// Get a reference to the `ParkThread` handle for this thread.
|
||||
/// Gets a reference to the `ParkThread` handle for this thread.
|
||||
fn with_current<F, R>(&self, f: F) -> Result<R, ParkError>
|
||||
where
|
||||
F: FnOnce(&ParkThread) -> R,
|
||||
|
||||
@@ -578,7 +578,7 @@ impl Command {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set executable argument
|
||||
/// Sets executable argument.
|
||||
///
|
||||
/// Set the first process argument, `argv[0]`, to something other than the
|
||||
/// default executable path.
|
||||
@@ -1173,7 +1173,7 @@ pub struct ChildStderr {
|
||||
}
|
||||
|
||||
impl ChildStdin {
|
||||
/// Create an asynchronous `ChildStdin` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStdin` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -1188,7 +1188,7 @@ impl ChildStdin {
|
||||
}
|
||||
|
||||
impl ChildStdout {
|
||||
/// Create an asynchronous `ChildStderr` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStderr` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -1203,7 +1203,7 @@ impl ChildStdout {
|
||||
}
|
||||
|
||||
impl ChildStderr {
|
||||
/// Create an asynchronous `ChildStderr` from a synchronous one.
|
||||
/// Creates an asynchronous `ChildStderr` from a synchronous one.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![cfg_attr(not(feature = "rt"), allow(dead_code))]
|
||||
|
||||
//! Process driver
|
||||
//! Process driver.
|
||||
|
||||
use crate::park::Park;
|
||||
use crate::process::unix::GlobalOrphanQueue;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Unix handling of child processes
|
||||
//! Unix handling of child processes.
|
||||
//!
|
||||
//! Right now the only "fancy" thing about this is how we implement the
|
||||
//! `Future` implementation on `Child` to get the exit status. Unix offers
|
||||
|
||||
@@ -2,8 +2,10 @@ use crate::future::poll_fn;
|
||||
use crate::loom::sync::atomic::AtomicBool;
|
||||
use crate::loom::sync::Mutex;
|
||||
use crate::park::{Park, Unpark};
|
||||
use crate::runtime::context::EnterGuard;
|
||||
use crate::runtime::stats::{RuntimeStats, WorkerStatsBatcher};
|
||||
use crate::runtime::task::{self, JoinHandle, OwnedTasks, Schedule, Task};
|
||||
use crate::runtime::Callback;
|
||||
use crate::sync::notify::Notify;
|
||||
use crate::util::{waker_ref, Wake, WakerRef};
|
||||
|
||||
@@ -11,7 +13,7 @@ use std::cell::RefCell;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
||||
use std::sync::atomic::Ordering::{AcqRel, Release};
|
||||
use std::sync::Arc;
|
||||
use std::task::Poll::{Pending, Ready};
|
||||
use std::time::Duration;
|
||||
@@ -28,6 +30,12 @@ pub(crate) struct BasicScheduler<P: Park> {
|
||||
|
||||
/// Sendable task spawner
|
||||
spawner: Spawner,
|
||||
|
||||
/// This is usually None, but right before dropping the BasicScheduler, it
|
||||
/// is changed to `Some` with the context being the runtime's own context.
|
||||
/// This ensures that any tasks dropped in the `BasicScheduler`s destructor
|
||||
/// run in that runtime's context.
|
||||
context_guard: Option<EnterGuard>,
|
||||
}
|
||||
|
||||
/// The inner scheduler that owns the task queue and the main parker P.
|
||||
@@ -49,6 +57,11 @@ struct Inner<P: Park> {
|
||||
/// Thread park handle
|
||||
park: P,
|
||||
|
||||
/// Callback for a worker parking itself
|
||||
before_park: Option<Callback>,
|
||||
/// Callback for a worker unparking itself
|
||||
after_unpark: Option<Callback>,
|
||||
|
||||
/// Stats batcher
|
||||
stats: WorkerStatsBatcher,
|
||||
}
|
||||
@@ -121,7 +134,11 @@ const REMOTE_FIRST_INTERVAL: u8 = 31;
|
||||
scoped_thread_local!(static CURRENT: Context);
|
||||
|
||||
impl<P: Park> BasicScheduler<P> {
|
||||
pub(crate) fn new(park: P) -> BasicScheduler<P> {
|
||||
pub(crate) fn new(
|
||||
park: P,
|
||||
before_park: Option<Callback>,
|
||||
after_unpark: Option<Callback>,
|
||||
) -> BasicScheduler<P> {
|
||||
let unpark = Box::new(park.unpark());
|
||||
|
||||
let spawner = Spawner {
|
||||
@@ -141,6 +158,8 @@ impl<P: Park> BasicScheduler<P> {
|
||||
spawner: spawner.clone(),
|
||||
tick: 0,
|
||||
park,
|
||||
before_park,
|
||||
after_unpark,
|
||||
stats: WorkerStatsBatcher::new(0),
|
||||
}));
|
||||
|
||||
@@ -148,6 +167,7 @@ impl<P: Park> BasicScheduler<P> {
|
||||
inner,
|
||||
notify: Notify::new(),
|
||||
spawner,
|
||||
context_guard: None,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,22 +218,24 @@ impl<P: Park> BasicScheduler<P> {
|
||||
basic_scheduler: self,
|
||||
})
|
||||
}
|
||||
|
||||
pub(super) fn set_context_guard(&mut self, guard: EnterGuard) {
|
||||
self.context_guard = Some(guard);
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: Park> Inner<P> {
|
||||
/// Block on the future provided and drive the runtime's driver.
|
||||
/// Blocks on the provided future and drives the runtime's driver.
|
||||
fn block_on<F: Future>(&mut self, future: F) -> F::Output {
|
||||
enter(self, |scheduler, context| {
|
||||
let _enter = crate::runtime::enter(false);
|
||||
let waker = scheduler.spawner.waker_ref();
|
||||
let mut cx = std::task::Context::from_waker(&waker);
|
||||
let mut polled = false;
|
||||
|
||||
pin!(future);
|
||||
|
||||
'outer: loop {
|
||||
if scheduler.spawner.was_woken() || !polled {
|
||||
polled = true;
|
||||
if scheduler.spawner.reset_woken() {
|
||||
scheduler.stats.incr_poll_count();
|
||||
if let Ready(v) = crate::coop::budget(|| future.as_mut().poll(&mut cx)) {
|
||||
return v;
|
||||
@@ -247,11 +269,21 @@ impl<P: Park> Inner<P> {
|
||||
let entry = match entry {
|
||||
Some(entry) => entry,
|
||||
None => {
|
||||
// Park until the thread is signaled
|
||||
scheduler.stats.about_to_park();
|
||||
scheduler.stats.submit(&scheduler.spawner.shared.stats);
|
||||
scheduler.park.park().expect("failed to park");
|
||||
scheduler.stats.returned_from_park();
|
||||
if let Some(f) = &scheduler.before_park {
|
||||
f();
|
||||
}
|
||||
// This check will fail if `before_park` spawns a task for us to run
|
||||
// instead of parking the thread
|
||||
if context.tasks.borrow_mut().queue.is_empty() {
|
||||
// Park until the thread is signaled
|
||||
scheduler.stats.about_to_park();
|
||||
scheduler.stats.submit(&scheduler.spawner.shared.stats);
|
||||
scheduler.park.park().expect("failed to park");
|
||||
scheduler.stats.returned_from_park();
|
||||
}
|
||||
if let Some(f) = &scheduler.after_unpark {
|
||||
f();
|
||||
}
|
||||
|
||||
// Try polling the `block_on` future next
|
||||
continue 'outer;
|
||||
@@ -279,8 +311,8 @@ impl<P: Park> Inner<P> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Enter the scheduler context. This sets the queue and other necessary
|
||||
/// scheduler state in the thread-local
|
||||
/// Enters the scheduler context. This sets the queue and other necessary
|
||||
/// scheduler state in the thread-local.
|
||||
fn enter<F, R, P>(scheduler: &mut Inner<P>, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Inner<P>, &Context) -> R,
|
||||
@@ -396,13 +428,15 @@ impl Spawner {
|
||||
}
|
||||
|
||||
fn waker_ref(&self) -> WakerRef<'_> {
|
||||
// clear the woken bit
|
||||
self.shared.woken.swap(false, AcqRel);
|
||||
// Set woken to true when enter block_on, ensure outer future
|
||||
// be polled for the first time when enter loop
|
||||
self.shared.woken.store(true, Release);
|
||||
waker_ref(&self.shared)
|
||||
}
|
||||
|
||||
fn was_woken(&self) -> bool {
|
||||
self.shared.woken.load(Acquire)
|
||||
// reset woken to false and return original value
|
||||
pub(crate) fn reset_woken(&self) -> bool {
|
||||
self.shared.woken.swap(false, AcqRel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ use crate::runtime::builder::ThreadNameFn;
|
||||
use crate::runtime::context;
|
||||
use crate::runtime::task::{self, JoinHandle};
|
||||
use crate::runtime::{Builder, Callback, Handle};
|
||||
use crate::util::error::CONTEXT_MISSING_ERROR;
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::fmt;
|
||||
@@ -25,28 +24,28 @@ pub(crate) struct Spawner {
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
/// State shared between worker threads
|
||||
/// State shared between worker threads.
|
||||
shared: Mutex<Shared>,
|
||||
|
||||
/// Pool threads wait on this.
|
||||
condvar: Condvar,
|
||||
|
||||
/// Spawned threads use this name
|
||||
/// Spawned threads use this name.
|
||||
thread_name: ThreadNameFn,
|
||||
|
||||
/// Spawned thread stack size
|
||||
/// Spawned thread stack size.
|
||||
stack_size: Option<usize>,
|
||||
|
||||
/// Call after a thread starts
|
||||
/// Call after a thread starts.
|
||||
after_start: Option<Callback>,
|
||||
|
||||
/// Call before a thread stops
|
||||
/// Call before a thread stops.
|
||||
before_stop: Option<Callback>,
|
||||
|
||||
// Maximum number of threads
|
||||
// Maximum number of threads.
|
||||
thread_cap: usize,
|
||||
|
||||
// Customizable wait timeout
|
||||
// Customizable wait timeout.
|
||||
keep_alive: Duration,
|
||||
}
|
||||
|
||||
@@ -67,7 +66,7 @@ struct Shared {
|
||||
/// calling shutdown handles joining on these.
|
||||
worker_threads: HashMap<usize, thread::JoinHandle<()>>,
|
||||
/// This is a counter used to iterate worker_threads in a consistent order (for loom's
|
||||
/// benefit)
|
||||
/// benefit).
|
||||
worker_thread_index: usize,
|
||||
}
|
||||
|
||||
@@ -75,13 +74,13 @@ type Task = task::UnownedTask<NoopSchedule>;
|
||||
|
||||
const KEEP_ALIVE: Duration = Duration::from_secs(10);
|
||||
|
||||
/// Run the provided function on an executor dedicated to blocking operations.
|
||||
/// Runs the provided function on an executor dedicated to blocking operations.
|
||||
pub(crate) fn spawn_blocking<F, R>(func: F) -> JoinHandle<R>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let rt = context::current().expect(CONTEXT_MISSING_ERROR);
|
||||
let rt = context::current();
|
||||
rt.spawn_blocking(func)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::time::Duration;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(super) struct Sender {
|
||||
tx: Arc<oneshot::Sender<()>>,
|
||||
_tx: Arc<oneshot::Sender<()>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -20,7 +20,7 @@ pub(super) struct Receiver {
|
||||
|
||||
pub(super) fn channel() -> (Sender, Receiver) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let tx = Sender { tx: Arc::new(tx) };
|
||||
let tx = Sender { _tx: Arc::new(tx) };
|
||||
let rx = Receiver { rx };
|
||||
|
||||
(tx, rx)
|
||||
|
||||
@@ -2,13 +2,13 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Converts a function to a future that completes on poll
|
||||
/// Converts a function to a future that completes on poll.
|
||||
pub(crate) struct BlockingTask<T> {
|
||||
func: Option<T>,
|
||||
}
|
||||
|
||||
impl<T> BlockingTask<T> {
|
||||
/// Initializes a new blocking task from the given function
|
||||
/// Initializes a new blocking task from the given function.
|
||||
pub(crate) fn new(func: T) -> BlockingTask<T> {
|
||||
BlockingTask { func: Some(func) }
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user