mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
131
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1a997ffbd6 | ||
|
|
a8fe333cc4 | ||
|
|
ba81945ffc | ||
|
|
763bdc967e | ||
|
|
9f98535877 | ||
|
|
9241c3eddf | ||
|
|
699573d550 | ||
|
|
3ce5a2681c | ||
|
|
644cb8207d | ||
|
|
a1316cd792 | ||
|
|
86ffabe2af | ||
|
|
00bf5ee8a8 | ||
|
|
87510100ce | ||
|
|
2be71ad746 | ||
|
|
d1b789f33a | ||
|
|
22862739dd | ||
|
|
993a60b7c7 | ||
|
|
5d25ec46d5 | ||
|
|
766f22fae3 | ||
|
|
f5686f6bc0 | ||
|
|
224acd2500 | ||
|
|
2fcc6c2cb0 | ||
|
|
28ec4a6161 | ||
|
|
939b5bb42f | ||
|
|
718d6ce8ca | ||
|
|
e316428210 | ||
|
|
fc83e01949 | ||
|
|
3a5f7b7f2f | ||
|
|
6da81471f9 | ||
|
|
299bd6aee3 | ||
|
|
e14307393a | ||
|
|
45e37dbfa2 | ||
|
|
304b5152a7 | ||
|
|
f15d14ee91 | ||
|
|
6a2cd9a652 | ||
|
|
2682c505e8 | ||
|
|
808d52563e | ||
|
|
bf31759bff | ||
|
|
d65826236b | ||
|
|
1cbbcc9ad5 | ||
|
|
a668020150 | ||
|
|
01f0193971 | ||
|
|
71bd49e146 | ||
|
|
582d512907 | ||
|
|
b7812c85ca | ||
|
|
9e3fb1673a | ||
|
|
53cba023da | ||
|
|
236d026667 | ||
|
|
909439c9f5 | ||
|
|
9884fe3394 | ||
|
|
a1002a2203 | ||
|
|
fc9518b627 | ||
|
|
f4643608ad | ||
|
|
687aa2bae5 | ||
|
|
b2f5dbea47 | ||
|
|
23fdd32b01 | ||
|
|
5b46395a1e | ||
|
|
32da1aa9da | ||
|
|
df6348fb4a | ||
|
|
74a29be607 | ||
|
|
26791a62bc | ||
|
|
467adec4e1 | ||
|
|
d8ee205530 | ||
|
|
ca8e176ce9 | ||
|
|
a051ed726f | ||
|
|
203a079743 | ||
|
|
b1f40f4356 | ||
|
|
a9d5eb2fc7 | ||
|
|
c2210dfe37 | ||
|
|
df99428c17 | ||
|
|
d1a8ec6495 | ||
|
|
fe1843c0e0 | ||
|
|
15b362d2dd | ||
|
|
620880f4ca | ||
|
|
29c6de0e3e | ||
|
|
3886a3eaf4 | ||
|
|
3b5ef4eb98 | ||
|
|
cbbf81b922 | ||
|
|
1ab80ba580 | ||
|
|
7483509746 | ||
|
|
121f839b4b | ||
|
|
b749013a54 | ||
|
|
2f24451434 | ||
|
|
5c2e275659 | ||
|
|
6c19748f90 | ||
|
|
32d68fe579 | ||
|
|
4bcd08b938 | ||
|
|
cb67f28fe3 | ||
|
|
58c457190b | ||
|
|
ec66a92b01 | ||
|
|
1ca17bef67 | ||
|
|
80568dfc6d | ||
|
|
b248be2879 | ||
|
|
d32ba2cf9d | ||
|
|
a03e042024 | ||
|
|
00082c668f | ||
|
|
607816491d | ||
|
|
1e437595a3 | ||
|
|
bf5eed8fa0 | ||
|
|
37d1d09d4a | ||
|
|
6929decf2c | ||
|
|
f8097437dd | ||
|
|
964535eab0 | ||
|
|
23a1ccf24f | ||
|
|
ca9dd726b1 | ||
|
|
ae0d49d59c | ||
|
|
992a168122 | ||
|
|
9b486357ed | ||
|
|
fdc28bc883 | ||
|
|
5dbd8ca8d4 | ||
|
|
ff4ab49bcf | ||
|
|
6bdcb813c6 | ||
|
|
b821e436c5 | ||
|
|
f4e08aec66 | ||
|
|
96fab053ab | ||
|
|
9b87daad7e | ||
|
|
e8354279fb | ||
|
|
78fc94099b | ||
|
|
2df45234ef | ||
|
|
aedcec666d | ||
|
|
909de08845 | ||
|
|
3db330dad2 | ||
|
|
feef48fb58 | ||
|
|
47b6e194fb | ||
|
|
a79b824faa | ||
|
|
3fbf314985 | ||
|
|
3d95a4626f | ||
|
|
2063d6692e | ||
|
|
5c76d070e2 | ||
|
|
05e661490b | ||
|
|
7096a80075 |
@@ -22,4 +22,4 @@ jobs:
|
||||
workflows:
|
||||
ci:
|
||||
jobs:
|
||||
- test-arm
|
||||
- test-arm
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@ freebsd_instance:
|
||||
image: freebsd-12-3-release-amd64
|
||||
env:
|
||||
RUST_STABLE: stable
|
||||
RUST_NIGHTLY: nightly-2022-03-21
|
||||
RUST_NIGHTLY: nightly-2022-10-25
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
# Test FreeBSD in a full VM on cirrus-ci.com. Test the i686 target too, in the
|
||||
|
||||
@@ -9,8 +9,15 @@ on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
permissions:
|
||||
checks: write # for actions-rs/audit-check to create check
|
||||
contents: read # for actions/checkout to fetch code
|
||||
issues: write # for actions-rs/audit-check to create issues
|
||||
runs-on: ubuntu-latest
|
||||
if: "!contains(github.event.head_commit.message, 'ci skip')"
|
||||
steps:
|
||||
|
||||
+72
-14
@@ -11,8 +11,8 @@ env:
|
||||
RUST_BACKTRACE: 1
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: stable
|
||||
rust_nightly: nightly-2022-07-26
|
||||
rust_clippy: 1.52.0
|
||||
rust_nightly: nightly-2022-11-03
|
||||
rust_clippy: 1.60.0
|
||||
# When updating this, also update:
|
||||
# - README.md
|
||||
# - tokio/README.md
|
||||
@@ -27,6 +27,9 @@ defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# Depends on all action sthat are required for a "successful" CI run.
|
||||
tests-pass:
|
||||
@@ -39,7 +42,9 @@ jobs:
|
||||
- test-unstable
|
||||
- miri
|
||||
- asan
|
||||
- cross
|
||||
- cross-check
|
||||
- cross-test
|
||||
- no-atomic-u64
|
||||
- features
|
||||
- minrust
|
||||
- minimal-versions
|
||||
@@ -207,7 +212,7 @@ jobs:
|
||||
run: cargo miri test --features full --lib --no-fail-fast
|
||||
working-directory: tokio
|
||||
env:
|
||||
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-tag-raw-pointers
|
||||
MIRIFLAGS: -Zmiri-disable-isolation -Zmiri-strict-provenance -Zmiri-retag-fields
|
||||
PROPTEST_CASES: 10
|
||||
|
||||
asan:
|
||||
@@ -231,13 +236,12 @@ jobs:
|
||||
# Ignore `trybuild` errors as they are irrelevant and flaky on nightly
|
||||
TRYBUILD: overwrite
|
||||
|
||||
cross:
|
||||
name: cross
|
||||
cross-check:
|
||||
name: cross-check
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- i686-unknown-linux-gnu
|
||||
- powerpc-unknown-linux-gnu
|
||||
- powerpc64-unknown-linux-gnu
|
||||
- mips-unknown-linux-gnu
|
||||
@@ -256,13 +260,54 @@ jobs:
|
||||
use-cross: true
|
||||
command: check
|
||||
args: --workspace --all-features --target ${{ matrix.target }}
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
|
||||
cross-test:
|
||||
name: cross-test
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- i686-unknown-linux-gnu
|
||||
- arm-unknown-linux-gnueabihf
|
||||
- armv7-unknown-linux-gnueabihf
|
||||
- aarch64-unknown-linux-gnu
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_stable }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_stable }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: true
|
||||
command: check
|
||||
args: --workspace --all-features --target ${{ matrix.target }}
|
||||
command: test
|
||||
args: -p tokio --all-features --target ${{ matrix.target }} --tests
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_ipv6
|
||||
|
||||
# See https://github.com/tokio-rs/tokio/issues/5187
|
||||
no-atomic-u64:
|
||||
name: Test i686-unknown-linux-gnu without AtomicU64
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
components: rust-src
|
||||
override: true
|
||||
# Install linker and libraries for i686-unknown-linux-gnu
|
||||
- uses: taiki-e/setup-cross-toolchain-action@v1
|
||||
with:
|
||||
target: i686-unknown-linux-gnu
|
||||
- run: cargo test -Zbuild-std --target target-specs/i686-unknown-linux-gnu.json -p tokio --all-features
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings --cfg tokio_no_atomic_u64
|
||||
|
||||
features:
|
||||
name: features
|
||||
@@ -297,7 +342,18 @@ jobs:
|
||||
toolchain: ${{ env.rust_min }}
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: "test --workspace --all-features"
|
||||
# First compile just the main tokio crate with minrust and newest version
|
||||
# of all dependencies, then pin once_cell and compile the rest of the
|
||||
# crates with the pinned once_cell version.
|
||||
#
|
||||
# This is necessary because tokio-util transitively depends on once_cell,
|
||||
# which is not compatible with the current minrust after the 1.15.0
|
||||
# release.
|
||||
- name: "check -p tokio --all-features"
|
||||
run: cargo check -p tokio --all-features
|
||||
- name: "pin once_cell version"
|
||||
run: cargo update -p once_cell --precise 1.14.0
|
||||
- name: "check --workspace --all-features"
|
||||
run: cargo check --workspace --all-features
|
||||
|
||||
minimal-versions:
|
||||
@@ -541,15 +597,17 @@ jobs:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install Rust ${{ env.rust_nightly }}
|
||||
- name: Install Rust nightly-2022-07-25
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ env.rust_nightly }}
|
||||
# `check-external-types` requires a specific Rust nightly version. See
|
||||
# the README for details: https://github.com/awslabs/cargo-check-external-types
|
||||
toolchain: nightly-2022-07-25
|
||||
override: true
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
- name: check-external-types
|
||||
run: |
|
||||
set -x
|
||||
cargo install cargo-check-external-types --locked --version 0.1.3
|
||||
cargo install cargo-check-external-types --locked --version 0.1.5
|
||||
cargo check-external-types --all-features --config external-types.toml
|
||||
working-directory: tokio
|
||||
|
||||
@@ -4,8 +4,14 @@ on:
|
||||
|
||||
# See .github/labeler.yml file
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read # for actions/labeler to determine modified files
|
||||
pull-requests: write # for actions/labeler to add labels to PRs
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'tokio-rs'
|
||||
steps:
|
||||
|
||||
@@ -13,6 +13,9 @@ env:
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: stable
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
loom:
|
||||
name: loom
|
||||
@@ -42,4 +45,5 @@ jobs:
|
||||
env:
|
||||
RUSTFLAGS: --cfg loom --cfg tokio_unstable -Dwarnings
|
||||
LOOM_MAX_PREEMPTIONS: 2
|
||||
LOOM_MAX_BRANCHES: 10000
|
||||
SCOPE: ${{ matrix.scope }}
|
||||
|
||||
@@ -8,6 +8,9 @@ on:
|
||||
paths:
|
||||
- '**/Cargo.toml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -11,6 +11,9 @@ env:
|
||||
# Change to specific Rust release to pin
|
||||
rust_stable: stable
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
stress-test:
|
||||
name: Stress Test
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
[build.env]
|
||||
passthrough = [
|
||||
"RUSTFLAGS",
|
||||
"RUST_BACKTRACE",
|
||||
]
|
||||
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.21.1", features = ["full"] }
|
||||
tokio = { version = "1.23.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ edition = "2018"
|
||||
[dependencies]
|
||||
tokio = { version = "1.5.0", path = "../tokio", features = ["full"] }
|
||||
bencher = "0.1.5"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-util = { version = "0.7.0", path = "../tokio-util", features = ["full"] }
|
||||
@@ -50,3 +52,8 @@ harness = false
|
||||
name = "fs"
|
||||
path = "fs.rs"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "copy"
|
||||
path = "copy.rs"
|
||||
harness = false
|
||||
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
use bencher::{benchmark_group, benchmark_main, Bencher};
|
||||
|
||||
use rand::{Rng, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
|
||||
use tokio::io::{copy, repeat, AsyncRead, AsyncReadExt, AsyncWrite};
|
||||
use tokio::time::{interval, Interval, MissedTickBehavior};
|
||||
|
||||
use std::task::Poll;
|
||||
use std::time::Duration;
|
||||
|
||||
const KILO: usize = 1024;
|
||||
|
||||
// Tunable parameters if you want to change this benchmark. If reader and writer
|
||||
// are matched in kilobytes per second, then this only exposes buffering to the
|
||||
// benchmark.
|
||||
const RNG_SEED: u64 = 0;
|
||||
// How much data to copy in a single benchmark run
|
||||
const SOURCE_SIZE: u64 = 256 * KILO as u64;
|
||||
// Read side provides CHUNK_SIZE every READ_SERVICE_PERIOD. If it's not called
|
||||
// frequently, it'll burst to catch up (representing OS buffers draining)
|
||||
const CHUNK_SIZE: usize = 2 * KILO;
|
||||
const READ_SERVICE_PERIOD: Duration = Duration::from_millis(1);
|
||||
// Write side buffers up to WRITE_BUFFER, and flushes to disk every
|
||||
// WRITE_SERVICE_PERIOD.
|
||||
const WRITE_BUFFER: usize = 40 * KILO;
|
||||
const WRITE_SERVICE_PERIOD: Duration = Duration::from_millis(20);
|
||||
// How likely you are to have to wait for previously written data to be flushed
|
||||
// because another writer claimed the buffer space
|
||||
const PROBABILITY_FLUSH_WAIT: f64 = 0.1;
|
||||
|
||||
/// A slow writer that aims to simulate HDD behaviour under heavy load.
|
||||
///
|
||||
/// There is a limited buffer, which is fully drained on the next write after
|
||||
/// a time limit is reached. Flush waits for the time limit to be reached
|
||||
/// and then drains the buffer.
|
||||
///
|
||||
/// At random, the HDD will stall writers while it flushes out all buffers. If
|
||||
/// this happens to you, you will be unable to write until the next time the
|
||||
/// buffer is drained.
|
||||
struct SlowHddWriter {
|
||||
service_intervals: Interval,
|
||||
blocking_rng: ChaCha20Rng,
|
||||
buffer_size: usize,
|
||||
buffer_used: usize,
|
||||
}
|
||||
|
||||
impl SlowHddWriter {
|
||||
fn new(service_interval: Duration, buffer_size: usize) -> Self {
|
||||
let blocking_rng = ChaCha20Rng::seed_from_u64(RNG_SEED);
|
||||
let mut service_intervals = interval(service_interval);
|
||||
service_intervals.set_missed_tick_behavior(MissedTickBehavior::Delay);
|
||||
Self {
|
||||
service_intervals,
|
||||
blocking_rng,
|
||||
buffer_size,
|
||||
buffer_used: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn service_write(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
// If we hit a service interval, the buffer can be cleared
|
||||
let res = self.service_intervals.poll_tick(cx).map(|_| Ok(()));
|
||||
if let Poll::Ready(_) = res {
|
||||
self.buffer_used = 0;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn write_bytes(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
writeable: usize,
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
let service_res = self.as_mut().service_write(cx);
|
||||
|
||||
if service_res.is_pending() && self.blocking_rng.gen_bool(PROBABILITY_FLUSH_WAIT) {
|
||||
return Poll::Pending;
|
||||
}
|
||||
let available = self.buffer_size - self.buffer_used;
|
||||
|
||||
if available == 0 {
|
||||
assert!(service_res.is_pending());
|
||||
Poll::Pending
|
||||
} else {
|
||||
let written = available.min(writeable);
|
||||
self.buffer_used += written;
|
||||
Poll::Ready(Ok(written))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpin for SlowHddWriter {}
|
||||
|
||||
impl AsyncWrite for SlowHddWriter {
|
||||
fn poll_write(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
self.write_bytes(cx, buf.len())
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
self.service_write(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<Result<(), std::io::Error>> {
|
||||
self.service_write(cx)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> std::task::Poll<Result<usize, std::io::Error>> {
|
||||
let writeable = bufs.into_iter().fold(0, |acc, buf| acc + buf.len());
|
||||
self.write_bytes(cx, writeable)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/// A reader that limits the maximum chunk it'll give you back
|
||||
///
|
||||
/// Simulates something reading from a slow link - you get one chunk per call,
|
||||
/// and you are offered chunks on a schedule
|
||||
struct ChunkReader {
|
||||
data: Vec<u8>,
|
||||
service_intervals: Interval,
|
||||
}
|
||||
|
||||
impl ChunkReader {
|
||||
fn new(chunk_size: usize, service_interval: Duration) -> Self {
|
||||
let mut service_intervals = interval(service_interval);
|
||||
service_intervals.set_missed_tick_behavior(MissedTickBehavior::Burst);
|
||||
let data: Vec<u8> = std::iter::repeat(0).take(chunk_size).collect();
|
||||
Self {
|
||||
data,
|
||||
service_intervals,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncRead for ChunkReader {
|
||||
fn poll_read(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
if self.service_intervals.poll_tick(cx).is_pending() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
buf.put_slice(&self.data[..buf.remaining().min(self.data.len())]);
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn rt() -> tokio::runtime::Runtime {
|
||||
tokio::runtime::Builder::new_current_thread()
|
||||
.enable_time()
|
||||
.build()
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn copy_mem_to_mem(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = repeat(0).take(SOURCE_SIZE);
|
||||
let mut dest = Vec::new();
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
}
|
||||
|
||||
fn copy_mem_to_slow_hdd(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = repeat(0).take(SOURCE_SIZE);
|
||||
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
}
|
||||
|
||||
fn copy_chunk_to_mem(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
|
||||
let mut dest = Vec::new();
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
}
|
||||
|
||||
fn copy_chunk_to_slow_hdd(b: &mut Bencher) {
|
||||
let rt = rt();
|
||||
b.iter(|| {
|
||||
let task = || async {
|
||||
let mut source = ChunkReader::new(CHUNK_SIZE, READ_SERVICE_PERIOD).take(SOURCE_SIZE);
|
||||
let mut dest = SlowHddWriter::new(WRITE_SERVICE_PERIOD, WRITE_BUFFER);
|
||||
copy(&mut source, &mut dest).await.unwrap();
|
||||
};
|
||||
|
||||
rt.block_on(task());
|
||||
})
|
||||
}
|
||||
|
||||
benchmark_group!(
|
||||
copy_bench,
|
||||
copy_mem_to_mem,
|
||||
copy_mem_to_slow_hdd,
|
||||
copy_chunk_to_mem,
|
||||
copy_chunk_to_slow_hdd,
|
||||
);
|
||||
benchmark_main!(copy_bench);
|
||||
+2
-3
@@ -24,8 +24,8 @@ httpdate = "1.0"
|
||||
once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.winapi]
|
||||
version = "0.3.8"
|
||||
[target.'cfg(windows)'.dev-dependencies.windows-sys]
|
||||
version = "0.42.0"
|
||||
|
||||
[[example]]
|
||||
name = "chat"
|
||||
@@ -75,7 +75,6 @@ path = "tinyhttp.rs"
|
||||
name = "custom-executor"
|
||||
path = "custom-executor.rs"
|
||||
|
||||
|
||||
[[example]]
|
||||
name = "custom-executor-tokio-context"
|
||||
path = "custom-executor-tokio-context.rs"
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
//! Hello world server.
|
||||
//!
|
||||
//! A simple client that opens a TCP stream, writes "hello world\n", and closes
|
||||
//! the connection.
|
||||
//!
|
||||
//! You can test this out by running:
|
||||
//! To start a server that this client can talk to on port 6142, you can use this command:
|
||||
//!
|
||||
//! ncat -l 6142
|
||||
//!
|
||||
@@ -26,7 +24,7 @@ pub async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let mut stream = TcpStream::connect("127.0.0.1:6142").await?;
|
||||
println!("created stream");
|
||||
|
||||
let result = stream.write(b"hello world\n").await;
|
||||
let result = stream.write_all(b"hello world\n").await;
|
||||
println!("wrote to stream; success={:?}", result.is_ok());
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -6,7 +6,7 @@ async fn windows_main() -> io::Result<()> {
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
||||
use tokio::time;
|
||||
use winapi::shared::winerror;
|
||||
use windows_sys::Win32::Foundation::ERROR_PIPE_BUSY;
|
||||
|
||||
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-multi-client";
|
||||
const N: usize = 10;
|
||||
@@ -59,7 +59,7 @@ async fn windows_main() -> io::Result<()> {
|
||||
let mut client = loop {
|
||||
match ClientOptions::new().open(PIPE_NAME) {
|
||||
Ok(client) => break client,
|
||||
Err(e) if e.raw_os_error() == Some(winerror::ERROR_PIPE_BUSY as i32) => (),
|
||||
Err(e) if e.raw_os_error() == Some(ERROR_PIPE_BUSY as i32) => (),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"arch": "x86",
|
||||
"cpu": "pentium4",
|
||||
"crt-static-respected": true,
|
||||
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128",
|
||||
"dynamic-linking": true,
|
||||
"env": "gnu",
|
||||
"has-rpath": true,
|
||||
"has-thread-local": true,
|
||||
"llvm-target": "i686-unknown-linux-gnu",
|
||||
"max-atomic-width": 32,
|
||||
"os": "linux",
|
||||
"position-independent-executables": true,
|
||||
"pre-link-args": {
|
||||
"gcc": [
|
||||
"-m32"
|
||||
]
|
||||
},
|
||||
"relro-level": "full",
|
||||
"stack-probes": {
|
||||
"kind": "inline-or-call",
|
||||
"min-llvm-version-for-inline": [
|
||||
16,
|
||||
0,
|
||||
0
|
||||
]
|
||||
},
|
||||
"supported-sanitizers": [
|
||||
"address"
|
||||
],
|
||||
"supported-split-debuginfo": [
|
||||
"packed",
|
||||
"unpacked",
|
||||
"off"
|
||||
],
|
||||
"target-family": [
|
||||
"unix"
|
||||
],
|
||||
"target-pointer-width": "32"
|
||||
}
|
||||
@@ -58,3 +58,4 @@ tokio = { path = "../tokio" }
|
||||
tokio-test = { path = "../tokio-test", optional = true }
|
||||
doc-comment = "0.3.1"
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
bytes = "1.0.0"
|
||||
|
||||
@@ -190,3 +190,54 @@ async fn pipe_from_one_command_to_another() {
|
||||
assert!(second_status.expect("second status").success());
|
||||
assert!(third_status.expect("third status").success());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn vectored_writes() {
|
||||
use bytes::{Buf, Bytes};
|
||||
use std::{io::IoSlice, pin::Pin};
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
let mut cat = cat().spawn().unwrap();
|
||||
let mut stdin = cat.stdin.take().unwrap();
|
||||
let are_writes_vectored = stdin.is_write_vectored();
|
||||
let mut stdout = cat.stdout.take().unwrap();
|
||||
|
||||
let write = async {
|
||||
let mut input = Bytes::from_static(b"hello\n").chain(Bytes::from_static(b"world!\n"));
|
||||
let mut writes_completed = 0;
|
||||
|
||||
futures::future::poll_fn(|cx| loop {
|
||||
let mut slices = [IoSlice::new(&[]); 2];
|
||||
let vectored = input.chunks_vectored(&mut slices);
|
||||
if vectored == 0 {
|
||||
return std::task::Poll::Ready(std::io::Result::Ok(()));
|
||||
}
|
||||
let n = futures::ready!(Pin::new(&mut stdin).poll_write_vectored(cx, &slices))?;
|
||||
writes_completed += 1;
|
||||
input.advance(n);
|
||||
})
|
||||
.await?;
|
||||
|
||||
drop(stdin);
|
||||
|
||||
std::io::Result::Ok(writes_completed)
|
||||
};
|
||||
|
||||
let read = async {
|
||||
let mut buffer = Vec::with_capacity(6 + 7);
|
||||
stdout.read_to_end(&mut buffer).await?;
|
||||
std::io::Result::Ok(buffer)
|
||||
};
|
||||
|
||||
let (write, read, status) = future::join3(write, read, cat.wait()).await;
|
||||
|
||||
assert!(status.unwrap().success());
|
||||
|
||||
let writes_completed = write.unwrap();
|
||||
// on unix our small payload should always fit in whatever default sized pipe with a single
|
||||
// syscall. if multiple are used, then the forwarding does not work, or we are on a platform
|
||||
// for which the `std` does not support vectored writes.
|
||||
assert_eq!(writes_completed == 1, are_writes_vectored);
|
||||
|
||||
assert_eq!(&read.unwrap(), b"hello\nworld!\n");
|
||||
}
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
# 1.8.2 (November 30th, 2022)
|
||||
|
||||
- fix a regression introduced in 1.8.1 ([#5244])
|
||||
|
||||
[#5244]: https://github.com/tokio-rs/tokio/pull/5244
|
||||
|
||||
# 1.8.1 (November 29th, 2022)
|
||||
|
||||
(yanked)
|
||||
|
||||
- macros: Pin Futures in `#[tokio::test]` to stack ([#5205])
|
||||
- macros: Reduce usage of last statement spans in proc-macros ([#5092])
|
||||
- macros: Improve the documentation for `#[tokio::test]` ([#4761])
|
||||
|
||||
[#5205]: https://github.com/tokio-rs/tokio/pull/5205
|
||||
[#5092]: https://github.com/tokio-rs/tokio/pull/5092
|
||||
[#4761]: https://github.com/tokio-rs/tokio/pull/4761
|
||||
|
||||
# 1.8.0 (June 4th, 2022)
|
||||
|
||||
- macros: always emit return statement ([#4636])
|
||||
|
||||
@@ -3,8 +3,8 @@ name = "tokio-macros"
|
||||
# When releasing to crates.io:
|
||||
# - Remove path dependencies
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.0.x" git tag.
|
||||
version = "1.8.0"
|
||||
# - Create "tokio-macros-1.x.y" git tag.
|
||||
version = "1.8.2"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
|
||||
+43
-10
@@ -383,17 +383,50 @@ fn parse_knobs(mut input: syn::ItemFn, is_test: bool, config: FinalConfig) -> To
|
||||
|
||||
let body = &input.block;
|
||||
let brace_token = input.block.brace_token;
|
||||
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
|
||||
let body_ident = quote! { body };
|
||||
let block_expr = quote_spanned! {last_stmt_end_span=>
|
||||
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
|
||||
{
|
||||
return #rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("Failed building the Runtime")
|
||||
.block_on(#body_ident);
|
||||
}
|
||||
};
|
||||
|
||||
// For test functions pin the body to the stack and use `Pin<&mut dyn
|
||||
// Future>` to reduce the amount of `Runtime::block_on` (and related
|
||||
// functions) copies we generate during compilation due to the generic
|
||||
// parameter `F` (the future to block on). This could have an impact on
|
||||
// performance, but because it's only for testing it's unlikely to be very
|
||||
// large.
|
||||
//
|
||||
// We don't do this for the main function as it should only be used once so
|
||||
// there will be no benefit.
|
||||
let body = if is_test {
|
||||
let output_type = match &input.sig.output {
|
||||
// For functions with no return value syn doesn't print anything,
|
||||
// but that doesn't work as `Output` for our boxed `Future`, so
|
||||
// default to `()` (the same type as the function output).
|
||||
syn::ReturnType::Default => quote! { () },
|
||||
syn::ReturnType::Type(_, ret_type) => quote! { #ret_type },
|
||||
};
|
||||
quote! {
|
||||
let body = async #body;
|
||||
#[allow(clippy::expect_used, clippy::diverging_sub_expression)]
|
||||
{
|
||||
return #rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.expect("Failed building the Runtime")
|
||||
.block_on(body);
|
||||
}
|
||||
#crate_ident::pin!(body);
|
||||
let body: ::std::pin::Pin<&mut dyn ::std::future::Future<Output = #output_type>> = body;
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
let body = async #body;
|
||||
}
|
||||
};
|
||||
|
||||
input.block = syn::parse2(quote! {
|
||||
{
|
||||
#body
|
||||
#block_expr
|
||||
}
|
||||
})
|
||||
.expect("Parsing failure");
|
||||
@@ -447,7 +480,7 @@ pub(crate) fn test(args: TokenStream, item: TokenStream, rt_multi_thread: bool)
|
||||
};
|
||||
let config = if let Some(attr) = input.attrs.iter().find(|attr| attr.path.is_ident("test")) {
|
||||
let msg = "second test attribute is supplied";
|
||||
Err(syn::Error::new_spanned(&attr, msg))
|
||||
Err(syn::Error::new_spanned(attr, msg))
|
||||
} else {
|
||||
AttributeArgs::parse_terminated
|
||||
.parse(args)
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
# 0.1.11 (October 11, 2022)
|
||||
|
||||
- time: allow `StreamExt::chunks_timeout` outside of a runtime ([#5036])
|
||||
|
||||
[#5036]: https://github.com/tokio-rs/tokio/pull/5036
|
||||
|
||||
# 0.1.10 (Sept 18, 2022)
|
||||
|
||||
- time: add `StreamExt::chunks_timeout` ([#4695])
|
||||
|
||||
@@ -4,7 +4,7 @@ name = "tokio-stream"
|
||||
# - Remove path dependencies
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-stream-0.1.x" git tag.
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
|
||||
@@ -982,6 +982,8 @@ pub trait StreamExt: Stream {
|
||||
|
||||
/// Slows down a stream by enforcing a delay between items.
|
||||
///
|
||||
/// The underlying timer behind this utility has a granularity of one millisecond.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Create a throttled stream.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::stream_ext::Fuse;
|
||||
use crate::Stream;
|
||||
use tokio::time::{sleep, Instant, Sleep};
|
||||
use tokio::time::{sleep, Sleep};
|
||||
|
||||
use core::future::Future;
|
||||
use core::pin::Pin;
|
||||
@@ -16,7 +16,7 @@ pin_project! {
|
||||
#[pin]
|
||||
stream: Fuse<S>,
|
||||
#[pin]
|
||||
deadline: Sleep,
|
||||
deadline: Option<Sleep>,
|
||||
duration: Duration,
|
||||
items: Vec<S::Item>,
|
||||
cap: usize, // https://github.com/rust-lang/futures-rs/issues/1475
|
||||
@@ -27,7 +27,7 @@ impl<S: Stream> ChunksTimeout<S> {
|
||||
pub(super) fn new(stream: S, max_size: usize, duration: Duration) -> Self {
|
||||
ChunksTimeout {
|
||||
stream: Fuse::new(stream),
|
||||
deadline: sleep(duration),
|
||||
deadline: None,
|
||||
duration,
|
||||
items: Vec::with_capacity(max_size),
|
||||
cap: max_size,
|
||||
@@ -45,7 +45,7 @@ impl<S: Stream> Stream for ChunksTimeout<S> {
|
||||
Poll::Pending => break,
|
||||
Poll::Ready(Some(item)) => {
|
||||
if me.items.is_empty() {
|
||||
me.deadline.as_mut().reset(Instant::now() + *me.duration);
|
||||
me.deadline.set(Some(sleep(*me.duration)));
|
||||
me.items.reserve_exact(*me.cap);
|
||||
}
|
||||
me.items.push(item);
|
||||
@@ -67,7 +67,9 @@ impl<S: Stream> Stream for ChunksTimeout<S> {
|
||||
}
|
||||
|
||||
if !me.items.is_empty() {
|
||||
ready!(me.deadline.poll(cx));
|
||||
if let Some(deadline) = me.deadline.as_pin_mut() {
|
||||
ready!(deadline.poll(cx));
|
||||
}
|
||||
return Poll::Ready(Some(std::mem::take(me.items)));
|
||||
}
|
||||
|
||||
|
||||
@@ -195,11 +195,7 @@ where
|
||||
} else {
|
||||
let res = mem::replace(collection, Ok(U::initialize(sealed::Internal, 0, Some(0))));
|
||||
|
||||
if let Err(err) = res {
|
||||
Err(err)
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
Err(res.map(drop).unwrap_err())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::Stream;
|
||||
use tokio::time::{Duration, Instant, Sleep};
|
||||
|
||||
use std::future::Future;
|
||||
use std::marker::Unpin;
|
||||
use std::pin::Pin;
|
||||
use std::task::{self, Poll};
|
||||
|
||||
@@ -41,8 +40,7 @@ pin_project! {
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: are these safe if `T: !Unpin`?
|
||||
impl<T: Unpin> Throttle<T> {
|
||||
impl<T> Throttle<T> {
|
||||
/// Acquires a reference to the underlying stream that this combinator is
|
||||
/// pulling from.
|
||||
pub fn get_ref(&self) -> &T {
|
||||
|
||||
@@ -24,7 +24,7 @@ pin_project! {
|
||||
}
|
||||
|
||||
/// Error returned by `Timeout`.
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Elapsed(());
|
||||
|
||||
impl<S: Stream> Timeout<S> {
|
||||
|
||||
@@ -18,7 +18,7 @@ pub struct BroadcastStream<T> {
|
||||
}
|
||||
|
||||
/// An error returned from the inner stream of a [`BroadcastStream`].
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub enum BroadcastStreamRecvError {
|
||||
/// The receiver lagged too far behind. Attempting to receive again will
|
||||
/// return the oldest message still retained by the channel.
|
||||
|
||||
+37
-8
@@ -1,4 +1,29 @@
|
||||
//! Futures task based helpers
|
||||
//! Futures task based helpers to easily test futures and manually written futures.
|
||||
//!
|
||||
//! The [`Spawn`] type is used as a mock task harness that allows you to poll futures
|
||||
//! without needing to setup pinning or context. Any future can be polled but if the
|
||||
//! future requires the tokio async context you will need to ensure that you poll the
|
||||
//! [`Spawn`] within a tokio context, this means that as long as you are inside the
|
||||
//! runtime it will work and you can poll it via [`Spawn`].
|
||||
//!
|
||||
//! [`Spawn`] also supports [`Stream`] to call `poll_next` without pinning
|
||||
//! or context.
|
||||
//!
|
||||
//! In addition to circumventing the need for pinning and context, [`Spawn`] also tracks
|
||||
//! the amount of times the future/task was woken. This can be useful to track if some
|
||||
//! leaf future notified the root task correctly.
|
||||
//!
|
||||
//! # Example
|
||||
//!
|
||||
//! ```
|
||||
//! use tokio_test::task;
|
||||
//!
|
||||
//! let fut = async {};
|
||||
//!
|
||||
//! let mut task = task::spawn(fut);
|
||||
//!
|
||||
//! assert!(task.poll().is_ready(), "Task was not ready!");
|
||||
//! ```
|
||||
|
||||
#![allow(clippy::mutex_atomic)]
|
||||
|
||||
@@ -11,7 +36,11 @@ use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
use tokio_stream::Stream;
|
||||
|
||||
/// TODO: dox
|
||||
/// Spawn a future into a [`Spawn`] which wraps the future in a mocked executor.
|
||||
///
|
||||
/// This can be used to spawn a [`Future`] or a [`Stream`].
|
||||
///
|
||||
/// For more information, check the module docs.
|
||||
pub fn spawn<T>(task: T) -> Spawn<T> {
|
||||
Spawn {
|
||||
task: MockTask::new(),
|
||||
@@ -19,16 +48,14 @@ pub fn spawn<T>(task: T) -> Spawn<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Future spawned on a mock task
|
||||
/// Future spawned on a mock task that can be used to poll the future or stream
|
||||
/// without needing pinning or context types.
|
||||
#[derive(Debug)]
|
||||
pub struct Spawn<T> {
|
||||
task: MockTask,
|
||||
future: Pin<Box<T>>,
|
||||
}
|
||||
|
||||
/// Mock task
|
||||
///
|
||||
/// A mock task is able to intercept and track wake notifications.
|
||||
#[derive(Debug, Clone)]
|
||||
struct MockTask {
|
||||
waker: Arc<ThreadWaker>,
|
||||
@@ -91,7 +118,8 @@ impl<T: Unpin> ops::DerefMut for Spawn<T> {
|
||||
}
|
||||
|
||||
impl<T: Future> Spawn<T> {
|
||||
/// Polls a future
|
||||
/// If `T` is a [`Future`] then poll it. This will handle pinning and the context
|
||||
/// type for the future.
|
||||
pub fn poll(&mut self) -> Poll<T::Output> {
|
||||
let fut = self.future.as_mut();
|
||||
self.task.enter(|cx| fut.poll(cx))
|
||||
@@ -99,7 +127,8 @@ impl<T: Future> Spawn<T> {
|
||||
}
|
||||
|
||||
impl<T: Stream> Spawn<T> {
|
||||
/// Polls a stream
|
||||
/// If `T` is a [`Stream`] then poll_next it. This will handle pinning and the context
|
||||
/// type for the stream.
|
||||
pub fn poll_next(&mut self) -> Poll<Option<T::Item>> {
|
||||
let stream = self.future.as_mut();
|
||||
self.task.enter(|cx| stream.poll_next(cx))
|
||||
|
||||
@@ -253,6 +253,16 @@ impl<T, U> Framed<T, U> {
|
||||
&mut self.inner.state.write.buffer
|
||||
}
|
||||
|
||||
/// Returns backpressure boundary
|
||||
pub fn backpressure_boundary(&self) -> usize {
|
||||
self.inner.state.write.backpressure_boundary
|
||||
}
|
||||
|
||||
/// Updates backpressure boundary
|
||||
pub fn set_backpressure_boundary(&mut self, boundary: usize) {
|
||||
self.inner.state.write.backpressure_boundary = boundary;
|
||||
}
|
||||
|
||||
/// Consumes the `Framed`, returning its underlying I/O stream.
|
||||
///
|
||||
/// Note that care should be taken to not tamper with the underlying stream
|
||||
|
||||
@@ -25,7 +25,6 @@ pin_project! {
|
||||
}
|
||||
|
||||
const INITIAL_CAPACITY: usize = 8 * 1024;
|
||||
const BACKPRESSURE_BOUNDARY: usize = INITIAL_CAPACITY;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ReadFrame {
|
||||
@@ -37,6 +36,7 @@ pub(crate) struct ReadFrame {
|
||||
|
||||
pub(crate) struct WriteFrame {
|
||||
pub(crate) buffer: BytesMut,
|
||||
pub(crate) backpressure_boundary: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -60,6 +60,7 @@ impl Default for WriteFrame {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
buffer: BytesMut::with_capacity(INITIAL_CAPACITY),
|
||||
backpressure_boundary: INITIAL_CAPACITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +88,10 @@ impl From<BytesMut> for WriteFrame {
|
||||
buffer.reserve(INITIAL_CAPACITY - size);
|
||||
}
|
||||
|
||||
Self { buffer }
|
||||
Self {
|
||||
buffer,
|
||||
backpressure_boundary: INITIAL_CAPACITY,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +260,7 @@ where
|
||||
type Error = U::Error;
|
||||
|
||||
fn poll_ready(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
if self.state.borrow().buffer.len() >= BACKPRESSURE_BOUNDARY {
|
||||
if self.state.borrow().buffer.len() >= self.state.borrow().backpressure_boundary {
|
||||
self.as_mut().poll_flush(cx)
|
||||
} else {
|
||||
Poll::Ready(Ok(()))
|
||||
@@ -277,7 +281,7 @@ where
|
||||
let mut pinned = self.project();
|
||||
|
||||
while !pinned.state.borrow_mut().buffer.is_empty() {
|
||||
let WriteFrame { buffer } = pinned.state.borrow_mut();
|
||||
let WriteFrame { buffer, .. } = pinned.state.borrow_mut();
|
||||
trace!(remaining = buffer.len(), "writing;");
|
||||
|
||||
let n = ready!(poll_write_buf(pinned.inner.as_mut(), cx, buffer))?;
|
||||
|
||||
@@ -123,6 +123,16 @@ impl<T, E> FramedWrite<T, E> {
|
||||
pub fn write_buffer_mut(&mut self) -> &mut BytesMut {
|
||||
&mut self.inner.state.buffer
|
||||
}
|
||||
|
||||
/// Returns backpressure boundary
|
||||
pub fn backpressure_boundary(&self) -> usize {
|
||||
self.inner.state.backpressure_boundary
|
||||
}
|
||||
|
||||
/// Updates backpressure boundary
|
||||
pub fn set_backpressure_boundary(&mut self, boundary: usize) {
|
||||
self.inner.state.backpressure_boundary = boundary;
|
||||
}
|
||||
}
|
||||
|
||||
// This impl just defers to the underlying FramedImpl
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
use bytes::Bytes;
|
||||
use futures_sink::Sink;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// A helper that wraps a [`Sink`]`<`[`Bytes`]`>` and converts it into a
|
||||
/// [`Sink`]`<&'a [u8]>` by copying each byte slice into an owned [`Bytes`].
|
||||
///
|
||||
/// See the documentation for [`SinkWriter`] for an example.
|
||||
///
|
||||
/// [`Bytes`]: bytes::Bytes
|
||||
/// [`SinkWriter`]: crate::io::SinkWriter
|
||||
/// [`Sink`]: futures_sink::Sink
|
||||
#[derive(Debug)]
|
||||
pub struct CopyToBytes<S> {
|
||||
#[pin]
|
||||
inner: S,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> CopyToBytes<S> {
|
||||
/// Creates a new [`CopyToBytes`].
|
||||
pub fn new(inner: S) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying sink.
|
||||
pub fn get_ref(&self) -> &S {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying sink.
|
||||
pub fn get_mut(&mut self) -> &mut S {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes this [`CopyToBytes`], returning the underlying sink.
|
||||
pub fn into_inner(self) -> S {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Sink<&'a [u8]> for CopyToBytes<S>
|
||||
where
|
||||
S: Sink<Bytes>,
|
||||
{
|
||||
type Error = S::Error;
|
||||
|
||||
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project().inner.poll_ready(cx)
|
||||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, item: &'a [u8]) -> Result<(), Self::Error> {
|
||||
self.project()
|
||||
.inner
|
||||
.start_send(Bytes::copy_from_slice(item))
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project().inner.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||
self.project().inner.poll_close(cx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
use futures_core::ready;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io::{IoSlice, Result};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
pin_project! {
|
||||
/// An adapter that lets you inspect the data that's being read.
|
||||
///
|
||||
/// This is useful for things like hashing data as it's read in.
|
||||
pub struct InspectReader<R, F> {
|
||||
#[pin]
|
||||
reader: R,
|
||||
f: F,
|
||||
}
|
||||
}
|
||||
|
||||
impl<R, F> InspectReader<R, F> {
|
||||
/// Create a new InspectReader, wrapping `reader` and calling `f` for the
|
||||
/// new data supplied by each read call.
|
||||
///
|
||||
/// The closure will only be called with an empty slice if the inner reader
|
||||
/// returns without reading data into the buffer. This happens at EOF, or if
|
||||
/// `poll_read` is called with a zero-size buffer.
|
||||
pub fn new(reader: R, f: F) -> InspectReader<R, F>
|
||||
where
|
||||
R: AsyncRead,
|
||||
F: FnMut(&[u8]),
|
||||
{
|
||||
InspectReader { reader, f }
|
||||
}
|
||||
|
||||
/// Consumes the `InspectReader`, returning the wrapped reader
|
||||
pub fn into_inner(self) -> R {
|
||||
self.reader
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: AsyncRead, F: FnMut(&[u8])> AsyncRead for InspectReader<R, F> {
|
||||
fn poll_read(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<Result<()>> {
|
||||
let me = self.project();
|
||||
let filled_length = buf.filled().len();
|
||||
ready!(me.reader.poll_read(cx, buf))?;
|
||||
(me.f)(&buf.filled()[filled_length..]);
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// An adapter that lets you inspect the data that's being written.
|
||||
///
|
||||
/// This is useful for things like hashing data as it's written out.
|
||||
pub struct InspectWriter<W, F> {
|
||||
#[pin]
|
||||
writer: W,
|
||||
f: F,
|
||||
}
|
||||
}
|
||||
|
||||
impl<W, F> InspectWriter<W, F> {
|
||||
/// Create a new InspectWriter, wrapping `write` and calling `f` for the
|
||||
/// data successfully written by each write call.
|
||||
///
|
||||
/// The closure `f` will never be called with an empty slice. A vectored
|
||||
/// write can result in multiple calls to `f` - at most one call to `f` per
|
||||
/// buffer supplied to `poll_write_vectored`.
|
||||
pub fn new(writer: W, f: F) -> InspectWriter<W, F>
|
||||
where
|
||||
W: AsyncWrite,
|
||||
F: FnMut(&[u8]),
|
||||
{
|
||||
InspectWriter { writer, f }
|
||||
}
|
||||
|
||||
/// Consumes the `InspectWriter`, returning the wrapped writer
|
||||
pub fn into_inner(self) -> W {
|
||||
self.writer
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsyncWrite, F: FnMut(&[u8])> AsyncWrite for InspectWriter<W, F> {
|
||||
fn poll_write(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8]) -> Poll<Result<usize>> {
|
||||
let me = self.project();
|
||||
let res = me.writer.poll_write(cx, buf);
|
||||
if let Poll::Ready(Ok(count)) = res {
|
||||
if count != 0 {
|
||||
(me.f)(&buf[..count]);
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||
let me = self.project();
|
||||
me.writer.poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<()>> {
|
||||
let me = self.project();
|
||||
me.writer.poll_shutdown(cx)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<Result<usize>> {
|
||||
let me = self.project();
|
||||
let res = me.writer.poll_write_vectored(cx, bufs);
|
||||
if let Poll::Ready(Ok(mut count)) = res {
|
||||
for buf in bufs {
|
||||
if count == 0 {
|
||||
break;
|
||||
}
|
||||
let size = count.min(buf.len());
|
||||
if size != 0 {
|
||||
(me.f)(&buf[..size]);
|
||||
count -= size;
|
||||
}
|
||||
}
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.writer.is_write_vectored()
|
||||
}
|
||||
}
|
||||
@@ -10,15 +10,22 @@
|
||||
//! [`Body`]: https://docs.rs/hyper/0.13/hyper/struct.Body.html
|
||||
//! [`AsyncRead`]: tokio::io::AsyncRead
|
||||
|
||||
mod copy_to_bytes;
|
||||
mod inspect;
|
||||
mod read_buf;
|
||||
mod reader_stream;
|
||||
mod sink_writer;
|
||||
mod stream_reader;
|
||||
|
||||
cfg_io_util! {
|
||||
mod sync_bridge;
|
||||
pub use self::sync_bridge::SyncIoBridge;
|
||||
}
|
||||
|
||||
pub use self::copy_to_bytes::CopyToBytes;
|
||||
pub use self::inspect::{InspectReader, InspectWriter};
|
||||
pub use self::read_buf::read_buf;
|
||||
pub use self::reader_stream::ReaderStream;
|
||||
pub use self::sink_writer::SinkWriter;
|
||||
pub use self::stream_reader::StreamReader;
|
||||
pub use crate::util::{poll_read_buf, poll_write_buf};
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
use futures_sink::Sink;
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::AsyncWrite;
|
||||
|
||||
pin_project! {
|
||||
/// Convert a [`Sink`] of byte chunks into an [`AsyncWrite`].
|
||||
///
|
||||
/// Whenever you write to this [`SinkWriter`], the supplied bytes are
|
||||
/// forwarded to the inner [`Sink`]. When `shutdown` is called on this
|
||||
/// [`SinkWriter`], the inner sink is closed.
|
||||
///
|
||||
/// This adapter takes a `Sink<&[u8]>` and provides an [`AsyncWrite`] impl
|
||||
/// for it. Because of the lifetime, this trait is relatively rarely
|
||||
/// implemented. The main ways to get a `Sink<&[u8]>` that you can use with
|
||||
/// this type are:
|
||||
///
|
||||
/// * With the codec module by implementing the [`Encoder`]`<&[u8]>` trait.
|
||||
/// * By wrapping a `Sink<Bytes>` in a [`CopyToBytes`].
|
||||
/// * Manually implementing `Sink<&[u8]>` directly.
|
||||
///
|
||||
/// The opposite conversion of implementing `Sink<_>` for an [`AsyncWrite`]
|
||||
/// is done using the [`codec`] module.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::Bytes;
|
||||
/// use futures_util::SinkExt;
|
||||
/// use std::io::{Error, ErrorKind};
|
||||
/// use tokio::io::AsyncWriteExt;
|
||||
/// use tokio_util::io::{SinkWriter, CopyToBytes};
|
||||
/// use tokio_util::sync::PollSender;
|
||||
///
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() -> Result<(), Error> {
|
||||
/// // We use an mpsc channel as an example of a `Sink<Bytes>`.
|
||||
/// let (tx, mut rx) = tokio::sync::mpsc::channel::<Bytes>(1);
|
||||
/// let sink = PollSender::new(tx).sink_map_err(|_| Error::from(ErrorKind::BrokenPipe));
|
||||
///
|
||||
/// // Wrap it in `CopyToBytes` to get a `Sink<&[u8]>`.
|
||||
/// let mut writer = SinkWriter::new(CopyToBytes::new(sink));
|
||||
///
|
||||
/// // Write data to our interface...
|
||||
/// let data: [u8; 4] = [1, 2, 3, 4];
|
||||
/// let _ = writer.write(&data).await?;
|
||||
///
|
||||
/// // ... and receive it.
|
||||
/// assert_eq!(data.as_slice(), &*rx.recv().await.unwrap());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncWrite`]: tokio::io::AsyncWrite
|
||||
/// [`CopyToBytes`]: crate::io::CopyToBytes
|
||||
/// [`Encoder`]: crate::codec::Encoder
|
||||
/// [`Sink`]: futures_sink::Sink
|
||||
/// [`codec`]: tokio_util::codec
|
||||
#[derive(Debug)]
|
||||
pub struct SinkWriter<S> {
|
||||
#[pin]
|
||||
inner: S,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> SinkWriter<S> {
|
||||
/// Creates a new [`SinkWriter`].
|
||||
pub fn new(sink: S) -> Self {
|
||||
Self { inner: sink }
|
||||
}
|
||||
|
||||
/// Gets a reference to the underlying sink.
|
||||
pub fn get_ref(&self) -> &S {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
/// Gets a mutable reference to the underlying sink.
|
||||
pub fn get_mut(&mut self) -> &mut S {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Consumes this [`SinkWriter`], returning the underlying sink.
|
||||
pub fn into_inner(self) -> S {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
impl<S, E> AsyncWrite for SinkWriter<S>
|
||||
where
|
||||
for<'a> S: Sink<&'a [u8], Error = E>,
|
||||
E: Into<io::Error>,
|
||||
{
|
||||
fn poll_write(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
let mut this = self.project();
|
||||
match this.inner.as_mut().poll_ready(cx) {
|
||||
Poll::Ready(Ok(())) => {
|
||||
if let Err(e) = this.inner.as_mut().start_send(buf) {
|
||||
Poll::Ready(Err(e.into()))
|
||||
} else {
|
||||
Poll::Ready(Ok(buf.len()))
|
||||
}
|
||||
}
|
||||
Poll::Ready(Err(e)) => Poll::Ready(Err(e.into())),
|
||||
Poll::Pending => {
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
self.project().inner.poll_flush(cx).map_err(Into::into)
|
||||
}
|
||||
|
||||
fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
self.project().inner.poll_close(cx).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
+177
-103
@@ -1,113 +1,162 @@
|
||||
use bytes::Buf;
|
||||
use futures_core::stream::Stream;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use tokio::io::{AsyncBufRead, AsyncRead, ReadBuf};
|
||||
|
||||
pin_project! {
|
||||
/// Convert a [`Stream`] of byte chunks into an [`AsyncRead`].
|
||||
///
|
||||
/// This type performs the inverse operation of [`ReaderStream`].
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::Bytes;
|
||||
/// use tokio::io::{AsyncReadExt, Result};
|
||||
/// use tokio_util::io::StreamReader;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// // Create a stream from an iterator.
|
||||
/// let stream = tokio_stream::iter(vec![
|
||||
/// Result::Ok(Bytes::from_static(&[0, 1, 2, 3])),
|
||||
/// Result::Ok(Bytes::from_static(&[4, 5, 6, 7])),
|
||||
/// Result::Ok(Bytes::from_static(&[8, 9, 10, 11])),
|
||||
/// ]);
|
||||
///
|
||||
/// // Convert it to an AsyncRead.
|
||||
/// let mut read = StreamReader::new(stream);
|
||||
///
|
||||
/// // Read five bytes from the stream.
|
||||
/// let mut buf = [0; 5];
|
||||
/// read.read_exact(&mut buf).await?;
|
||||
/// assert_eq!(buf, [0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// // Read the rest of the current chunk.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 3);
|
||||
/// assert_eq!(&buf[..3], [5, 6, 7]);
|
||||
///
|
||||
/// // Read the next chunk.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 4);
|
||||
/// assert_eq!(&buf[..4], [8, 9, 10, 11]);
|
||||
///
|
||||
/// // We have now reached the end.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 0);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// If the stream produces errors which are not [std::io::Error],
|
||||
/// the errors can be converted using [`StreamExt`] to map each
|
||||
/// element.
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::Bytes;
|
||||
/// use tokio::io::AsyncReadExt;
|
||||
/// use tokio_util::io::StreamReader;
|
||||
/// use tokio_stream::StreamExt;
|
||||
/// # #[tokio::main]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// // Create a stream from an iterator, including an error.
|
||||
/// let stream = tokio_stream::iter(vec![
|
||||
/// Result::Ok(Bytes::from_static(&[0, 1, 2, 3])),
|
||||
/// Result::Ok(Bytes::from_static(&[4, 5, 6, 7])),
|
||||
/// Result::Err("Something bad happened!")
|
||||
/// ]);
|
||||
///
|
||||
/// // Use StreamExt to map the stream and error to a std::io::Error
|
||||
/// let stream = stream.map(|result| result.map_err(|err| {
|
||||
/// std::io::Error::new(std::io::ErrorKind::Other, err)
|
||||
/// }));
|
||||
///
|
||||
/// // Convert it to an AsyncRead.
|
||||
/// let mut read = StreamReader::new(stream);
|
||||
///
|
||||
/// // Read five bytes from the stream.
|
||||
/// let mut buf = [0; 5];
|
||||
/// read.read_exact(&mut buf).await?;
|
||||
/// assert_eq!(buf, [0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// // Read the rest of the current chunk.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 3);
|
||||
/// assert_eq!(&buf[..3], [5, 6, 7]);
|
||||
///
|
||||
/// // Reading the next chunk will produce an error
|
||||
/// let error = read.read(&mut buf).await.unwrap_err();
|
||||
/// assert_eq!(error.kind(), std::io::ErrorKind::Other);
|
||||
/// assert_eq!(error.into_inner().unwrap().to_string(), "Something bad happened!");
|
||||
///
|
||||
/// // We have now reached the end.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 0);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncRead`]: tokio::io::AsyncRead
|
||||
/// [`Stream`]: futures_core::Stream
|
||||
/// [`ReaderStream`]: crate::io::ReaderStream
|
||||
/// [`StreamExt`]: tokio_stream::StreamExt
|
||||
#[derive(Debug)]
|
||||
pub struct StreamReader<S, B> {
|
||||
#[pin]
|
||||
inner: S,
|
||||
chunk: Option<B>,
|
||||
}
|
||||
/// Convert a [`Stream`] of byte chunks into an [`AsyncRead`].
|
||||
///
|
||||
/// This type performs the inverse operation of [`ReaderStream`].
|
||||
///
|
||||
/// This type also implements the [`AsyncBufRead`] trait, so you can use it
|
||||
/// to read a `Stream` of byte chunks line-by-line. See the examples below.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::Bytes;
|
||||
/// use tokio::io::{AsyncReadExt, Result};
|
||||
/// use tokio_util::io::StreamReader;
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// // Create a stream from an iterator.
|
||||
/// let stream = tokio_stream::iter(vec![
|
||||
/// Result::Ok(Bytes::from_static(&[0, 1, 2, 3])),
|
||||
/// Result::Ok(Bytes::from_static(&[4, 5, 6, 7])),
|
||||
/// Result::Ok(Bytes::from_static(&[8, 9, 10, 11])),
|
||||
/// ]);
|
||||
///
|
||||
/// // Convert it to an AsyncRead.
|
||||
/// let mut read = StreamReader::new(stream);
|
||||
///
|
||||
/// // Read five bytes from the stream.
|
||||
/// let mut buf = [0; 5];
|
||||
/// read.read_exact(&mut buf).await?;
|
||||
/// assert_eq!(buf, [0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// // Read the rest of the current chunk.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 3);
|
||||
/// assert_eq!(&buf[..3], [5, 6, 7]);
|
||||
///
|
||||
/// // Read the next chunk.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 4);
|
||||
/// assert_eq!(&buf[..4], [8, 9, 10, 11]);
|
||||
///
|
||||
/// // We have now reached the end.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 0);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// If the stream produces errors which are not [`std::io::Error`],
|
||||
/// the errors can be converted using [`StreamExt`] to map each
|
||||
/// element.
|
||||
///
|
||||
/// ```
|
||||
/// use bytes::Bytes;
|
||||
/// use tokio::io::AsyncReadExt;
|
||||
/// use tokio_util::io::StreamReader;
|
||||
/// use tokio_stream::StreamExt;
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// // Create a stream from an iterator, including an error.
|
||||
/// let stream = tokio_stream::iter(vec![
|
||||
/// Result::Ok(Bytes::from_static(&[0, 1, 2, 3])),
|
||||
/// Result::Ok(Bytes::from_static(&[4, 5, 6, 7])),
|
||||
/// Result::Err("Something bad happened!")
|
||||
/// ]);
|
||||
///
|
||||
/// // Use StreamExt to map the stream and error to a std::io::Error
|
||||
/// let stream = stream.map(|result| result.map_err(|err| {
|
||||
/// std::io::Error::new(std::io::ErrorKind::Other, err)
|
||||
/// }));
|
||||
///
|
||||
/// // Convert it to an AsyncRead.
|
||||
/// let mut read = StreamReader::new(stream);
|
||||
///
|
||||
/// // Read five bytes from the stream.
|
||||
/// let mut buf = [0; 5];
|
||||
/// read.read_exact(&mut buf).await?;
|
||||
/// assert_eq!(buf, [0, 1, 2, 3, 4]);
|
||||
///
|
||||
/// // Read the rest of the current chunk.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 3);
|
||||
/// assert_eq!(&buf[..3], [5, 6, 7]);
|
||||
///
|
||||
/// // Reading the next chunk will produce an error
|
||||
/// let error = read.read(&mut buf).await.unwrap_err();
|
||||
/// assert_eq!(error.kind(), std::io::ErrorKind::Other);
|
||||
/// assert_eq!(error.into_inner().unwrap().to_string(), "Something bad happened!");
|
||||
///
|
||||
/// // We have now reached the end.
|
||||
/// assert_eq!(read.read(&mut buf).await?, 0);
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// Using the [`AsyncBufRead`] impl, you can read a `Stream` of byte chunks
|
||||
/// line-by-line. Note that you will usually also need to convert the error
|
||||
/// type when doing this. See the second example for an explanation of how
|
||||
/// to do this.
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::{Result, AsyncBufReadExt};
|
||||
/// use tokio_util::io::StreamReader;
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// // Create a stream of byte chunks.
|
||||
/// let stream = tokio_stream::iter(vec![
|
||||
/// Result::Ok(b"The first line.\n".as_slice()),
|
||||
/// Result::Ok(b"The second line.".as_slice()),
|
||||
/// Result::Ok(b"\nThe third".as_slice()),
|
||||
/// Result::Ok(b" line.\nThe fourth line.\nThe fifth line.\n".as_slice()),
|
||||
/// ]);
|
||||
///
|
||||
/// // Convert it to an AsyncRead.
|
||||
/// let mut read = StreamReader::new(stream);
|
||||
///
|
||||
/// // Loop through the lines from the `StreamReader`.
|
||||
/// let mut line = String::new();
|
||||
/// let mut lines = Vec::new();
|
||||
/// loop {
|
||||
/// line.clear();
|
||||
/// let len = read.read_line(&mut line).await?;
|
||||
/// if len == 0 { break; }
|
||||
/// lines.push(line.clone());
|
||||
/// }
|
||||
///
|
||||
/// // Verify that we got the lines we expected.
|
||||
/// assert_eq!(
|
||||
/// lines,
|
||||
/// vec![
|
||||
/// "The first line.\n",
|
||||
/// "The second line.\n",
|
||||
/// "The third line.\n",
|
||||
/// "The fourth line.\n",
|
||||
/// "The fifth line.\n",
|
||||
/// ]
|
||||
/// );
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [`AsyncRead`]: tokio::io::AsyncRead
|
||||
/// [`AsyncBufRead`]: tokio::io::AsyncBufRead
|
||||
/// [`Stream`]: futures_core::Stream
|
||||
/// [`ReaderStream`]: crate::io::ReaderStream
|
||||
/// [`StreamExt`]: https://docs.rs/tokio-stream/latest/tokio_stream/trait.StreamExt.html
|
||||
#[derive(Debug)]
|
||||
pub struct StreamReader<S, B> {
|
||||
// This field is pinned.
|
||||
inner: S,
|
||||
// This field is not pinned.
|
||||
chunk: Option<B>,
|
||||
}
|
||||
|
||||
impl<S, B, E> StreamReader<S, B>
|
||||
@@ -250,3 +299,28 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The code below is a manual expansion of the code that pin-project-lite would
|
||||
// generate. This is done because pin-project-lite fails by hitting the recusion
|
||||
// limit on this struct. (Every line of documentation is handled recursively by
|
||||
// the macro.)
|
||||
|
||||
impl<S: Unpin, B> Unpin for StreamReader<S, B> {}
|
||||
|
||||
struct StreamReaderProject<'a, S, B> {
|
||||
inner: Pin<&'a mut S>,
|
||||
chunk: &'a mut Option<B>,
|
||||
}
|
||||
|
||||
impl<S, B> StreamReader<S, B> {
|
||||
#[inline]
|
||||
fn project(self: Pin<&mut Self>) -> StreamReaderProject<'_, S, B> {
|
||||
// SAFETY: We define that only `inner` should be pinned when `Self` is
|
||||
// and have an appropriate `impl Unpin` for this.
|
||||
let me = unsafe { Pin::into_inner_unchecked(self) };
|
||||
StreamReaderProject {
|
||||
inner: unsafe { Pin::new_unchecked(&mut me.inner) },
|
||||
chunk: &mut me.chunk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
use std::io::{Read, Write};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
|
||||
use std::io::{BufRead, Read, Write};
|
||||
use tokio::io::{
|
||||
AsyncBufRead, AsyncBufReadExt, AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt,
|
||||
};
|
||||
|
||||
/// Use a [`tokio::io::AsyncRead`] synchronously as a [`std::io::Read`] or
|
||||
/// a [`tokio::io::AsyncWrite`] as a [`std::io::Write`].
|
||||
@@ -9,6 +11,28 @@ pub struct SyncIoBridge<T> {
|
||||
rt: tokio::runtime::Handle,
|
||||
}
|
||||
|
||||
impl<T: AsyncBufRead + Unpin> BufRead for SyncIoBridge<T> {
|
||||
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(AsyncBufReadExt::fill_buf(src))
|
||||
}
|
||||
|
||||
fn consume(&mut self, amt: usize) {
|
||||
let src = &mut self.src;
|
||||
AsyncBufReadExt::consume(src, amt)
|
||||
}
|
||||
|
||||
fn read_until(&mut self, byte: u8, buf: &mut Vec<u8>) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt
|
||||
.block_on(AsyncBufReadExt::read_until(src, byte, buf))
|
||||
}
|
||||
fn read_line(&mut self, buf: &mut String) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
self.rt.block_on(AsyncBufReadExt::read_line(src, buf))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsyncRead + Unpin> Read for SyncIoBridge<T> {
|
||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
||||
let src = &mut self.src;
|
||||
|
||||
@@ -66,6 +66,23 @@ pin_project! {
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
/// A Future that is resolved once the corresponding [`CancellationToken`]
|
||||
/// is cancelled.
|
||||
///
|
||||
/// This is the counterpart to [`WaitForCancellationFuture`] that takes
|
||||
/// [`CancellationToken`] by value instead of using a reference.
|
||||
#[must_use = "futures do nothing unless polled"]
|
||||
pub struct WaitForCancellationFutureOwned {
|
||||
// Since `future` is the first field, it is dropped before the
|
||||
// cancellation_token field. This ensures that the reference inside the
|
||||
// `Notified` remains valid.
|
||||
#[pin]
|
||||
future: tokio::sync::futures::Notified<'static>,
|
||||
cancellation_token: CancellationToken,
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl CancellationToken =====
|
||||
|
||||
impl core::fmt::Debug for CancellationToken {
|
||||
@@ -183,6 +200,21 @@ impl CancellationToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a `Future` that gets fulfilled when cancellation is requested.
|
||||
///
|
||||
/// The future will complete immediately if the token is already cancelled
|
||||
/// when this method is called.
|
||||
///
|
||||
/// The function takes self by value and returns a future that owns the
|
||||
/// token.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe.
|
||||
pub fn cancelled_owned(self) -> WaitForCancellationFutureOwned {
|
||||
WaitForCancellationFutureOwned::new(self)
|
||||
}
|
||||
|
||||
/// Creates a `DropGuard` for this token.
|
||||
///
|
||||
/// Returned guard will cancel this token (and all its children) on drop
|
||||
@@ -222,3 +254,68 @@ impl<'a> Future for WaitForCancellationFuture<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl WaitForCancellationFutureOwned =====
|
||||
|
||||
impl core::fmt::Debug for WaitForCancellationFutureOwned {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("WaitForCancellationFutureOwned").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl WaitForCancellationFutureOwned {
|
||||
fn new(cancellation_token: CancellationToken) -> Self {
|
||||
WaitForCancellationFutureOwned {
|
||||
// cancellation_token holds a heap allocation and is guaranteed to have a
|
||||
// stable deref, thus it would be ok to move the cancellation_token while
|
||||
// the future holds a reference to it.
|
||||
//
|
||||
// # Safety
|
||||
//
|
||||
// cancellation_token is dropped after future due to the field ordering.
|
||||
future: unsafe { Self::new_future(&cancellation_token) },
|
||||
cancellation_token,
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// The returned future must be destroyed before the cancellation token is
|
||||
/// destroyed.
|
||||
unsafe fn new_future(
|
||||
cancellation_token: &CancellationToken,
|
||||
) -> tokio::sync::futures::Notified<'static> {
|
||||
let inner_ptr = Arc::as_ptr(&cancellation_token.inner);
|
||||
// SAFETY: The `Arc::as_ptr` method guarantees that `inner_ptr` remains
|
||||
// valid until the strong count of the Arc drops to zero, and the caller
|
||||
// guarantees that they will drop the future before that happens.
|
||||
(*inner_ptr).notified()
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for WaitForCancellationFutureOwned {
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
let mut this = self.project();
|
||||
|
||||
loop {
|
||||
if this.cancellation_token.is_cancelled() {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
// No wakeups can be lost here because there is always a call to
|
||||
// `is_cancelled` between the creation of the future and the call to
|
||||
// `poll`, and the code that sets the cancelled flag does so before
|
||||
// waking the `Notified`.
|
||||
if this.future.as_mut().poll(cx).is_pending() {
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
// # Safety
|
||||
//
|
||||
// cancellation_token is dropped after future due to the field ordering.
|
||||
this.future
|
||||
.set(unsafe { Self::new_future(this.cancellation_token) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
//! Synchronization primitives
|
||||
|
||||
mod cancellation_token;
|
||||
pub use cancellation_token::{guard::DropGuard, CancellationToken, WaitForCancellationFuture};
|
||||
pub use cancellation_token::{
|
||||
guard::DropGuard, CancellationToken, WaitForCancellationFuture, WaitForCancellationFutureOwned,
|
||||
};
|
||||
|
||||
mod mpsc;
|
||||
pub use mpsc::{PollSendError, PollSender};
|
||||
|
||||
@@ -12,7 +12,10 @@ use super::ReusableBoxFuture;
|
||||
/// [`Semaphore`]: tokio::sync::Semaphore
|
||||
pub struct PollSemaphore {
|
||||
semaphore: Arc<Semaphore>,
|
||||
permit_fut: Option<ReusableBoxFuture<'static, Result<OwnedSemaphorePermit, AcquireError>>>,
|
||||
permit_fut: Option<(
|
||||
u32, // The number of permits requested.
|
||||
ReusableBoxFuture<'static, Result<OwnedSemaphorePermit, AcquireError>>,
|
||||
)>,
|
||||
}
|
||||
|
||||
impl PollSemaphore {
|
||||
@@ -53,25 +56,57 @@ impl PollSemaphore {
|
||||
/// the `Waker` from the `Context` passed to the most recent call is
|
||||
/// scheduled to receive a wakeup.
|
||||
pub fn poll_acquire(&mut self, cx: &mut Context<'_>) -> Poll<Option<OwnedSemaphorePermit>> {
|
||||
self.poll_acquire_many(cx, 1)
|
||||
}
|
||||
|
||||
/// Poll to acquire many permits from the semaphore.
|
||||
///
|
||||
/// This can return the following values:
|
||||
///
|
||||
/// - `Poll::Pending` if a permit is not currently available.
|
||||
/// - `Poll::Ready(Some(permit))` if a permit was acquired.
|
||||
/// - `Poll::Ready(None)` if the semaphore has been closed.
|
||||
///
|
||||
/// When this method returns `Poll::Pending`, the current task is scheduled
|
||||
/// to receive a wakeup when the permits become available, or when the
|
||||
/// semaphore is closed. Note that on multiple calls to `poll_acquire`, only
|
||||
/// the `Waker` from the `Context` passed to the most recent call is
|
||||
/// scheduled to receive a wakeup.
|
||||
pub fn poll_acquire_many(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
permits: u32,
|
||||
) -> Poll<Option<OwnedSemaphorePermit>> {
|
||||
let permit_future = match self.permit_fut.as_mut() {
|
||||
Some(fut) => fut,
|
||||
Some((prev_permits, fut)) if *prev_permits == permits => fut,
|
||||
Some((old_permits, fut_box)) => {
|
||||
// We're requesting a different number of permits, so replace the future
|
||||
// and record the new amount.
|
||||
let fut = Arc::clone(&self.semaphore).acquire_many_owned(permits);
|
||||
fut_box.set(fut);
|
||||
*old_permits = permits;
|
||||
fut_box
|
||||
}
|
||||
None => {
|
||||
// avoid allocations completely if we can grab a permit immediately
|
||||
match Arc::clone(&self.semaphore).try_acquire_owned() {
|
||||
match Arc::clone(&self.semaphore).try_acquire_many_owned(permits) {
|
||||
Ok(permit) => return Poll::Ready(Some(permit)),
|
||||
Err(TryAcquireError::Closed) => return Poll::Ready(None),
|
||||
Err(TryAcquireError::NoPermits) => {}
|
||||
}
|
||||
|
||||
let next_fut = Arc::clone(&self.semaphore).acquire_owned();
|
||||
self.permit_fut
|
||||
.get_or_insert(ReusableBoxFuture::new(next_fut))
|
||||
let next_fut = Arc::clone(&self.semaphore).acquire_many_owned(permits);
|
||||
&mut self
|
||||
.permit_fut
|
||||
.get_or_insert((permits, ReusableBoxFuture::new(next_fut)))
|
||||
.1
|
||||
}
|
||||
};
|
||||
|
||||
let result = ready!(permit_future.poll(cx));
|
||||
|
||||
let next_fut = Arc::clone(&self.semaphore).acquire_owned();
|
||||
// Assume we'll request the same amount of permits in a subsequent call.
|
||||
let next_fut = Arc::clone(&self.semaphore).acquire_many_owned(permits);
|
||||
permit_future.set(next_fut);
|
||||
|
||||
match result {
|
||||
@@ -95,7 +130,7 @@ impl PollSemaphore {
|
||||
|
||||
/// Adds `n` new permits to the semaphore.
|
||||
///
|
||||
/// The maximum number of permits is `usize::MAX >> 3`, and this function
|
||||
/// The maximum number of permits is [`Semaphore::MAX_PERMITS`], and this function
|
||||
/// will panic if the limit is exceeded.
|
||||
///
|
||||
/// This is equivalent to the [`Semaphore::add_permits`] method on the
|
||||
|
||||
@@ -24,6 +24,27 @@ fn cancel_token() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_token_owned() {
|
||||
loom::model(|| {
|
||||
let token = CancellationToken::new();
|
||||
let token1 = token.clone();
|
||||
|
||||
let th1 = thread::spawn(move || {
|
||||
block_on(async {
|
||||
token1.cancelled_owned().await;
|
||||
});
|
||||
});
|
||||
|
||||
let th2 = thread::spawn(move || {
|
||||
token.cancel();
|
||||
});
|
||||
|
||||
assert_ok!(th1.join());
|
||||
assert_ok!(th2.join());
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_with_child() {
|
||||
loom::model(|| {
|
||||
|
||||
@@ -363,10 +363,7 @@ where
|
||||
fn insert(&mut self, key: K, abort: AbortHandle) {
|
||||
let hash = self.hash(&key);
|
||||
let id = abort.id();
|
||||
let map_key = Key {
|
||||
id: id.clone(),
|
||||
key,
|
||||
};
|
||||
let map_key = Key { id, key };
|
||||
|
||||
// Insert the new key into the map of tasks by keys.
|
||||
let entry = self
|
||||
|
||||
@@ -275,7 +275,7 @@ impl<T> SlabStorage<T> {
|
||||
fn remap_key(&self, key: &Key) -> Option<KeyInternal> {
|
||||
let key_map = &self.key_map;
|
||||
if self.compact_called {
|
||||
key_map.get(&*key).copied()
|
||||
key_map.get(key).copied()
|
||||
} else {
|
||||
Some((*key).into())
|
||||
}
|
||||
@@ -740,6 +740,43 @@ impl<T> DelayQueue<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to remove the item associated with `key` from the queue.
|
||||
///
|
||||
/// Removes the item associated with `key`, and returns it along with the
|
||||
/// `Instant` at which it would have expired, if it exists.
|
||||
///
|
||||
/// Returns `None` if `key` is not in the queue.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Basic usage
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio_util::time::DelayQueue;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// # #[tokio::main(flavor = "current_thread")]
|
||||
/// # async fn main() {
|
||||
/// let mut delay_queue = DelayQueue::new();
|
||||
/// let key = delay_queue.insert("foo", Duration::from_secs(5));
|
||||
///
|
||||
/// // The item is in the queue, `try_remove` returns `Some(Expired("foo"))`.
|
||||
/// let item = delay_queue.try_remove(&key);
|
||||
/// assert_eq!(item.unwrap().into_inner(), "foo");
|
||||
///
|
||||
/// // The item is not in the queue anymore, `try_remove` returns `None`.
|
||||
/// let item = delay_queue.try_remove(&key);
|
||||
/// assert!(item.is_none());
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_remove(&mut self, key: &Key) -> Option<Expired<T>> {
|
||||
if self.slab.contains(key) {
|
||||
Some(self.remove(key))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the delay of the item associated with `key` to expire at `when`.
|
||||
///
|
||||
/// This function is identical to `reset` but takes an `Instant` instead of
|
||||
|
||||
@@ -21,5 +21,5 @@ fn tokio_context_with_another_runtime() {
|
||||
|
||||
// Without the `HandleExt.wrap()` there would be a panic because there is
|
||||
// no timer running, since it would be referencing runtime r1.
|
||||
let _ = rt1.block_on(rt2.wrap(async move { sleep(Duration::from_millis(2)).await }));
|
||||
rt1.block_on(rt2.wrap(async move { sleep(Duration::from_millis(2)).await }));
|
||||
}
|
||||
|
||||
@@ -109,12 +109,12 @@ fn write_hits_backpressure() {
|
||||
const ITER: usize = 2 * 1024;
|
||||
|
||||
let mut mock = mock! {
|
||||
// Block the `ITER`th write
|
||||
// Block the `ITER*2`th write
|
||||
Err(io::Error::new(io::ErrorKind::WouldBlock, "not ready")),
|
||||
Ok(b"".to_vec()),
|
||||
};
|
||||
|
||||
for i in 0..=ITER {
|
||||
for i in 0..=ITER * 2 {
|
||||
let mut b = BytesMut::with_capacity(4);
|
||||
b.put_u32(i as u32);
|
||||
|
||||
@@ -133,14 +133,15 @@ fn write_hits_backpressure() {
|
||||
// Push a new chunk
|
||||
mock.calls.push_back(Ok(b[..].to_vec()));
|
||||
}
|
||||
// 1 'wouldblock', 4 * 2KB buffers, 1 b-byte buffer
|
||||
assert_eq!(mock.calls.len(), 6);
|
||||
// 1 'wouldblock', 8 * 2KB buffers, 1 b-byte buffer
|
||||
assert_eq!(mock.calls.len(), 10);
|
||||
|
||||
let mut task = task::spawn(());
|
||||
let mut framed = FramedWrite::new(mock, U32Encoder);
|
||||
framed.set_backpressure_boundary(ITER * 8);
|
||||
task.enter(|cx, _| {
|
||||
// Send 8KB. This fills up FramedWrite2 buffer
|
||||
for i in 0..ITER {
|
||||
// Send 16KB. This fills up FramedWrite buffer
|
||||
for i in 0..ITER * 2 {
|
||||
assert!(assert_ready!(pin!(framed).poll_ready(cx)).is_ok());
|
||||
assert!(pin!(framed).start_send(i as u32).is_ok());
|
||||
}
|
||||
@@ -150,11 +151,11 @@ fn write_hits_backpressure() {
|
||||
assert!(pin!(framed).poll_ready(cx).is_pending());
|
||||
|
||||
// We poll again, forcing another flush, which this time succeeds
|
||||
// The whole 8KB buffer is flushed
|
||||
// The whole 16KB buffer is flushed
|
||||
assert!(assert_ready!(pin!(framed).poll_ready(cx)).is_ok());
|
||||
|
||||
// Send more data. This matches the final message expected by the mock
|
||||
assert!(pin!(framed).start_send(ITER as u32).is_ok());
|
||||
assert!(pin!(framed).start_send((ITER * 2) as u32).is_ok());
|
||||
|
||||
// Flush the rest of the buffer
|
||||
assert!(assert_ready!(pin!(framed).poll_flush(cx)).is_ok());
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
use futures::future::poll_fn;
|
||||
use std::{
|
||||
io::IoSlice,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, ReadBuf};
|
||||
use tokio_util::io::{InspectReader, InspectWriter};
|
||||
|
||||
/// An AsyncRead implementation that works byte-by-byte, to catch out callers
|
||||
/// who don't allow for `buf` being part-filled before the call
|
||||
struct SmallReader {
|
||||
contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Unpin for SmallReader {}
|
||||
|
||||
impl AsyncRead for SmallReader {
|
||||
fn poll_read(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
if let Some(byte) = self.contents.pop() {
|
||||
buf.put_slice(&[byte])
|
||||
}
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn read_tee() {
|
||||
let contents = b"This could be really long, you know".to_vec();
|
||||
let reader = SmallReader {
|
||||
contents: contents.clone(),
|
||||
};
|
||||
let mut altout: Vec<u8> = Vec::new();
|
||||
let mut teeout = Vec::new();
|
||||
{
|
||||
let mut tee = InspectReader::new(reader, |bytes| altout.extend(bytes));
|
||||
tee.read_to_end(&mut teeout).await.unwrap();
|
||||
}
|
||||
assert_eq!(teeout, altout);
|
||||
assert_eq!(altout.len(), contents.len());
|
||||
}
|
||||
|
||||
/// An AsyncWrite implementation that works byte-by-byte for poll_write, and
|
||||
/// that reads the whole of the first buffer plus one byte from the second in
|
||||
/// poll_write_vectored.
|
||||
///
|
||||
/// This is designed to catch bugs in handling partially written buffers
|
||||
#[derive(Debug)]
|
||||
struct SmallWriter {
|
||||
contents: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Unpin for SmallWriter {}
|
||||
|
||||
impl AsyncWrite for SmallWriter {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
// Just write one byte at a time
|
||||
if buf.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
self.contents.push(buf[0]);
|
||||
Poll::Ready(Ok(1))
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), std::io::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), std::io::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<Result<usize, std::io::Error>> {
|
||||
// Write all of the first buffer, then one byte from the second buffer
|
||||
// This should trip up anything that doesn't correctly handle multiple
|
||||
// buffers.
|
||||
if bufs.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
let mut written_len = bufs[0].len();
|
||||
self.contents.extend_from_slice(&bufs[0]);
|
||||
|
||||
if bufs.len() > 1 {
|
||||
let buf = bufs[1];
|
||||
if !buf.is_empty() {
|
||||
written_len += 1;
|
||||
self.contents.push(buf[0]);
|
||||
}
|
||||
}
|
||||
Poll::Ready(Ok(written_len))
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn write_tee() {
|
||||
let mut altout: Vec<u8> = Vec::new();
|
||||
let mut writeout = SmallWriter {
|
||||
contents: Vec::new(),
|
||||
};
|
||||
{
|
||||
let mut tee = InspectWriter::new(&mut writeout, |bytes| altout.extend(bytes));
|
||||
tee.write_all(b"A testing string, very testing")
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
assert_eq!(altout, writeout.contents);
|
||||
}
|
||||
|
||||
// This is inefficient, but works well enough for test use.
|
||||
// If you want something similar for real code, you'll want to avoid all the
|
||||
// fun of manipulating `bufs` - ideally, by the time you read this,
|
||||
// IoSlice::advance_slices will be stable, and you can use that.
|
||||
async fn write_all_vectored<W: AsyncWrite + Unpin>(
|
||||
mut writer: W,
|
||||
mut bufs: Vec<Vec<u8>>,
|
||||
) -> Result<usize, std::io::Error> {
|
||||
let mut res = 0;
|
||||
while !bufs.is_empty() {
|
||||
let mut written = poll_fn(|cx| {
|
||||
let bufs: Vec<IoSlice> = bufs.iter().map(|v| IoSlice::new(v)).collect();
|
||||
Pin::new(&mut writer).poll_write_vectored(cx, &bufs)
|
||||
})
|
||||
.await?;
|
||||
res += written;
|
||||
while written > 0 {
|
||||
let buf_len = bufs[0].len();
|
||||
if buf_len <= written {
|
||||
bufs.remove(0);
|
||||
written -= buf_len;
|
||||
} else {
|
||||
let buf = &mut bufs[0];
|
||||
let drain_len = written.min(buf.len());
|
||||
buf.drain(..drain_len);
|
||||
written -= drain_len;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn write_tee_vectored() {
|
||||
let mut altout: Vec<u8> = Vec::new();
|
||||
let mut writeout = SmallWriter {
|
||||
contents: Vec::new(),
|
||||
};
|
||||
let original = b"A very long string split up";
|
||||
let bufs: Vec<Vec<u8>> = original
|
||||
.split(|b| b.is_ascii_whitespace())
|
||||
.map(Vec::from)
|
||||
.collect();
|
||||
assert!(bufs.len() > 1);
|
||||
let expected: Vec<u8> = {
|
||||
let mut out = Vec::new();
|
||||
for item in &bufs {
|
||||
out.extend_from_slice(item)
|
||||
}
|
||||
out
|
||||
};
|
||||
{
|
||||
let mut bufcount = 0;
|
||||
let tee = InspectWriter::new(&mut writeout, |bytes| {
|
||||
bufcount += 1;
|
||||
altout.extend(bytes)
|
||||
});
|
||||
|
||||
assert!(tee.is_write_vectored());
|
||||
|
||||
write_all_vectored(tee, bufs.clone()).await.unwrap();
|
||||
|
||||
assert!(bufcount >= bufs.len());
|
||||
}
|
||||
assert_eq!(altout, writeout.contents);
|
||||
assert_eq!(writeout.contents, expected);
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_util::SinkExt;
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio_util::codec::{Encoder, FramedWrite};
|
||||
use tokio_util::io::{CopyToBytes, SinkWriter};
|
||||
use tokio_util::sync::PollSender;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_copied_sink_writer() -> Result<(), Error> {
|
||||
// Construct a channel pair to send data across and wrap a pollable sink.
|
||||
// Note that the sink must mimic a writable object, e.g. have `std::io::Error`
|
||||
// as its error type.
|
||||
// As `PollSender` requires an owned copy of the buffer, we wrap it additionally
|
||||
// with a `CopyToBytes` helper.
|
||||
let (tx, mut rx) = tokio::sync::mpsc::channel::<Bytes>(1);
|
||||
let mut writer = SinkWriter::new(CopyToBytes::new(
|
||||
PollSender::new(tx).sink_map_err(|_| io::Error::from(ErrorKind::BrokenPipe)),
|
||||
));
|
||||
|
||||
// Write data to our interface...
|
||||
let data: [u8; 4] = [1, 2, 3, 4];
|
||||
let _ = writer.write(&data).await;
|
||||
|
||||
// ... and receive it.
|
||||
assert_eq!(data.to_vec(), rx.recv().await.unwrap().to_vec());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// A trivial encoder.
|
||||
struct SliceEncoder;
|
||||
|
||||
impl SliceEncoder {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Encoder<&'a [u8]> for SliceEncoder {
|
||||
type Error = Error;
|
||||
|
||||
fn encode(&mut self, item: &'a [u8], dst: &mut bytes::BytesMut) -> Result<(), Self::Error> {
|
||||
// This is where we'd write packet headers, lengths, etc. in a real encoder.
|
||||
// For simplicity and demonstration purposes, we just pack a copy of
|
||||
// the slice at the end of a buffer.
|
||||
dst.extend_from_slice(item);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_direct_sink_writer() -> Result<(), Error> {
|
||||
// We define a framed writer which accepts byte slices
|
||||
// and 'reverse' this construction immediately.
|
||||
let framed_byte_lc = FramedWrite::new(Vec::new(), SliceEncoder::new());
|
||||
let mut writer = SinkWriter::new(framed_byte_lc);
|
||||
|
||||
// Write multiple slices to the sink...
|
||||
let _ = writer.write(&[1, 2, 3]).await;
|
||||
let _ = writer.write(&[4, 5, 6]).await;
|
||||
|
||||
// ... and compare it with the buffer.
|
||||
assert_eq!(
|
||||
writer.into_inner().write_buffer().to_vec().as_slice(),
|
||||
&[1, 2, 3, 4, 5, 6]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -13,6 +13,14 @@ fn semaphore_poll(
|
||||
tokio_test::task::spawn(fut)
|
||||
}
|
||||
|
||||
fn semaphore_poll_many(
|
||||
sem: &mut PollSemaphore,
|
||||
permits: u32,
|
||||
) -> tokio_test::task::Spawn<impl Future<Output = SemRet> + '_> {
|
||||
let fut = futures::future::poll_fn(move |cx| sem.poll_acquire_many(cx, permits));
|
||||
tokio_test::task::spawn(fut)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn it_works() {
|
||||
let sem = Arc::new(Semaphore::new(1));
|
||||
@@ -34,3 +42,43 @@ async fn it_works() {
|
||||
assert!(semaphore_poll(&mut poll_sem).await.is_none());
|
||||
assert!(semaphore_poll(&mut poll_sem).await.is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn can_acquire_many_permits() {
|
||||
let sem = Arc::new(Semaphore::new(4));
|
||||
let mut poll_sem = PollSemaphore::new(sem.clone());
|
||||
|
||||
let permit1 = semaphore_poll(&mut poll_sem).poll();
|
||||
assert!(matches!(permit1, Poll::Ready(Some(_))));
|
||||
|
||||
let permit2 = semaphore_poll_many(&mut poll_sem, 2).poll();
|
||||
assert!(matches!(permit2, Poll::Ready(Some(_))));
|
||||
|
||||
assert_eq!(sem.available_permits(), 1);
|
||||
|
||||
drop(permit2);
|
||||
|
||||
let mut permit4 = semaphore_poll_many(&mut poll_sem, 4);
|
||||
assert!(permit4.poll().is_pending());
|
||||
|
||||
drop(permit1);
|
||||
|
||||
let permit4 = permit4.poll();
|
||||
assert!(matches!(permit4, Poll::Ready(Some(_))));
|
||||
assert_eq!(sem.available_permits(), 0);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn can_poll_different_amounts_of_permits() {
|
||||
let sem = Arc::new(Semaphore::new(4));
|
||||
let mut poll_sem = PollSemaphore::new(sem.clone());
|
||||
assert!(semaphore_poll_many(&mut poll_sem, 5).poll().is_pending());
|
||||
assert!(semaphore_poll_many(&mut poll_sem, 4).poll().is_ready());
|
||||
|
||||
let permit = sem.acquire_many(4).await.unwrap();
|
||||
assert!(semaphore_poll_many(&mut poll_sem, 5).poll().is_pending());
|
||||
assert!(semaphore_poll_many(&mut poll_sem, 4).poll().is_pending());
|
||||
drop(permit);
|
||||
assert!(semaphore_poll_many(&mut poll_sem, 5).poll().is_pending());
|
||||
assert!(semaphore_poll_many(&mut poll_sem, 4).poll().is_ready());
|
||||
}
|
||||
|
||||
@@ -39,6 +39,56 @@ fn cancel_token() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_token_owned() {
|
||||
let (waker, wake_counter) = new_count_waker();
|
||||
let token = CancellationToken::new();
|
||||
assert!(!token.is_cancelled());
|
||||
|
||||
let wait_fut = token.clone().cancelled_owned();
|
||||
pin!(wait_fut);
|
||||
|
||||
assert_eq!(
|
||||
Poll::Pending,
|
||||
wait_fut.as_mut().poll(&mut Context::from_waker(&waker))
|
||||
);
|
||||
assert_eq!(wake_counter, 0);
|
||||
|
||||
let wait_fut_2 = token.clone().cancelled_owned();
|
||||
pin!(wait_fut_2);
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 1);
|
||||
assert!(token.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
wait_fut.as_mut().poll(&mut Context::from_waker(&waker))
|
||||
);
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
wait_fut_2.as_mut().poll(&mut Context::from_waker(&waker))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_token_owned_drop_test() {
|
||||
let (waker, wake_counter) = new_count_waker();
|
||||
let token = CancellationToken::new();
|
||||
|
||||
let future = token.cancelled_owned();
|
||||
pin!(future);
|
||||
|
||||
assert_eq!(
|
||||
Poll::Pending,
|
||||
future.as_mut().poll(&mut Context::from_waker(&waker))
|
||||
);
|
||||
assert_eq!(wake_counter, 0);
|
||||
|
||||
// let future be dropped while pinned and under pending state to
|
||||
// find potential memory related bugs.
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cancel_child_token_through_parent() {
|
||||
let (waker, wake_counter) = new_count_waker();
|
||||
@@ -206,9 +256,6 @@ fn create_child_token_after_parent_was_cancelled() {
|
||||
parent_fut.as_mut().poll(&mut Context::from_waker(&waker))
|
||||
);
|
||||
assert_eq!(wake_counter, 0);
|
||||
|
||||
drop(child_fut);
|
||||
drop(parent_fut);
|
||||
}
|
||||
|
||||
if drop_child_first {
|
||||
|
||||
@@ -1,3 +1,173 @@
|
||||
# 1.23.1 (January 4, 2022)
|
||||
|
||||
This release forward ports changes from 1.18.4.
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe server builder to maintain option when toggling
|
||||
pipe mode ([#5336]).
|
||||
|
||||
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
|
||||
|
||||
# 1.23.0 (December 5, 2022)
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe connect ([#5208])
|
||||
- io: support vectored writes for `ChildStdin` ([#5216])
|
||||
- io: fix `async fn ready()` false positive for OS-specific events ([#5231])
|
||||
|
||||
### Changed
|
||||
- runtime: `yield_now` defers task until after driver poll ([#5223])
|
||||
- runtime: reduce amount of codegen needed per spawned task ([#5213])
|
||||
- windows: replace `winapi` dependency with `windows-sys` ([#5204])
|
||||
|
||||
[#5208]: https://github.com/tokio-rs/tokio/pull/5208
|
||||
[#5216]: https://github.com/tokio-rs/tokio/pull/5216
|
||||
[#5213]: https://github.com/tokio-rs/tokio/pull/5213
|
||||
[#5204]: https://github.com/tokio-rs/tokio/pull/5204
|
||||
[#5223]: https://github.com/tokio-rs/tokio/pull/5223
|
||||
[#5231]: https://github.com/tokio-rs/tokio/pull/5231
|
||||
|
||||
# 1.22.0 (November 17, 2022)
|
||||
|
||||
### Added
|
||||
- runtime: add `Handle::runtime_flavor` ([#5138])
|
||||
- sync: add `Mutex::blocking_lock_owned` ([#5130])
|
||||
- sync: add `Semaphore::MAX_PERMITS` ([#5144])
|
||||
- sync: add `merge()` to semaphore permits ([#4948])
|
||||
- sync: add `mpsc::WeakUnboundedSender` ([#5189])
|
||||
|
||||
### Added (unstable)
|
||||
|
||||
- process: add `Command::process_group` ([#5114])
|
||||
- runtime: export metrics about the blocking thread pool ([#5161])
|
||||
- task: add `task::id()` and `task::try_id()` ([#5171])
|
||||
|
||||
### Fixed
|
||||
- macros: don't take ownership of futures in macros ([#5087])
|
||||
- runtime: fix Stacked Borrows violation in `LocalOwnedTasks` ([#5099])
|
||||
- runtime: mitigate ABA with 32-bit queue indices when possible ([#5042])
|
||||
- task: wake local tasks to the local queue when woken by the same thread ([#5095])
|
||||
- time: panic in release mode when `mark_pending` called illegally ([#5093])
|
||||
- runtime: fix typo in expect message ([#5169])
|
||||
- runtime: fix `unsync_load` on atomic types ([#5175])
|
||||
- task: elaborate safety comments in task deallocation ([#5172])
|
||||
- runtime: fix `LocalSet` drop in thread local ([#5179])
|
||||
- net: remove libc type leakage in a public API ([#5191])
|
||||
- runtime: update the alignment of `CachePadded` ([#5106])
|
||||
|
||||
### Changed
|
||||
- io: make `tokio::io::copy` continue filling the buffer when writer stalls ([#5066])
|
||||
- runtime: remove `coop::budget` from `LocalSet::run_until` ([#5155])
|
||||
- sync: make `Notify` panic safe ([#5154])
|
||||
|
||||
### Documented
|
||||
- io: fix doc for `write_i8` to use signed integers ([#5040])
|
||||
- net: fix doc typos for TCP and UDP `set_tos` methods ([#5073])
|
||||
- net: fix function name in `UdpSocket::recv` documentation ([#5150])
|
||||
- sync: typo in `TryLockError` for `RwLock::try_write` ([#5160])
|
||||
- task: document that spawned tasks execute immediately ([#5117])
|
||||
- time: document return type of `timeout` ([#5118])
|
||||
- time: document that `timeout` checks only before poll ([#5126])
|
||||
- sync: specify return type of `oneshot::Receiver` in docs ([#5198])
|
||||
|
||||
### Internal changes
|
||||
- runtime: use const `Mutex::new` for globals ([#5061])
|
||||
- runtime: remove `Option` around `mio::Events` in io driver ([#5078])
|
||||
- runtime: remove a conditional compilation clause ([#5104])
|
||||
- runtime: remove a reference to internal time handle ([#5107])
|
||||
- runtime: misc time driver cleanup ([#5120])
|
||||
- runtime: move signal driver to runtime module ([#5121])
|
||||
- runtime: signal driver now uses I/O driver directly ([#5125])
|
||||
- runtime: start decoupling I/O driver and I/O handle ([#5127])
|
||||
- runtime: switch `io::handle` refs with scheduler:Handle ([#5128])
|
||||
- runtime: remove Arc from I/O driver ([#5134])
|
||||
- runtime: use signal driver handle via `scheduler::Handle` ([#5135])
|
||||
- runtime: move internal clock fns out of context ([#5139])
|
||||
- runtime: remove `runtime::context` module ([#5140])
|
||||
- runtime: keep driver cfgs in `driver.rs` ([#5141])
|
||||
- runtime: add `runtime::context` to unify thread-locals ([#5143])
|
||||
- runtime: rename some confusing internal variables/fns ([#5151])
|
||||
- runtime: move `coop` mod into `runtime` ([#5152])
|
||||
- runtime: move budget state to context thread-local ([#5157])
|
||||
- runtime: move park logic into runtime module ([#5158])
|
||||
- runtime: move `Runtime` into its own file ([#5159])
|
||||
- runtime: unify entering a runtime with `Handle::enter` ([#5163])
|
||||
- runtime: remove handle reference from each scheduler ([#5166])
|
||||
- runtime: move `enter` into `context` ([#5167])
|
||||
- runtime: combine context and entered thread-locals ([#5168])
|
||||
- runtime: fix accidental unsetting of current handle ([#5178])
|
||||
- runtime: move `CoreStage` methods to `Core` ([#5182])
|
||||
- sync: name mpsc semaphore types ([#5146])
|
||||
|
||||
[#4948]: https://github.com/tokio-rs/tokio/pull/4948
|
||||
[#5040]: https://github.com/tokio-rs/tokio/pull/5040
|
||||
[#5042]: https://github.com/tokio-rs/tokio/pull/5042
|
||||
[#5061]: https://github.com/tokio-rs/tokio/pull/5061
|
||||
[#5066]: https://github.com/tokio-rs/tokio/pull/5066
|
||||
[#5073]: https://github.com/tokio-rs/tokio/pull/5073
|
||||
[#5078]: https://github.com/tokio-rs/tokio/pull/5078
|
||||
[#5087]: https://github.com/tokio-rs/tokio/pull/5087
|
||||
[#5093]: https://github.com/tokio-rs/tokio/pull/5093
|
||||
[#5095]: https://github.com/tokio-rs/tokio/pull/5095
|
||||
[#5099]: https://github.com/tokio-rs/tokio/pull/5099
|
||||
[#5104]: https://github.com/tokio-rs/tokio/pull/5104
|
||||
[#5106]: https://github.com/tokio-rs/tokio/pull/5106
|
||||
[#5107]: https://github.com/tokio-rs/tokio/pull/5107
|
||||
[#5114]: https://github.com/tokio-rs/tokio/pull/5114
|
||||
[#5117]: https://github.com/tokio-rs/tokio/pull/5117
|
||||
[#5118]: https://github.com/tokio-rs/tokio/pull/5118
|
||||
[#5120]: https://github.com/tokio-rs/tokio/pull/5120
|
||||
[#5121]: https://github.com/tokio-rs/tokio/pull/5121
|
||||
[#5125]: https://github.com/tokio-rs/tokio/pull/5125
|
||||
[#5126]: https://github.com/tokio-rs/tokio/pull/5126
|
||||
[#5127]: https://github.com/tokio-rs/tokio/pull/5127
|
||||
[#5128]: https://github.com/tokio-rs/tokio/pull/5128
|
||||
[#5130]: https://github.com/tokio-rs/tokio/pull/5130
|
||||
[#5134]: https://github.com/tokio-rs/tokio/pull/5134
|
||||
[#5135]: https://github.com/tokio-rs/tokio/pull/5135
|
||||
[#5138]: https://github.com/tokio-rs/tokio/pull/5138
|
||||
[#5138]: https://github.com/tokio-rs/tokio/pull/5138
|
||||
[#5139]: https://github.com/tokio-rs/tokio/pull/5139
|
||||
[#5140]: https://github.com/tokio-rs/tokio/pull/5140
|
||||
[#5141]: https://github.com/tokio-rs/tokio/pull/5141
|
||||
[#5143]: https://github.com/tokio-rs/tokio/pull/5143
|
||||
[#5144]: https://github.com/tokio-rs/tokio/pull/5144
|
||||
[#5144]: https://github.com/tokio-rs/tokio/pull/5144
|
||||
[#5146]: https://github.com/tokio-rs/tokio/pull/5146
|
||||
[#5150]: https://github.com/tokio-rs/tokio/pull/5150
|
||||
[#5151]: https://github.com/tokio-rs/tokio/pull/5151
|
||||
[#5152]: https://github.com/tokio-rs/tokio/pull/5152
|
||||
[#5154]: https://github.com/tokio-rs/tokio/pull/5154
|
||||
[#5155]: https://github.com/tokio-rs/tokio/pull/5155
|
||||
[#5157]: https://github.com/tokio-rs/tokio/pull/5157
|
||||
[#5158]: https://github.com/tokio-rs/tokio/pull/5158
|
||||
[#5159]: https://github.com/tokio-rs/tokio/pull/5159
|
||||
[#5160]: https://github.com/tokio-rs/tokio/pull/5160
|
||||
[#5161]: https://github.com/tokio-rs/tokio/pull/5161
|
||||
[#5163]: https://github.com/tokio-rs/tokio/pull/5163
|
||||
[#5166]: https://github.com/tokio-rs/tokio/pull/5166
|
||||
[#5167]: https://github.com/tokio-rs/tokio/pull/5167
|
||||
[#5168]: https://github.com/tokio-rs/tokio/pull/5168
|
||||
[#5169]: https://github.com/tokio-rs/tokio/pull/5169
|
||||
[#5171]: https://github.com/tokio-rs/tokio/pull/5171
|
||||
[#5172]: https://github.com/tokio-rs/tokio/pull/5172
|
||||
[#5175]: https://github.com/tokio-rs/tokio/pull/5175
|
||||
[#5178]: https://github.com/tokio-rs/tokio/pull/5178
|
||||
[#5179]: https://github.com/tokio-rs/tokio/pull/5179
|
||||
[#5182]: https://github.com/tokio-rs/tokio/pull/5182
|
||||
[#5189]: https://github.com/tokio-rs/tokio/pull/5189
|
||||
[#5191]: https://github.com/tokio-rs/tokio/pull/5191
|
||||
[#5198]: https://github.com/tokio-rs/tokio/pull/5198
|
||||
|
||||
# 1.21.2 (September 27, 2022)
|
||||
|
||||
This release removes the dependency on the `once_cell` crate to restore the MSRV
|
||||
of 1.21.x, which is the latest minor version at the time of release. ([#5048])
|
||||
|
||||
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
|
||||
|
||||
# 1.21.1 (September 13, 2022)
|
||||
|
||||
### Fixed
|
||||
@@ -103,6 +273,24 @@ wasm32-wasi target is given unstable support for the `net` feature.
|
||||
[#4956]: https://github.com/tokio-rs/tokio/pull/4956
|
||||
[#4959]: https://github.com/tokio-rs/tokio/pull/4959
|
||||
|
||||
# 1.20.3 (January 3, 2022)
|
||||
|
||||
This release forward ports changes from 1.18.4.
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe server builder to maintain option when toggling
|
||||
pipe mode ([#5336]).
|
||||
|
||||
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
|
||||
|
||||
# 1.20.2 (September 27, 2022)
|
||||
|
||||
This release removes the dependency on the `once_cell` crate to restore the MSRV
|
||||
of the 1.20.x LTS release. ([#5048])
|
||||
|
||||
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
|
||||
|
||||
# 1.20.1 (July 25, 2022)
|
||||
|
||||
### Fixed
|
||||
@@ -221,6 +409,22 @@ This release fixes a bug in `Notified::enable`. ([#4747])
|
||||
[#4729]: https://github.com/tokio-rs/tokio/pull/4729
|
||||
[#4739]: https://github.com/tokio-rs/tokio/pull/4739
|
||||
|
||||
# 1.18.4 (January 3, 2022)
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: fix Windows named pipe server builder to maintain option when toggling
|
||||
pipe mode ([#5336]).
|
||||
|
||||
[#5336]: https://github.com/tokio-rs/tokio/pull/5336
|
||||
|
||||
# 1.18.3 (September 27, 2022)
|
||||
|
||||
This release removes the dependency on the `once_cell` crate to restore the MSRV
|
||||
of the 1.18.x LTS release. ([#5048])
|
||||
|
||||
[#5048]: https://github.com/tokio-rs/tokio/pull/5048
|
||||
|
||||
# 1.18.2 (May 5, 2022)
|
||||
|
||||
Add missing features for the `winapi` dependency. ([#4663])
|
||||
|
||||
+22
-27
@@ -5,8 +5,8 @@ name = "tokio"
|
||||
# - Update doc url
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.0.x" git tag.
|
||||
version = "1.21.1"
|
||||
# - Create "v1.x.y" git tag.
|
||||
version = "1.23.1"
|
||||
edition = "2018"
|
||||
rust-version = "1.49"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
@@ -52,46 +52,37 @@ net = [
|
||||
"mio/os-ext",
|
||||
"mio/net",
|
||||
"socket2",
|
||||
"winapi/fileapi",
|
||||
"winapi/handleapi",
|
||||
"winapi/namedpipeapi",
|
||||
"winapi/winbase",
|
||||
"winapi/winnt",
|
||||
"winapi/minwindef",
|
||||
"winapi/accctrl",
|
||||
"winapi/aclapi",
|
||||
"windows-sys/Win32_Foundation",
|
||||
"windows-sys/Win32_Security",
|
||||
"windows-sys/Win32_Storage_FileSystem",
|
||||
"windows-sys/Win32_System_Pipes",
|
||||
"windows-sys/Win32_System_SystemServices",
|
||||
]
|
||||
process = [
|
||||
"bytes",
|
||||
"once_cell",
|
||||
"libc",
|
||||
"mio/os-poll",
|
||||
"mio/os-ext",
|
||||
"mio/net",
|
||||
"signal-hook-registry",
|
||||
"winapi/handleapi",
|
||||
"winapi/minwindef",
|
||||
"winapi/processthreadsapi",
|
||||
"winapi/threadpoollegacyapiset",
|
||||
"winapi/winbase",
|
||||
"winapi/winnt",
|
||||
"windows-sys/Win32_Foundation",
|
||||
"windows-sys/Win32_System_Threading",
|
||||
"windows-sys/Win32_System_WindowsProgramming",
|
||||
]
|
||||
# Includes basic task execution capabilities
|
||||
rt = ["once_cell"]
|
||||
rt = []
|
||||
rt-multi-thread = [
|
||||
"num_cpus",
|
||||
"rt",
|
||||
]
|
||||
signal = [
|
||||
"once_cell",
|
||||
"libc",
|
||||
"mio/os-poll",
|
||||
"mio/net",
|
||||
"mio/os-ext",
|
||||
"signal-hook-registry",
|
||||
"winapi/consoleapi",
|
||||
"winapi/wincon",
|
||||
"winapi/minwindef",
|
||||
"windows-sys/Win32_Foundation",
|
||||
"windows-sys/Win32_System_Console",
|
||||
]
|
||||
sync = []
|
||||
test-util = ["rt", "sync", "time"]
|
||||
@@ -112,7 +103,6 @@ pin-project-lite = "0.2.0"
|
||||
|
||||
# Everything else is optional...
|
||||
bytes = { version = "1.0.0", optional = true }
|
||||
once_cell = { version = "1.5.2", optional = true }
|
||||
memchr = { version = "2.2", optional = true }
|
||||
mio = { version = "0.8.4", optional = true }
|
||||
num_cpus = { version = "1.8.0", optional = true }
|
||||
@@ -134,12 +124,17 @@ signal-hook-registry = { version = "1.1.1", optional = true }
|
||||
libc = { version = "0.2.42" }
|
||||
nix = { version = "0.24", default-features = false, features = ["fs", "socket"] }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.winapi]
|
||||
version = "0.3.8"
|
||||
default-features = false
|
||||
features = ["std"]
|
||||
[target.'cfg(windows)'.dependencies.windows-sys]
|
||||
version = "0.42.0"
|
||||
optional = true
|
||||
|
||||
[target.'cfg(docsrs)'.dependencies.windows-sys]
|
||||
version = "0.42.0"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Security_Authorization",
|
||||
]
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.ntapi]
|
||||
version = "0.3.6"
|
||||
|
||||
|
||||
+1
-1
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.21.1", features = ["full"] }
|
||||
tokio = { version = "1.23.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
|
||||
@@ -17,9 +17,17 @@ const ADDR_OF_PROBE: &str = r#"
|
||||
}
|
||||
"#;
|
||||
|
||||
const CONST_MUTEX_NEW_PROBE: &str = r#"
|
||||
{
|
||||
static MY_MUTEX: ::std::sync::Mutex<i32> = ::std::sync::Mutex::new(1);
|
||||
*MY_MUTEX.lock().unwrap()
|
||||
}
|
||||
"#;
|
||||
|
||||
fn main() {
|
||||
let mut enable_const_thread_local = false;
|
||||
let mut enable_addr_of = false;
|
||||
let mut enable_const_mutex_new = false;
|
||||
|
||||
match AutoCfg::new() {
|
||||
Ok(ac) => {
|
||||
@@ -57,6 +65,21 @@ fn main() {
|
||||
enable_addr_of = true;
|
||||
}
|
||||
}
|
||||
|
||||
// The `Mutex::new` method was made const in 1.63.
|
||||
if ac.probe_rustc_version(1, 64) {
|
||||
enable_const_mutex_new = true;
|
||||
} else if ac.probe_rustc_version(1, 63) {
|
||||
// This compiler claims to be 1.63, but there are some nightly
|
||||
// compilers that claim to be 1.63 without supporting the
|
||||
// feature. Explicitly probe to check if code using them
|
||||
// compiles.
|
||||
//
|
||||
// The oldest nightly that supports the feature is 2022-06-20.
|
||||
if ac.probe_expression(CONST_MUTEX_NEW_PROBE) {
|
||||
enable_const_mutex_new = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
@@ -86,6 +109,14 @@ fn main() {
|
||||
autocfg::emit("tokio_no_addr_of")
|
||||
}
|
||||
|
||||
if !enable_const_mutex_new {
|
||||
// To disable this feature on compilers that support it, you can
|
||||
// explicitly pass this flag with the following environment variable:
|
||||
//
|
||||
// RUSTFLAGS="--cfg tokio_no_const_mutex_new"
|
||||
autocfg::emit("tokio_no_const_mutex_new")
|
||||
}
|
||||
|
||||
let target = ::std::env::var("TARGET").unwrap_or_default();
|
||||
|
||||
// We emit cfgs instead of using `target_family = "wasm"` that requires Rust 1.54.
|
||||
|
||||
@@ -7,10 +7,5 @@ allowed_external_types = [
|
||||
"bytes::buf::buf_mut::BufMut",
|
||||
|
||||
"tokio_macros::*",
|
||||
|
||||
# TODO(https://github.com/tokio-rs/tokio/issues/4916): Remove the libc types
|
||||
"libc::unix::gid_t",
|
||||
"libc::unix::pid_t",
|
||||
"libc::unix::uid_t",
|
||||
]
|
||||
|
||||
|
||||
@@ -21,4 +21,3 @@
|
||||
pub enum NotDefinedHere {}
|
||||
|
||||
pub mod os;
|
||||
pub mod winapi;
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
//! See [winapi].
|
||||
//!
|
||||
//! [winapi]: https://docs.rs/winapi
|
||||
|
||||
/// See [winapi::shared](https://docs.rs/winapi/*/winapi/shared/index.html).
|
||||
pub mod shared {
|
||||
/// See [winapi::shared::winerror](https://docs.rs/winapi/*/winapi/shared/winerror/index.html).
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod winerror {
|
||||
/// See [winapi::shared::winerror::ERROR_ACCESS_DENIED][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_ACCESS_DENIED.html
|
||||
pub type ERROR_ACCESS_DENIED = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::shared::winerror::ERROR_PIPE_BUSY][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_PIPE_BUSY.html
|
||||
pub type ERROR_PIPE_BUSY = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::shared::winerror::ERROR_MORE_DATA][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/shared/winerror/constant.ERROR_MORE_DATA.html
|
||||
pub type ERROR_MORE_DATA = crate::doc::NotDefinedHere;
|
||||
}
|
||||
}
|
||||
|
||||
/// See [winapi::um](https://docs.rs/winapi/*/winapi/um/index.html).
|
||||
pub mod um {
|
||||
/// See [winapi::um::winbase](https://docs.rs/winapi/*/winapi/um/winbase/index.html).
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod winbase {
|
||||
/// See [winapi::um::winbase::PIPE_TYPE_MESSAGE][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_MESSAGE.html
|
||||
pub type PIPE_TYPE_MESSAGE = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::PIPE_TYPE_BYTE][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_TYPE_BYTE.html
|
||||
pub type PIPE_TYPE_BYTE = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::PIPE_CLIENT_END][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_CLIENT_END.html
|
||||
pub type PIPE_CLIENT_END = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::PIPE_SERVER_END][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.PIPE_SERVER_END.html
|
||||
pub type PIPE_SERVER_END = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [winapi::um::winbase::SECURITY_IDENTIFICATION][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/winbase/constant.SECURITY_IDENTIFICATION.html
|
||||
pub type SECURITY_IDENTIFICATION = crate::doc::NotDefinedHere;
|
||||
}
|
||||
|
||||
/// See [winapi::um::minwinbase](https://docs.rs/winapi/*/winapi/um/minwinbase/index.html).
|
||||
#[allow(non_camel_case_types)]
|
||||
pub mod minwinbase {
|
||||
/// See [winapi::um::minwinbase::SECURITY_ATTRIBUTES][winapi]
|
||||
///
|
||||
/// [winapi]: https://docs.rs/winapi/*/winapi/um/minwinbase/constant.SECURITY_ATTRIBUTES.html
|
||||
pub type SECURITY_ATTRIBUTES = crate::doc::NotDefinedHere;
|
||||
}
|
||||
}
|
||||
@@ -81,7 +81,7 @@ impl Write for &'_ MockFile {
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
tokio_thread_local! {
|
||||
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
|
||||
}
|
||||
|
||||
|
||||
@@ -542,7 +542,7 @@ feature! {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winapi::um::winbase::FILE_FLAG_DELETE_ON_CLOSE;
|
||||
/// use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_DELETE_ON_CLOSE;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
@@ -581,7 +581,7 @@ feature! {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN;
|
||||
/// use windows_sys::Win32::Storage::FileSystem::FILE_ATTRIBUTE_HIDDEN;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
@@ -624,7 +624,7 @@ feature! {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use winapi::um::winbase::SECURITY_IDENTIFICATION;
|
||||
/// use windows_sys::Win32::Storage::FileSystem::SECURITY_IDENTIFICATION;
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// # #[tokio::main]
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#![allow(unreachable_pub)]
|
||||
//! Mock version of std::fs::OpenOptions;
|
||||
use mockall::mock;
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@ use std::future::Future;
|
||||
cfg_rt! {
|
||||
#[track_caller]
|
||||
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
|
||||
let mut e = crate::runtime::enter::enter(false);
|
||||
let mut e = crate::runtime::context::try_enter_blocking_region().expect(
|
||||
"Cannot block the current thread from within a runtime. This \
|
||||
happens because a function attempted to block the current \
|
||||
thread while the thread is being used to drive asynchronous \
|
||||
tasks."
|
||||
);
|
||||
e.block_on(f).unwrap()
|
||||
}
|
||||
}
|
||||
@@ -11,7 +16,7 @@ cfg_rt! {
|
||||
cfg_not_rt! {
|
||||
#[track_caller]
|
||||
pub(crate) fn block_on<F: Future>(f: F) -> F::Output {
|
||||
let mut park = crate::park::thread::CachedParkThread::new();
|
||||
let mut park = crate::runtime::park::CachedParkThread::new();
|
||||
park.block_on(f).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,23 @@ use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
// This struct is intentionally `!Unpin` when `F` is `!Unpin`. This is to
|
||||
// mitigate the issue where rust puts noalias on mutable references to the
|
||||
// `PollFn` type if it is `Unpin`. If the closure has ownership of a future,
|
||||
// then this "leaks" and the future is affected by noalias too, which we don't
|
||||
// want.
|
||||
//
|
||||
// See this thread for more information:
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
//
|
||||
// The fact that `PollFn` is not `Unpin` when it shouldn't be is tested in
|
||||
// `tests/async_send_sync.rs`.
|
||||
|
||||
/// Future for the [`poll_fn`] function.
|
||||
pub struct PollFn<F> {
|
||||
f: F,
|
||||
}
|
||||
|
||||
impl<F> Unpin for PollFn<F> {}
|
||||
|
||||
/// Creates a new future wrapping around a function returning [`Poll`].
|
||||
pub fn poll_fn<T, F>(f: F) -> PollFn<F>
|
||||
where
|
||||
@@ -34,7 +44,17 @@ where
|
||||
{
|
||||
type Output = T;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||
(self.f)(cx)
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
|
||||
// Safety: We never construct a `Pin<&mut F>` anywhere, so accessing `f`
|
||||
// mutably in an unpinned way is sound.
|
||||
//
|
||||
// This use of unsafe cannot be replaced with the pin-project macro
|
||||
// because:
|
||||
// * If we put `#[pin]` on the field, then it gives us a `Pin<&mut F>`,
|
||||
// which we can't use to call the closure.
|
||||
// * If we don't put `#[pin]` on the field, then it makes `PollFn` be
|
||||
// unconditionally `Unpin`, which we also don't want.
|
||||
let me = unsafe { Pin::into_inner_unchecked(self) };
|
||||
(me.f)(cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::io::Interest;
|
||||
use crate::runtime::io::{Handle, ReadyEvent, Registration};
|
||||
use crate::runtime::io::{ReadyEvent, Registration};
|
||||
use crate::runtime::scheduler;
|
||||
|
||||
use mio::unix::SourceFd;
|
||||
use std::io;
|
||||
@@ -200,12 +201,13 @@ impl<T: AsRawFd> AsyncFd<T> {
|
||||
where
|
||||
T: AsRawFd,
|
||||
{
|
||||
Self::new_with_handle_and_interest(inner, Handle::current(), interest)
|
||||
Self::new_with_handle_and_interest(inner, scheduler::Handle::current(), interest)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn new_with_handle_and_interest(
|
||||
inner: T,
|
||||
handle: Handle,
|
||||
handle: scheduler::Handle,
|
||||
interest: Interest,
|
||||
) -> io::Result<Self> {
|
||||
let fd = inner.as_raw_fd();
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! Use POSIX AIO futures with Tokio.
|
||||
|
||||
use crate::io::interest::Interest;
|
||||
use crate::runtime::io::{Handle, ReadyEvent, Registration};
|
||||
use crate::runtime::io::{ReadyEvent, Registration};
|
||||
use crate::runtime::scheduler;
|
||||
use mio::event::Source;
|
||||
use mio::Registry;
|
||||
use mio::Token;
|
||||
@@ -118,7 +119,7 @@ impl<E: AioSource> Aio<E> {
|
||||
|
||||
fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
||||
let mut io = MioSource(io);
|
||||
let handle = Handle::current();
|
||||
let handle = scheduler::Handle::current();
|
||||
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
|
||||
Ok(Self { io, registration })
|
||||
}
|
||||
|
||||
@@ -178,6 +178,12 @@
|
||||
//! [`Sink`]: https://docs.rs/futures/0.3/futures/sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures/0.3/futures/stream/trait.Stream.html
|
||||
//! [`Write`]: std::io::Write
|
||||
|
||||
#![cfg_attr(
|
||||
not(all(feature = "rt", feature = "net")),
|
||||
allow(dead_code, unused_imports)
|
||||
)]
|
||||
|
||||
cfg_io_blocking! {
|
||||
pub(crate) mod blocking;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use crate::io::interest::Interest;
|
||||
use crate::runtime::io::{Handle, Registration};
|
||||
use crate::runtime::io::Registration;
|
||||
use crate::runtime::scheduler;
|
||||
|
||||
use mio::event::Source;
|
||||
use std::fmt;
|
||||
@@ -103,13 +104,14 @@ impl<E: Source> PollEvented<E> {
|
||||
#[track_caller]
|
||||
#[cfg_attr(feature = "signal", allow(unused))]
|
||||
pub(crate) fn new_with_interest(io: E, interest: Interest) -> io::Result<Self> {
|
||||
Self::new_with_interest_and_handle(io, interest, Handle::current())
|
||||
Self::new_with_interest_and_handle(io, interest, scheduler::Handle::current())
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub(crate) fn new_with_interest_and_handle(
|
||||
mut io: E,
|
||||
interest: Interest,
|
||||
handle: Handle,
|
||||
handle: scheduler::Handle,
|
||||
) -> io::Result<Self> {
|
||||
let registration = Registration::new_with_interest_and_handle(&mut io, interest, handle)?;
|
||||
Ok(Self {
|
||||
@@ -119,11 +121,7 @@ impl<E: Source> PollEvented<E> {
|
||||
}
|
||||
|
||||
/// Returns a reference to the registration.
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
#[cfg(any(feature = "net"))]
|
||||
pub(crate) fn registration(&self) -> &Registration {
|
||||
&self.registration
|
||||
}
|
||||
@@ -210,7 +208,7 @@ feature! {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "net")]
|
||||
#[cfg(any(feature = "net", feature = "process"))]
|
||||
pub(crate) fn poll_write_vectored<'a>(
|
||||
&'a self,
|
||||
cx: &mut Context<'_>,
|
||||
|
||||
@@ -12,7 +12,7 @@ const WRITE_CLOSED: usize = 0b0_1000;
|
||||
///
|
||||
/// `Ready` tracks which operation an I/O resource is ready to perform.
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "net")))]
|
||||
#[derive(Clone, Copy, PartialEq, PartialOrd)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Ready(usize);
|
||||
|
||||
impl Ready {
|
||||
|
||||
@@ -406,7 +406,7 @@ cfg_io_util! {
|
||||
/// ```
|
||||
fn write_u8(&mut self, n: u8) -> WriteU8;
|
||||
|
||||
/// Writes an unsigned 8-bit integer to the underlying writer.
|
||||
/// Writes a signed 8-bit integer to the underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
@@ -425,7 +425,7 @@ cfg_io_util! {
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write unsigned 8 bit integers to a `AsyncWrite`:
|
||||
/// Write signed 8 bit integers to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
@@ -434,10 +434,10 @@ cfg_io_util! {
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_u8(2).await?;
|
||||
/// writer.write_u8(5).await?;
|
||||
/// writer.write_i8(-2).await?;
|
||||
/// writer.write_i8(126).await?;
|
||||
///
|
||||
/// assert_eq!(writer, b"\x02\x05");
|
||||
/// assert_eq!(writer, b"\xFE\x7E");
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
+49
-13
@@ -27,6 +27,51 @@ impl CopyBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_fill_buf<R>(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
reader: Pin<&mut R>,
|
||||
) -> Poll<io::Result<()>>
|
||||
where
|
||||
R: AsyncRead + ?Sized,
|
||||
{
|
||||
let me = &mut *self;
|
||||
let mut buf = ReadBuf::new(&mut me.buf);
|
||||
buf.set_filled(me.cap);
|
||||
|
||||
let res = reader.poll_read(cx, &mut buf);
|
||||
if let Poll::Ready(Ok(_)) = res {
|
||||
let filled_len = buf.filled().len();
|
||||
me.read_done = me.cap == filled_len;
|
||||
me.cap = filled_len;
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
fn poll_write_buf<R, W>(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
mut reader: Pin<&mut R>,
|
||||
mut writer: Pin<&mut W>,
|
||||
) -> Poll<io::Result<usize>>
|
||||
where
|
||||
R: AsyncRead + ?Sized,
|
||||
W: AsyncWrite + ?Sized,
|
||||
{
|
||||
let me = &mut *self;
|
||||
match writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]) {
|
||||
Poll::Pending => {
|
||||
// Top up the buffer towards full if we can read a bit more
|
||||
// data - this should improve the chances of a large write
|
||||
if !me.read_done && me.cap < me.buf.len() {
|
||||
ready!(me.poll_fill_buf(cx, reader.as_mut()))?;
|
||||
}
|
||||
Poll::Pending
|
||||
}
|
||||
res => res,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn poll_copy<R, W>(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
@@ -41,10 +86,10 @@ impl CopyBuffer {
|
||||
// If our buffer is empty, then we need to read some data to
|
||||
// continue.
|
||||
if self.pos == self.cap && !self.read_done {
|
||||
let me = &mut *self;
|
||||
let mut buf = ReadBuf::new(&mut me.buf);
|
||||
self.pos = 0;
|
||||
self.cap = 0;
|
||||
|
||||
match reader.as_mut().poll_read(cx, &mut buf) {
|
||||
match self.poll_fill_buf(cx, reader.as_mut()) {
|
||||
Poll::Ready(Ok(_)) => (),
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
Poll::Pending => {
|
||||
@@ -58,20 +103,11 @@ impl CopyBuffer {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let n = buf.filled().len();
|
||||
if n == 0 {
|
||||
self.read_done = true;
|
||||
} else {
|
||||
self.pos = 0;
|
||||
self.cap = n;
|
||||
}
|
||||
}
|
||||
|
||||
// If our buffer has some data, let's write it out!
|
||||
while self.pos < self.cap {
|
||||
let me = &mut *self;
|
||||
let i = ready!(writer.as_mut().poll_write(cx, &me.buf[me.pos..me.cap]))?;
|
||||
let i = ready!(self.poll_write_buf(cx, reader.as_mut(), writer.as_mut()))?;
|
||||
if i == 0 {
|
||||
return Poll::Ready(Err(io::Error::new(
|
||||
io::ErrorKind::WriteZero,
|
||||
|
||||
@@ -77,7 +77,7 @@ impl fmt::Debug for Empty {
|
||||
|
||||
cfg_coop! {
|
||||
fn poll_proceed_and_make_progress(cx: &mut Context<'_>) -> Poll<()> {
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
|
||||
coop.made_progress();
|
||||
Poll::Ready(())
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ impl AsyncRead for Pipe {
|
||||
cx: &mut task::Context<'_>,
|
||||
buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<std::io::Result<()>> {
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_read_internal(cx, buf);
|
||||
if ret.is_ready() {
|
||||
@@ -261,7 +261,7 @@ impl AsyncWrite for Pipe {
|
||||
cx: &mut task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
let coop = ready!(crate::coop::poll_proceed(cx));
|
||||
let coop = ready!(crate::runtime::coop::poll_proceed(cx));
|
||||
|
||||
let ret = self.poll_write_internal(cx, buf);
|
||||
if ret.is_ready() {
|
||||
|
||||
+8
-29
@@ -1,7 +1,9 @@
|
||||
#![allow(
|
||||
clippy::cognitive_complexity,
|
||||
clippy::large_enum_variant,
|
||||
clippy::needless_doctest_main
|
||||
clippy::module_inception,
|
||||
clippy::needless_doctest_main,
|
||||
clippy::declare_interior_mutable_const
|
||||
)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
@@ -326,20 +328,15 @@
|
||||
//! - `signal`: Enables all `tokio::signal` types.
|
||||
//! - `fs`: Enables `tokio::fs` types.
|
||||
//! - `test-util`: Enables testing based infrastructure for the Tokio runtime.
|
||||
//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's
|
||||
//! synchronization primitives internally. Also, this
|
||||
//! dependency is necessary to construct some of our primitives
|
||||
//! in a const context. MSRV may increase according to the
|
||||
//! _parking_lot_ release in use.
|
||||
//!
|
||||
//! _Note: `AsyncRead` and `AsyncWrite` traits do not require any features and are
|
||||
//! always available._
|
||||
//!
|
||||
//! ### Internal features
|
||||
//!
|
||||
//! These features do not expose any new API, but influence internal
|
||||
//! implementation aspects of Tokio, and can pull in additional
|
||||
//! dependencies.
|
||||
//!
|
||||
//! - `parking_lot`: As a potential optimization, use the _parking_lot_ crate's
|
||||
//! synchronization primitives internally. MSRV may increase according to the
|
||||
//! _parking_lot_ release in use.
|
||||
//!
|
||||
//! ### Unstable features
|
||||
//!
|
||||
//! Some feature flags are only available when specifying the `tokio_unstable` flag:
|
||||
@@ -479,7 +476,6 @@ pub mod io;
|
||||
pub mod net;
|
||||
|
||||
mod loom;
|
||||
mod park;
|
||||
|
||||
cfg_process! {
|
||||
pub mod process;
|
||||
@@ -497,18 +493,9 @@ cfg_rt! {
|
||||
pub mod runtime;
|
||||
}
|
||||
cfg_not_rt! {
|
||||
// The `runtime` module is used when the IO or time driver is needed.
|
||||
#[cfg(any(
|
||||
feature = "net",
|
||||
feature = "time",
|
||||
all(unix, feature = "process"),
|
||||
all(unix, feature = "signal"),
|
||||
))]
|
||||
pub(crate) mod runtime;
|
||||
}
|
||||
|
||||
pub(crate) mod coop;
|
||||
|
||||
cfg_signal! {
|
||||
pub mod signal;
|
||||
}
|
||||
@@ -591,14 +578,6 @@ pub(crate) use self::doc::os;
|
||||
#[allow(unused)]
|
||||
pub(crate) use std::os;
|
||||
|
||||
#[cfg(docsrs)]
|
||||
#[allow(unused)]
|
||||
pub(crate) use self::doc::winapi;
|
||||
|
||||
#[cfg(all(not(docsrs), windows, feature = "net"))]
|
||||
#[allow(unused)]
|
||||
pub(crate) use winapi;
|
||||
|
||||
cfg_macros! {
|
||||
/// Implementation detail of the `select!` macro. This macro is **not**
|
||||
/// intended to be used as part of the public API and is permitted to
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// `AtomicPtr` providing an additional `load_unsync` function.
|
||||
pub(crate) struct AtomicPtr<T> {
|
||||
inner: std::sync::atomic::AtomicPtr<T>,
|
||||
}
|
||||
|
||||
impl<T> AtomicPtr<T> {
|
||||
pub(crate) fn new(ptr: *mut T) -> AtomicPtr<T> {
|
||||
let inner = std::sync::atomic::AtomicPtr::new(ptr);
|
||||
AtomicPtr { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for AtomicPtr<T> {
|
||||
type Target = std::sync::atomic::AtomicPtr<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for AtomicPtr<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for AtomicPtr<T> {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.deref().fmt(fmt)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// `AtomicU16` providing an additional `load_unsync` function.
|
||||
/// `AtomicU16` providing an additional `unsync_load` function.
|
||||
pub(crate) struct AtomicU16 {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicU16>,
|
||||
}
|
||||
@@ -23,7 +23,7 @@ impl AtomicU16 {
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
pub(crate) unsafe fn unsync_load(&self) -> u16 {
|
||||
*(*self.inner.get()).get_mut()
|
||||
core::ptr::read(self.inner.get() as *const u16)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// `AtomicU32` providing an additional `load_unsync` function.
|
||||
/// `AtomicU32` providing an additional `unsync_load` function.
|
||||
pub(crate) struct AtomicU32 {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicU32>,
|
||||
}
|
||||
@@ -15,6 +15,16 @@ impl AtomicU32 {
|
||||
let inner = UnsafeCell::new(std::sync::atomic::AtomicU32::new(val));
|
||||
AtomicU32 { inner }
|
||||
}
|
||||
|
||||
/// Performs an unsynchronized load.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
pub(crate) unsafe fn unsync_load(&self) -> u32 {
|
||||
core::ptr::read(self.inner.get() as *const u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AtomicU32 {
|
||||
|
||||
@@ -11,76 +11,8 @@ cfg_has_atomic_u64! {
|
||||
}
|
||||
|
||||
cfg_not_has_atomic_u64! {
|
||||
use crate::loom::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
#[path = "atomic_u64_as_mutex.rs"]
|
||||
mod atomic_u64_as_mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AtomicU64 {
|
||||
inner: Mutex<u64>,
|
||||
}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub(crate) fn new(val: u64) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(val),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, _: Ordering) -> u64 {
|
||||
*self.inner.lock()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self, val: u64, _: Ordering) {
|
||||
*self.inner.lock() = val;
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_add(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev + val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev | val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
Ok(current)
|
||||
} else {
|
||||
Err(*lock)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange_weak(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
self.compare_exchange(current, new, success, failure)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AtomicU64 {
|
||||
fn default() -> AtomicU64 {
|
||||
Self {
|
||||
inner: Mutex::new(0),
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) use atomic_u64_as_mutex::AtomicU64;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
use crate::loom::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AtomicU64 {
|
||||
inner: Mutex<u64>,
|
||||
}
|
||||
|
||||
impl AtomicU64 {
|
||||
pub(crate) fn new(val: u64) -> Self {
|
||||
Self {
|
||||
inner: Mutex::new(val),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, _: Ordering) -> u64 {
|
||||
*self.inner.lock()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self, val: u64, _: Ordering) {
|
||||
*self.inner.lock() = val;
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_add(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev + val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev | val;
|
||||
prev
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
Ok(current)
|
||||
} else {
|
||||
Err(*lock)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn compare_exchange_weak(
|
||||
&self,
|
||||
current: u64,
|
||||
new: u64,
|
||||
success: Ordering,
|
||||
failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
self.compare_exchange(current, new, success, failure)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AtomicU64 {
|
||||
fn default() -> AtomicU64 {
|
||||
AtomicU64::new(u64::default())
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops::Deref;
|
||||
|
||||
/// `AtomicU8` providing an additional `load_unsync` function.
|
||||
pub(crate) struct AtomicU8 {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicU8>,
|
||||
}
|
||||
|
||||
unsafe impl Send for AtomicU8 {}
|
||||
unsafe impl Sync for AtomicU8 {}
|
||||
|
||||
impl AtomicU8 {
|
||||
pub(crate) const fn new(val: u8) -> AtomicU8 {
|
||||
let inner = UnsafeCell::new(std::sync::atomic::AtomicU8::new(val));
|
||||
AtomicU8 { inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for AtomicU8 {
|
||||
type Target = std::sync::atomic::AtomicU8;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// safety: it is always safe to access `&self` fns on the inner value as
|
||||
// we never perform unsafe mutations.
|
||||
unsafe { &*self.inner.get() }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for AtomicU8 {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.deref().fmt(fmt)
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
||||
use std::fmt;
|
||||
use std::ops;
|
||||
|
||||
/// `AtomicUsize` providing an additional `load_unsync` function.
|
||||
/// `AtomicUsize` providing an additional `unsync_load` function.
|
||||
pub(crate) struct AtomicUsize {
|
||||
inner: UnsafeCell<std::sync::atomic::AtomicUsize>,
|
||||
}
|
||||
@@ -23,7 +23,7 @@ impl AtomicUsize {
|
||||
/// All mutations must have happened before the unsynchronized load.
|
||||
/// Additionally, there must be no concurrent mutations.
|
||||
pub(crate) unsafe fn unsync_load(&self) -> usize {
|
||||
*(*self.inner.get()).get_mut()
|
||||
core::ptr::read(self.inner.get() as *const usize)
|
||||
}
|
||||
|
||||
pub(crate) fn with_mut<R>(&mut self, f: impl FnOnce(&mut usize) -> R) -> R {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
#![cfg_attr(any(not(feature = "full"), loom), allow(unused_imports, dead_code))]
|
||||
|
||||
mod atomic_ptr;
|
||||
mod atomic_u16;
|
||||
mod atomic_u32;
|
||||
mod atomic_u64;
|
||||
mod atomic_u8;
|
||||
mod atomic_usize;
|
||||
mod mutex;
|
||||
#[cfg(feature = "parking_lot")]
|
||||
@@ -71,14 +69,12 @@ pub(crate) mod sync {
|
||||
pub(crate) use crate::loom::std::mutex::Mutex;
|
||||
|
||||
pub(crate) mod atomic {
|
||||
pub(crate) use crate::loom::std::atomic_ptr::AtomicPtr;
|
||||
pub(crate) use crate::loom::std::atomic_u16::AtomicU16;
|
||||
pub(crate) use crate::loom::std::atomic_u32::AtomicU32;
|
||||
pub(crate) use crate::loom::std::atomic_u64::AtomicU64;
|
||||
pub(crate) use crate::loom::std::atomic_u8::AtomicU8;
|
||||
pub(crate) use crate::loom::std::atomic_usize::AtomicUsize;
|
||||
|
||||
pub(crate) use std::sync::atomic::{fence, AtomicBool, Ordering};
|
||||
pub(crate) use std::sync::atomic::{fence, AtomicBool, AtomicPtr, AtomicU8, Ordering};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,12 @@ impl<T> Mutex<T> {
|
||||
Mutex(sync::Mutex::new(t))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(not(tokio_no_const_mutex_new))]
|
||||
pub(crate) const fn const_new(t: T) -> Mutex<T> {
|
||||
Mutex(sync::Mutex::new(t))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn lock(&self) -> MutexGuard<'_, T> {
|
||||
match self.0.lock() {
|
||||
|
||||
@@ -52,7 +52,7 @@ impl<T> Mutex<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[cfg(all(feature = "parking_lot", not(all(loom, test)),))]
|
||||
#[cfg(all(feature = "parking_lot", not(all(loom, test))))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "parking_lot",))))]
|
||||
pub(crate) const fn const_new(t: T) -> Mutex<T> {
|
||||
Mutex(PhantomData, parking_lot::const_mutex(t))
|
||||
|
||||
+51
-12
@@ -297,6 +297,13 @@ macro_rules! cfg_signal_internal {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_signal_internal_and_unix {
|
||||
($($item:item)*) => {
|
||||
#[cfg(unix)]
|
||||
cfg_signal_internal! { $($item)* }
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_signal_internal {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -455,12 +462,13 @@ macro_rules! cfg_has_atomic_u64 {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32",
|
||||
tokio_wasm
|
||||
)))]
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32",
|
||||
tokio_wasm,
|
||||
tokio_no_atomic_u64,
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
@@ -470,12 +478,43 @@ macro_rules! cfg_not_has_atomic_u64 {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32",
|
||||
tokio_wasm
|
||||
))]
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc",
|
||||
target_arch = "riscv32",
|
||||
tokio_wasm,
|
||||
tokio_no_atomic_u64,
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_has_const_mutex_new {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(all(
|
||||
not(all(loom, test)),
|
||||
any(
|
||||
feature = "parking_lot",
|
||||
not(tokio_no_const_mutex_new)
|
||||
)
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_has_const_mutex_new {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(all(
|
||||
not(all(loom, test)),
|
||||
any(
|
||||
feature = "parking_lot",
|
||||
not(tokio_no_const_mutex_new)
|
||||
)
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
|
||||
@@ -72,8 +72,18 @@ macro_rules! join {
|
||||
|
||||
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
||||
// the requirement of `Pin::new_unchecked` called below.
|
||||
//
|
||||
// We can't use the `pin!` macro for this because `futures` is a tuple
|
||||
// and the standard library provides no way to pin-project to the fields
|
||||
// of a tuple.
|
||||
let mut futures = ( $( maybe_done($e), )* );
|
||||
|
||||
// This assignment makes sure that the `poll_fn` closure only has a
|
||||
// reference to the futures, instead of taking ownership of them. This
|
||||
// mitigates the issue described in
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
let mut futures = &mut futures;
|
||||
|
||||
// Each time the future created by poll_fn is polled, a different future will be polled first
|
||||
// to ensure every future passed to join! gets a chance to make progress even if
|
||||
// one of the futures consumes the whole budget.
|
||||
@@ -106,7 +116,7 @@ macro_rules! join {
|
||||
to_run -= 1;
|
||||
|
||||
// Extract the future for this branch from the tuple.
|
||||
let ( $($skip,)* fut, .. ) = &mut futures;
|
||||
let ( $($skip,)* fut, .. ) = &mut *futures;
|
||||
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
|
||||
@@ -10,7 +10,7 @@ macro_rules! scoped_thread_local {
|
||||
$vis static $name: $crate::macros::scoped_tls::ScopedKey<$ty>
|
||||
= $crate::macros::scoped_tls::ScopedKey {
|
||||
inner: {
|
||||
thread_local!(static FOO: ::std::cell::Cell<*const ()> = const {
|
||||
tokio_thread_local!(static FOO: ::std::cell::Cell<*const ()> = const {
|
||||
std::cell::Cell::new(::std::ptr::null())
|
||||
});
|
||||
&FOO
|
||||
|
||||
@@ -460,8 +460,18 @@ macro_rules! select {
|
||||
let mut output = {
|
||||
// Safety: Nothing must be moved out of `futures`. This is to
|
||||
// satisfy the requirement of `Pin::new_unchecked` called below.
|
||||
//
|
||||
// We can't use the `pin!` macro for this because `futures` is a
|
||||
// tuple and the standard library provides no way to pin-project to
|
||||
// the fields of a tuple.
|
||||
let mut futures = ( $( $fut , )+ );
|
||||
|
||||
// This assignment makes sure that the `poll_fn` closure only has a
|
||||
// reference to the futures, instead of taking ownership of them.
|
||||
// This mitigates the issue described in
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
let mut futures = &mut futures;
|
||||
|
||||
$crate::macros::support::poll_fn(|cx| {
|
||||
// Track if any branch returns pending. If no branch completes
|
||||
// **or** returns pending, this implies that all branches are
|
||||
@@ -497,7 +507,7 @@ macro_rules! select {
|
||||
|
||||
// Extract the future for this branch from the
|
||||
// tuple
|
||||
let ( $($skip,)* fut, .. ) = &mut futures;
|
||||
let ( $($skip,)* fut, .. ) = &mut *futures;
|
||||
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
cfg_macros! {
|
||||
pub use crate::future::poll_fn;
|
||||
pub use crate::future::maybe_done::maybe_done;
|
||||
pub use crate::util::thread_rng_n;
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn thread_rng_n(n: u32) -> u32 {
|
||||
crate::runtime::context::thread_rng_n(n)
|
||||
}
|
||||
}
|
||||
|
||||
pub use std::future::Future;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#[cfg(all(loom, test))]
|
||||
macro_rules! thread_local {
|
||||
macro_rules! tokio_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty = const { $expr:expr } $(;)?) => {
|
||||
loom::thread_local! {
|
||||
$(#[$attrs])*
|
||||
@@ -12,13 +12,15 @@ macro_rules! thread_local {
|
||||
|
||||
#[cfg(not(tokio_no_const_thread_local))]
|
||||
#[cfg(not(all(loom, test)))]
|
||||
macro_rules! thread_local {
|
||||
($($tts:tt)+) => { ::std::thread_local!{ $($tts)+ } }
|
||||
macro_rules! tokio_thread_local {
|
||||
($($tts:tt)+) => {
|
||||
::std::thread_local!{ $($tts)+ }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(tokio_no_const_thread_local)]
|
||||
#[cfg(not(all(loom, test)))]
|
||||
macro_rules! thread_local {
|
||||
macro_rules! tokio_thread_local {
|
||||
($(#[$attrs:meta])* $vis:vis static $name:ident: $ty:ty = const { $expr:expr } $(;)?) => {
|
||||
::std::thread_local! {
|
||||
$(#[$attrs])*
|
||||
|
||||
@@ -118,8 +118,18 @@ macro_rules! try_join {
|
||||
|
||||
// Safety: nothing must be moved out of `futures`. This is to satisfy
|
||||
// the requirement of `Pin::new_unchecked` called below.
|
||||
//
|
||||
// We can't use the `pin!` macro for this because `futures` is a tuple
|
||||
// and the standard library provides no way to pin-project to the fields
|
||||
// of a tuple.
|
||||
let mut futures = ( $( maybe_done($e), )* );
|
||||
|
||||
// This assignment makes sure that the `poll_fn` closure only has a
|
||||
// reference to the futures, instead of taking ownership of them. This
|
||||
// mitigates the issue described in
|
||||
// <https://internals.rust-lang.org/t/surprising-soundness-trouble-around-pollfn/17484>
|
||||
let mut futures = &mut futures;
|
||||
|
||||
// Each time the future created by poll_fn is polled, a different future will be polled first
|
||||
// to ensure every future passed to join! gets a chance to make progress even if
|
||||
// one of the futures consumes the whole budget.
|
||||
@@ -152,7 +162,7 @@ macro_rules! try_join {
|
||||
to_run -= 1;
|
||||
|
||||
// Extract the future for this branch from the tuple.
|
||||
let ( $($skip,)* fut, .. ) = &mut futures;
|
||||
let ( $($skip,)* fut, .. ) = &mut *futures;
|
||||
|
||||
// Safety: future is stored on the stack above
|
||||
// and never moved.
|
||||
|
||||
@@ -244,7 +244,7 @@ cfg_net! {
|
||||
type Future = <str as sealed::ToSocketAddrsPriv>::Future;
|
||||
|
||||
fn to_socket_addrs(&self, _: sealed::Internal) -> Self::Future {
|
||||
(&self[..]).to_socket_addrs(sealed::Internal)
|
||||
self[..].to_socket_addrs(sealed::Internal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -428,8 +428,8 @@ impl TcpSocket {
|
||||
|
||||
/// Sets the value for the `IP_TOS` option on this socket.
|
||||
///
|
||||
/// This value sets the time-to-live field that is used in every packet sent
|
||||
/// from this socket.
|
||||
/// This value sets the type-of-service field that is used in every packet
|
||||
/// sent from this socket.
|
||||
///
|
||||
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
|
||||
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
|
||||
|
||||
@@ -145,6 +145,12 @@ impl ReadHalf<'_> {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
@@ -273,6 +279,12 @@ impl WriteHalf<'_> {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
|
||||
@@ -200,6 +200,12 @@ impl OwnedReadHalf {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
@@ -355,6 +361,12 @@ impl OwnedWriteHalf {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// This function is equivalent to [`TcpStream::ready`].
|
||||
///
|
||||
/// # Cancel safety
|
||||
|
||||
@@ -377,6 +377,12 @@ impl TcpStream {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
@@ -964,7 +970,7 @@ impl TcpStream {
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
@@ -983,6 +989,11 @@ impl TcpStream {
|
||||
/// defined on the Tokio `TcpStream` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: TcpStream::readable()
|
||||
|
||||
+14
-7
@@ -357,7 +357,9 @@ impl UdpSocket {
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`.
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
@@ -740,7 +742,7 @@ impl UdpSocket {
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If `recv_from` is used as the event in a
|
||||
/// This method is cancel safe. If `recv` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, it is guaranteed that no messages were received on this
|
||||
/// socket.
|
||||
@@ -925,7 +927,7 @@ impl UdpSocket {
|
||||
|
||||
// Safety: We trust `UdpSocket::recv` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let n = (&*self.io).recv(dst)?;
|
||||
let n = (*self.io).recv(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
@@ -989,7 +991,7 @@ impl UdpSocket {
|
||||
|
||||
// Safety: We trust `UdpSocket::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let (n, addr) = (&*self.io).recv_from(dst)?;
|
||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
@@ -1271,7 +1273,7 @@ impl UdpSocket {
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
@@ -1290,6 +1292,11 @@ impl UdpSocket {
|
||||
/// defined on the Tokio `UdpSocket` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: UdpSocket::readable()
|
||||
@@ -1544,8 +1551,8 @@ impl UdpSocket {
|
||||
|
||||
/// Sets the value for the `IP_TOS` option on this socket.
|
||||
///
|
||||
/// This value sets the time-to-live field that is used in every packet sent
|
||||
/// from this socket.
|
||||
/// This value sets the type-of-service field that is used in every packet
|
||||
/// sent from this socket.
|
||||
///
|
||||
/// **NOTE:** On Windows, `IP_TOS` is only supported on [Windows 8+ or
|
||||
/// Windows Server 2012+.](https://docs.microsoft.com/en-us/windows/win32/winsock/ipproto-ip-socket-options)
|
||||
|
||||
@@ -104,7 +104,9 @@ impl UnixDatagram {
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`.
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
@@ -846,7 +848,7 @@ impl UnixDatagram {
|
||||
|
||||
// Safety: We trust `UnixDatagram::recv_from` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let (n, addr) = (&*self.io).recv_from(dst)?;
|
||||
let (n, addr) = (*self.io).recv_from(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
@@ -909,7 +911,7 @@ impl UnixDatagram {
|
||||
|
||||
// Safety: We trust `UnixDatagram::recv` to have filled up `n` bytes in the
|
||||
// buffer.
|
||||
let n = (&*self.io).recv(dst)?;
|
||||
let n = (*self.io).recv(dst)?;
|
||||
|
||||
unsafe {
|
||||
buf.advance_mut(n);
|
||||
@@ -1214,7 +1216,7 @@ impl UnixDatagram {
|
||||
/// Tries to read or write from the socket using a user-provided IO operation.
|
||||
///
|
||||
/// If the socket is ready, the provided closure is called. The closure
|
||||
/// should attempt to perform IO operation from the socket by manually
|
||||
/// should attempt to perform IO operation on the socket by manually
|
||||
/// calling the appropriate syscall. If the operation fails because the
|
||||
/// socket is not actually ready, then the closure should return a
|
||||
/// `WouldBlock` error and the readiness flag is cleared. The return value
|
||||
@@ -1233,6 +1235,11 @@ impl UnixDatagram {
|
||||
/// defined on the Tokio `UnixDatagram` type, as this will mess with the
|
||||
/// readiness flag and can cause the socket to behave incorrectly.
|
||||
///
|
||||
/// This method is not intended to be used with combined interests.
|
||||
/// The closure should perform only one type of IO operation, so it should not
|
||||
/// require more than one ready state. This method may panic or sleep forever
|
||||
/// if it is called with a combined interest.
|
||||
///
|
||||
/// Usually, [`readable()`], [`writable()`] or [`ready()`] is used with this function.
|
||||
///
|
||||
/// [`readable()`]: UnixDatagram::readable()
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
//! Unix domain socket utility types.
|
||||
|
||||
// This module does not currently provide any public API, but it was
|
||||
// unintentionally defined as a public module. Hide it from the documentation
|
||||
// instead of changing it to a private module to avoid breakage.
|
||||
@@ -22,3 +21,15 @@ pub(crate) use stream::UnixStream;
|
||||
|
||||
mod ucred;
|
||||
pub use ucred::UCred;
|
||||
|
||||
/// A type representing process and process group IDs.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type uid_t = u32;
|
||||
|
||||
/// A type representing user ID.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type gid_t = u32;
|
||||
|
||||
/// A type representing group ID.
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type pid_t = i32;
|
||||
|
||||
@@ -182,6 +182,12 @@ impl WriteHalf<'_> {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
||||
@@ -114,6 +114,12 @@ impl OwnedReadHalf {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
@@ -265,6 +271,12 @@ impl OwnedWriteHalf {
|
||||
/// can be used to concurrently read / write to the same socket on a single
|
||||
/// task without splitting the socket.
|
||||
///
|
||||
/// The function may complete without the socket being ready. This is a
|
||||
/// false-positive and attempting an operation will return with
|
||||
/// `io::ErrorKind::WouldBlock`. The function can also return with an empty
|
||||
/// [`Ready`] set, so you should always check the returned value and possibly
|
||||
/// wait again if the requested states are not set.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. Once a readiness event occurs, the method
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user