mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
139
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ed89aa5cf | ||
|
|
d9b2dc81ca | ||
|
|
8e54145c8b | ||
|
|
279e8b001a | ||
|
|
e9f6faee67 | ||
|
|
f1b89675eb | ||
|
|
ddd33f2b05 | ||
|
|
8e92f05795 | ||
|
|
957ed3eac0 | ||
|
|
ab34805849 | ||
|
|
8b298d9ed4 | ||
|
|
57563e218b | ||
|
|
33f0a1fd2e | ||
|
|
4c9b469562 | ||
|
|
7af0f32751 | ||
|
|
3fe1662e4b | ||
|
|
a73428252b | ||
|
|
b99eedc2ea | ||
|
|
6ebd0575e4 | ||
|
|
7e51b44a20 | ||
|
|
bd1e4aaea6 | ||
|
|
fd22164f5d | ||
|
|
98e78a6f7b | ||
|
|
01a6feb0dc | ||
|
|
e31e06c5c2 | ||
|
|
d0dd74a058 | ||
|
|
6778a7def6 | ||
|
|
9a97eb36bc | ||
|
|
1409041525 | ||
|
|
23b0aee5dd | ||
|
|
909d3ec0ff | ||
|
|
b67d46403f | ||
|
|
98578a6f4a | ||
|
|
8a097d27b5 | ||
|
|
1e2e38b7cd | ||
|
|
80bda3bf5f | ||
|
|
51f4f0594c | ||
|
|
897fed1609 | ||
|
|
fd52f9f66b | ||
|
|
dd060b16f5 | ||
|
|
4152918a39 | ||
|
|
7e474640dd | ||
|
|
84f6845bf2 | ||
|
|
2bc9a42d2b | ||
|
|
8aa2bfe23e | ||
|
|
5ac32934b4 | ||
|
|
d0305d57e5 | ||
|
|
5c19b5a162 | ||
|
|
f478647a8e | ||
|
|
c0974bad94 | ||
|
|
b67534a20b | ||
|
|
c4e56232ff | ||
|
|
84c4a6d89f | ||
|
|
b501f25202 | ||
|
|
032c55e77f | ||
|
|
1e95d6994a | ||
|
|
362df5a317 | ||
|
|
106bb94896 | ||
|
|
8198ef3881 | ||
|
|
e66217575b | ||
|
|
175d84e2b1 | ||
|
|
69a6585429 | ||
|
|
cf02b3f32d | ||
|
|
d01bda86a4 | ||
|
|
e60d7a474b | ||
|
|
0d9430b99c | ||
|
|
f51676891f | ||
|
|
3340ae6aa9 | ||
|
|
f957f7f9a7 | ||
|
|
8b447649bb | ||
|
|
4d8cc28b76 | ||
|
|
f2a06bff1b | ||
|
|
0de05422ce | ||
|
|
afb734d189 | ||
|
|
2b731c0e01 | ||
|
|
8f27c04a9e | ||
|
|
c85a0e524e | ||
|
|
1eb468be08 | ||
|
|
378409d15d | ||
|
|
51ff95c144 | ||
|
|
df10b68d47 | ||
|
|
b280c6dcd7 | ||
|
|
998dc5a2eb | ||
|
|
ced7992f65 | ||
|
|
0cefa85bfa | ||
|
|
2087f3e0eb | ||
|
|
5ae2855eaf | ||
|
|
252004811f | ||
|
|
549e89e9cd | ||
|
|
c8fc492748 | ||
|
|
7147042534 | ||
|
|
634fcac2d9 | ||
|
|
7c4183a45d | ||
|
|
aef2d64b0a | ||
|
|
8f10d81613 | ||
|
|
7a11cfd77a | ||
|
|
6610ba9bd6 | ||
|
|
3fd88dac64 | ||
|
|
ccd495647d | ||
|
|
80d8d40a34 | ||
|
|
3b38ebd7f5 | ||
|
|
127983e5b4 | ||
|
|
c306bf853a | ||
|
|
c6fbb9aeb1 | ||
|
|
5d61c997e9 | ||
|
|
c505a2f81a | ||
|
|
e2589a0e40 | ||
|
|
51fad066e2 | ||
|
|
ae233c1e9f | ||
|
|
be26ca7625 | ||
|
|
2d41f0303e | ||
|
|
e0c527f383 | ||
|
|
1160e8864c | ||
|
|
4818c2ed05 | ||
|
|
80f0801e19 | ||
|
|
8148b2107c | ||
|
|
2e7de1ae1d | ||
|
|
37e60fc7f9 | ||
|
|
8170e2787c | ||
|
|
677107d8d9 | ||
|
|
c8ecfc894d | ||
|
|
08ed41f339 | ||
|
|
90e1935c48 | ||
|
|
b877629cb1 | ||
|
|
0531549b6e | ||
|
|
38204f5fba | ||
|
|
8fa29cb00a | ||
|
|
b521cc2689 | ||
|
|
57c90c9750 | ||
|
|
d35ff7064f | ||
|
|
ab0791b817 | ||
|
|
959c5c997f | ||
|
|
845626410a | ||
|
|
3207479fa7 | ||
|
|
d1aa2df80e | ||
|
|
e979ad7f2d | ||
|
|
5ad3dd3378 | ||
|
|
60bd40d529 | ||
|
|
4a93af4d25 |
@@ -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
|
||||
+36
-23
@@ -9,7 +9,7 @@ name: CI
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
nightly: nightly-2021-04-25
|
||||
nightly: nightly-2021-07-09
|
||||
minrust: 1.45.2
|
||||
|
||||
jobs:
|
||||
@@ -27,8 +27,9 @@ jobs:
|
||||
- fmt
|
||||
- clippy
|
||||
- docs
|
||||
- loom
|
||||
- valgrind
|
||||
- loom-compile
|
||||
- check-readme
|
||||
steps:
|
||||
- run: exit 0
|
||||
|
||||
@@ -45,6 +46,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
|
||||
@@ -82,6 +84,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
|
||||
- name: Install Valgrind
|
||||
run: |
|
||||
@@ -95,7 +98,7 @@ jobs:
|
||||
|
||||
# Run with valgrind
|
||||
- name: Run valgrind test-mem
|
||||
run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-mem
|
||||
run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/test-mem
|
||||
|
||||
# Compile tests
|
||||
- name: cargo build test-process-signal
|
||||
@@ -104,7 +107,7 @@ jobs:
|
||||
|
||||
# Run with valgrind
|
||||
- name: Run valgrind test-process-signal
|
||||
run: valgrind --leak-check=full --show-leak-kinds=all ./target/debug/test-process-signal
|
||||
run: valgrind --error-exitcode=1 --leak-check=full --show-leak-kinds=all ./target/debug/test-process-signal
|
||||
|
||||
test-unstable:
|
||||
name: test tokio full --unstable
|
||||
@@ -119,10 +122,11 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
|
||||
# Run `tokio` with "unstable" cfg flag.
|
||||
- name: test tokio full --cfg unstable
|
||||
run: cargo test --features full
|
||||
run: cargo test --all-features
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
@@ -136,6 +140,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install Miri
|
||||
run: |
|
||||
set -e
|
||||
@@ -155,6 +160,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ env.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
|
||||
@@ -180,6 +186,7 @@ jobs:
|
||||
toolchain: stable
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
@@ -195,6 +202,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
|
||||
@@ -216,7 +224,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ env.minrust }}
|
||||
override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "test --workspace --all-features"
|
||||
run: cargo check --workspace --all-features
|
||||
|
||||
@@ -229,6 +237,7 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ env.nightly }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install cargo-hack
|
||||
run: cargo install cargo-hack
|
||||
- name: "check --all-features -Z minimal-versions"
|
||||
@@ -247,6 +256,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install rustfmt
|
||||
run: rustup component add rustfmt
|
||||
|
||||
@@ -266,6 +276,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update 1.52.1 && rustup default 1.52.1
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install clippy
|
||||
run: rustup component add clippy
|
||||
|
||||
@@ -282,33 +293,35 @@ jobs:
|
||||
with:
|
||||
toolchain: ${{ env.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
|
||||
- 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
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Verify that both READMEs are identical
|
||||
run: diff README.md tokio/README.md
|
||||
|
||||
- name: Verify that Tokio version is up to date in README
|
||||
working-directory: tokio
|
||||
run: grep -q "$(sed '/^version = /!d' Cargo.toml | head -n1)" README.md
|
||||
|
||||
@@ -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,41 @@
|
||||
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
|
||||
nightly: nightly-2021-07-09
|
||||
|
||||
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
|
||||
run: rustup update stable
|
||||
- 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 }}
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update stable
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: Install Valgrind
|
||||
run: |
|
||||
sudo apt-get update -y
|
||||
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.7.0", features = ["full"] }
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
@@ -140,8 +140,7 @@ several other libraries, including:
|
||||
|
||||
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
|
||||
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
|
||||
tracing and async-aware diagnostics.
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
@@ -164,9 +163,35 @@ several other libraries, including:
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable release. The minimum supported version is 1.45.
|
||||
The current Tokio version is not guaranteed to build on Rust versions earlier than the
|
||||
minimum supported version.
|
||||
Tokio is built against the latest stable release. The minimum supported version
|
||||
is 1.45. The current Tokio version is not guaranteed to build on Rust versions
|
||||
earlier than the minimum supported version.
|
||||
|
||||
## Release schedule
|
||||
|
||||
Tokio doesn't follow a fixed release schedule, but we typically make one to two
|
||||
new minor releases each month. We make patch releases for bugfixes as necessary.
|
||||
|
||||
## Bug patching policy
|
||||
|
||||
For the purposes of making patch releases with bugfixes, we have designated
|
||||
certain minor releases as LTS (long term support) releases. Whenever a bug
|
||||
warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.8.x` - LTS release until February 2022.
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least half a
|
||||
year. If you wish to use a fixed minor release in your project, we recommend
|
||||
that you use an LTS release.
|
||||
|
||||
To use a fixed minor version, you can specify the version with a tilde. For
|
||||
example, to specify that you wish to use the newest `1.8.x` patch release, you
|
||||
can use the following dependency specification:
|
||||
```text
|
||||
tokio = { version = "~1.8", features = [...] }
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Benchmark implementation details of the theaded scheduler. These benches are
|
||||
//! Benchmark implementation details of the threaded scheduler. These benches are
|
||||
//! intended to be used as a form of regression testing and not as a general
|
||||
//! purpose benchmark demonstrating real-world performance.
|
||||
|
||||
|
||||
+57
-37
@@ -2,63 +2,83 @@
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
async fn work() -> usize {
|
||||
let val = 1 + 1;
|
||||
tokio::task::yield_now().await;
|
||||
black_box(val)
|
||||
}
|
||||
|
||||
fn basic_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
fn basic_scheduler_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.block_on(async {
|
||||
bench.iter(|| {
|
||||
let h = tokio::spawn(work());
|
||||
black_box(h);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.block_on(async {
|
||||
bench.iter(|| {
|
||||
let h = tokio::spawn(work());
|
||||
black_box(h);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn basic_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
let h = runtime.spawn(work());
|
||||
black_box(h);
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
|
||||
|
||||
fn basic_scheduler_spawn10(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
let h = runtime.spawn(work());
|
||||
black_box(h);
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn10(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
spawn,
|
||||
basic_scheduler_local_spawn,
|
||||
threaded_scheduler_local_spawn,
|
||||
basic_scheduler_remote_spawn,
|
||||
threaded_scheduler_remote_spawn
|
||||
basic_scheduler_spawn,
|
||||
basic_scheduler_spawn10,
|
||||
threaded_scheduler_spawn,
|
||||
threaded_scheduler_spawn10,
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(spawn);
|
||||
|
||||
@@ -84,6 +84,10 @@ path = "custom-executor-tokio-context.rs"
|
||||
name = "named-pipe"
|
||||
path = "named-pipe.rs"
|
||||
|
||||
[[example]]
|
||||
name = "named-pipe-ready"
|
||||
path = "named-pipe-ready.rs"
|
||||
|
||||
[[example]]
|
||||
name = "named-pipe-multi-client"
|
||||
path = "named-pipe-multi-client.rs"
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
use std::io;
|
||||
|
||||
#[cfg(windows)]
|
||||
async fn windows_main() -> io::Result<()> {
|
||||
use tokio::io::Interest;
|
||||
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
||||
|
||||
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";
|
||||
|
||||
let server = ServerOptions::new().create(PIPE_NAME)?;
|
||||
|
||||
let server = tokio::spawn(async move {
|
||||
// Note: we wait for a client to connect.
|
||||
server.connect().await?;
|
||||
|
||||
let buf = {
|
||||
let mut read_buf = [0u8; 5];
|
||||
let mut read_buf_cursor = 0;
|
||||
|
||||
loop {
|
||||
server.readable().await?;
|
||||
|
||||
let buf = &mut read_buf[read_buf_cursor..];
|
||||
|
||||
match server.try_read(buf) {
|
||||
Ok(n) => {
|
||||
read_buf_cursor += n;
|
||||
|
||||
if read_buf_cursor == read_buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read_buf
|
||||
};
|
||||
|
||||
{
|
||||
let write_buf = b"pong\n";
|
||||
let mut write_buf_cursor = 0;
|
||||
|
||||
loop {
|
||||
let buf = &write_buf[write_buf_cursor..];
|
||||
|
||||
if buf.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
server.writable().await?;
|
||||
|
||||
match server.try_write(buf) {
|
||||
Ok(n) => {
|
||||
write_buf_cursor += n;
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<_, io::Error>(buf)
|
||||
});
|
||||
|
||||
let client = tokio::spawn(async move {
|
||||
// There's no need to use a connect loop here, since we know that the
|
||||
// server is already up - `open` was called before spawning any of the
|
||||
// tasks.
|
||||
let client = ClientOptions::new().open(PIPE_NAME)?;
|
||||
|
||||
let mut read_buf = [0u8; 5];
|
||||
let mut read_buf_cursor = 0;
|
||||
let write_buf = b"ping\n";
|
||||
let mut write_buf_cursor = 0;
|
||||
|
||||
loop {
|
||||
let mut interest = Interest::READABLE;
|
||||
if write_buf_cursor < write_buf.len() {
|
||||
interest |= Interest::WRITABLE;
|
||||
}
|
||||
|
||||
let ready = client.ready(interest).await?;
|
||||
|
||||
if ready.is_readable() {
|
||||
let buf = &mut read_buf[read_buf_cursor..];
|
||||
|
||||
match client.try_read(buf) {
|
||||
Ok(n) => {
|
||||
read_buf_cursor += n;
|
||||
|
||||
if read_buf_cursor == read_buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ready.is_writable() {
|
||||
let buf = &write_buf[write_buf_cursor..];
|
||||
|
||||
if buf.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match client.try_write(buf) {
|
||||
Ok(n) => {
|
||||
write_buf_cursor += n;
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let buf = String::from_utf8_lossy(&read_buf).into_owned();
|
||||
|
||||
Ok::<_, io::Error>(buf)
|
||||
});
|
||||
|
||||
let (server, client) = tokio::try_join!(server, client)?;
|
||||
|
||||
assert_eq!(server?, *b"ping\n");
|
||||
assert_eq!(client?, "pong\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
windows_main().await?;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!("Named pipes are only supported on Windows!");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,4 +4,4 @@ error: The default runtime flavor is `multi_thread`, but the `rt-multi-thread` f
|
||||
3 | #[tokio::main]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
@@ -7,25 +7,11 @@ async fn missing_semicolon_or_return_type() {
|
||||
|
||||
#[tokio::main]
|
||||
async fn missing_return_type() {
|
||||
/* TODO(taiki-e): one of help messages still wrong
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
16 | return Ok(());;
|
||||
|
|
||||
*/
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn extra_semicolon() -> Result<(), ()> {
|
||||
/* TODO(taiki-e): help message still wrong
|
||||
help: try using a variant of the expected enum
|
||||
|
|
||||
29 | Ok(Ok(());)
|
||||
|
|
||||
29 | Err(Ok(());)
|
||||
|
|
||||
*/
|
||||
Ok(());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:5:5
|
||||
--> $DIR/macros_type_mismatch.rs:5:7
|
||||
|
|
||||
5 | Ok(())
|
||||
| ^^^^^^ expected `()`, found enum `Result`
|
||||
| ^^^^ expected `()`, found enum `Result`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
@@ -16,36 +16,23 @@ help: try adding a return type
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:16:5
|
||||
--> $DIR/macros_type_mismatch.rs:10:18
|
||||
|
|
||||
16 | return Ok(());
|
||||
| ^^^^^^^^^^^^^^ expected `()`, found enum `Result`
|
||||
9 | async fn missing_return_type() {
|
||||
| - help: try adding a return type: `-> Result<(), _>`
|
||||
10 | return Ok(());
|
||||
| ^ expected `()`, found enum `Result`
|
||||
|
|
||||
= note: expected unit type `()`
|
||||
found enum `Result<(), _>`
|
||||
help: consider using a semicolon here
|
||||
|
|
||||
16 | return Ok(());;
|
||||
| ^
|
||||
help: try adding a return type
|
||||
|
|
||||
9 | async fn missing_return_type() -> Result<(), _> {
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/macros_type_mismatch.rs:29:5
|
||||
--> $DIR/macros_type_mismatch.rs:14:31
|
||||
|
|
||||
20 | async fn extra_semicolon() -> Result<(), ()> {
|
||||
| -------------- expected `Result<(), ()>` because of return type
|
||||
...
|
||||
29 | Ok(());
|
||||
| ^^^^^^^ expected enum `Result`, found `()`
|
||||
14 | async fn extra_semicolon() -> Result<(), ()> {
|
||||
| --------------- ^^^^^^^^^^^^^^ expected enum `Result`, found `()`
|
||||
| |
|
||||
| implicitly returns `()` as its body has no tail or `return` expression
|
||||
|
|
||||
= note: expected enum `Result<(), ()>`
|
||||
found unit type `()`
|
||||
help: try using a variant of the expected enum
|
||||
|
|
||||
29 | Ok(Ok(());)
|
||||
|
|
||||
29 | Err(Ok(());)
|
||||
|
|
||||
|
||||
@@ -5,6 +5,12 @@ fn compile_fail_full() {
|
||||
#[cfg(feature = "full")]
|
||||
t.pass("tests/pass/forward_args_and_output.rs");
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
t.pass("tests/pass/macros_main_return.rs");
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
t.pass("tests/pass/macros_main_loop.rs");
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
t.compile_fail("tests/fail/macros_invalid_input.rs");
|
||||
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#[cfg(feature = "full")]
|
||||
#[tokio::test]
|
||||
async fn test_with_semicolon_without_return_type() {
|
||||
#![deny(clippy::semicolon_if_nothing_returned)]
|
||||
|
||||
dbg!(0);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
use tests_build::tokio;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), ()> {
|
||||
loop {
|
||||
if !never() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn never() -> bool {
|
||||
std::time::Instant::now() > std::time::Instant::now()
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
use tests_build::tokio;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), ()> {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
# 1.3.0 (July 7, 2021)
|
||||
|
||||
- macros: don't trigger `clippy::unwrap_used` ([#3926])
|
||||
|
||||
[#3926]: https://github.com/tokio-rs/tokio/pull/3926
|
||||
|
||||
# 1.2.0 (May 14, 2021)
|
||||
|
||||
- macros: forward input arguments in `#[tokio::test]` ([#3691])
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-macros"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.0.x" git tag.
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
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.2.0/tokio_macros"
|
||||
documentation = "https://docs.rs/tokio-macros/1.3.0/tokio_macros"
|
||||
description = """
|
||||
Tokio's proc macros.
|
||||
"""
|
||||
|
||||
+33
-18
@@ -201,12 +201,15 @@ fn parse_knobs(
|
||||
for arg in args {
|
||||
match arg {
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(namevalue)) => {
|
||||
let ident = namevalue.path.get_ident();
|
||||
if ident.is_none() {
|
||||
let msg = "Must have specified ident";
|
||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||
}
|
||||
match ident.unwrap().to_string().to_lowercase().as_str() {
|
||||
let ident = namevalue
|
||||
.path
|
||||
.get_ident()
|
||||
.ok_or_else(|| {
|
||||
syn::Error::new_spanned(&namevalue, "Must have specified ident")
|
||||
})?
|
||||
.to_string()
|
||||
.to_lowercase();
|
||||
match ident.as_str() {
|
||||
"worker_threads" => {
|
||||
config.set_worker_threads(
|
||||
namevalue.lit.clone(),
|
||||
@@ -239,12 +242,11 @@ fn parse_knobs(
|
||||
}
|
||||
}
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
|
||||
let ident = path.get_ident();
|
||||
if ident.is_none() {
|
||||
let msg = "Must have specified ident";
|
||||
return Err(syn::Error::new_spanned(path, msg));
|
||||
}
|
||||
let name = ident.unwrap().to_string().to_lowercase();
|
||||
let name = path
|
||||
.get_ident()
|
||||
.ok_or_else(|| syn::Error::new_spanned(&path, "Must have specified ident"))?
|
||||
.to_string()
|
||||
.to_lowercase();
|
||||
let msg = match name.as_str() {
|
||||
"threaded_scheduler" | "multi_thread" => {
|
||||
format!(
|
||||
@@ -321,16 +323,29 @@ 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! {},
|
||||
},
|
||||
quote! {
|
||||
;
|
||||
},
|
||||
),
|
||||
_ => (quote! {}, quote! {}),
|
||||
};
|
||||
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
|
||||
{
|
||||
#rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async #body)
|
||||
let body = async #body;
|
||||
#[allow(clippy::expect_used)]
|
||||
#tail_return tokio::task::LocalSet::new().block_on(
|
||||
&#rt.enable_all().build().expect("Failed building the Runtime"),
|
||||
body,
|
||||
)#tail_semicolon
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
.expect("Parsing failure");
|
||||
input.block.brace_token = brace_token;
|
||||
|
||||
let result = quote! {
|
||||
|
||||
+34
-26
@@ -5,7 +5,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
# or
|
||||
/// [Builder](../tokio/runtime/struct.Builder.html) directly.
|
||||
/// The function executes in the context of a
|
||||
/// [LocalSet](../tokio/task/struct.LocalSet.html), allowing calls to
|
||||
/// [spawn_local](../tokio/task/fn.spawn_local.html) without further setup.
|
||||
///
|
||||
/// Note: This macro is designed to be simplistic and targets applications that
|
||||
/// do not require a complex setup. If the provided functionality is not
|
||||
@@ -84,13 +87,14 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new_multi_thread()
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -109,13 +113,14 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -132,14 +137,15 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new_multi_thread()
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_multi_thread()
|
||||
/// .worker_threads(2)
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -156,14 +162,15 @@ use proc_macro::TokenStream;
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .start_paused(true)
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -204,13 +211,14 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
///
|
||||
/// ```rust
|
||||
/// fn main() {
|
||||
/// tokio::runtime::Builder::new_current_thread()
|
||||
/// let ls = tokio::task::LocalSet::new();
|
||||
/// let rt = tokio::runtime::Builder::new_current_thread()
|
||||
/// .enable_all()
|
||||
/// .build()
|
||||
/// .unwrap()
|
||||
/// .block_on(async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// .unwrap();
|
||||
/// ls.block_on(&rt, async {
|
||||
/// println!("Hello world");
|
||||
/// })
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# 0.1.7 (July 7, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: fix watch wrapper ([#3914])
|
||||
- time: fix `Timeout::size_hint` ([#3902])
|
||||
|
||||
[#3902]: https://github.com/tokio-rs/tokio/pull/3902
|
||||
[#3914]: https://github.com/tokio-rs/tokio/pull/3914
|
||||
|
||||
# 0.1.6 (May 14, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-stream"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-stream-0.1.x" git tag.
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
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.6/tokio_stream"
|
||||
documentation = "https://docs.rs/tokio-stream/0.1.7/tokio_stream"
|
||||
description = """
|
||||
Utilities to work with `Stream` and `tokio`.
|
||||
"""
|
||||
@@ -30,7 +30,7 @@ signal = ["tokio/signal"]
|
||||
[dependencies]
|
||||
futures-core = { version = "0.3.0" }
|
||||
pin-project-lite = "0.2.0"
|
||||
tokio = { version = "1.2.0", path = "../tokio", features = ["sync"] }
|
||||
tokio = { version = "1.8.0", path = "../tokio", features = ["sync"] }
|
||||
tokio-util = { version = "0.6.3", path = "../tokio-util", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -44,3 +44,8 @@ proptest = "1"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
# Issue #3770
|
||||
#
|
||||
# This should allow `docsrs` to be read across projects, so that `tokio-stream`
|
||||
# can pick up stubbed types exported by `tokio`.
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
@@ -515,7 +515,7 @@ pub trait StreamExt: Stream {
|
||||
/// Skip elements from the underlying stream while the provided predicate
|
||||
/// resolves to `true`.
|
||||
///
|
||||
/// This function, like [`Iterator::skip_while`], will ignore elemets from the
|
||||
/// This function, like [`Iterator::skip_while`], will ignore elements from the
|
||||
/// stream until the predicate `f` resolves to `false`. Once one element
|
||||
/// returns false, the rest of the elements will be yielded.
|
||||
///
|
||||
|
||||
@@ -113,7 +113,7 @@ impl<T: AsRef<str>> sealed::FromStreamPriv<T> for String {
|
||||
}
|
||||
|
||||
fn finalize(_: sealed::Internal, collection: &mut String) -> String {
|
||||
mem::replace(collection, String::new())
|
||||
mem::take(collection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ impl<T> sealed::FromStreamPriv<T> for Vec<T> {
|
||||
}
|
||||
|
||||
fn finalize(_: sealed::Internal, collection: &mut Vec<T>) -> Vec<T> {
|
||||
mem::replace(collection, vec![])
|
||||
mem::take(collection)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,18 @@ impl<S: Stream> Stream for Timeout<S> {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.stream.size_hint()
|
||||
let (lower, upper) = self.stream.size_hint();
|
||||
|
||||
// The timeout stream may insert an error before and after each message
|
||||
// from the underlying stream, but no more than one error between each
|
||||
// message. Hence the upper bound is computed as 2x+1.
|
||||
|
||||
// Using a helper function to enable use of question mark operator.
|
||||
fn twice_plus_one(value: Option<usize>) -> Option<usize> {
|
||||
value?.checked_mul(2)?.checked_add(1)
|
||||
}
|
||||
|
||||
(lower, twice_plus_one(upper))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,11 +364,11 @@ impl<K, V> StreamMap<K, V> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
/// use tokio_stream::{StreamMap, pending};
|
||||
///
|
||||
/// let mut a = HashMap::new();
|
||||
/// let mut a = StreamMap::new();
|
||||
/// assert!(a.is_empty());
|
||||
/// a.insert(1, "a");
|
||||
/// a.insert(1, pending::<i32>());
|
||||
/// assert!(!a.is_empty());
|
||||
/// ```
|
||||
pub fn is_empty(&self) -> bool {
|
||||
@@ -568,6 +568,23 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> std::iter::FromIterator<(K, V)> for StreamMap<K, V>
|
||||
where
|
||||
K: Hash + Eq,
|
||||
{
|
||||
fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
|
||||
let iterator = iter.into_iter();
|
||||
let (lower_bound, _) = iterator.size_hint();
|
||||
let mut stream_map = Self::with_capacity(lower_bound);
|
||||
|
||||
for (key, value) in iterator {
|
||||
stream_map.insert(key, value);
|
||||
}
|
||||
|
||||
stream_map
|
||||
}
|
||||
}
|
||||
|
||||
mod rand {
|
||||
use std::cell::Cell;
|
||||
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
//! Wrappers for Tokio types that implement `Stream`.
|
||||
//!
|
||||
#![cfg_attr(
|
||||
unix,
|
||||
doc = "You are viewing documentation built under unix. To view windows-specific wrappers, change to the `x86_64-pc-windows-msvc` platform."
|
||||
)]
|
||||
#![cfg_attr(
|
||||
windows,
|
||||
doc = "You are viewing documentation built under windows. To view unix-specific wrappers, change to the `x86_64-unknown-linux-gnu` platform."
|
||||
)]
|
||||
|
||||
/// Error types for the wrappers.
|
||||
pub mod errors {
|
||||
@@ -36,9 +27,9 @@ cfg_signal! {
|
||||
#[cfg(unix)]
|
||||
pub use signal_unix::SignalStream;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, docsrs))]
|
||||
mod signal_windows;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, docsrs))]
|
||||
pub use signal_windows::{CtrlCStream, CtrlBreakStream};
|
||||
}
|
||||
|
||||
|
||||
@@ -71,3 +71,9 @@ impl<T> fmt::Debug for BroadcastStream<T> {
|
||||
f.debug_struct("BroadcastStream").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone + Send> From<Receiver<T>> for BroadcastStream<T> {
|
||||
fn from(recv: Receiver<T>) -> Self {
|
||||
Self::new(recv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,3 +57,9 @@ impl<T> AsMut<Receiver<T>> for ReceiverStream<T> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Receiver<T>> for ReceiverStream<T> {
|
||||
fn from(recv: Receiver<T>) -> Self {
|
||||
Self::new(recv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,3 +51,9 @@ impl<T> AsMut<UnboundedReceiver<T>> for UnboundedReceiverStream<T> {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<UnboundedReceiver<T>> for UnboundedReceiverStream<T> {
|
||||
fn from(recv: UnboundedReceiver<T>) -> Self {
|
||||
Self::new(recv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use tokio::sync::watch::error::RecvError;
|
||||
/// A wrapper around [`tokio::sync::watch::Receiver`] that implements [`Stream`].
|
||||
///
|
||||
/// This stream will always start by yielding the current value when the WatchStream is polled,
|
||||
/// regardles of whether it was the initial value or sent afterwards.
|
||||
/// regardless of whether it was the initial value or sent afterwards.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -59,7 +59,7 @@ async fn make_future<T: Clone + Send + Sync>(
|
||||
(result, rx)
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone + Unpin + Send + Sync> WatchStream<T> {
|
||||
impl<T: 'static + Clone + Send + Sync> WatchStream<T> {
|
||||
/// Create a new `WatchStream`.
|
||||
pub fn new(rx: Receiver<T>) -> Self {
|
||||
Self {
|
||||
@@ -72,10 +72,10 @@ impl<T: Clone + 'static + Send + Sync> Stream for WatchStream<T> {
|
||||
type Item = T;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let (result, rx) = ready!(self.inner.poll(cx));
|
||||
let (result, mut rx) = ready!(self.inner.poll(cx));
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let received = (*rx.borrow()).clone();
|
||||
let received = (*rx.borrow_and_update()).clone();
|
||||
self.inner.set(make_future(rx));
|
||||
Poll::Ready(Some(received))
|
||||
}
|
||||
@@ -94,3 +94,9 @@ impl<T> fmt::Debug for WatchStream<T> {
|
||||
f.debug_struct("WatchStream").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static + Clone + Send + Sync> From<Receiver<T>> for WatchStream<T> {
|
||||
fn from(recv: Receiver<T>) -> Self {
|
||||
Self::new(recv)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use tokio::sync::watch;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn message_not_twice() {
|
||||
let (tx, rx) = watch::channel("hello");
|
||||
|
||||
let mut counter = 0;
|
||||
let mut stream = WatchStream::new(rx).map(move |payload| {
|
||||
println!("{}", payload);
|
||||
if payload == "goodbye" {
|
||||
counter += 1;
|
||||
}
|
||||
if counter >= 2 {
|
||||
panic!("too many goodbyes");
|
||||
}
|
||||
});
|
||||
|
||||
let task = tokio::spawn(async move { while stream.next().await.is_some() {} });
|
||||
|
||||
// Send goodbye just once
|
||||
tx.send("goodbye").unwrap();
|
||||
|
||||
drop(tx);
|
||||
task.await.unwrap();
|
||||
}
|
||||
@@ -4,13 +4,13 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
|
||||
//! Tokio and Futures based testing utilites
|
||||
//! Tokio and Futures based testing utilities
|
||||
|
||||
pub mod io;
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ impl ThreadWaker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears any previously received wakes, avoiding potential spurrious
|
||||
/// Clears any previously received wakes, avoiding potential spurious
|
||||
/// wake notifications. This should only be called immediately before running the
|
||||
/// task.
|
||||
fn clear(&self) {
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
# 0.6.8 (September 3, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- sync: add drop guard for `CancellationToken` ([#3839])
|
||||
- compact: added `AsyncSeek` compat ([#4078])
|
||||
- time: expose `Key` used in `DelayQueue`'s `Expired` ([#4081])
|
||||
- io: add `with_capacity` to `ReaderStream` ([#4086])
|
||||
|
||||
### Fixed
|
||||
|
||||
- codec: remove unnecessary `doc(cfg(...))` ([#3989])
|
||||
|
||||
[#3839]: https://github.com/tokio-rs/tokio/pull/3839
|
||||
[#4078]: https://github.com/tokio-rs/tokio/pull/4078
|
||||
[#4081]: https://github.com/tokio-rs/tokio/pull/4081
|
||||
[#4086]: https://github.com/tokio-rs/tokio/pull/4086
|
||||
[#3989]: https://github.com/tokio-rs/tokio/pull/3989
|
||||
|
||||
# 0.6.7 (May 14, 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.7"
|
||||
version = "0.6.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-util/0.6.7/tokio_util"
|
||||
documentation = "https://docs.rs/tokio-util/0.6.8/tokio_util"
|
||||
description = """
|
||||
Additional utilities for working with Tokio.
|
||||
"""
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# tokio-util
|
||||
|
||||
Utilities for encoding and decoding frames.
|
||||
Utilities for working with Tokio.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ impl Default for AnyDelimiterCodec {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error occured while encoding or decoding a chunk.
|
||||
/// An error occurred while encoding or decoding a chunk.
|
||||
#[derive(Debug)]
|
||||
pub enum AnyDelimiterCodecError {
|
||||
/// The maximum chunk length was exceeded.
|
||||
|
||||
@@ -28,7 +28,7 @@ use std::io;
|
||||
/// It is up to the Decoder to keep track of a restart after an EOF,
|
||||
/// and to decide how to handle such an event by, for example,
|
||||
/// allowing frames to cross EOF boundaries, re-emitting opening frames, or
|
||||
/// reseting the entire internal state.
|
||||
/// resetting the entire internal state.
|
||||
///
|
||||
/// [`Framed`]: crate::codec::Framed
|
||||
/// [`FramedRead`]: crate::codec::FramedRead
|
||||
|
||||
@@ -124,7 +124,7 @@ where
|
||||
// to a combination of the `is_readable` and `eof` flags. States persist across
|
||||
// loop entries and most state transitions occur with a return.
|
||||
//
|
||||
// The intitial state is `reading`.
|
||||
// The initial state is `reading`.
|
||||
//
|
||||
// | state | eof | is_readable |
|
||||
// |---------|-------|-------------|
|
||||
@@ -155,10 +155,10 @@ where
|
||||
// Both signal that there is no such data by returning `None`.
|
||||
//
|
||||
// If `decode` couldn't read a frame and the upstream source has returned eof,
|
||||
// `decode_eof` will attemp to decode the remaining bytes as closing frames.
|
||||
// `decode_eof` will attempt to decode the remaining bytes as closing frames.
|
||||
//
|
||||
// If the underlying AsyncRead is resumable, we may continue after an EOF,
|
||||
// but must finish emmiting all of it's associated `decode_eof` frames.
|
||||
// but must finish emitting all of it's associated `decode_eof` frames.
|
||||
// Furthermore, we don't want to emit any `decode_eof` frames on retried
|
||||
// reads after an EOF unless we've actually read more data.
|
||||
if state.is_readable {
|
||||
|
||||
@@ -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,
|
||||
@@ -486,7 +486,7 @@ impl LengthDelimitedCodec {
|
||||
// Skip the required bytes
|
||||
src.advance(self.builder.length_field_offset);
|
||||
|
||||
// match endianess
|
||||
// match endianness
|
||||
let n = if self.builder.length_field_is_big_endian {
|
||||
src.get_uint(field_len)
|
||||
} else {
|
||||
|
||||
@@ -203,12 +203,12 @@ impl Default for LinesCodec {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error occured while encoding or decoding a line.
|
||||
/// An error occurred while encoding or decoding a line.
|
||||
#[derive(Debug)]
|
||||
pub enum LinesCodecError {
|
||||
/// The maximum line length was exceeded.
|
||||
MaxLineLengthExceeded,
|
||||
/// An IO error occured.
|
||||
/// An IO error occurred.
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ pin_project! {
|
||||
pub struct Compat<T> {
|
||||
#[pin]
|
||||
inner: T,
|
||||
seek_pos: Option<io::SeekFrom>,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +81,10 @@ impl<T: tokio::io::AsyncWrite> TokioAsyncWriteCompatExt for T {}
|
||||
|
||||
impl<T> Compat<T> {
|
||||
fn new(inner: T) -> Self {
|
||||
Self { inner }
|
||||
Self {
|
||||
inner,
|
||||
seek_pos: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a reference to the `Future`, `Stream`, `AsyncRead`, or `AsyncWrite` object
|
||||
@@ -216,6 +220,45 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: tokio::io::AsyncSeek> futures_io::AsyncSeek for Compat<T> {
|
||||
fn poll_seek(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
pos: io::SeekFrom,
|
||||
) -> Poll<io::Result<u64>> {
|
||||
if self.seek_pos != Some(pos) {
|
||||
self.as_mut().project().inner.start_seek(pos)?;
|
||||
*self.as_mut().project().seek_pos = Some(pos);
|
||||
}
|
||||
let res = ready!(self.as_mut().project().inner.poll_complete(cx));
|
||||
*self.as_mut().project().seek_pos = None;
|
||||
Poll::Ready(res.map(|p| p as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: futures_io::AsyncSeek> tokio::io::AsyncSeek for Compat<T> {
|
||||
fn start_seek(mut self: Pin<&mut Self>, pos: io::SeekFrom) -> io::Result<()> {
|
||||
*self.as_mut().project().seek_pos = Some(pos);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
let pos = match self.seek_pos {
|
||||
None => {
|
||||
// tokio 1.x AsyncSeek recommends calling poll_complete before start_seek.
|
||||
// We don't have to guarantee that the value returned by
|
||||
// poll_complete called without start_seek is correct,
|
||||
// so we'll return 0.
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
Some(pos) => pos,
|
||||
};
|
||||
let res = ready!(self.as_mut().project().inner.poll_seek(cx, pos));
|
||||
*self.as_mut().project().seek_pos = None;
|
||||
Poll::Ready(res.map(|p| p as u64))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl<T: std::os::unix::io::AsRawFd> std::os::unix::io::AsRawFd for Compat<T> {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd {
|
||||
|
||||
@@ -67,7 +67,7 @@ pub enum Either<L, R> {
|
||||
}
|
||||
|
||||
/// A small helper macro which reduces amount of boilerplate in the actual trait method implementation.
|
||||
/// It takes an invokation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
|
||||
/// It takes an invocation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
|
||||
/// enum variant held in `self`.
|
||||
macro_rules! delegate_call {
|
||||
($self:ident.$method:ident($($args:ident),+)) => {
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::AsyncRead;
|
||||
|
||||
const CAPACITY: usize = 4096;
|
||||
const DEFAULT_CAPACITY: usize = 4096;
|
||||
|
||||
pin_project! {
|
||||
/// Convert an [`AsyncRead`] into a [`Stream`] of byte chunks.
|
||||
@@ -50,6 +50,7 @@ pin_project! {
|
||||
reader: Option<R>,
|
||||
// Working buffer, used to optimize allocations.
|
||||
buf: BytesMut,
|
||||
capacity: usize,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +64,21 @@ impl<R: AsyncRead> ReaderStream<R> {
|
||||
ReaderStream {
|
||||
reader: Some(reader),
|
||||
buf: BytesMut::new(),
|
||||
capacity: DEFAULT_CAPACITY,
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert an [`AsyncRead`] into a [`Stream`] with item type
|
||||
/// `Result<Bytes, std::io::Error>`,
|
||||
/// with a specific read buffer initial capacity.
|
||||
///
|
||||
/// [`AsyncRead`]: tokio::io::AsyncRead
|
||||
/// [`Stream`]: futures_core::Stream
|
||||
pub fn with_capacity(reader: R, capacity: usize) -> Self {
|
||||
ReaderStream {
|
||||
reader: Some(reader),
|
||||
buf: BytesMut::with_capacity(capacity),
|
||||
capacity,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,7 +96,7 @@ impl<R: AsyncRead> Stream for ReaderStream<R> {
|
||||
};
|
||||
|
||||
if this.buf.capacity() == 0 {
|
||||
this.buf.reserve(CAPACITY);
|
||||
this.buf.reserve(*this.capacity);
|
||||
}
|
||||
|
||||
match poll_read_buf(reader, cx, &mut this.buf) {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! An asynchronously awaitable `CancellationToken`.
|
||||
//! The token allows to signal a cancellation request to one or more tasks.
|
||||
pub(crate) mod guard;
|
||||
|
||||
use crate::loom::sync::atomic::AtomicUsize;
|
||||
use crate::loom::sync::Mutex;
|
||||
@@ -11,6 +12,8 @@ use core::ptr::NonNull;
|
||||
use core::sync::atomic::Ordering;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
use guard::DropGuard;
|
||||
|
||||
/// A token which can be used to signal a cancellation request to one or more
|
||||
/// tasks.
|
||||
///
|
||||
@@ -275,6 +278,14 @@ impl CancellationToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `DropGuard` for this token.
|
||||
///
|
||||
/// Returned guard will cancel this token (and all its children) on drop
|
||||
/// unless disarmed.
|
||||
pub fn drop_guard(self) -> DropGuard {
|
||||
DropGuard { inner: Some(self) }
|
||||
}
|
||||
|
||||
unsafe fn register(
|
||||
&self,
|
||||
wait_node: &mut ListNode<WaitQueueEntry>,
|
||||
@@ -764,8 +775,8 @@ impl CancellationTokenState {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
// So far the token is not cancelled. However it could be cancelld before
|
||||
// we get the chance to store the `Waker`. Therfore we need to check
|
||||
// So far the token is not cancelled. However it could be cancelled before
|
||||
// we get the chance to store the `Waker`. Therefore we need to check
|
||||
// for cancellation again inside the mutex.
|
||||
let mut guard = self.synchronized.lock().unwrap();
|
||||
if guard.is_cancelled {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::sync::CancellationToken;
|
||||
|
||||
/// A wrapper for cancellation token which automatically cancels
|
||||
/// it on drop. It is created using `drop_guard` method on the `CancellationToken`.
|
||||
#[derive(Debug)]
|
||||
pub struct DropGuard {
|
||||
pub(super) inner: Option<CancellationToken>,
|
||||
}
|
||||
|
||||
impl DropGuard {
|
||||
/// Returns stored cancellation token and removes this drop guard instance
|
||||
/// (i.e. it will no longer cancel token). Other guards for this token
|
||||
/// are not affected.
|
||||
pub fn disarm(mut self) -> CancellationToken {
|
||||
self.inner
|
||||
.take()
|
||||
.expect("`inner` can be only None in a destructor")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = &self.inner {
|
||||
inner.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +222,7 @@ impl<T> LinkedList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the linked list doesn not contain any node
|
||||
/// Returns whether the linked list does not contain any node
|
||||
pub fn is_empty(&self) -> bool {
|
||||
if self.head.is_some() {
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Synchronization primitives
|
||||
|
||||
mod cancellation_token;
|
||||
pub use cancellation_token::{CancellationToken, WaitForCancellationFuture};
|
||||
pub use cancellation_token::{guard::DropGuard, CancellationToken, WaitForCancellationFuture};
|
||||
|
||||
mod intrusive_double_linked_list;
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ pub struct Expired<T> {
|
||||
///
|
||||
/// [`DelayQueue`]: struct@DelayQueue
|
||||
/// [`DelayQueue::insert`]: method@DelayQueue::insert
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct Key {
|
||||
index: usize,
|
||||
}
|
||||
@@ -921,4 +921,9 @@ impl<T> Expired<T> {
|
||||
pub fn deadline(&self) -> Instant {
|
||||
self.deadline
|
||||
}
|
||||
|
||||
/// Returns the key that the expiration is indexed by.
|
||||
pub fn key(&self) -> Key {
|
||||
self.key
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,6 @@ use std::{io, mem::MaybeUninit};
|
||||
/// [`Sink`]: futures_sink::Sink
|
||||
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
|
||||
#[must_use = "sinks do nothing unless polled"]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "codec", feature = "udp"))))]
|
||||
#[derive(Debug)]
|
||||
pub struct UdpFramed<C, T = UdpSocket> {
|
||||
socket: T,
|
||||
@@ -144,7 +143,7 @@ where
|
||||
..
|
||||
} = *self;
|
||||
|
||||
let n = ready!(socket.borrow().poll_send_to(cx, &wr, *out_addr))?;
|
||||
let n = ready!(socket.borrow().poll_send_to(cx, wr, *out_addr))?;
|
||||
|
||||
let wrote_all = n == self.wr.len();
|
||||
self.wr.clear();
|
||||
|
||||
@@ -11,7 +11,7 @@ use futures_test::task::new_count_waker;
|
||||
fn cancel_token() {
|
||||
let (waker, wake_counter) = new_count_waker();
|
||||
let token = CancellationToken::new();
|
||||
assert_eq!(false, token.is_cancelled());
|
||||
assert!(!token.is_cancelled());
|
||||
|
||||
let wait_fut = token.cancelled();
|
||||
pin!(wait_fut);
|
||||
@@ -27,7 +27,7 @@ fn cancel_token() {
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 1);
|
||||
assert_eq!(true, token.is_cancelled());
|
||||
assert!(token.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
@@ -64,8 +64,8 @@ fn cancel_child_token_through_parent() {
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 2);
|
||||
assert_eq!(true, token.is_cancelled());
|
||||
assert_eq!(true, child_token.is_cancelled());
|
||||
assert!(token.is_cancelled());
|
||||
assert!(child_token.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
@@ -101,8 +101,8 @@ fn cancel_child_token_without_parent() {
|
||||
|
||||
child_token_1.cancel();
|
||||
assert_eq!(wake_counter, 1);
|
||||
assert_eq!(false, token.is_cancelled());
|
||||
assert_eq!(true, child_token_1.is_cancelled());
|
||||
assert!(!token.is_cancelled());
|
||||
assert!(child_token_1.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
@@ -128,8 +128,8 @@ fn cancel_child_token_without_parent() {
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 3);
|
||||
assert_eq!(true, token.is_cancelled());
|
||||
assert_eq!(true, child_token_2.is_cancelled());
|
||||
assert!(token.is_cancelled());
|
||||
assert!(child_token_2.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
|
||||
@@ -1,3 +1,230 @@
|
||||
# 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])
|
||||
|
||||
### Changed
|
||||
|
||||
- macros: run runtime inside `LocalSet` when using macro ([#4027])
|
||||
|
||||
### 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
|
||||
|
||||
- time: don't panic when Instant is not monotonic ([#4044])
|
||||
- io: fix panic in `fill_buf` by not calling `poll_fill_buf` twice ([#4084])
|
||||
|
||||
### Added
|
||||
|
||||
- watch: add `watch::Sender::subscribe` ([#3800])
|
||||
- process: add `from_std` to `ChildStd*` ([#4045])
|
||||
- stats: initial work on runtime stats ([#4043])
|
||||
|
||||
### Changed
|
||||
|
||||
- tracing: change span naming to new console convention ([#4042])
|
||||
- io: speed-up waking by using uninitialized array ([#4055], [#4071], [#4075])
|
||||
|
||||
### Documented
|
||||
|
||||
- time: make Sleep examples easier to find ([#4040])
|
||||
|
||||
[#3800]: https://github.com/tokio-rs/tokio/pull/3800
|
||||
[#4040]: https://github.com/tokio-rs/tokio/pull/4040
|
||||
[#4042]: https://github.com/tokio-rs/tokio/pull/4042
|
||||
[#4043]: https://github.com/tokio-rs/tokio/pull/4043
|
||||
[#4044]: https://github.com/tokio-rs/tokio/pull/4044
|
||||
[#4045]: https://github.com/tokio-rs/tokio/pull/4045
|
||||
[#4055]: https://github.com/tokio-rs/tokio/pull/4055
|
||||
[#4071]: https://github.com/tokio-rs/tokio/pull/4071
|
||||
[#4075]: https://github.com/tokio-rs/tokio/pull/4075
|
||||
[#4084]: https://github.com/tokio-rs/tokio/pull/4084
|
||||
|
||||
# 1.10.1 (August 24, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: fix leak in UnownedTask ([#4063])
|
||||
|
||||
[#4063]: https://github.com/tokio-rs/tokio/pull/4063
|
||||
|
||||
# 1.10.0 (August 12, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `(read|write)_f(32|64)[_le]` methods ([#4022])
|
||||
- io: add `fill_buf` and `consume` to `AsyncBufReadExt` ([#3991])
|
||||
- process: add `Child::raw_handle()` on windows ([#3998])
|
||||
|
||||
### Fixed
|
||||
|
||||
- doc: fix non-doc builds with `--cfg docsrs` ([#4020])
|
||||
- io: flush eagerly in `io::copy` ([#4001])
|
||||
- runtime: a debug assert was sometimes triggered during shutdown ([#4005])
|
||||
- sync: use `spin_loop_hint` instead of `yield_now` in mpsc ([#4037])
|
||||
- tokio: the test-util feature depends on rt, sync, and time ([#4036])
|
||||
|
||||
### Changes
|
||||
|
||||
- runtime: reorganize parts of the runtime ([#3979], [#4005])
|
||||
- signal: make windows docs for signal module show up on unix builds ([#3770])
|
||||
- task: quickly send task to heap on debug mode ([#4009])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: document cancellation safety of `AsyncBufReadExt` ([#3997])
|
||||
- sync: document when `watch::send` fails ([#4021])
|
||||
|
||||
[#3770]: https://github.com/tokio-rs/tokio/pull/3770
|
||||
[#3979]: https://github.com/tokio-rs/tokio/pull/3979
|
||||
[#3991]: https://github.com/tokio-rs/tokio/pull/3991
|
||||
[#3997]: https://github.com/tokio-rs/tokio/pull/3997
|
||||
[#3998]: https://github.com/tokio-rs/tokio/pull/3998
|
||||
[#4001]: https://github.com/tokio-rs/tokio/pull/4001
|
||||
[#4005]: https://github.com/tokio-rs/tokio/pull/4005
|
||||
[#4009]: https://github.com/tokio-rs/tokio/pull/4009
|
||||
[#4020]: https://github.com/tokio-rs/tokio/pull/4020
|
||||
[#4021]: https://github.com/tokio-rs/tokio/pull/4021
|
||||
[#4022]: https://github.com/tokio-rs/tokio/pull/4022
|
||||
[#4036]: https://github.com/tokio-rs/tokio/pull/4036
|
||||
[#4037]: https://github.com/tokio-rs/tokio/pull/4037
|
||||
|
||||
# 1.9.0 (July 22, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- net: allow customized I/O operations for `TcpStream` ([#3888])
|
||||
- sync: add getter for the mutex from a guard ([#3928])
|
||||
- task: expose nameable future for `TaskLocal::scope` ([#3273])
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix leak if output of future panics on drop ([#3967])
|
||||
- Fix leak in `LocalSet` ([#3978])
|
||||
|
||||
### Changes
|
||||
|
||||
- runtime: reorganize parts of the runtime ([#3909], [#3939], [#3950], [#3955], [#3980])
|
||||
- sync: clean up `OnceCell` ([#3945])
|
||||
- task: remove mutex in `JoinError` ([#3959])
|
||||
|
||||
[#3273]: https://github.com/tokio-rs/tokio/pull/3273
|
||||
[#3888]: https://github.com/tokio-rs/tokio/pull/3888
|
||||
[#3909]: https://github.com/tokio-rs/tokio/pull/3909
|
||||
[#3928]: https://github.com/tokio-rs/tokio/pull/3928
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
[#3939]: https://github.com/tokio-rs/tokio/pull/3939
|
||||
[#3945]: https://github.com/tokio-rs/tokio/pull/3945
|
||||
[#3950]: https://github.com/tokio-rs/tokio/pull/3950
|
||||
[#3955]: https://github.com/tokio-rs/tokio/pull/3955
|
||||
[#3959]: https://github.com/tokio-rs/tokio/pull/3959
|
||||
[#3967]: https://github.com/tokio-rs/tokio/pull/3967
|
||||
[#3978]: https://github.com/tokio-rs/tokio/pull/3978
|
||||
[#3980]: https://github.com/tokio-rs/tokio/pull/3980
|
||||
|
||||
# 1.8.3 (July 26, 2021)
|
||||
|
||||
This release backports two fixes from 1.9.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix leak if output of future panics on drop ([#3967])
|
||||
- Fix leak in `LocalSet` ([#3978])
|
||||
|
||||
[#3967]: https://github.com/tokio-rs/tokio/pull/3967
|
||||
[#3978]: https://github.com/tokio-rs/tokio/pull/3978
|
||||
|
||||
# 1.8.2 (July 19, 2021)
|
||||
|
||||
Fixes a missed edge case from 1.8.1.
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: drop canceled future on next poll (#3965)
|
||||
|
||||
# 1.8.1 (July 6, 2021)
|
||||
|
||||
Forward ports 1.5.1 fixes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
|
||||
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
|
||||
# 1.8.0 (July 2, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `get_{ref,mut}` methods to `AsyncFdReadyGuard` and `AsyncFdReadyMutGuard` ([#3807])
|
||||
- io: efficient implementation of vectored writes for `BufWriter` ([#3163])
|
||||
- net: add ready/try methods to `NamedPipe{Client,Server}` ([#3866], [#3899])
|
||||
- sync: add `watch::Receiver::borrow_and_update` ([#3813])
|
||||
- sync: implement `From<T>` for `OnceCell<T>` ([#3877])
|
||||
- time: allow users to specify Interval behaviour when delayed ([#3721])
|
||||
|
||||
### Added (unstable)
|
||||
|
||||
- rt: add `tokio::task::Builder` ([#3881])
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: handle HUP event with `UnixStream` ([#3898])
|
||||
|
||||
### Documented
|
||||
|
||||
- doc: document cancellation safety ([#3900])
|
||||
- time: add wait alias to sleep ([#3897])
|
||||
- time: document auto-advancing behaviour of runtime ([#3763])
|
||||
|
||||
[#3163]: https://github.com/tokio-rs/tokio/pull/3163
|
||||
[#3721]: https://github.com/tokio-rs/tokio/pull/3721
|
||||
[#3763]: https://github.com/tokio-rs/tokio/pull/3763
|
||||
[#3807]: https://github.com/tokio-rs/tokio/pull/3807
|
||||
[#3813]: https://github.com/tokio-rs/tokio/pull/3813
|
||||
[#3866]: https://github.com/tokio-rs/tokio/pull/3866
|
||||
[#3877]: https://github.com/tokio-rs/tokio/pull/3877
|
||||
[#3881]: https://github.com/tokio-rs/tokio/pull/3881
|
||||
[#3897]: https://github.com/tokio-rs/tokio/pull/3897
|
||||
[#3898]: https://github.com/tokio-rs/tokio/pull/3898
|
||||
[#3899]: https://github.com/tokio-rs/tokio/pull/3899
|
||||
[#3900]: https://github.com/tokio-rs/tokio/pull/3900
|
||||
|
||||
# 1.7.2 (July 6, 2021)
|
||||
|
||||
Forward ports 1.5.1 fixes.
|
||||
|
||||
+9
-4
@@ -7,12 +7,12 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.0.x" git tag.
|
||||
version = "1.7.2"
|
||||
version = "1.12.0"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/1.7.2/tokio/"
|
||||
documentation = "https://docs.rs/tokio/1.12.0/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -47,6 +47,7 @@ io-util = ["memchr", "bytes"]
|
||||
# stdin, stdout, stderr
|
||||
io-std = []
|
||||
macros = ["tokio-macros"]
|
||||
stats = []
|
||||
net = [
|
||||
"libc",
|
||||
"mio/os-poll",
|
||||
@@ -82,7 +83,7 @@ signal = [
|
||||
"winapi/consoleapi",
|
||||
]
|
||||
sync = []
|
||||
test-util = []
|
||||
test-util = ["rt", "sync", "time"]
|
||||
time = []
|
||||
|
||||
[dependencies]
|
||||
@@ -109,7 +110,7 @@ signal-hook-registry = { version = "1.1.1", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
libc = { version = "0.2.42" }
|
||||
nix = { version = "0.19.0" }
|
||||
nix = { version = "0.22.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.winapi]
|
||||
version = "0.3.8"
|
||||
@@ -123,12 +124,16 @@ version = "0.3.6"
|
||||
tokio-test = { version = "0.4.0", path = "../tokio-test" }
|
||||
tokio-stream = { version = "0.1", path = "../tokio-stream" }
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
mockall = "0.10.2"
|
||||
proptest = "1"
|
||||
rand = "0.8.0"
|
||||
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"] }
|
||||
|
||||
|
||||
+40
-6
@@ -50,7 +50,15 @@ an asynchronous application.
|
||||
|
||||
## Example
|
||||
|
||||
A basic TCP echo server with Tokio:
|
||||
A basic TCP echo server with Tokio.
|
||||
|
||||
Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.12.0", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
```rust,no_run
|
||||
use tokio::net::TcpListener;
|
||||
@@ -58,7 +66,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
let listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
|
||||
loop {
|
||||
let (mut socket, _) = listener.accept().await?;
|
||||
@@ -132,7 +140,7 @@ several other libraries, including:
|
||||
|
||||
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
|
||||
|
||||
* [`tracing`]: A framework for application-level tracing and async-aware diagnostics.
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
@@ -155,9 +163,35 @@ several other libraries, including:
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable release. The minimum supported version is 1.45.
|
||||
The current Tokio version is not guaranteed to build on Rust versions earlier than the
|
||||
minimum supported version.
|
||||
Tokio is built against the latest stable release. The minimum supported version
|
||||
is 1.45. The current Tokio version is not guaranteed to build on Rust versions
|
||||
earlier than the minimum supported version.
|
||||
|
||||
## Release schedule
|
||||
|
||||
Tokio doesn't follow a fixed release schedule, but we typically make one to two
|
||||
new minor releases each month. We make patch releases for bugfixes as necessary.
|
||||
|
||||
## Bug patching policy
|
||||
|
||||
For the purposes of making patch releases with bugfixes, we have designated
|
||||
certain minor releases as LTS (long term support) releases. Whenever a bug
|
||||
warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.8.x` - LTS release until February 2022.
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least half a
|
||||
year. If you wish to use a fixed minor release in your project, we recommend
|
||||
that you use an LTS release.
|
||||
|
||||
To use a fixed minor version, you can specify the version with a tilde. For
|
||||
example, to specify that you wish to use the newest `1.8.x` patch release, you
|
||||
can use the following dependency specification:
|
||||
```text
|
||||
tokio = { version = "~1.8", features = [...] }
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ It is only possible to implement `AsyncRead` and `AsyncWrite` for resource types
|
||||
themselves and not for `&Resource`. Implementing the traits for `&Resource`
|
||||
would permit concurrent operations to the resource. Because only a single waker
|
||||
is stored per direction, any concurrent usage would result in deadlocks. An
|
||||
alterate implementation would call for a `Vec<Waker>` but this would result in
|
||||
alternate implementation would call for a `Vec<Waker>` but this would result in
|
||||
memory leaks.
|
||||
|
||||
## Enabling reads and writes for `&TcpStream`
|
||||
@@ -268,9 +268,9 @@ select! {
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible to sotre a `TcpStream` in an `Arc`.
|
||||
It is also possible to store a `TcpStream` in an `Arc`.
|
||||
|
||||
```rust
|
||||
let arc_stream = Arc::new(my_tcp_stream);
|
||||
let n = arc_stream.by_ref().read(buf).await?;
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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;
|
||||
|
||||
+32
-16
@@ -3,7 +3,7 @@
|
||||
//! [`File`]: File
|
||||
|
||||
use self::State::*;
|
||||
use crate::fs::{asyncify, sys};
|
||||
use crate::fs::asyncify;
|
||||
use crate::io::blocking::Buf;
|
||||
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
use crate::sync::Mutex;
|
||||
@@ -19,6 +19,19 @@ use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use std::task::Poll::*;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::mocks::spawn_blocking;
|
||||
#[cfg(test)]
|
||||
use super::mocks::JoinHandle;
|
||||
#[cfg(test)]
|
||||
use super::mocks::MockFile as StdFile;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::spawn_blocking;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::JoinHandle;
|
||||
#[cfg(not(test))]
|
||||
use std::fs::File as StdFile;
|
||||
|
||||
/// A reference to an open file on the filesystem.
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::File`][std] for usage from the
|
||||
@@ -78,7 +91,7 @@ use std::task::Poll::*;
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct File {
|
||||
std: Arc<sys::File>,
|
||||
std: Arc<StdFile>,
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
@@ -96,7 +109,7 @@ struct Inner {
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle(Option<Buf>),
|
||||
Busy(sys::Blocking<(Operation, Buf)>),
|
||||
Busy(JoinHandle<(Operation, Buf)>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -142,7 +155,7 @@ impl File {
|
||||
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
|
||||
pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std = asyncify(|| sys::File::open(path)).await?;
|
||||
let std = asyncify(|| StdFile::open(path)).await?;
|
||||
|
||||
Ok(File::from_std(std))
|
||||
}
|
||||
@@ -182,7 +195,7 @@ impl File {
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std_file = asyncify(move || sys::File::create(path)).await?;
|
||||
let std_file = asyncify(move || StdFile::create(path)).await?;
|
||||
Ok(File::from_std(std_file))
|
||||
}
|
||||
|
||||
@@ -199,7 +212,7 @@ impl File {
|
||||
/// let std_file = std::fs::File::open("foo.txt").unwrap();
|
||||
/// let file = tokio::fs::File::from_std(std_file);
|
||||
/// ```
|
||||
pub fn from_std(std: sys::File) -> File {
|
||||
pub fn from_std(std: StdFile) -> File {
|
||||
File {
|
||||
std: Arc::new(std),
|
||||
inner: Mutex::new(Inner {
|
||||
@@ -323,7 +336,7 @@ impl File {
|
||||
|
||||
let std = self.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = if let Some(seek) = seek {
|
||||
(&*std).seek(seek).and_then(|_| std.set_len(size))
|
||||
} else {
|
||||
@@ -409,7 +422,7 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn into_std(mut self) -> sys::File {
|
||||
pub async fn into_std(mut self) -> StdFile {
|
||||
self.inner.get_mut().complete_inflight().await;
|
||||
Arc::try_unwrap(self.std).expect("Arc::try_unwrap failed")
|
||||
}
|
||||
@@ -434,7 +447,7 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_into_std(mut self) -> Result<sys::File, Self> {
|
||||
pub fn try_into_std(mut self) -> Result<StdFile, Self> {
|
||||
match Arc::try_unwrap(self.std) {
|
||||
Ok(file) => Ok(file),
|
||||
Err(std_file_arc) => {
|
||||
@@ -502,7 +515,7 @@ impl AsyncRead for File {
|
||||
buf.ensure_capacity_for(dst);
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = buf.read_from(&mut &*std);
|
||||
(Operation::Read(res), buf)
|
||||
}));
|
||||
@@ -569,7 +582,7 @@ impl AsyncSeek for File {
|
||||
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = (&*std).seek(pos);
|
||||
(Operation::Seek(res), buf)
|
||||
}));
|
||||
@@ -636,7 +649,7 @@ impl AsyncWrite for File {
|
||||
let n = buf.copy_from(src);
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = if let Some(seek) = seek {
|
||||
(&*std).seek(seek).and_then(|_| buf.write_to(&mut &*std))
|
||||
} else {
|
||||
@@ -685,8 +698,8 @@ impl AsyncWrite for File {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys::File> for File {
|
||||
fn from(std: sys::File) -> Self {
|
||||
impl From<StdFile> for File {
|
||||
fn from(std: StdFile) -> Self {
|
||||
Self::from_std(std)
|
||||
}
|
||||
}
|
||||
@@ -709,7 +722,7 @@ impl std::os::unix::io::AsRawFd for File {
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
|
||||
sys::File::from_raw_fd(fd).into()
|
||||
StdFile::from_raw_fd(fd).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,7 +736,7 @@ impl std::os::windows::io::AsRawHandle for File {
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
|
||||
sys::File::from_raw_handle(handle).into()
|
||||
StdFile::from_raw_handle(handle).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,3 +769,6 @@ impl Inner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -1,80 +1,21 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(feature = "full")]
|
||||
|
||||
macro_rules! ready {
|
||||
($e:expr $(,)?) => {
|
||||
match $e {
|
||||
std::task::Poll::Ready(t) => t,
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cfg_fs {
|
||||
($($item:item)*) => { $($item)* }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cfg_io_std {
|
||||
($($item:item)*) => { $($item)* }
|
||||
}
|
||||
|
||||
use futures::future;
|
||||
|
||||
// Load source
|
||||
#[allow(warnings)]
|
||||
#[path = "../src/fs/file.rs"]
|
||||
mod file;
|
||||
use file::File;
|
||||
|
||||
#[allow(warnings)]
|
||||
#[path = "../src/io/blocking.rs"]
|
||||
mod blocking;
|
||||
|
||||
// Load mocked types
|
||||
mod support {
|
||||
pub(crate) mod mock_file;
|
||||
pub(crate) mod mock_pool;
|
||||
}
|
||||
pub(crate) use support::mock_pool as pool;
|
||||
|
||||
// Place them where the source expects them
|
||||
pub(crate) mod io {
|
||||
pub(crate) use tokio::io::*;
|
||||
|
||||
pub(crate) use crate::blocking;
|
||||
|
||||
pub(crate) mod sys {
|
||||
pub(crate) use crate::support::mock_pool::{run, Blocking};
|
||||
}
|
||||
}
|
||||
pub(crate) mod fs {
|
||||
pub(crate) mod sys {
|
||||
pub(crate) use crate::support::mock_file::File;
|
||||
pub(crate) use crate::support::mock_pool::{run, Blocking};
|
||||
}
|
||||
|
||||
pub(crate) use crate::support::mock_pool::asyncify;
|
||||
}
|
||||
pub(crate) mod sync {
|
||||
pub(crate) use tokio::sync::Mutex;
|
||||
}
|
||||
use fs::sys;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
||||
use tokio_test::{assert_pending, assert_ready, assert_ready_err, assert_ready_ok, task};
|
||||
|
||||
use std::io::SeekFrom;
|
||||
use super::*;
|
||||
use crate::{
|
||||
fs::mocks::*,
|
||||
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
|
||||
};
|
||||
use mockall::{predicate::eq, Sequence};
|
||||
use tokio_test::{assert_pending, assert_ready_err, assert_ready_ok, task};
|
||||
|
||||
const HELLO: &[u8] = b"hello world...";
|
||||
const FOO: &[u8] = b"foo bar baz...";
|
||||
|
||||
#[test]
|
||||
fn open_read() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO);
|
||||
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read().once().returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
@@ -83,12 +24,10 @@ fn open_read() {
|
||||
assert_eq!(0, pool::len());
|
||||
assert_pending!(t.poll());
|
||||
|
||||
assert_eq!(1, mock.remaining());
|
||||
assert_eq!(1, pool::len());
|
||||
|
||||
pool::run_one();
|
||||
|
||||
assert_eq!(0, mock.remaining());
|
||||
assert!(t.is_woken());
|
||||
|
||||
let n = assert_ready_ok!(t.poll());
|
||||
@@ -98,9 +37,11 @@ fn open_read() {
|
||||
|
||||
#[test]
|
||||
fn read_twice_before_dispatch() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO);
|
||||
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read().once().returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
@@ -120,8 +61,11 @@ fn read_twice_before_dispatch() {
|
||||
|
||||
#[test]
|
||||
fn read_with_smaller_buf() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read().once().returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -153,8 +97,22 @@ fn read_with_smaller_buf() {
|
||||
|
||||
#[test]
|
||||
fn read_with_bigger_buf() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(&HELLO[..4]).read(&HELLO[4..]);
|
||||
let mut seq = Sequence::new();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..4].copy_from_slice(&HELLO[..4]);
|
||||
Ok(4)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len() - 4].copy_from_slice(&HELLO[4..]);
|
||||
Ok(HELLO.len() - 4)
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -194,8 +152,19 @@ fn read_with_bigger_buf() {
|
||||
|
||||
#[test]
|
||||
fn read_err_then_read_success() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read_err().read(&HELLO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -225,8 +194,11 @@ fn read_err_then_read_success() {
|
||||
|
||||
#[test]
|
||||
fn open_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.with(eq(HELLO))
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -235,12 +207,10 @@ fn open_write() {
|
||||
assert_eq!(0, pool::len());
|
||||
assert_ready_ok!(t.poll());
|
||||
|
||||
assert_eq!(1, mock.remaining());
|
||||
assert_eq!(1, pool::len());
|
||||
|
||||
pool::run_one();
|
||||
|
||||
assert_eq!(0, mock.remaining());
|
||||
assert!(!t.is_woken());
|
||||
|
||||
let mut t = task::spawn(file.flush());
|
||||
@@ -249,7 +219,7 @@ fn open_write() {
|
||||
|
||||
#[test]
|
||||
fn flush_while_idle() {
|
||||
let (_mock, file) = sys::File::mock();
|
||||
let file = MockFile::default();
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -271,13 +241,42 @@ fn read_with_buffer_larger_than_max() {
|
||||
for i in 0..(chunk_d - 1) {
|
||||
data.push((i % 151) as u8);
|
||||
}
|
||||
let data = Arc::new(data);
|
||||
let d0 = data.clone();
|
||||
let d1 = data.clone();
|
||||
let d2 = data.clone();
|
||||
let d3 = data.clone();
|
||||
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(&data[0..chunk_a])
|
||||
.read(&data[chunk_a..chunk_b])
|
||||
.read(&data[chunk_b..chunk_c])
|
||||
.read(&data[chunk_c..]);
|
||||
|
||||
let mut seq = Sequence::new();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[0..chunk_a].copy_from_slice(&d0[0..chunk_a]);
|
||||
Ok(chunk_a)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[..chunk_a].copy_from_slice(&d1[chunk_a..chunk_b]);
|
||||
Ok(chunk_b - chunk_a)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[..chunk_a].copy_from_slice(&d2[chunk_b..chunk_c]);
|
||||
Ok(chunk_c - chunk_b)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[..chunk_a - 1].copy_from_slice(&d3[chunk_c..]);
|
||||
Ok(chunk_a - 1)
|
||||
});
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
let mut actual = vec![0; chunk_d];
|
||||
@@ -296,8 +295,7 @@ fn read_with_buffer_larger_than_max() {
|
||||
pos += n;
|
||||
}
|
||||
|
||||
assert_eq!(mock.remaining(), 0);
|
||||
assert_eq!(data, &actual[..data.len()]);
|
||||
assert_eq!(&data[..], &actual[..data.len()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -314,12 +312,34 @@ fn write_with_buffer_larger_than_max() {
|
||||
for i in 0..(chunk_d - 1) {
|
||||
data.push((i % 151) as u8);
|
||||
}
|
||||
let data = Arc::new(data);
|
||||
let d0 = data.clone();
|
||||
let d1 = data.clone();
|
||||
let d2 = data.clone();
|
||||
let d3 = data.clone();
|
||||
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(&data[0..chunk_a])
|
||||
.write(&data[chunk_a..chunk_b])
|
||||
.write(&data[chunk_b..chunk_c])
|
||||
.write(&data[chunk_c..]);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d0[0..chunk_a])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d1[chunk_a..chunk_b])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d2[chunk_b..chunk_c])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d3[chunk_c..chunk_d - 1])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -344,14 +364,22 @@ fn write_with_buffer_larger_than_max() {
|
||||
}
|
||||
|
||||
pool::run_one();
|
||||
|
||||
assert_eq!(mock.remaining(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_twice_before_dispatch() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -380,10 +408,24 @@ fn write_twice_before_dispatch() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_read_followed_by_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO)
|
||||
.seek_current_ok(-(HELLO.len() as i64), 0)
|
||||
.write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Current(-(HELLO.len() as i64))))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -406,8 +448,25 @@ fn incomplete_read_followed_by_write() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_partial_read_followed_by_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO).seek_current_ok(-10, 0).write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(SeekFrom::Current(-10)))
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -433,10 +492,25 @@ fn incomplete_partial_read_followed_by_write() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_read_followed_by_flush() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO)
|
||||
.seek_current_ok(-(HELLO.len() as i64), 0)
|
||||
.write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(SeekFrom::Current(-(HELLO.len() as i64))))
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -458,8 +532,18 @@ fn incomplete_read_followed_by_flush() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_flush_followed_by_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -484,8 +568,10 @@ fn incomplete_flush_followed_by_write() {
|
||||
|
||||
#[test]
|
||||
fn read_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read_err();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -502,8 +588,10 @@ fn read_err() {
|
||||
|
||||
#[test]
|
||||
fn write_write_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -518,8 +606,19 @@ fn write_write_err() {
|
||||
|
||||
#[test]
|
||||
fn write_read_write_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().read(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -541,8 +640,19 @@ fn write_read_write_err() {
|
||||
|
||||
#[test]
|
||||
fn write_read_flush_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().read(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -564,8 +674,17 @@ fn write_read_flush_err() {
|
||||
|
||||
#[test]
|
||||
fn write_seek_write_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().seek_start_ok(0);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Start(0)))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -587,8 +706,17 @@ fn write_seek_write_err() {
|
||||
|
||||
#[test]
|
||||
fn write_seek_flush_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().seek_start_ok(0);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Start(0)))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -610,8 +738,14 @@ fn write_seek_flush_err() {
|
||||
|
||||
#[test]
|
||||
fn sync_all_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_all();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_all().once().returning(|| Ok(()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -635,8 +769,16 @@ fn sync_all_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn sync_all_err_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_all_err();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_all()
|
||||
.once()
|
||||
.returning(|| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -660,8 +802,14 @@ fn sync_all_err_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn sync_data_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_data();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_data().once().returning(|| Ok(()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -685,8 +833,16 @@ fn sync_data_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn sync_data_err_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_data_err();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_data()
|
||||
.once()
|
||||
.returning(|| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -710,17 +866,15 @@ fn sync_data_err_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn open_set_len_ok() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.set_len(123);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_set_len().with(eq(123)).returning(|_| Ok(()));
|
||||
|
||||
let file = File::from_std(file);
|
||||
let mut t = task::spawn(file.set_len(123));
|
||||
|
||||
assert_pending!(t.poll());
|
||||
assert_eq!(1, mock.remaining());
|
||||
|
||||
pool::run_one();
|
||||
assert_eq!(0, mock.remaining());
|
||||
|
||||
assert!(t.is_woken());
|
||||
assert_ready_ok!(t.poll());
|
||||
@@ -728,17 +882,17 @@ fn open_set_len_ok() {
|
||||
|
||||
#[test]
|
||||
fn open_set_len_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.set_len_err(123);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_set_len()
|
||||
.with(eq(123))
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let file = File::from_std(file);
|
||||
let mut t = task::spawn(file.set_len(123));
|
||||
|
||||
assert_pending!(t.poll());
|
||||
assert_eq!(1, mock.remaining());
|
||||
|
||||
pool::run_one();
|
||||
assert_eq!(0, mock.remaining());
|
||||
|
||||
assert!(t.is_woken());
|
||||
assert_ready_err!(t.poll());
|
||||
@@ -746,11 +900,32 @@ fn open_set_len_err() {
|
||||
|
||||
#[test]
|
||||
fn partial_read_set_len_ok() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO)
|
||||
.seek_current_ok(-14, 0)
|
||||
.set_len(123)
|
||||
.read(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Current(-(HELLO.len() as i64))))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_set_len()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(123))
|
||||
.returning(|_| Ok(()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..FOO.len()].copy_from_slice(FOO);
|
||||
Ok(FOO.len())
|
||||
});
|
||||
|
||||
let mut buf = [0; 32];
|
||||
let mut file = File::from_std(file);
|
||||
@@ -0,0 +1,136 @@
|
||||
//! Mock version of std::fs::File;
|
||||
use mockall::mock;
|
||||
|
||||
use crate::sync::oneshot;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::VecDeque,
|
||||
fs::{Metadata, Permissions},
|
||||
future::Future,
|
||||
io::{self, Read, Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
mock! {
|
||||
#[derive(Debug)]
|
||||
pub File {
|
||||
pub fn create(pb: PathBuf) -> io::Result<Self>;
|
||||
// These inner_ methods exist because std::fs::File has two
|
||||
// implementations for each of these methods: one on "&mut self" and
|
||||
// one on "&&self". Defining both of those in terms of an inner_ method
|
||||
// allows us to specify the expectation the same way, regardless of
|
||||
// which method is used.
|
||||
pub fn inner_flush(&self) -> io::Result<()>;
|
||||
pub fn inner_read(&self, dst: &mut [u8]) -> io::Result<usize>;
|
||||
pub fn inner_seek(&self, pos: SeekFrom) -> io::Result<u64>;
|
||||
pub fn inner_write(&self, src: &[u8]) -> io::Result<usize>;
|
||||
pub fn metadata(&self) -> io::Result<Metadata>;
|
||||
pub fn open(pb: PathBuf) -> io::Result<Self>;
|
||||
pub fn set_len(&self, size: u64) -> io::Result<()>;
|
||||
pub fn set_permissions(&self, _perm: Permissions) -> io::Result<()>;
|
||||
pub fn sync_all(&self) -> io::Result<()>;
|
||||
pub fn sync_data(&self) -> io::Result<()>;
|
||||
pub fn try_clone(&self) -> io::Result<Self>;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(h: std::os::windows::io::RawHandle) -> Self;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for File {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
unsafe fn from_raw_fd(h: std::os::unix::io::RawFd) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for MockFile {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner_read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for &'_ MockFile {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner_read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for &'_ MockFile {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.inner_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for &'_ MockFile {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.inner_write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner_flush()
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct JoinHandle<T> {
|
||||
rx: oneshot::Receiver<T>,
|
||||
}
|
||||
|
||||
pub(super) fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let task = Box::new(move || {
|
||||
let _ = tx.send(f());
|
||||
});
|
||||
|
||||
QUEUE.with(|cell| cell.borrow_mut().push_back(task));
|
||||
|
||||
JoinHandle { rx }
|
||||
}
|
||||
|
||||
impl<T> Future for JoinHandle<T> {
|
||||
type Output = Result<T, io::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
use std::task::Poll::*;
|
||||
|
||||
match Pin::new(&mut self.rx).poll(cx) {
|
||||
Ready(Ok(v)) => Ready(Ok(v)),
|
||||
Ready(Err(e)) => panic!("error = {:?}", e),
|
||||
Pending => Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) mod pool {
|
||||
use super::*;
|
||||
|
||||
pub(in super::super) fn len() -> usize {
|
||||
QUEUE.with(|cell| cell.borrow().len())
|
||||
}
|
||||
|
||||
pub(in super::super) fn run_one() {
|
||||
let task = QUEUE
|
||||
.with(|cell| cell.borrow_mut().pop_front())
|
||||
.expect("expected task to run, but none ready");
|
||||
|
||||
task();
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -84,6 +84,9 @@ pub use self::write::write;
|
||||
mod copy;
|
||||
pub use self::copy::copy;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mocks;
|
||||
|
||||
feature! {
|
||||
#![unix]
|
||||
|
||||
@@ -103,12 +106,17 @@ feature! {
|
||||
|
||||
use std::io;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::spawn_blocking;
|
||||
#[cfg(test)]
|
||||
use mocks::spawn_blocking;
|
||||
|
||||
pub(crate) async fn asyncify<F, T>(f: F) -> io::Result<T>
|
||||
where
|
||||
F: FnOnce() -> io::Result<T> + Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
match sys::run(f).await {
|
||||
match spawn_blocking(f).await {
|
||||
Ok(res) => res,
|
||||
Err(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@@ -116,12 +124,3 @@ where
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Types in this module can be mocked out in tests.
|
||||
mod sys {
|
||||
pub(crate) use std::fs::File;
|
||||
|
||||
// TODO: don't rename
|
||||
pub(crate) use crate::blocking::spawn_blocking as run;
|
||||
pub(crate) use crate::blocking::JoinHandle as Blocking;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,13 @@ use crate::fs::{asyncify, File};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock_open_options;
|
||||
#[cfg(test)]
|
||||
use mock_open_options::MockOpenOptions as StdOpenOptions;
|
||||
#[cfg(not(test))]
|
||||
use std::fs::OpenOptions as StdOpenOptions;
|
||||
|
||||
/// Options and flags which can be used to configure how a file is opened.
|
||||
///
|
||||
/// This builder exposes the ability to configure how a [`File`] is opened and
|
||||
@@ -69,7 +76,7 @@ use std::path::Path;
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpenOptions(std::fs::OpenOptions);
|
||||
pub struct OpenOptions(StdOpenOptions);
|
||||
|
||||
impl OpenOptions {
|
||||
/// Creates a blank new set of options ready for configuration.
|
||||
@@ -89,7 +96,7 @@ impl OpenOptions {
|
||||
/// let future = options.read(true).open("foo.txt");
|
||||
/// ```
|
||||
pub fn new() -> OpenOptions {
|
||||
OpenOptions(std::fs::OpenOptions::new())
|
||||
OpenOptions(StdOpenOptions::new())
|
||||
}
|
||||
|
||||
/// Sets the option for read access.
|
||||
@@ -384,7 +391,7 @@ impl OpenOptions {
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying `std::fs::OpenOptions`
|
||||
pub(super) fn as_inner_mut(&mut self) -> &mut std::fs::OpenOptions {
|
||||
pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -645,8 +652,8 @@ feature! {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::fs::OpenOptions> for OpenOptions {
|
||||
fn from(options: std::fs::OpenOptions) -> OpenOptions {
|
||||
impl From<StdOpenOptions> for OpenOptions {
|
||||
fn from(options: StdOpenOptions) -> OpenOptions {
|
||||
OpenOptions(options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Mock version of std::fs::OpenOptions;
|
||||
use mockall::mock;
|
||||
|
||||
use crate::fs::mocks::MockFile;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use std::{io, path::Path};
|
||||
|
||||
mock! {
|
||||
#[derive(Debug)]
|
||||
pub OpenOptions {
|
||||
pub fn append(&mut self, append: bool) -> &mut Self;
|
||||
pub fn create(&mut self, create: bool) -> &mut Self;
|
||||
pub fn create_new(&mut self, create_new: bool) -> &mut Self;
|
||||
pub fn open<P: AsRef<Path> + 'static>(&self, path: P) -> io::Result<MockFile>;
|
||||
pub fn read(&mut self, read: bool) -> &mut Self;
|
||||
pub fn truncate(&mut self, truncate: bool) -> &mut Self;
|
||||
pub fn write(&mut self, write: bool) -> &mut Self;
|
||||
}
|
||||
impl Clone for OpenOptions {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
impl OpenOptionsExt for OpenOptions {
|
||||
fn custom_flags(&mut self, flags: i32) -> &mut Self;
|
||||
fn mode(&mut self, mode: u32) -> &mut Self;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl OpenOptionsExt for OpenOptions {
|
||||
fn access_mode(&mut self, access: u32) -> &mut Self;
|
||||
fn share_mode(&mut self, val: u32) -> &mut Self;
|
||||
fn custom_flags(&mut self, flags: u32) -> &mut Self;
|
||||
fn attributes(&mut self, val: u32) -> &mut Self;
|
||||
fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,12 @@ use std::{io, path::Path};
|
||||
/// buffer based on the file size when available, so it is generally faster than
|
||||
/// reading into a vector created with `Vec::new()`.
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking operation
|
||||
/// on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`File::open`]: super::File::open
|
||||
/// [`read_to_end`]: crate::io::AsyncReadExt::read_to_end
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::fs::{asyncify, sys};
|
||||
use crate::fs::asyncify;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{FileType, Metadata};
|
||||
@@ -10,9 +10,23 @@ use std::sync::Arc;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::mocks::spawn_blocking;
|
||||
#[cfg(test)]
|
||||
use super::mocks::JoinHandle;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::spawn_blocking;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::JoinHandle;
|
||||
|
||||
/// Returns a stream over the entries within a directory.
|
||||
///
|
||||
/// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking
|
||||
/// operation on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std = asyncify(|| std::fs::read_dir(path)).await?;
|
||||
@@ -45,11 +59,15 @@ pub struct ReadDir(State);
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle(Option<std::fs::ReadDir>),
|
||||
Pending(sys::Blocking<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
|
||||
Pending(JoinHandle<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
|
||||
}
|
||||
|
||||
impl ReadDir {
|
||||
/// Returns the next entry in the directory stream.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe.
|
||||
pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
|
||||
use crate::future::poll_fn;
|
||||
poll_fn(|cx| self.poll_next_entry(cx)).await
|
||||
@@ -79,7 +97,7 @@ impl ReadDir {
|
||||
State::Idle(ref mut std) => {
|
||||
let mut std = std.take().unwrap();
|
||||
|
||||
self.0 = State::Pending(sys::run(move || {
|
||||
self.0 = State::Pending(spawn_blocking(move || {
|
||||
let ret = std.next();
|
||||
(ret, std)
|
||||
}));
|
||||
|
||||
@@ -7,6 +7,10 @@ use std::{io, path::Path};
|
||||
///
|
||||
/// This is the async equivalent of [`std::fs::read_to_string`][std].
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking operation
|
||||
/// on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
/// [std]: fn@std::fs::read_to_string
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -7,6 +7,10 @@ use std::{io, path::Path};
|
||||
///
|
||||
/// This is the async equivalent of [`std::fs::write`][std].
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking operation
|
||||
/// on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
/// [std]: fn@std::fs::write
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -540,6 +540,16 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
|
||||
result => Ok(result),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner [`AsyncFd`].
|
||||
pub fn get_ref(&self) -> &AsyncFd<Inner> {
|
||||
self.async_fd
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
|
||||
pub fn get_inner(&self) -> &Inner {
|
||||
self.get_ref().get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
|
||||
@@ -601,6 +611,26 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
|
||||
result => Ok(result),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner [`AsyncFd`].
|
||||
pub fn get_ref(&self) -> &AsyncFd<Inner> {
|
||||
self.async_fd
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the inner [`AsyncFd`].
|
||||
pub fn get_mut(&mut self) -> &mut AsyncFd<Inner> {
|
||||
self.async_fd
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
|
||||
pub fn get_inner(&self) -> &Inner {
|
||||
self.get_ref().get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the backing object of the inner [`AsyncFd`].
|
||||
pub fn get_inner_mut(&mut self) -> &mut Inner {
|
||||
self.get_mut().get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: std::fmt::Debug + AsRawFd> std::fmt::Debug for AsyncFdReadyGuard<'a, T> {
|
||||
|
||||
@@ -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 {
|
||||
/// Register this AIO event source with Tokio's reactor
|
||||
fn register(&mut self, kq: RawFd, token: usize);
|
||||
|
||||
/// Deregister this AIO event source with Tokio's reactor
|
||||
fn deregister(&mut self);
|
||||
}
|
||||
|
||||
/// Wrap 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);
|
||||
@@ -14,6 +14,26 @@ 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.
|
||||
@@ -58,7 +78,7 @@ impl Interest {
|
||||
self.0.is_writable()
|
||||
}
|
||||
|
||||
/// Add together two `Interst` values.
|
||||
/// Add together two `Interest` values.
|
||||
///
|
||||
/// This function works from a `const` context.
|
||||
///
|
||||
|
||||
@@ -51,6 +51,7 @@ pub(crate) struct Handle {
|
||||
inner: Weak<Inner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReadyEvent {
|
||||
tick: u8,
|
||||
pub(crate) ready: Ready,
|
||||
@@ -96,7 +97,7 @@ const ADDRESS: bit::Pack = bit::Pack::least_significant(24);
|
||||
//
|
||||
// The generation prevents a race condition where a slab slot is reused for a
|
||||
// new socket while the I/O driver is about to apply a readiness event. The
|
||||
// generaton value is checked when setting new readiness. If the generation do
|
||||
// generation value is checked when setting new readiness. If the generation do
|
||||
// not match, then the readiness event is discarded.
|
||||
const GENERATION: bit::Pack = ADDRESS.then(7);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use crate::loom::sync::atomic::AtomicUsize;
|
||||
use crate::loom::sync::Mutex;
|
||||
use crate::util::bit;
|
||||
use crate::util::slab::Entry;
|
||||
use crate::util::WakeList;
|
||||
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
||||
use std::task::{Context, Poll, Waker};
|
||||
@@ -84,9 +85,9 @@ cfg_io_readiness! {
|
||||
|
||||
// The `ScheduledIo::readiness` (`AtomicUsize`) is packed full of goodness.
|
||||
//
|
||||
// | reserved | generation | driver tick | readinesss |
|
||||
// |----------+------------+--------------+------------|
|
||||
// | 1 bit | 7 bits + 8 bits + 16 bits |
|
||||
// | reserved | generation | driver tick | readiness |
|
||||
// |----------+------------+--------------+-----------|
|
||||
// | 1 bit | 7 bits + 8 bits + 16 bits |
|
||||
|
||||
const READINESS: bit::Pack = bit::Pack::least_significant(16);
|
||||
|
||||
@@ -212,10 +213,7 @@ impl ScheduledIo {
|
||||
}
|
||||
|
||||
fn wake0(&self, ready: Ready, shutdown: bool) {
|
||||
const NUM_WAKERS: usize = 32;
|
||||
|
||||
let mut wakers: [Option<Waker>; NUM_WAKERS] = Default::default();
|
||||
let mut curr = 0;
|
||||
let mut wakers = WakeList::new();
|
||||
|
||||
let mut waiters = self.waiters.lock();
|
||||
|
||||
@@ -224,16 +222,14 @@ impl ScheduledIo {
|
||||
// check for AsyncRead slot
|
||||
if ready.is_readable() {
|
||||
if let Some(waker) = waiters.reader.take() {
|
||||
wakers[curr] = Some(waker);
|
||||
curr += 1;
|
||||
wakers.push(waker);
|
||||
}
|
||||
}
|
||||
|
||||
// check for AsyncWrite slot
|
||||
if ready.is_writable() {
|
||||
if let Some(waker) = waiters.writer.take() {
|
||||
wakers[curr] = Some(waker);
|
||||
curr += 1;
|
||||
wakers.push(waker);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,15 +237,14 @@ impl ScheduledIo {
|
||||
'outer: loop {
|
||||
let mut iter = waiters.list.drain_filter(|w| ready.satisfies(w.interest));
|
||||
|
||||
while curr < NUM_WAKERS {
|
||||
while wakers.can_push() {
|
||||
match iter.next() {
|
||||
Some(waiter) => {
|
||||
let waiter = unsafe { &mut *waiter.as_ptr() };
|
||||
|
||||
if let Some(waker) = waiter.waker.take() {
|
||||
waiter.is_ready = true;
|
||||
wakers[curr] = Some(waker);
|
||||
curr += 1;
|
||||
wakers.push(waker);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@@ -260,11 +255,7 @@ impl ScheduledIo {
|
||||
|
||||
drop(waiters);
|
||||
|
||||
for waker in wakers.iter_mut().take(curr) {
|
||||
waker.take().unwrap().wake();
|
||||
}
|
||||
|
||||
curr = 0;
|
||||
wakers.wake_all();
|
||||
|
||||
// Acquire the lock again.
|
||||
waiters = self.waiters.lock();
|
||||
@@ -273,9 +264,7 @@ impl ScheduledIo {
|
||||
// Release the lock before notifying
|
||||
drop(waiters);
|
||||
|
||||
for waker in wakers.iter_mut().take(curr) {
|
||||
waker.take().unwrap().wake();
|
||||
}
|
||||
wakers.wake_all();
|
||||
}
|
||||
|
||||
pub(super) fn ready_event(&self, interest: Interest) -> ReadyEvent {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -40,9 +40,8 @@ cfg_io_driver! {
|
||||
/// [`poll_read_ready`] again will also indicate read readiness.
|
||||
///
|
||||
/// When the operation is attempted and is unable to succeed due to the I/O
|
||||
/// resource not being ready, the caller must call `clear_read_ready` or
|
||||
/// `clear_write_ready`. This clears the readiness state until a new
|
||||
/// readiness event is received.
|
||||
/// resource not being ready, the caller must call `clear_readiness`.
|
||||
/// This clears the readiness state until a new readiness event is received.
|
||||
///
|
||||
/// This allows the caller to implement additional functions. For example,
|
||||
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
|
||||
|
||||
@@ -45,7 +45,7 @@ impl<'a> ReadBuf<'a> {
|
||||
|
||||
/// Creates a new `ReadBuf` from a fully uninitialized buffer.
|
||||
///
|
||||
/// Use `assume_init` if part of the buffer is known to be already inintialized.
|
||||
/// Use `assume_init` if part of the buffer is known to be already initialized.
|
||||
#[inline]
|
||||
pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> {
|
||||
ReadBuf {
|
||||
@@ -85,7 +85,7 @@ impl<'a> ReadBuf<'a> {
|
||||
#[inline]
|
||||
pub fn take(&mut self, n: usize) -> ReadBuf<'_> {
|
||||
let max = std::cmp::min(self.remaining(), n);
|
||||
// Saftey: We don't set any of the `unfilled_mut` with `MaybeUninit::uninit`.
|
||||
// Safety: We don't set any of the `unfilled_mut` with `MaybeUninit::uninit`.
|
||||
unsafe { ReadBuf::uninit(&mut self.unfilled_mut()[..max]) }
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ impl<'a> ReadBuf<'a> {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the filled region of the buffer would become larger than the intialized region.
|
||||
/// Panics if the filled region of the buffer would become larger than the initialized region.
|
||||
#[inline]
|
||||
pub fn set_filled(&mut self, n: usize) {
|
||||
assert!(
|
||||
|
||||
@@ -63,7 +63,7 @@ impl<T> ReadHalf<T> {
|
||||
/// Checks if this `ReadHalf` and some `WriteHalf` were split from the same
|
||||
/// stream.
|
||||
pub fn is_pair_of(&self, other: &WriteHalf<T>) -> bool {
|
||||
other.is_pair_of(&self)
|
||||
other.is_pair_of(self)
|
||||
}
|
||||
|
||||
/// Reunites with a previously split `WriteHalf`.
|
||||
|
||||
@@ -52,10 +52,10 @@ where
|
||||
|
||||
buf = &buf[..crate::io::blocking::MAX_BUF];
|
||||
|
||||
// Now there are two possibilites.
|
||||
// Now there are two possibilities.
|
||||
// If caller gave is binary buffer, we **should not** shrink it
|
||||
// anymore, because excessive shrinking hits performance.
|
||||
// If caller gave as binary buffer, we **must** additionaly
|
||||
// If caller gave as binary buffer, we **must** additionally
|
||||
// shrink it to strip incomplete char at the end of buffer.
|
||||
// that's why check we will perform now is allowed to have
|
||||
// false-positive.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::io::util::fill_buf::{fill_buf, FillBuf};
|
||||
use crate::io::util::lines::{lines, Lines};
|
||||
use crate::io::util::read_line::{read_line, ReadLine};
|
||||
use crate::io::util::read_until::{read_until, ReadUntil};
|
||||
@@ -36,6 +37,18 @@ cfg_io_util! {
|
||||
/// [`fill_buf`]: AsyncBufRead::poll_fill_buf
|
||||
/// [`ErrorKind::Interrupted`]: std::io::ErrorKind::Interrupted
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// If the method is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then some data may have been partially read. Any
|
||||
/// partially read bytes are appended to `buf`, and the method can be
|
||||
/// called again to continue reading until `byte`.
|
||||
///
|
||||
/// This method returns the total number of bytes read. If you cancel
|
||||
/// the call to `read_until` and then call it again to continue reading,
|
||||
/// the counter is reset.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
|
||||
@@ -114,6 +127,30 @@ cfg_io_util! {
|
||||
///
|
||||
/// [`read_until`]: AsyncBufReadExt::read_until
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may have been partially
|
||||
/// read, and this data is lost. There are no guarantees regarding the
|
||||
/// contents of `buf` when the call is cancelled. The current
|
||||
/// implementation replaces `buf` with the empty string, but this may
|
||||
/// change in the future.
|
||||
///
|
||||
/// This function does not behave like [`read_until`] because of the
|
||||
/// requirement that a string contains only valid utf-8. If you need a
|
||||
/// cancellation safe `read_line`, there are three options:
|
||||
///
|
||||
/// * Call [`read_until`] with a newline character and manually perform the utf-8 check.
|
||||
/// * The stream returned by [`lines`] has a cancellation safe
|
||||
/// [`next_line`] method.
|
||||
/// * Use [`tokio_util::codec::LinesCodec`][LinesCodec].
|
||||
///
|
||||
/// [LinesCodec]: https://docs.rs/tokio-util/0.6/tokio_util/codec/struct.LinesCodec.html
|
||||
/// [`read_until`]: Self::read_until
|
||||
/// [`lines`]: Self::lines
|
||||
/// [`next_line`]: crate::io::Lines::next_line
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`std::io::Cursor`][`Cursor`] is a type that implements
|
||||
@@ -173,10 +210,11 @@ cfg_io_util! {
|
||||
/// [`BufRead::split`](std::io::BufRead::split).
|
||||
///
|
||||
/// The stream returned from this function will yield instances of
|
||||
/// [`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have
|
||||
/// [`io::Result`]`<`[`Option`]`<`[`Vec<u8>`]`>>`. Each vector returned will *not* have
|
||||
/// the delimiter byte at the end.
|
||||
///
|
||||
/// [`io::Result`]: std::io::Result
|
||||
/// [`Option`]: core::option::Option
|
||||
/// [`Vec<u8>`]: std::vec::Vec
|
||||
///
|
||||
/// # Errors
|
||||
@@ -206,14 +244,68 @@ cfg_io_util! {
|
||||
split(self, byte)
|
||||
}
|
||||
|
||||
/// Returns the contents of the internal buffer, filling it with more
|
||||
/// data from the inner reader if it is empty.
|
||||
///
|
||||
/// This function is a lower-level call. It needs to be paired with the
|
||||
/// [`consume`] method to function properly. When calling this method,
|
||||
/// none of the contents will be "read" in the sense that later calling
|
||||
/// `read` may return the same contents. As such, [`consume`] must be
|
||||
/// called with the number of bytes that are consumed from this buffer
|
||||
/// to ensure that the bytes are never returned twice.
|
||||
///
|
||||
/// An empty buffer returned indicates that the stream has reached EOF.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn fill_buf(&mut self) -> io::Result<&[u8]>;
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an I/O error if the underlying reader was
|
||||
/// read, but returned an error.
|
||||
///
|
||||
/// [`consume`]: crate::io::AsyncBufReadExt::consume
|
||||
fn fill_buf(&mut self) -> FillBuf<'_, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
fill_buf(self)
|
||||
}
|
||||
|
||||
/// Tells this buffer that `amt` bytes have been consumed from the
|
||||
/// buffer, so they should no longer be returned in calls to [`read`].
|
||||
///
|
||||
/// This function is a lower-level call. It needs to be paired with the
|
||||
/// [`fill_buf`] method to function properly. This function does not
|
||||
/// perform any I/O, it simply informs this object that some amount of
|
||||
/// its buffer, returned from [`fill_buf`], has been consumed and should
|
||||
/// no longer be returned. As such, this function may do odd things if
|
||||
/// [`fill_buf`] isn't called before calling it.
|
||||
///
|
||||
/// The `amt` must be less than the number of bytes in the buffer
|
||||
/// returned by [`fill_buf`].
|
||||
///
|
||||
/// [`read`]: crate::io::AsyncReadExt::read
|
||||
/// [`fill_buf`]: crate::io::AsyncBufReadExt::fill_buf
|
||||
fn consume(&mut self, amt: usize)
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
std::pin::Pin::new(self).consume(amt)
|
||||
}
|
||||
|
||||
/// Returns a stream over the lines of this reader.
|
||||
/// This method is the async equivalent to [`BufRead::lines`](std::io::BufRead::lines).
|
||||
///
|
||||
/// The stream returned from this function will yield instances of
|
||||
/// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline
|
||||
/// [`io::Result`]`<`[`Option`]`<`[`String`]`>>`. Each string returned will *not* have a newline
|
||||
/// byte (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end.
|
||||
///
|
||||
/// [`io::Result`]: std::io::Result
|
||||
/// [`Option`]: core::option::Option
|
||||
/// [`String`]: String
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::io::util::chain::{chain, Chain};
|
||||
use crate::io::util::read::{read, Read};
|
||||
use crate::io::util::read_buf::{read_buf, ReadBuf};
|
||||
use crate::io::util::read_exact::{read_exact, ReadExact};
|
||||
use crate::io::util::read_int::{ReadF32, ReadF32Le, ReadF64, ReadF64Le};
|
||||
use crate::io::util::read_int::{
|
||||
ReadI128, ReadI128Le, ReadI16, ReadI16Le, ReadI32, ReadI32Le, ReadI64, ReadI64Le, ReadI8,
|
||||
};
|
||||
@@ -105,8 +106,8 @@ cfg_io_util! {
|
||||
/// async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
|
||||
/// ```
|
||||
///
|
||||
/// This function does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously
|
||||
/// This method does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
@@ -138,6 +139,12 @@ cfg_io_util! {
|
||||
/// variant will be returned. If an error is returned then it must be
|
||||
/// guaranteed that no bytes were read.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`][crate::fs::File]s implement `Read`:
|
||||
@@ -177,8 +184,8 @@ cfg_io_util! {
|
||||
/// Usually, only a single `read` syscall is issued, even if there is
|
||||
/// more space in the supplied buffer.
|
||||
///
|
||||
/// This function does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously
|
||||
/// This method does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
@@ -197,6 +204,12 @@ cfg_io_util! {
|
||||
/// variant will be returned. If an error is returned then it must be
|
||||
/// guaranteed that no bytes were read.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`] implements `Read` and [`BytesMut`] implements [`BufMut`]:
|
||||
@@ -261,6 +274,13 @@ cfg_io_util! {
|
||||
/// it has read, but it will never read more than would be necessary to
|
||||
/// completely fill the buffer.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may already have been
|
||||
/// read into `buf`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`][crate::fs::File]s implement `Read`:
|
||||
@@ -672,6 +692,82 @@ cfg_io_util! {
|
||||
/// ```
|
||||
fn read_i128(&mut self) -> ReadI128;
|
||||
|
||||
/// Reads an 32-bit floating point type in big-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f32(&mut self) -> io::Result<f32>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 32-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0xff, 0x7f, 0xff, 0xff]);
|
||||
///
|
||||
/// assert_eq!(f32::MIN, reader.read_f32().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f32(&mut self) -> ReadF32;
|
||||
|
||||
/// Reads an 64-bit floating point type in big-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f64(&mut self) -> io::Result<f64>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 64-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(f64::MIN, reader.read_f64().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f64(&mut self) -> ReadF64;
|
||||
|
||||
/// Reads an unsigned 16-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
@@ -978,6 +1074,82 @@ cfg_io_util! {
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i128_le(&mut self) -> ReadI128Le;
|
||||
|
||||
/// Reads an 32-bit floating point type in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f32_le(&mut self) -> io::Result<f32>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 32-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0xff, 0xff, 0x7f, 0xff]);
|
||||
///
|
||||
/// assert_eq!(f32::MIN, reader.read_f32_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f32_le(&mut self) -> ReadF32Le;
|
||||
|
||||
/// Reads an 64-bit floating point type in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f64_le(&mut self) -> io::Result<f64>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 64-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(f64::MIN, reader.read_f64_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f64_le(&mut self) -> ReadF64Le;
|
||||
}
|
||||
|
||||
/// Reads all bytes until EOF in this source, placing them into `buf`.
|
||||
|
||||
@@ -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.
|
||||
///
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::io::util::write::{write, Write};
|
||||
use crate::io::util::write_all::{write_all, WriteAll};
|
||||
use crate::io::util::write_all_buf::{write_all_buf, WriteAllBuf};
|
||||
use crate::io::util::write_buf::{write_buf, WriteBuf};
|
||||
use crate::io::util::write_int::{WriteF32, WriteF32Le, WriteF64, WriteF64Le};
|
||||
use crate::io::util::write_int::{
|
||||
WriteI128, WriteI128Le, WriteI16, WriteI16Le, WriteI32, WriteI32Le, WriteI64, WriteI64Le,
|
||||
WriteI8,
|
||||
@@ -97,6 +98,13 @@ cfg_io_util! {
|
||||
/// It is **not** considered an error if the entire buffer could not be
|
||||
/// written to this writer.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -129,6 +137,13 @@ cfg_io_util! {
|
||||
///
|
||||
/// See [`AsyncWrite::poll_write_vectored`] for more details.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -195,6 +210,13 @@ cfg_io_util! {
|
||||
/// It is **not** considered an error if the entire buffer could not be
|
||||
/// written to this writer.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`] implements [`AsyncWrite`] and [`Cursor`]`<&[u8]>` implements [`Buf`]:
|
||||
@@ -243,6 +265,7 @@ cfg_io_util! {
|
||||
/// while buf.has_remaining() {
|
||||
/// self.write_buf(&mut buf).await?;
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -254,6 +277,15 @@ cfg_io_util! {
|
||||
/// The buffer is advanced after each chunk is successfully written. After failure,
|
||||
/// `src.chunk()` will return the chunk that failed to write.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// If `write_all_buf` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then the data in the provided buffer may have been
|
||||
/// partially written. However, it is guaranteed that the provided
|
||||
/// buffer has been [advanced] by the amount of bytes that have been
|
||||
/// partially written.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`] implements [`AsyncWrite`] and [`Cursor`]`<&[u8]>` implements [`Buf`]:
|
||||
@@ -261,6 +293,7 @@ cfg_io_util! {
|
||||
/// [`File`]: crate::fs::File
|
||||
/// [`Buf`]: bytes::Buf
|
||||
/// [`Cursor`]: std::io::Cursor
|
||||
/// [advanced]: bytes::Buf::advance
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
@@ -300,6 +333,14 @@ cfg_io_util! {
|
||||
/// has been successfully written or such an error occurs. The first
|
||||
/// error generated from this method will be returned.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If it is used as the event
|
||||
/// in a [`tokio::select!`](crate::select) statement and some other
|
||||
/// branch completes first, then the provided buffer may have been
|
||||
/// partially written, but future calls to `write_all` will start over
|
||||
/// from the beginning of the buffer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return the first error that [`write`] returns.
|
||||
@@ -710,6 +751,81 @@ cfg_io_util! {
|
||||
/// ```
|
||||
fn write_i128(&mut self, n: i128) -> WriteI128;
|
||||
|
||||
/// Writes an 32-bit floating point type in big-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f32(&mut self, n: f32) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 32-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f32(f32::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![0xff, 0x7f, 0xff, 0xff]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f32(&mut self, n: f32) -> WriteF32;
|
||||
|
||||
/// Writes an 64-bit floating point type in big-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f64(&mut self, n: f64) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 64-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f64(f64::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![
|
||||
/// 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
/// ]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f64(&mut self, n: f64) -> WriteF64;
|
||||
|
||||
/// Writes an unsigned 16-bit integer in little-endian order to the
|
||||
/// underlying writer.
|
||||
@@ -1018,6 +1134,82 @@ cfg_io_util! {
|
||||
/// }
|
||||
/// ```
|
||||
fn write_i128_le(&mut self, n: i128) -> WriteI128Le;
|
||||
|
||||
/// Writes an 32-bit floating point type in little-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f32_le(&mut self, n: f32) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 32-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f32_le(f32::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![0xff, 0xff, 0x7f, 0xff]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f32_le(&mut self, n: f32) -> WriteF32Le;
|
||||
|
||||
/// Writes an 64-bit floating point type in little-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f64_le(&mut self, n: f64) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 64-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f64_le(f64::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![
|
||||
/// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff
|
||||
/// ]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f64_le(&mut self, n: f64) -> WriteF64Le;
|
||||
}
|
||||
|
||||
/// Flushes this output stream, ensuring that all intermediately buffered
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::io::util::DEFAULT_BUF_SIZE;
|
||||
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io::{self, SeekFrom};
|
||||
use std::io::{self, IoSlice, SeekFrom};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{cmp, fmt, mem};
|
||||
@@ -268,6 +268,18 @@ impl<R: AsyncRead + AsyncWrite> AsyncWrite for BufReader<R> {
|
||||
self.get_pin_mut().poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.get_pin_mut().poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.get_ref().is_write_vectored()
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.get_pin_mut().poll_flush(cx)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::io::util::{BufReader, BufWriter};
|
||||
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io::{self, SeekFrom};
|
||||
use std::io::{self, IoSlice, SeekFrom};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -127,6 +127,18 @@ impl<RW: AsyncRead + AsyncWrite> AsyncWrite for BufStream<RW> {
|
||||
self.project().inner.poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.project().inner.poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner.is_write_vectored()
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.project().inner.poll_flush(cx)
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
use std::io::{self, SeekFrom, Write};
|
||||
use std::io::{self, IoSlice, SeekFrom, Write};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -133,6 +133,72 @@ impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
mut bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
if self.inner.is_write_vectored() {
|
||||
let total_len = bufs
|
||||
.iter()
|
||||
.fold(0usize, |acc, b| acc.saturating_add(b.len()));
|
||||
if total_len > self.buf.capacity() - self.buf.len() {
|
||||
ready!(self.as_mut().flush_buf(cx))?;
|
||||
}
|
||||
let me = self.as_mut().project();
|
||||
if total_len >= me.buf.capacity() {
|
||||
// It's more efficient to pass the slices directly to the
|
||||
// underlying writer than to buffer them.
|
||||
// The case when the total_len calculation saturates at
|
||||
// usize::MAX is also handled here.
|
||||
me.inner.poll_write_vectored(cx, bufs)
|
||||
} else {
|
||||
bufs.iter().for_each(|b| me.buf.extend_from_slice(b));
|
||||
Poll::Ready(Ok(total_len))
|
||||
}
|
||||
} else {
|
||||
// Remove empty buffers at the beginning of bufs.
|
||||
while bufs.first().map(|buf| buf.len()) == Some(0) {
|
||||
bufs = &bufs[1..];
|
||||
}
|
||||
if bufs.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
// Flush if the first buffer doesn't fit.
|
||||
let first_len = bufs[0].len();
|
||||
if first_len > self.buf.capacity() - self.buf.len() {
|
||||
ready!(self.as_mut().flush_buf(cx))?;
|
||||
debug_assert!(self.buf.is_empty());
|
||||
}
|
||||
let me = self.as_mut().project();
|
||||
if first_len >= me.buf.capacity() {
|
||||
// The slice is at least as large as the buffering capacity,
|
||||
// so it's better to write it directly, bypassing the buffer.
|
||||
debug_assert!(me.buf.is_empty());
|
||||
return me.inner.poll_write(cx, &bufs[0]);
|
||||
} else {
|
||||
me.buf.extend_from_slice(&bufs[0]);
|
||||
bufs = &bufs[1..];
|
||||
}
|
||||
let mut total_written = first_len;
|
||||
debug_assert!(total_written != 0);
|
||||
// Append the buffers that fit in the internal buffer.
|
||||
for buf in bufs {
|
||||
if buf.len() > me.buf.capacity() - me.buf.len() {
|
||||
break;
|
||||
} else {
|
||||
me.buf.extend_from_slice(buf);
|
||||
total_written += buf.len();
|
||||
}
|
||||
}
|
||||
Poll::Ready(Ok(total_written))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
ready!(self.as_mut().flush_buf(cx))?;
|
||||
self.get_pin_mut().poll_flush(cx)
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::task::{Context, Poll};
|
||||
#[derive(Debug)]
|
||||
pub(super) struct CopyBuffer {
|
||||
read_done: bool,
|
||||
need_flush: bool,
|
||||
pos: usize,
|
||||
cap: usize,
|
||||
amt: u64,
|
||||
@@ -18,6 +19,7 @@ impl CopyBuffer {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
read_done: false,
|
||||
need_flush: false,
|
||||
pos: 0,
|
||||
cap: 0,
|
||||
amt: 0,
|
||||
@@ -41,7 +43,22 @@ impl CopyBuffer {
|
||||
if self.pos == self.cap && !self.read_done {
|
||||
let me = &mut *self;
|
||||
let mut buf = ReadBuf::new(&mut me.buf);
|
||||
ready!(reader.as_mut().poll_read(cx, &mut buf))?;
|
||||
|
||||
match reader.as_mut().poll_read(cx, &mut buf) {
|
||||
Poll::Ready(Ok(_)) => (),
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
Poll::Pending => {
|
||||
// Try flushing when the reader has no progress to avoid deadlock
|
||||
// when the reader depends on buffered writer.
|
||||
if self.need_flush {
|
||||
ready!(writer.as_mut().poll_flush(cx))?;
|
||||
self.need_flush = false;
|
||||
}
|
||||
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let n = buf.filled().len();
|
||||
if n == 0 {
|
||||
self.read_done = true;
|
||||
@@ -63,6 +80,7 @@ impl CopyBuffer {
|
||||
} else {
|
||||
self.pos += i;
|
||||
self.amt += i as u64;
|
||||
self.need_flush = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
use crate::io::AsyncBufRead;
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::marker::PhantomPinned;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Future for the [`fill_buf`](crate::io::AsyncBufReadExt::fill_buf) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct FillBuf<'a, R: ?Sized> {
|
||||
reader: Option<&'a mut R>,
|
||||
#[pin]
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fill_buf<R>(reader: &mut R) -> FillBuf<'_, R>
|
||||
where
|
||||
R: AsyncBufRead + ?Sized + Unpin,
|
||||
{
|
||||
FillBuf {
|
||||
reader: Some(reader),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for FillBuf<'a, R> {
|
||||
type Output = io::Result<&'a [u8]>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let me = self.project();
|
||||
|
||||
let reader = me.reader.take().expect("Polled after completion.");
|
||||
match Pin::new(&mut *reader).poll_fill_buf(cx) {
|
||||
Poll::Ready(Ok(slice)) => unsafe {
|
||||
// Safety: This is necessary only due to a limitation in the
|
||||
// borrow checker. Once Rust starts using the polonius borrow
|
||||
// checker, this can be simplified.
|
||||
let slice = std::mem::transmute::<&[u8], &'a [u8]>(slice);
|
||||
Poll::Ready(Ok(slice))
|
||||
},
|
||||
Poll::Ready(Err(err)) => Poll::Ready(Err(err)),
|
||||
Poll::Pending => {
|
||||
*me.reader = Some(reader);
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,10 @@ where
|
||||
{
|
||||
/// Returns the next line in the stream.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -102,11 +106,9 @@ where
|
||||
///
|
||||
/// When the method returns `Poll::Pending`, the `Waker` in the provided
|
||||
/// `Context` is scheduled to receive a wakeup when more bytes become
|
||||
/// available on the underlying IO resource.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_next_line`, only the `Waker` from
|
||||
/// the `Context` passed to the most recent call is scheduled to receive a
|
||||
/// wakeup.
|
||||
/// available on the underlying IO resource. Note that on multiple calls to
|
||||
/// `poll_next_line`, only the `Waker` from the `Context` passed to the most
|
||||
/// recent call is scheduled to receive a wakeup.
|
||||
pub fn poll_next_line(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
|
||||
@@ -49,6 +49,7 @@ cfg_io_util! {
|
||||
mod read_exact;
|
||||
mod read_int;
|
||||
mod read_line;
|
||||
mod fill_buf;
|
||||
|
||||
mod read_to_end;
|
||||
mod vec_with_initialized;
|
||||
|
||||
@@ -142,6 +142,9 @@ reader!(ReadI32, i32, get_i32);
|
||||
reader!(ReadI64, i64, get_i64);
|
||||
reader!(ReadI128, i128, get_i128);
|
||||
|
||||
reader!(ReadF32, f32, get_f32);
|
||||
reader!(ReadF64, f64, get_f64);
|
||||
|
||||
reader!(ReadU16Le, u16, get_u16_le);
|
||||
reader!(ReadU32Le, u32, get_u32_le);
|
||||
reader!(ReadU64Le, u64, get_u64_le);
|
||||
@@ -151,3 +154,6 @@ reader!(ReadI16Le, i16, get_i16_le);
|
||||
reader!(ReadI32Le, i32, get_i32_le);
|
||||
reader!(ReadI64Le, i64, get_i64_le);
|
||||
reader!(ReadI128Le, i128, get_i128_le);
|
||||
|
||||
reader!(ReadF32Le, f32, get_f32_le);
|
||||
reader!(ReadF64Le, f64, get_f64_le);
|
||||
|
||||
@@ -10,12 +10,12 @@ use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Future for the [`read_until`](crate::io::AsyncBufReadExt::read_until) method.
|
||||
/// The delimeter is included in the resulting vector.
|
||||
/// The delimiter is included in the resulting vector.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct ReadUntil<'a, R: ?Sized> {
|
||||
reader: &'a mut R,
|
||||
delimeter: u8,
|
||||
delimiter: u8,
|
||||
buf: &'a mut Vec<u8>,
|
||||
// The number of bytes appended to buf. This can be less than buf.len() if
|
||||
// the buffer was not empty when the operation was started.
|
||||
@@ -28,7 +28,7 @@ pin_project! {
|
||||
|
||||
pub(crate) fn read_until<'a, R>(
|
||||
reader: &'a mut R,
|
||||
delimeter: u8,
|
||||
delimiter: u8,
|
||||
buf: &'a mut Vec<u8>,
|
||||
) -> ReadUntil<'a, R>
|
||||
where
|
||||
@@ -36,7 +36,7 @@ where
|
||||
{
|
||||
ReadUntil {
|
||||
reader,
|
||||
delimeter,
|
||||
delimiter,
|
||||
buf,
|
||||
read: 0,
|
||||
_pin: PhantomPinned,
|
||||
@@ -46,14 +46,14 @@ where
|
||||
pub(super) fn read_until_internal<R: AsyncBufRead + ?Sized>(
|
||||
mut reader: Pin<&mut R>,
|
||||
cx: &mut Context<'_>,
|
||||
delimeter: u8,
|
||||
delimiter: u8,
|
||||
buf: &mut Vec<u8>,
|
||||
read: &mut usize,
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
let (done, used) = {
|
||||
let available = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
||||
if let Some(i) = memchr::memchr(delimeter, available) {
|
||||
if let Some(i) = memchr::memchr(delimiter, available) {
|
||||
buf.extend_from_slice(&available[..=i]);
|
||||
(true, i + 1)
|
||||
} else {
|
||||
@@ -74,6 +74,6 @@ impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadUntil<'_, R> {
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let me = self.project();
|
||||
read_until_internal(Pin::new(*me.reader), cx, *me.delimeter, me.buf, me.read)
|
||||
read_until_internal(Pin::new(*me.reader), cx, *me.delimiter, me.buf, me.read)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ where
|
||||
let n = ready!(read_until_internal(
|
||||
me.reader, cx, *me.delim, me.buf, me.read,
|
||||
))?;
|
||||
// read_until_internal resets me.read to zero once it finds the delimeter
|
||||
// read_until_internal resets me.read to zero once it finds the delimiter
|
||||
debug_assert_eq!(*me.read, 0);
|
||||
|
||||
if n == 0 && me.buf.is_empty() {
|
||||
|
||||
@@ -135,6 +135,9 @@ writer!(WriteI32, i32, put_i32);
|
||||
writer!(WriteI64, i64, put_i64);
|
||||
writer!(WriteI128, i128, put_i128);
|
||||
|
||||
writer!(WriteF32, f32, put_f32);
|
||||
writer!(WriteF64, f64, put_f64);
|
||||
|
||||
writer!(WriteU16Le, u16, put_u16_le);
|
||||
writer!(WriteU32Le, u32, put_u32_le);
|
||||
writer!(WriteU64Le, u64, put_u64_le);
|
||||
@@ -144,3 +147,6 @@ writer!(WriteI16Le, i16, put_i16_le);
|
||||
writer!(WriteI32Le, i32, put_i32_le);
|
||||
writer!(WriteI64Le, i64, put_i64_le);
|
||||
writer!(WriteI128Le, i128, put_i128_le);
|
||||
|
||||
writer!(WriteF32Le, f32, put_f32_le);
|
||||
writer!(WriteF64Le, f64, put_f64_le);
|
||||
|
||||
+14
-6
@@ -10,12 +10,13 @@
|
||||
unreachable_pub
|
||||
)]
|
||||
#![deny(unused_must_use)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, allow(unused_attributes))]
|
||||
|
||||
//! A runtime for writing reliable network applications without compromising speed.
|
||||
//!
|
||||
@@ -204,9 +205,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
|
||||
@@ -307,8 +314,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.
|
||||
|
||||
@@ -2,21 +2,17 @@
|
||||
//! re-export of `AtomicU64`. On 32 bit platforms, this is implemented using a
|
||||
//! `Mutex`.
|
||||
|
||||
pub(crate) use self::imp::AtomicU64;
|
||||
|
||||
// `AtomicU64` can only be used on targets with `target_has_atomic` is 64 or greater.
|
||||
// Once `cfg_target_has_atomic` feature is stable, we can replace it with
|
||||
// `#[cfg(target_has_atomic = "64")]`.
|
||||
// Refs: https://github.com/rust-lang/rust/tree/master/src/librustc_target
|
||||
#[cfg(not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")))]
|
||||
mod imp {
|
||||
cfg_has_atomic_u64! {
|
||||
pub(crate) use std::sync::atomic::AtomicU64;
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc"))]
|
||||
mod imp {
|
||||
cfg_not_has_atomic_u64! {
|
||||
use crate::loom::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AtomicU64 {
|
||||
@@ -31,15 +27,15 @@ mod imp {
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, _: Ordering) -> u64 {
|
||||
*self.inner.lock().unwrap()
|
||||
*self.inner.lock()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self, val: u64, _: Ordering) {
|
||||
*self.inner.lock().unwrap() = val;
|
||||
*self.inner.lock() = val;
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock().unwrap();
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev | val;
|
||||
prev
|
||||
@@ -52,7 +48,7 @@ mod imp {
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner.lock().unwrap();
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user