mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
132
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dd060b16f5 | ||
|
|
4152918a39 | ||
|
|
c0974bad94 | ||
|
|
b67534a20b | ||
|
|
c4e56232ff | ||
|
|
84c4a6d89f | ||
|
|
b501f25202 | ||
|
|
032c55e77f | ||
|
|
1e95d6994a | ||
|
|
362df5a317 | ||
|
|
106bb94896 | ||
|
|
8198ef3881 | ||
|
|
e66217575b | ||
|
|
175d84e2b1 | ||
|
|
69a6585429 | ||
|
|
cf02b3f32d | ||
|
|
d01bda86a4 | ||
|
|
e60d7a474b | ||
|
|
0d9430b99c | ||
|
|
f51676891f | ||
|
|
3340ae6aa9 | ||
|
|
f957f7f9a7 | ||
|
|
8b447649bb | ||
|
|
4d8cc28b76 | ||
|
|
f2a06bff1b | ||
|
|
0de05422ce | ||
|
|
afb734d189 | ||
|
|
2b731c0e01 | ||
|
|
8f27c04a9e | ||
|
|
c85a0e524e | ||
|
|
1eb468be08 | ||
|
|
378409d15d | ||
|
|
51ff95c144 | ||
|
|
df10b68d47 | ||
|
|
b280c6dcd7 | ||
|
|
998dc5a2eb | ||
|
|
ced7992f65 | ||
|
|
0cefa85bfa | ||
|
|
2087f3e0eb | ||
|
|
5ae2855eaf | ||
|
|
252004811f | ||
|
|
549e89e9cd | ||
|
|
c8fc492748 | ||
|
|
7147042534 | ||
|
|
634fcac2d9 | ||
|
|
7c4183a45d | ||
|
|
aef2d64b0a | ||
|
|
8f10d81613 | ||
|
|
7a11cfd77a | ||
|
|
6610ba9bd6 | ||
|
|
3fd88dac64 | ||
|
|
ccd495647d | ||
|
|
80d8d40a34 | ||
|
|
3b38ebd7f5 | ||
|
|
127983e5b4 | ||
|
|
c306bf853a | ||
|
|
c6fbb9aeb1 | ||
|
|
5d61c997e9 | ||
|
|
c505a2f81a | ||
|
|
e2589a0e40 | ||
|
|
51fad066e2 | ||
|
|
ae233c1e9f | ||
|
|
be26ca7625 | ||
|
|
2d41f0303e | ||
|
|
e0c527f383 | ||
|
|
1160e8864c | ||
|
|
998a125717 | ||
|
|
cbfdc9d69e | ||
|
|
08337c5f79 | ||
|
|
87f4969fbc | ||
|
|
e385108920 | ||
|
|
4818c2ed05 | ||
|
|
80f0801e19 | ||
|
|
8148b2107c | ||
|
|
2e7de1ae1d | ||
|
|
37e60fc7f9 | ||
|
|
8170e2787c | ||
|
|
677107d8d9 | ||
|
|
c8ecfc894d | ||
|
|
08ed41f339 | ||
|
|
90e1935c48 | ||
|
|
b877629cb1 | ||
|
|
0531549b6e | ||
|
|
38204f5fba | ||
|
|
8fa29cb00a | ||
|
|
b521cc2689 | ||
|
|
57c90c9750 | ||
|
|
d35ff7064f | ||
|
|
ab0791b817 | ||
|
|
959c5c997f | ||
|
|
845626410a | ||
|
|
3207479fa7 | ||
|
|
7601dc6d2a | ||
|
|
d1aa2df80e | ||
|
|
e979ad7f2d | ||
|
|
5ad3dd3378 | ||
|
|
60bd40d529 | ||
|
|
4a93af4d25 | ||
|
|
34c6a26c01 | ||
|
|
97e7830364 | ||
|
|
606206ecad | ||
|
|
dfe4013ff2 | ||
|
|
18779aa2e2 | ||
|
|
2c24a028f6 | ||
|
|
f55b77aadd | ||
|
|
21de476ae7 | ||
|
|
cb147a2b3f | ||
|
|
f759240254 | ||
|
|
2e44cd29df | ||
|
|
2ab1fb00a9 | ||
|
|
d16e50639a | ||
|
|
e7d74b3119 | ||
|
|
d101feac50 | ||
|
|
9d8b37d51a | ||
|
|
d4c89758fc | ||
|
|
932be12481 | ||
|
|
bdd6765016 | ||
|
|
8f6d8b25bf | ||
|
|
eb7aee980c | ||
|
|
21264f1d33 | ||
|
|
81ee3d202a | ||
|
|
a39e6c2439 | ||
|
|
4abeca7bc5 | ||
|
|
5ed84e1cd8 | ||
|
|
44a070d1b4 | ||
|
|
ce9ca45c92 | ||
|
|
f3ed064a26 | ||
|
|
652f0ae728 | ||
|
|
ff9b0ef7ca | ||
|
|
aaa150d211 | ||
|
|
deb1f98125 | ||
|
|
3a659c47c3 |
@@ -0,0 +1 @@
|
||||
msrv = "1.45"
|
||||
@@ -0,0 +1,3 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: [tokio-rs]
|
||||
@@ -9,7 +9,7 @@ name: CI
|
||||
env:
|
||||
RUSTFLAGS: -Dwarnings
|
||||
RUST_BACKTRACE: 1
|
||||
nightly: nightly-2021-04-25
|
||||
nightly: nightly-2021-07-09
|
||||
minrust: 1.45.2
|
||||
|
||||
jobs:
|
||||
@@ -122,7 +122,7 @@ jobs:
|
||||
|
||||
# Run `tokio` with "unstable" cfg flag.
|
||||
- name: test tokio full --cfg unstable
|
||||
run: cargo test --features full
|
||||
run: cargo test --all-features
|
||||
working-directory: tokio
|
||||
env:
|
||||
RUSTFLAGS: --cfg tokio_unstable -Dwarnings
|
||||
@@ -238,7 +238,7 @@ jobs:
|
||||
cargo hack --remove-dev-deps --workspace
|
||||
# Update Cargo.lock to minimal version dependencies.
|
||||
cargo update -Z minimal-versions
|
||||
cargo check --all-features
|
||||
cargo hack check --all-features --ignore-private
|
||||
|
||||
fmt:
|
||||
name: fmt
|
||||
@@ -265,7 +265,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Rust
|
||||
run: rustup update ${{ env.minrust }} && rustup default ${{ env.minrust }}
|
||||
run: rustup update 1.52.1 && rustup default 1.52.1
|
||||
- name: Install clippy
|
||||
run: rustup component add clippy
|
||||
|
||||
@@ -286,6 +286,7 @@ jobs:
|
||||
- name: "doc --lib --all-features"
|
||||
run: cargo doc --lib --no-deps --all-features --document-private-items
|
||||
env:
|
||||
RUSTFLAGS: --cfg docsrs
|
||||
RUSTDOCFLAGS: --cfg docsrs -Dwarnings
|
||||
|
||||
loom:
|
||||
|
||||
@@ -56,7 +56,7 @@ Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.5.0", features = ["full"] }
|
||||
tokio = { version = "1.10.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
@@ -66,7 +66,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
let listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
|
||||
loop {
|
||||
let (mut socket, _) = listener.accept().await?;
|
||||
@@ -140,8 +140,7 @@ several other libraries, including:
|
||||
|
||||
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
|
||||
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level
|
||||
tracing and async-aware diagnostics.
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
@@ -164,9 +163,35 @@ several other libraries, including:
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable release. The minimum supported version is 1.45.
|
||||
The current Tokio version is not guaranteed to build on Rust versions earlier than the
|
||||
minimum supported version.
|
||||
Tokio is built against the latest stable release. The minimum supported version
|
||||
is 1.45. The current Tokio version is not guaranteed to build on Rust versions
|
||||
earlier than the minimum supported version.
|
||||
|
||||
## Release schedule
|
||||
|
||||
Tokio doesn't follow a fixed release schedule, but we typically make one to two
|
||||
new minor releases each month. We make patch releases for bugfixes as necessary.
|
||||
|
||||
## Bug patching policy
|
||||
|
||||
For the purposes of making patch releases with bugfixes, we have designated
|
||||
certain minor releases as LTS (long term support) releases. Whenever a bug
|
||||
warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.8.x` - LTS release until February 2022.
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least half a
|
||||
year. If you wish to use a fixed minor release in your project, we recommend
|
||||
that you use an LTS release.
|
||||
|
||||
To use a fixed minor version, you can specify the version with a tilde. For
|
||||
example, to specify that you wish to use the newest `1.8.x` patch release, you
|
||||
can use the following dependency specification:
|
||||
```text
|
||||
tokio = { version = "~1.8", features = [...] }
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Benchmark implementation details of the theaded scheduler. These benches are
|
||||
//! Benchmark implementation details of the threaded scheduler. These benches are
|
||||
//! intended to be used as a form of regression testing and not as a general
|
||||
//! purpose benchmark demonstrating real-world performance.
|
||||
|
||||
|
||||
+57
-37
@@ -2,63 +2,83 @@
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
#[macro_use]
|
||||
extern crate bencher;
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
async fn work() -> usize {
|
||||
let val = 1 + 1;
|
||||
tokio::task::yield_now().await;
|
||||
black_box(val)
|
||||
}
|
||||
|
||||
fn basic_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
fn basic_scheduler_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.block_on(async {
|
||||
bench.iter(|| {
|
||||
let h = tokio::spawn(work());
|
||||
black_box(h);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
runtime.block_on(async {
|
||||
bench.iter(|| {
|
||||
let h = tokio::spawn(work());
|
||||
black_box(h);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn basic_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
|
||||
bench.iter(|| {
|
||||
let h = runtime.spawn(work());
|
||||
black_box(h);
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
|
||||
|
||||
fn basic_scheduler_spawn10(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
let h = runtime.spawn(work());
|
||||
black_box(h);
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let h = tokio::spawn(work());
|
||||
assert_eq!(h.await.unwrap(), 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_spawn10(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new_multi_thread()
|
||||
.worker_threads(1)
|
||||
.build()
|
||||
.unwrap();
|
||||
bench.iter(|| {
|
||||
runtime.block_on(async {
|
||||
let mut handles = Vec::with_capacity(10);
|
||||
for _ in 0..10 {
|
||||
handles.push(tokio::spawn(work()));
|
||||
}
|
||||
for handle in handles {
|
||||
assert_eq!(handle.await.unwrap(), 2);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
spawn,
|
||||
basic_scheduler_local_spawn,
|
||||
threaded_scheduler_local_spawn,
|
||||
basic_scheduler_remote_spawn,
|
||||
threaded_scheduler_remote_spawn
|
||||
basic_scheduler_spawn,
|
||||
basic_scheduler_spawn10,
|
||||
threaded_scheduler_spawn,
|
||||
threaded_scheduler_spawn10,
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(spawn);
|
||||
|
||||
@@ -22,7 +22,10 @@ serde_json = "1.0"
|
||||
httparse = "1.0"
|
||||
time = "0.1"
|
||||
once_cell = "1.5.2"
|
||||
rand = "0.8.3"
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.winapi]
|
||||
version = "0.3.8"
|
||||
|
||||
[[example]]
|
||||
name = "chat"
|
||||
@@ -76,3 +79,15 @@ path = "custom-executor.rs"
|
||||
[[example]]
|
||||
name = "custom-executor-tokio-context"
|
||||
path = "custom-executor-tokio-context.rs"
|
||||
|
||||
[[example]]
|
||||
name = "named-pipe"
|
||||
path = "named-pipe.rs"
|
||||
|
||||
[[example]]
|
||||
name = "named-pipe-ready"
|
||||
path = "named-pipe-ready.rs"
|
||||
|
||||
[[example]]
|
||||
name = "named-pipe-multi-client"
|
||||
path = "named-pipe-multi-client.rs"
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
use std::io;
|
||||
|
||||
#[cfg(windows)]
|
||||
async fn windows_main() -> io::Result<()> {
|
||||
use std::time::Duration;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
||||
use tokio::time;
|
||||
use winapi::shared::winerror;
|
||||
|
||||
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-multi-client";
|
||||
const N: usize = 10;
|
||||
|
||||
// The first server needs to be constructed early so that clients can
|
||||
// be correctly connected. Otherwise a waiting client will error.
|
||||
//
|
||||
// Here we also make use of `first_pipe_instance`, which will ensure
|
||||
// that there are no other servers up and running already.
|
||||
let mut server = ServerOptions::new()
|
||||
.first_pipe_instance(true)
|
||||
.create(PIPE_NAME)?;
|
||||
|
||||
let server = tokio::spawn(async move {
|
||||
// Artificial workload.
|
||||
time::sleep(Duration::from_secs(1)).await;
|
||||
|
||||
for _ in 0..N {
|
||||
// Wait for client to connect.
|
||||
server.connect().await?;
|
||||
let mut inner = server;
|
||||
|
||||
// Construct the next server to be connected before sending the one
|
||||
// we already have of onto a task. This ensures that the server
|
||||
// isn't closed (after it's done in the task) before a new one is
|
||||
// available. Otherwise the client might error with
|
||||
// `io::ErrorKind::NotFound`.
|
||||
server = ServerOptions::new().create(PIPE_NAME)?;
|
||||
|
||||
let _ = tokio::spawn(async move {
|
||||
let mut buf = vec![0u8; 4];
|
||||
inner.read_exact(&mut buf).await?;
|
||||
inner.write_all(b"pong").await?;
|
||||
Ok::<_, io::Error>(())
|
||||
});
|
||||
}
|
||||
|
||||
Ok::<_, io::Error>(())
|
||||
});
|
||||
|
||||
let mut clients = Vec::new();
|
||||
|
||||
for _ in 0..N {
|
||||
clients.push(tokio::spawn(async move {
|
||||
// This showcases a generic connect loop.
|
||||
//
|
||||
// We immediately try to create a client, if it's not found or
|
||||
// the pipe is busy we use the specialized wait function on the
|
||||
// client builder.
|
||||
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) => return Err(e),
|
||||
}
|
||||
|
||||
time::sleep(Duration::from_millis(5)).await;
|
||||
};
|
||||
|
||||
let mut buf = [0u8; 4];
|
||||
client.write_all(b"ping").await?;
|
||||
client.read_exact(&mut buf).await?;
|
||||
Ok::<_, io::Error>(buf)
|
||||
}));
|
||||
}
|
||||
|
||||
for client in clients {
|
||||
let result = client.await?;
|
||||
assert_eq!(&result?[..], b"pong");
|
||||
}
|
||||
|
||||
server.await??;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
windows_main().await?;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!("Named pipes are only supported on Windows!");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
use std::io;
|
||||
|
||||
#[cfg(windows)]
|
||||
async fn windows_main() -> io::Result<()> {
|
||||
use tokio::io::Interest;
|
||||
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
||||
|
||||
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";
|
||||
|
||||
let server = ServerOptions::new().create(PIPE_NAME)?;
|
||||
|
||||
let server = tokio::spawn(async move {
|
||||
// Note: we wait for a client to connect.
|
||||
server.connect().await?;
|
||||
|
||||
let buf = {
|
||||
let mut read_buf = [0u8; 5];
|
||||
let mut read_buf_cursor = 0;
|
||||
|
||||
loop {
|
||||
server.readable().await?;
|
||||
|
||||
let buf = &mut read_buf[read_buf_cursor..];
|
||||
|
||||
match server.try_read(buf) {
|
||||
Ok(n) => {
|
||||
read_buf_cursor += n;
|
||||
|
||||
if read_buf_cursor == read_buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read_buf
|
||||
};
|
||||
|
||||
{
|
||||
let write_buf = b"pong\n";
|
||||
let mut write_buf_cursor = 0;
|
||||
|
||||
loop {
|
||||
let buf = &write_buf[write_buf_cursor..];
|
||||
|
||||
if buf.is_empty() {
|
||||
break;
|
||||
}
|
||||
|
||||
server.writable().await?;
|
||||
|
||||
match server.try_write(buf) {
|
||||
Ok(n) => {
|
||||
write_buf_cursor += n;
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok::<_, io::Error>(buf)
|
||||
});
|
||||
|
||||
let client = tokio::spawn(async move {
|
||||
// There's no need to use a connect loop here, since we know that the
|
||||
// server is already up - `open` was called before spawning any of the
|
||||
// tasks.
|
||||
let client = ClientOptions::new().open(PIPE_NAME)?;
|
||||
|
||||
let mut read_buf = [0u8; 5];
|
||||
let mut read_buf_cursor = 0;
|
||||
let write_buf = b"ping\n";
|
||||
let mut write_buf_cursor = 0;
|
||||
|
||||
loop {
|
||||
let mut interest = Interest::READABLE;
|
||||
if write_buf_cursor < write_buf.len() {
|
||||
interest |= Interest::WRITABLE;
|
||||
}
|
||||
|
||||
let ready = client.ready(interest).await?;
|
||||
|
||||
if ready.is_readable() {
|
||||
let buf = &mut read_buf[read_buf_cursor..];
|
||||
|
||||
match client.try_read(buf) {
|
||||
Ok(n) => {
|
||||
read_buf_cursor += n;
|
||||
|
||||
if read_buf_cursor == read_buf.len() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ready.is_writable() {
|
||||
let buf = &write_buf[write_buf_cursor..];
|
||||
|
||||
if buf.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
match client.try_write(buf) {
|
||||
Ok(n) => {
|
||||
write_buf_cursor += n;
|
||||
}
|
||||
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
|
||||
continue;
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let buf = String::from_utf8_lossy(&read_buf).into_owned();
|
||||
|
||||
Ok::<_, io::Error>(buf)
|
||||
});
|
||||
|
||||
let (server, client) = tokio::try_join!(server, client)?;
|
||||
|
||||
assert_eq!(server?, *b"ping\n");
|
||||
assert_eq!(client?, "pong\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
windows_main().await?;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!("Named pipes are only supported on Windows!");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
use std::io;
|
||||
|
||||
#[cfg(windows)]
|
||||
async fn windows_main() -> io::Result<()> {
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::io::{AsyncBufReadExt, BufReader};
|
||||
use tokio::net::windows::named_pipe::{ClientOptions, ServerOptions};
|
||||
|
||||
const PIPE_NAME: &str = r"\\.\pipe\named-pipe-single-client";
|
||||
|
||||
let server = ServerOptions::new().create(PIPE_NAME)?;
|
||||
|
||||
let server = tokio::spawn(async move {
|
||||
// Note: we wait for a client to connect.
|
||||
server.connect().await?;
|
||||
|
||||
let mut server = BufReader::new(server);
|
||||
|
||||
let mut buf = String::new();
|
||||
server.read_line(&mut buf).await?;
|
||||
server.write_all(b"pong\n").await?;
|
||||
Ok::<_, io::Error>(buf)
|
||||
});
|
||||
|
||||
let client = tokio::spawn(async move {
|
||||
// There's no need to use a connect loop here, since we know that the
|
||||
// server is already up - `open` was called before spawning any of the
|
||||
// tasks.
|
||||
let client = ClientOptions::new().open(PIPE_NAME)?;
|
||||
|
||||
let mut client = BufReader::new(client);
|
||||
|
||||
let mut buf = String::new();
|
||||
client.write_all(b"ping\n").await?;
|
||||
client.read_line(&mut buf).await?;
|
||||
Ok::<_, io::Error>(buf)
|
||||
});
|
||||
|
||||
let (server, client) = tokio::try_join!(server, client)?;
|
||||
|
||||
assert_eq!(server?, "ping\n");
|
||||
assert_eq!(client?, "pong\n");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> io::Result<()> {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
windows_main().await?;
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
println!("Named pipes are only supported on Windows!");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -4,4 +4,4 @@ error: The default runtime flavor is `multi_thread`, but the `rt-multi-thread` f
|
||||
3 | #[tokio::main]
|
||||
| ^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
= note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
# 1.3.0 (July 7, 2021)
|
||||
|
||||
- macros: don't trigger `clippy::unwrap_used` ([#3926])
|
||||
|
||||
[#3926]: https://github.com/tokio-rs/tokio/pull/3926
|
||||
|
||||
# 1.2.0 (May 14, 2021)
|
||||
|
||||
- macros: forward input arguments in `#[tokio::test]` ([#3691])
|
||||
- macros: improve diagnostics on type mismatch ([#3766])
|
||||
- macros: various error message improvements ([#3677])
|
||||
|
||||
[#3677]: https://github.com/tokio-rs/tokio/pull/3677
|
||||
[#3691]: https://github.com/tokio-rs/tokio/pull/3691
|
||||
[#3766]: https://github.com/tokio-rs/tokio/pull/3766
|
||||
|
||||
# 1.1.0 (February 5, 2021)
|
||||
|
||||
- add `start_paused` option to macros ([#3492])
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-macros"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-macros-1.0.x" git tag.
|
||||
version = "1.1.0"
|
||||
version = "1.3.0"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-macros/1.1.0/tokio_macros"
|
||||
documentation = "https://docs.rs/tokio-macros/1.3.0/tokio_macros"
|
||||
description = """
|
||||
Tokio's proc macros.
|
||||
"""
|
||||
|
||||
+19
-15
@@ -201,12 +201,15 @@ fn parse_knobs(
|
||||
for arg in args {
|
||||
match arg {
|
||||
syn::NestedMeta::Meta(syn::Meta::NameValue(namevalue)) => {
|
||||
let ident = namevalue.path.get_ident();
|
||||
if ident.is_none() {
|
||||
let msg = "Must have specified ident";
|
||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||
}
|
||||
match ident.unwrap().to_string().to_lowercase().as_str() {
|
||||
let ident = namevalue
|
||||
.path
|
||||
.get_ident()
|
||||
.ok_or_else(|| {
|
||||
syn::Error::new_spanned(&namevalue, "Must have specified ident")
|
||||
})?
|
||||
.to_string()
|
||||
.to_lowercase();
|
||||
match ident.as_str() {
|
||||
"worker_threads" => {
|
||||
config.set_worker_threads(
|
||||
namevalue.lit.clone(),
|
||||
@@ -239,12 +242,11 @@ fn parse_knobs(
|
||||
}
|
||||
}
|
||||
syn::NestedMeta::Meta(syn::Meta::Path(path)) => {
|
||||
let ident = path.get_ident();
|
||||
if ident.is_none() {
|
||||
let msg = "Must have specified ident";
|
||||
return Err(syn::Error::new_spanned(path, msg));
|
||||
}
|
||||
let name = ident.unwrap().to_string().to_lowercase();
|
||||
let name = path
|
||||
.get_ident()
|
||||
.ok_or_else(|| syn::Error::new_spanned(&path, "Must have specified ident"))?
|
||||
.to_string()
|
||||
.to_lowercase();
|
||||
let msg = match name.as_str() {
|
||||
"threaded_scheduler" | "multi_thread" => {
|
||||
format!(
|
||||
@@ -323,14 +325,16 @@ fn parse_knobs(
|
||||
let brace_token = input.block.brace_token;
|
||||
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
|
||||
{
|
||||
let body = async #body;
|
||||
#[allow(clippy::expect_used)]
|
||||
#rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async #body)
|
||||
.expect("Failed building the Runtime")
|
||||
.block_on(body)
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
.expect("Parsing failure");
|
||||
input.block.brace_token = brace_token;
|
||||
|
||||
let result = quote! {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
@@ -1,3 +1,26 @@
|
||||
# 0.1.7 (July 7, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- sync: fix watch wrapper ([#3914])
|
||||
- time: fix `Timeout::size_hint` ([#3902])
|
||||
|
||||
[#3902]: https://github.com/tokio-rs/tokio/pull/3902
|
||||
[#3914]: https://github.com/tokio-rs/tokio/pull/3914
|
||||
|
||||
# 0.1.6 (May 14, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- stream: implement `Error` and `Display` for `BroadcastStreamRecvError` ([#3745])
|
||||
|
||||
### Fixed
|
||||
|
||||
- stream: avoid yielding in `AllFuture` and `AnyFuture` ([#3625])
|
||||
|
||||
[#3745]: https://github.com/tokio-rs/tokio/pull/3745
|
||||
[#3625]: https://github.com/tokio-rs/tokio/pull/3625
|
||||
|
||||
# 0.1.5 (March 20, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-stream"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-stream-0.1.x" git tag.
|
||||
version = "0.1.5"
|
||||
version = "0.1.7"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-stream/0.1.5/tokio_stream"
|
||||
documentation = "https://docs.rs/tokio-stream/0.1.7/tokio_stream"
|
||||
description = """
|
||||
Utilities to work with `Stream` and `tokio`.
|
||||
"""
|
||||
@@ -30,7 +30,7 @@ signal = ["tokio/signal"]
|
||||
[dependencies]
|
||||
futures-core = { version = "0.3.0" }
|
||||
pin-project-lite = "0.2.0"
|
||||
tokio = { version = "1.2.0", path = "../tokio", features = ["sync"] }
|
||||
tokio = { version = "1.8.0", path = "../tokio", features = ["sync"] }
|
||||
tokio-util = { version = "0.6.3", path = "../tokio-util", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
@@ -44,3 +44,8 @@ proptest = "1"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
# Issue #3770
|
||||
#
|
||||
# This should allow `docsrs` to be read across projects, so that `tokio-stream`
|
||||
# can pick up stubbed types exported by `tokio`.
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
@@ -515,7 +515,7 @@ pub trait StreamExt: Stream {
|
||||
/// Skip elements from the underlying stream while the provided predicate
|
||||
/// resolves to `true`.
|
||||
///
|
||||
/// This function, like [`Iterator::skip_while`], will ignore elemets from the
|
||||
/// This function, like [`Iterator::skip_while`], will ignore elements from the
|
||||
/// stream until the predicate `f` resolves to `false`. Once one element
|
||||
/// returns false, the rest of the elements will be yielded.
|
||||
///
|
||||
|
||||
@@ -113,7 +113,7 @@ impl<T: AsRef<str>> sealed::FromStreamPriv<T> for String {
|
||||
}
|
||||
|
||||
fn finalize(_: sealed::Internal, collection: &mut String) -> String {
|
||||
mem::replace(collection, String::new())
|
||||
mem::take(collection)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ impl<T> sealed::FromStreamPriv<T> for Vec<T> {
|
||||
}
|
||||
|
||||
fn finalize(_: sealed::Internal, collection: &mut Vec<T>) -> Vec<T> {
|
||||
mem::replace(collection, vec![])
|
||||
mem::take(collection)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,7 +69,18 @@ impl<S: Stream> Stream for Timeout<S> {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.stream.size_hint()
|
||||
let (lower, upper) = self.stream.size_hint();
|
||||
|
||||
// The timeout stream may insert an error before and after each message
|
||||
// from the underlying stream, but no more than one error between each
|
||||
// message. Hence the upper bound is computed as 2x+1.
|
||||
|
||||
// Using a helper function to enable use of question mark operator.
|
||||
fn twice_plus_one(value: Option<usize>) -> Option<usize> {
|
||||
value?.checked_mul(2)?.checked_add(1)
|
||||
}
|
||||
|
||||
(lower, twice_plus_one(upper))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -364,11 +364,11 @@ impl<K, V> StreamMap<K, V> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::collections::HashMap;
|
||||
/// use tokio_stream::{StreamMap, pending};
|
||||
///
|
||||
/// let mut a = HashMap::new();
|
||||
/// let mut a = StreamMap::new();
|
||||
/// assert!(a.is_empty());
|
||||
/// a.insert(1, "a");
|
||||
/// a.insert(1, pending::<i32>());
|
||||
/// assert!(!a.is_empty());
|
||||
/// ```
|
||||
pub fn is_empty(&self) -> bool {
|
||||
|
||||
@@ -1,13 +1,4 @@
|
||||
//! Wrappers for Tokio types that implement `Stream`.
|
||||
//!
|
||||
#![cfg_attr(
|
||||
unix,
|
||||
doc = "You are viewing documentation built under unix. To view windows-specific wrappers, change to the `x86_64-pc-windows-msvc` platform."
|
||||
)]
|
||||
#![cfg_attr(
|
||||
windows,
|
||||
doc = "You are viewing documentation built under windows. To view unix-specific wrappers, change to the `x86_64-unknown-linux-gnu` platform."
|
||||
)]
|
||||
|
||||
/// Error types for the wrappers.
|
||||
pub mod errors {
|
||||
@@ -36,9 +27,9 @@ cfg_signal! {
|
||||
#[cfg(unix)]
|
||||
pub use signal_unix::SignalStream;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, docsrs))]
|
||||
mod signal_windows;
|
||||
#[cfg(windows)]
|
||||
#[cfg(any(windows, docsrs))]
|
||||
pub use signal_windows::{CtrlCStream, CtrlBreakStream};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use tokio::sync::watch::error::RecvError;
|
||||
/// A wrapper around [`tokio::sync::watch::Receiver`] that implements [`Stream`].
|
||||
///
|
||||
/// This stream will always start by yielding the current value when the WatchStream is polled,
|
||||
/// regardles of whether it was the initial value or sent afterwards.
|
||||
/// regardless of whether it was the initial value or sent afterwards.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -72,10 +72,10 @@ impl<T: Clone + 'static + Send + Sync> Stream for WatchStream<T> {
|
||||
type Item = T;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let (result, rx) = ready!(self.inner.poll(cx));
|
||||
let (result, mut rx) = ready!(self.inner.poll(cx));
|
||||
match result {
|
||||
Ok(_) => {
|
||||
let received = (*rx.borrow()).clone();
|
||||
let received = (*rx.borrow_and_update()).clone();
|
||||
self.inner.set(make_future(rx));
|
||||
Poll::Ready(Some(received))
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::diverging_sub_expression)]
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
||||
@@ -89,12 +89,12 @@ fn size_overflow() {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::max_value(), Some(usize::max_value()))
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
let m1 = Monster;
|
||||
let m2 = Monster;
|
||||
let m = m1.chain(m2);
|
||||
assert_eq!(m.size_hint(), (usize::max_value(), None));
|
||||
assert_eq!(m.size_hint(), (usize::MAX, None));
|
||||
}
|
||||
|
||||
@@ -72,12 +72,12 @@ fn size_overflow() {
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
(usize::max_value(), Some(usize::max_value()))
|
||||
(usize::MAX, Some(usize::MAX))
|
||||
}
|
||||
}
|
||||
|
||||
let m1 = Monster;
|
||||
let m2 = Monster;
|
||||
let m = m1.merge(m2);
|
||||
assert_eq!(m.size_hint(), (usize::max_value(), None));
|
||||
assert_eq!(m.size_hint(), (usize::MAX, None));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use tokio::sync::watch;
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tokio_stream::StreamExt;
|
||||
|
||||
#[tokio::test]
|
||||
async fn message_not_twice() {
|
||||
let (tx, rx) = watch::channel("hello");
|
||||
|
||||
let mut counter = 0;
|
||||
let mut stream = WatchStream::new(rx).map(move |payload| {
|
||||
println!("{}", payload);
|
||||
if payload == "goodbye" {
|
||||
counter += 1;
|
||||
}
|
||||
if counter >= 2 {
|
||||
panic!("too many goodbyes");
|
||||
}
|
||||
});
|
||||
|
||||
let task = tokio::spawn(async move { while stream.next().await.is_some() {} });
|
||||
|
||||
// Send goodbye just once
|
||||
tx.send("goodbye").unwrap();
|
||||
|
||||
drop(tx);
|
||||
task.await.unwrap();
|
||||
}
|
||||
@@ -1,3 +1,9 @@
|
||||
# 0.4.2 (May 14, 2021)
|
||||
|
||||
- test: add `assert_elapsed!` macro ([#3728])
|
||||
|
||||
[#3728]: https://github.com/tokio-rs/tokio/pull/3728
|
||||
|
||||
# 0.4.1 (March 10, 2021)
|
||||
|
||||
- Fix `io::Mock` to be `Send` and `Sync` ([#3594])
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-test"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-test-0.4.x" git tag.
|
||||
version = "0.4.1"
|
||||
version = "0.4.2"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-test/0.4.1/tokio_test"
|
||||
documentation = "https://docs.rs/tokio-test/0.4.2/tokio_test"
|
||||
description = """
|
||||
Testing utilities for Tokio- and futures-based code
|
||||
"""
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
))]
|
||||
|
||||
//! Tokio and Futures based testing utilites
|
||||
//! Tokio and Futures based testing utilities
|
||||
|
||||
pub mod io;
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ impl ThreadWaker {
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears any previously received wakes, avoiding potential spurrious
|
||||
/// Clears any previously received wakes, avoiding potential spurious
|
||||
/// wake notifications. This should only be called immediately before running the
|
||||
/// task.
|
||||
fn clear(&self) {
|
||||
|
||||
@@ -1,3 +1,13 @@
|
||||
# 0.6.7 (May 14, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- udp: make `UdpFramed` take `Borrow<UdpSocket>` ([#3451])
|
||||
- compat: implement `AsRawFd`/`AsRawHandle` for `Compat<T>` ([#3765])
|
||||
|
||||
[#3451]: https://github.com/tokio-rs/tokio/pull/3451
|
||||
[#3765]: https://github.com/tokio-rs/tokio/pull/3765
|
||||
|
||||
# 0.6.6 (April 12, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
@@ -6,13 +6,13 @@ name = "tokio-util"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "tokio-util-0.6.x" git tag.
|
||||
version = "0.6.6"
|
||||
version = "0.6.7"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-util/0.6.6/tokio_util"
|
||||
documentation = "https://docs.rs/tokio-util/0.6.7/tokio_util"
|
||||
description = """
|
||||
Additional utilities for working with Tokio.
|
||||
"""
|
||||
|
||||
@@ -234,7 +234,7 @@ impl Default for AnyDelimiterCodec {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error occured while encoding or decoding a chunk.
|
||||
/// An error occurred while encoding or decoding a chunk.
|
||||
#[derive(Debug)]
|
||||
pub enum AnyDelimiterCodecError {
|
||||
/// The maximum chunk length was exceeded.
|
||||
|
||||
@@ -28,7 +28,7 @@ use std::io;
|
||||
/// It is up to the Decoder to keep track of a restart after an EOF,
|
||||
/// and to decide how to handle such an event by, for example,
|
||||
/// allowing frames to cross EOF boundaries, re-emitting opening frames, or
|
||||
/// reseting the entire internal state.
|
||||
/// resetting the entire internal state.
|
||||
///
|
||||
/// [`Framed`]: crate::codec::Framed
|
||||
/// [`FramedRead`]: crate::codec::FramedRead
|
||||
|
||||
@@ -124,7 +124,7 @@ where
|
||||
// to a combination of the `is_readable` and `eof` flags. States persist across
|
||||
// loop entries and most state transitions occur with a return.
|
||||
//
|
||||
// The intitial state is `reading`.
|
||||
// The initial state is `reading`.
|
||||
//
|
||||
// | state | eof | is_readable |
|
||||
// |---------|-------|-------------|
|
||||
@@ -155,10 +155,10 @@ where
|
||||
// Both signal that there is no such data by returning `None`.
|
||||
//
|
||||
// If `decode` couldn't read a frame and the upstream source has returned eof,
|
||||
// `decode_eof` will attemp to decode the remaining bytes as closing frames.
|
||||
// `decode_eof` will attempt to decode the remaining bytes as closing frames.
|
||||
//
|
||||
// If the underlying AsyncRead is resumable, we may continue after an EOF,
|
||||
// but must finish emmiting all of it's associated `decode_eof` frames.
|
||||
// but must finish emitting all of it's associated `decode_eof` frames.
|
||||
// Furthermore, we don't want to emit any `decode_eof` frames on retried
|
||||
// reads after an EOF unless we've actually read more data.
|
||||
if state.is_readable {
|
||||
|
||||
@@ -486,7 +486,7 @@ impl LengthDelimitedCodec {
|
||||
// Skip the required bytes
|
||||
src.advance(self.builder.length_field_offset);
|
||||
|
||||
// match endianess
|
||||
// match endianness
|
||||
let n = if self.builder.length_field_is_big_endian {
|
||||
src.get_uint(field_len)
|
||||
} else {
|
||||
|
||||
@@ -203,12 +203,12 @@ impl Default for LinesCodec {
|
||||
}
|
||||
}
|
||||
|
||||
/// An error occured while encoding or decoding a line.
|
||||
/// An error occurred while encoding or decoding a line.
|
||||
#[derive(Debug)]
|
||||
pub enum LinesCodecError {
|
||||
/// The maximum line length was exceeded.
|
||||
MaxLineLengthExceeded,
|
||||
/// An IO error occured.
|
||||
/// An IO error occurred.
|
||||
Io(io::Error),
|
||||
}
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ pub enum Either<L, R> {
|
||||
}
|
||||
|
||||
/// A small helper macro which reduces amount of boilerplate in the actual trait method implementation.
|
||||
/// It takes an invokation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
|
||||
/// It takes an invocation of method as an argument (e.g. `self.poll(cx)`), and redirects it to either
|
||||
/// enum variant held in `self`.
|
||||
macro_rules! delegate_call {
|
||||
($self:ident.$method:ident($($args:ident),+)) => {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! An asynchronously awaitable `CancellationToken`.
|
||||
//! The token allows to signal a cancellation request to one or more tasks.
|
||||
pub(crate) mod guard;
|
||||
|
||||
use crate::loom::sync::atomic::AtomicUsize;
|
||||
use crate::loom::sync::Mutex;
|
||||
@@ -11,6 +12,8 @@ use core::ptr::NonNull;
|
||||
use core::sync::atomic::Ordering;
|
||||
use core::task::{Context, Poll, Waker};
|
||||
|
||||
use guard::DropGuard;
|
||||
|
||||
/// A token which can be used to signal a cancellation request to one or more
|
||||
/// tasks.
|
||||
///
|
||||
@@ -275,6 +278,14 @@ impl CancellationToken {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `DropGuard` for this token.
|
||||
///
|
||||
/// Returned guard will cancel this token (and all its children) on drop
|
||||
/// unless disarmed.
|
||||
pub fn drop_guard(self) -> DropGuard {
|
||||
DropGuard { inner: Some(self) }
|
||||
}
|
||||
|
||||
unsafe fn register(
|
||||
&self,
|
||||
wait_node: &mut ListNode<WaitQueueEntry>,
|
||||
@@ -764,8 +775,8 @@ impl CancellationTokenState {
|
||||
return Poll::Ready(());
|
||||
}
|
||||
|
||||
// So far the token is not cancelled. However it could be cancelld before
|
||||
// we get the chance to store the `Waker`. Therfore we need to check
|
||||
// So far the token is not cancelled. However it could be cancelled before
|
||||
// we get the chance to store the `Waker`. Therefore we need to check
|
||||
// for cancellation again inside the mutex.
|
||||
let mut guard = self.synchronized.lock().unwrap();
|
||||
if guard.is_cancelled {
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
use crate::sync::CancellationToken;
|
||||
|
||||
/// A wrapper for cancellation token which automatically cancels
|
||||
/// it on drop. It is created using `drop_guard` method on the `CancellationToken`.
|
||||
#[derive(Debug)]
|
||||
pub struct DropGuard {
|
||||
pub(super) inner: Option<CancellationToken>,
|
||||
}
|
||||
|
||||
impl DropGuard {
|
||||
/// Returns stored cancellation token and removes this drop guard instance
|
||||
/// (i.e. it will no longer cancel token). Other guards for this token
|
||||
/// are not affected.
|
||||
pub fn disarm(mut self) -> CancellationToken {
|
||||
self.inner
|
||||
.take()
|
||||
.expect("`inner` can be only None in a destructor")
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DropGuard {
|
||||
fn drop(&mut self) {
|
||||
if let Some(inner) = &self.inner {
|
||||
inner.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,7 +222,7 @@ impl<T> LinkedList<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns whether the linked list doesn not contain any node
|
||||
/// Returns whether the linked list does not contain any node
|
||||
pub fn is_empty(&self) -> bool {
|
||||
if self.head.is_some() {
|
||||
return false;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Synchronization primitives
|
||||
|
||||
mod cancellation_token;
|
||||
pub use cancellation_token::{CancellationToken, WaitForCancellationFuture};
|
||||
pub use cancellation_token::{guard::DropGuard, CancellationToken, WaitForCancellationFuture};
|
||||
|
||||
mod intrusive_double_linked_list;
|
||||
|
||||
|
||||
@@ -12,9 +12,9 @@ use std::time::Duration;
|
||||
|
||||
mod wheel;
|
||||
|
||||
#[doc(inline)]
|
||||
pub mod delay_queue;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use delay_queue::DelayQueue;
|
||||
|
||||
// ===== Internal utils =====
|
||||
|
||||
@@ -35,7 +35,6 @@ use std::{io, mem::MaybeUninit};
|
||||
/// [`Sink`]: futures_sink::Sink
|
||||
/// [`split`]: https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.split
|
||||
#[must_use = "sinks do nothing unless polled"]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "codec", feature = "udp"))))]
|
||||
#[derive(Debug)]
|
||||
pub struct UdpFramed<C, T = UdpSocket> {
|
||||
socket: T,
|
||||
@@ -144,7 +143,7 @@ where
|
||||
..
|
||||
} = *self;
|
||||
|
||||
let n = ready!(socket.borrow().poll_send_to(cx, &wr, *out_addr))?;
|
||||
let n = ready!(socket.borrow().poll_send_to(cx, wr, *out_addr))?;
|
||||
|
||||
let wrote_all = n == self.wr.len();
|
||||
self.wr.clear();
|
||||
|
||||
@@ -6,9 +6,9 @@ use tokio_util::sync::PollSemaphore;
|
||||
|
||||
type SemRet = Option<OwnedSemaphorePermit>;
|
||||
|
||||
fn semaphore_poll<'a>(
|
||||
sem: &'a mut PollSemaphore,
|
||||
) -> tokio_test::task::Spawn<impl Future<Output = SemRet> + 'a> {
|
||||
fn semaphore_poll(
|
||||
sem: &mut PollSemaphore,
|
||||
) -> tokio_test::task::Spawn<impl Future<Output = SemRet> + '_> {
|
||||
let fut = futures::future::poll_fn(move |cx| sem.poll_acquire(cx));
|
||||
tokio_test::task::spawn(fut)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ use futures_test::task::new_count_waker;
|
||||
fn cancel_token() {
|
||||
let (waker, wake_counter) = new_count_waker();
|
||||
let token = CancellationToken::new();
|
||||
assert_eq!(false, token.is_cancelled());
|
||||
assert!(!token.is_cancelled());
|
||||
|
||||
let wait_fut = token.cancelled();
|
||||
pin!(wait_fut);
|
||||
@@ -27,7 +27,7 @@ fn cancel_token() {
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 1);
|
||||
assert_eq!(true, token.is_cancelled());
|
||||
assert!(token.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
@@ -64,8 +64,8 @@ fn cancel_child_token_through_parent() {
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 2);
|
||||
assert_eq!(true, token.is_cancelled());
|
||||
assert_eq!(true, child_token.is_cancelled());
|
||||
assert!(token.is_cancelled());
|
||||
assert!(child_token.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
@@ -101,8 +101,8 @@ fn cancel_child_token_without_parent() {
|
||||
|
||||
child_token_1.cancel();
|
||||
assert_eq!(wake_counter, 1);
|
||||
assert_eq!(false, token.is_cancelled());
|
||||
assert_eq!(true, child_token_1.is_cancelled());
|
||||
assert!(!token.is_cancelled());
|
||||
assert!(child_token_1.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
@@ -128,8 +128,8 @@ fn cancel_child_token_without_parent() {
|
||||
|
||||
token.cancel();
|
||||
assert_eq!(wake_counter, 3);
|
||||
assert_eq!(true, token.is_cancelled());
|
||||
assert_eq!(true, child_token_2.is_cancelled());
|
||||
assert!(token.is_cancelled());
|
||||
assert!(child_token_2.is_cancelled());
|
||||
|
||||
assert_eq!(
|
||||
Poll::Ready(()),
|
||||
|
||||
@@ -1,3 +1,223 @@
|
||||
# 1.10.1 (August 24, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: fix leak in UnownedTask ([#4063])
|
||||
|
||||
[#4063]: https://github.com/tokio-rs/tokio/pull/4063
|
||||
|
||||
# 1.10.0 (August 12, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `(read|write)_f(32|64)[_le]` methods ([#4022])
|
||||
- io: add `fill_buf` and `consume` to `AsyncBufReadExt` ([#3991])
|
||||
- process: add `Child::raw_handle()` on windows ([#3998])
|
||||
|
||||
### Fixed
|
||||
|
||||
- doc: fix non-doc builds with `--cfg docsrs` ([#4020])
|
||||
- io: flush eagerly in `io::copy` ([#4001])
|
||||
- runtime: a debug assert was sometimes triggered during shutdown ([#4005])
|
||||
- sync: use `spin_loop_hint` instead of `yield_now` in mpsc ([#4037])
|
||||
- tokio: the test-util feature depends on rt, sync, and time ([#4036])
|
||||
|
||||
### Changes
|
||||
|
||||
- runtime: reorganize parts of the runtime ([#3979], [#4005])
|
||||
- signal: make windows docs for signal module show up on unix builds ([#3770])
|
||||
- task: quickly send task to heap on debug mode ([#4009])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: document cancellation safety of `AsyncBufReadExt` ([#3997])
|
||||
- sync: document when `watch::send` fails ([#4021])
|
||||
|
||||
[#3770]: https://github.com/tokio-rs/tokio/pull/3770
|
||||
[#3979]: https://github.com/tokio-rs/tokio/pull/3979
|
||||
[#3991]: https://github.com/tokio-rs/tokio/pull/3991
|
||||
[#3997]: https://github.com/tokio-rs/tokio/pull/3997
|
||||
[#3998]: https://github.com/tokio-rs/tokio/pull/3998
|
||||
[#4001]: https://github.com/tokio-rs/tokio/pull/4001
|
||||
[#4005]: https://github.com/tokio-rs/tokio/pull/4005
|
||||
[#4009]: https://github.com/tokio-rs/tokio/pull/4009
|
||||
[#4020]: https://github.com/tokio-rs/tokio/pull/4020
|
||||
[#4021]: https://github.com/tokio-rs/tokio/pull/4021
|
||||
[#4022]: https://github.com/tokio-rs/tokio/pull/4022
|
||||
[#4036]: https://github.com/tokio-rs/tokio/pull/4036
|
||||
[#4037]: https://github.com/tokio-rs/tokio/pull/4037
|
||||
|
||||
# 1.9.0 (July 22, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- net: allow customized I/O operations for `TcpStream` ([#3888])
|
||||
- sync: add getter for the mutex from a guard ([#3928])
|
||||
- task: expose nameable future for `TaskLocal::scope` ([#3273])
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix leak if output of future panics on drop ([#3967])
|
||||
- Fix leak in `LocalSet` ([#3978])
|
||||
|
||||
### Changes
|
||||
|
||||
- runtime: reorganize parts of the runtime ([#3909], [#3939], [#3950], [#3955], [#3980])
|
||||
- sync: clean up `OnceCell` ([#3945])
|
||||
- task: remove mutex in `JoinError` ([#3959])
|
||||
|
||||
[#3273]: https://github.com/tokio-rs/tokio/pull/3273
|
||||
[#3888]: https://github.com/tokio-rs/tokio/pull/3888
|
||||
[#3909]: https://github.com/tokio-rs/tokio/pull/3909
|
||||
[#3928]: https://github.com/tokio-rs/tokio/pull/3928
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
[#3939]: https://github.com/tokio-rs/tokio/pull/3939
|
||||
[#3945]: https://github.com/tokio-rs/tokio/pull/3945
|
||||
[#3950]: https://github.com/tokio-rs/tokio/pull/3950
|
||||
[#3955]: https://github.com/tokio-rs/tokio/pull/3955
|
||||
[#3959]: https://github.com/tokio-rs/tokio/pull/3959
|
||||
[#3967]: https://github.com/tokio-rs/tokio/pull/3967
|
||||
[#3978]: https://github.com/tokio-rs/tokio/pull/3978
|
||||
[#3980]: https://github.com/tokio-rs/tokio/pull/3980
|
||||
|
||||
# 1.8.3 (July 26, 2021)
|
||||
|
||||
This release backports two fixes from 1.9.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix leak if output of future panics on drop ([#3967])
|
||||
- Fix leak in `LocalSet` ([#3978])
|
||||
|
||||
[#3967]: https://github.com/tokio-rs/tokio/pull/3967
|
||||
[#3978]: https://github.com/tokio-rs/tokio/pull/3978
|
||||
|
||||
# 1.8.2 (July 19, 2021)
|
||||
|
||||
Fixes a missed edge case from 1.8.1.
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: drop canceled future on next poll (#3965)
|
||||
|
||||
# 1.8.1 (July 6, 2021)
|
||||
|
||||
Forward ports 1.5.1 fixes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
|
||||
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
|
||||
# 1.8.0 (July 2, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- io: add `get_{ref,mut}` methods to `AsyncFdReadyGuard` and `AsyncFdReadyMutGuard` ([#3807])
|
||||
- io: efficient implementation of vectored writes for `BufWriter` ([#3163])
|
||||
- net: add ready/try methods to `NamedPipe{Client,Server}` ([#3866], [#3899])
|
||||
- sync: add `watch::Receiver::borrow_and_update` ([#3813])
|
||||
- sync: implement `From<T>` for `OnceCell<T>` ([#3877])
|
||||
- time: allow users to specify Interval behaviour when delayed ([#3721])
|
||||
|
||||
### Added (unstable)
|
||||
|
||||
- rt: add `tokio::task::Builder` ([#3881])
|
||||
|
||||
### Fixed
|
||||
|
||||
- net: handle HUP event with `UnixStream` ([#3898])
|
||||
|
||||
### Documented
|
||||
|
||||
- doc: document cancellation safety ([#3900])
|
||||
- time: add wait alias to sleep ([#3897])
|
||||
- time: document auto-advancing behaviour of runtime ([#3763])
|
||||
|
||||
[#3163]: https://github.com/tokio-rs/tokio/pull/3163
|
||||
[#3721]: https://github.com/tokio-rs/tokio/pull/3721
|
||||
[#3763]: https://github.com/tokio-rs/tokio/pull/3763
|
||||
[#3807]: https://github.com/tokio-rs/tokio/pull/3807
|
||||
[#3813]: https://github.com/tokio-rs/tokio/pull/3813
|
||||
[#3866]: https://github.com/tokio-rs/tokio/pull/3866
|
||||
[#3877]: https://github.com/tokio-rs/tokio/pull/3877
|
||||
[#3881]: https://github.com/tokio-rs/tokio/pull/3881
|
||||
[#3897]: https://github.com/tokio-rs/tokio/pull/3897
|
||||
[#3898]: https://github.com/tokio-rs/tokio/pull/3898
|
||||
[#3899]: https://github.com/tokio-rs/tokio/pull/3899
|
||||
[#3900]: https://github.com/tokio-rs/tokio/pull/3900
|
||||
|
||||
# 1.7.2 (July 6, 2021)
|
||||
|
||||
Forward ports 1.5.1 fixes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
|
||||
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
|
||||
# 1.7.1 (June 18, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: fix early task shutdown during runtime shutdown ([#3870])
|
||||
|
||||
[#3870]: https://github.com/tokio-rs/tokio/pull/3870
|
||||
|
||||
# 1.7.0 (June 15, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
- net: add named pipes on windows ([#3760])
|
||||
- net: add `TcpSocket` from `std::net::TcpStream` conversion ([#3838])
|
||||
- sync: add `receiver_count` to `watch::Sender` ([#3729])
|
||||
- sync: export `sync::notify::Notified` future publicly ([#3840])
|
||||
- tracing: instrument task wakers ([#3836])
|
||||
|
||||
### Fixed
|
||||
|
||||
- macros: suppress `clippy::default_numeric_fallback` lint in generated code ([#3831])
|
||||
- runtime: immediately drop new tasks when runtime is shut down ([#3752])
|
||||
- sync: deprecate unused `mpsc::RecvError` type ([#3833])
|
||||
|
||||
### Documented
|
||||
|
||||
- io: clarify EOF condition for `AsyncReadExt::read_buf` ([#3850])
|
||||
- io: clarify limits on return values of `AsyncWrite::poll_write` ([#3820])
|
||||
- sync: add examples to Semaphore ([#3808])
|
||||
|
||||
[#3729]: https://github.com/tokio-rs/tokio/pull/3729
|
||||
[#3752]: https://github.com/tokio-rs/tokio/pull/3752
|
||||
[#3760]: https://github.com/tokio-rs/tokio/pull/3760
|
||||
[#3808]: https://github.com/tokio-rs/tokio/pull/3808
|
||||
[#3820]: https://github.com/tokio-rs/tokio/pull/3820
|
||||
[#3831]: https://github.com/tokio-rs/tokio/pull/3831
|
||||
[#3833]: https://github.com/tokio-rs/tokio/pull/3833
|
||||
[#3836]: https://github.com/tokio-rs/tokio/pull/3836
|
||||
[#3838]: https://github.com/tokio-rs/tokio/pull/3838
|
||||
[#3840]: https://github.com/tokio-rs/tokio/pull/3840
|
||||
[#3850]: https://github.com/tokio-rs/tokio/pull/3850
|
||||
|
||||
# 1.6.3 (July 6, 2021)
|
||||
|
||||
Forward ports 1.5.1 fixes.
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
|
||||
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
|
||||
# 1.6.2 (June 14, 2021)
|
||||
|
||||
### Fixes
|
||||
|
||||
- test: sub-ms `time:advance` regression introduced in 1.6 ([#3852])
|
||||
|
||||
[#3852]: https://github.com/tokio-rs/tokio/pull/3852
|
||||
|
||||
# 1.6.1 (May 28, 2021)
|
||||
|
||||
This release reverts [#3518] because it doesn't work on some kernels due to
|
||||
@@ -52,6 +272,14 @@ a kernel bug. ([#3803])
|
||||
[#3775]: https://github.com/tokio-rs/tokio/pull/3775
|
||||
[#3780]: https://github.com/tokio-rs/tokio/pull/3780
|
||||
|
||||
# 1.5.1 (July 6, 2021)
|
||||
|
||||
### Fixed
|
||||
|
||||
- runtime: remotely abort tasks on `JoinHandle::abort` ([#3934])
|
||||
|
||||
[#3934]: https://github.com/tokio-rs/tokio/pull/3934
|
||||
|
||||
# 1.5.0 (April 12, 2021)
|
||||
|
||||
### Added
|
||||
|
||||
+10
-4
@@ -7,12 +7,12 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v1.0.x" git tag.
|
||||
version = "1.6.1"
|
||||
version = "1.10.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/1.6.0/tokio/"
|
||||
documentation = "https://docs.rs/tokio/1.10.0/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -54,6 +54,7 @@ net = [
|
||||
"mio/tcp",
|
||||
"mio/udp",
|
||||
"mio/uds",
|
||||
"winapi/namedpipeapi",
|
||||
]
|
||||
process = [
|
||||
"bytes",
|
||||
@@ -81,7 +82,7 @@ signal = [
|
||||
"winapi/consoleapi",
|
||||
]
|
||||
sync = []
|
||||
test-util = []
|
||||
test-util = ["rt", "sync", "time"]
|
||||
time = []
|
||||
|
||||
[dependencies]
|
||||
@@ -108,21 +109,26 @@ signal-hook-registry = { version = "1.1.1", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dev-dependencies]
|
||||
libc = { version = "0.2.42" }
|
||||
nix = { version = "0.19.0" }
|
||||
nix = { version = "0.22.0" }
|
||||
|
||||
[target.'cfg(windows)'.dependencies.winapi]
|
||||
version = "0.3.8"
|
||||
default-features = false
|
||||
optional = true
|
||||
|
||||
[target.'cfg(windows)'.dev-dependencies.ntapi]
|
||||
version = "0.3.6"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio-test = { version = "0.4.0", path = "../tokio-test" }
|
||||
tokio-stream = { version = "0.1", path = "../tokio-stream" }
|
||||
futures = { version = "0.3.0", features = ["async-await"] }
|
||||
mockall = "0.10.2"
|
||||
proptest = "1"
|
||||
rand = "0.8.0"
|
||||
tempfile = "3.1.0"
|
||||
async-stream = "0.3"
|
||||
socket2 = "0.4"
|
||||
|
||||
[target.'cfg(loom)'.dev-dependencies]
|
||||
loom = { version = "0.5", features = ["futures", "checkpoint"] }
|
||||
|
||||
+40
-6
@@ -50,7 +50,15 @@ an asynchronous application.
|
||||
|
||||
## Example
|
||||
|
||||
A basic TCP echo server with Tokio:
|
||||
A basic TCP echo server with Tokio.
|
||||
|
||||
Make sure you activated the full features of the tokio crate on Cargo.toml:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
tokio = { version = "1.10.1", features = ["full"] }
|
||||
```
|
||||
Then, on your main.rs:
|
||||
|
||||
```rust,no_run
|
||||
use tokio::net::TcpListener;
|
||||
@@ -58,7 +66,7 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
let listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
|
||||
loop {
|
||||
let (mut socket, _) = listener.accept().await?;
|
||||
@@ -132,7 +140,7 @@ several other libraries, including:
|
||||
|
||||
* [`tower`]: A library of modular and reusable components for building robust networking clients and servers.
|
||||
|
||||
* [`tracing`]: A framework for application-level tracing and async-aware diagnostics.
|
||||
* [`tracing`] (formerly `tokio-trace`): A framework for application-level tracing and async-aware diagnostics.
|
||||
|
||||
* [`rdbc`]: A Rust database connectivity library for MySQL, Postgres and SQLite.
|
||||
|
||||
@@ -155,9 +163,35 @@ several other libraries, including:
|
||||
|
||||
## Supported Rust Versions
|
||||
|
||||
Tokio is built against the latest stable release. The minimum supported version is 1.45.
|
||||
The current Tokio version is not guaranteed to build on Rust versions earlier than the
|
||||
minimum supported version.
|
||||
Tokio is built against the latest stable release. The minimum supported version
|
||||
is 1.45. The current Tokio version is not guaranteed to build on Rust versions
|
||||
earlier than the minimum supported version.
|
||||
|
||||
## Release schedule
|
||||
|
||||
Tokio doesn't follow a fixed release schedule, but we typically make one to two
|
||||
new minor releases each month. We make patch releases for bugfixes as necessary.
|
||||
|
||||
## Bug patching policy
|
||||
|
||||
For the purposes of making patch releases with bugfixes, we have designated
|
||||
certain minor releases as LTS (long term support) releases. Whenever a bug
|
||||
warrants a patch release with a fix for the bug, it will be backported and
|
||||
released as a new patch release for each LTS minor version. Our current LTS
|
||||
releases are:
|
||||
|
||||
* `1.8.x` - LTS release until February 2022.
|
||||
|
||||
Each LTS release will continue to receive backported fixes for at least half a
|
||||
year. If you wish to use a fixed minor release in your project, we recommend
|
||||
that you use an LTS release.
|
||||
|
||||
To use a fixed minor version, you can specify the version with a tilde. For
|
||||
example, to specify that you wish to use the newest `1.8.x` patch release, you
|
||||
can use the following dependency specification:
|
||||
```text
|
||||
tokio = { version = "~1.8", features = [...] }
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -228,7 +228,7 @@ It is only possible to implement `AsyncRead` and `AsyncWrite` for resource types
|
||||
themselves and not for `&Resource`. Implementing the traits for `&Resource`
|
||||
would permit concurrent operations to the resource. Because only a single waker
|
||||
is stored per direction, any concurrent usage would result in deadlocks. An
|
||||
alterate implementation would call for a `Vec<Waker>` but this would result in
|
||||
alternate implementation would call for a `Vec<Waker>` but this would result in
|
||||
memory leaks.
|
||||
|
||||
## Enabling reads and writes for `&TcpStream`
|
||||
@@ -268,9 +268,9 @@ select! {
|
||||
}
|
||||
```
|
||||
|
||||
It is also possible to sotre a `TcpStream` in an `Arc`.
|
||||
It is also possible to store a `TcpStream` in an `Arc`.
|
||||
|
||||
```rust
|
||||
let arc_stream = Arc::new(my_tcp_stream);
|
||||
let n = arc_stream.by_ref().read(buf).await?;
|
||||
```
|
||||
```
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
//! Types which are documented locally in the Tokio crate, but does not actually
|
||||
//! live here.
|
||||
//!
|
||||
//! **Note** this module is only visible on docs.rs, you cannot use it directly
|
||||
//! in your own code.
|
||||
|
||||
/// The name of a type which is not defined here.
|
||||
///
|
||||
/// This is typically used as an alias for another type, like so:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// /// See [some::other::location](https://example.com).
|
||||
/// type DEFINED_ELSEWHERE = crate::doc::NotDefinedHere;
|
||||
/// ```
|
||||
///
|
||||
/// This type is uninhabitable like the [`never` type] to ensure that no one
|
||||
/// will ever accidentally use it.
|
||||
///
|
||||
/// [`never` type]: https://doc.rust-lang.org/std/primitive.never.html
|
||||
pub enum NotDefinedHere {}
|
||||
|
||||
pub mod os;
|
||||
pub mod winapi;
|
||||
@@ -0,0 +1,26 @@
|
||||
//! See [std::os](https://doc.rust-lang.org/std/os/index.html).
|
||||
|
||||
/// Platform-specific extensions to `std` for Windows.
|
||||
///
|
||||
/// See [std::os::windows](https://doc.rust-lang.org/std/os/windows/index.html).
|
||||
pub mod windows {
|
||||
/// Windows-specific extensions to general I/O primitives.
|
||||
///
|
||||
/// See [std::os::windows::io](https://doc.rust-lang.org/std/os/windows/io/index.html).
|
||||
pub mod io {
|
||||
/// See [std::os::windows::io::RawHandle](https://doc.rust-lang.org/std/os/windows/io/type.RawHandle.html)
|
||||
pub type RawHandle = crate::doc::NotDefinedHere;
|
||||
|
||||
/// See [std::os::windows::io::AsRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html)
|
||||
pub trait AsRawHandle {
|
||||
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.AsRawHandle.html#tymethod.as_raw_handle)
|
||||
fn as_raw_handle(&self) -> RawHandle;
|
||||
}
|
||||
|
||||
/// See [std::os::windows::io::FromRawHandle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html)
|
||||
pub trait FromRawHandle {
|
||||
/// See [std::os::windows::io::FromRawHandle::from_raw_handle](https://doc.rust-lang.org/std/os/windows/io/trait.FromRawHandle.html#tymethod.from_raw_handle)
|
||||
unsafe fn from_raw_handle(handle: RawHandle) -> Self;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
//! 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;
|
||||
}
|
||||
}
|
||||
+32
-16
@@ -3,7 +3,7 @@
|
||||
//! [`File`]: File
|
||||
|
||||
use self::State::*;
|
||||
use crate::fs::{asyncify, sys};
|
||||
use crate::fs::asyncify;
|
||||
use crate::io::blocking::Buf;
|
||||
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
use crate::sync::Mutex;
|
||||
@@ -19,6 +19,19 @@ use std::task::Context;
|
||||
use std::task::Poll;
|
||||
use std::task::Poll::*;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::mocks::spawn_blocking;
|
||||
#[cfg(test)]
|
||||
use super::mocks::JoinHandle;
|
||||
#[cfg(test)]
|
||||
use super::mocks::MockFile as StdFile;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::spawn_blocking;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::JoinHandle;
|
||||
#[cfg(not(test))]
|
||||
use std::fs::File as StdFile;
|
||||
|
||||
/// A reference to an open file on the filesystem.
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::File`][std] for usage from the
|
||||
@@ -78,7 +91,7 @@ use std::task::Poll::*;
|
||||
/// # }
|
||||
/// ```
|
||||
pub struct File {
|
||||
std: Arc<sys::File>,
|
||||
std: Arc<StdFile>,
|
||||
inner: Mutex<Inner>,
|
||||
}
|
||||
|
||||
@@ -96,7 +109,7 @@ struct Inner {
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle(Option<Buf>),
|
||||
Busy(sys::Blocking<(Operation, Buf)>),
|
||||
Busy(JoinHandle<(Operation, Buf)>),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -142,7 +155,7 @@ impl File {
|
||||
/// [`AsyncReadExt`]: trait@crate::io::AsyncReadExt
|
||||
pub async fn open(path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std = asyncify(|| sys::File::open(path)).await?;
|
||||
let std = asyncify(|| StdFile::open(path)).await?;
|
||||
|
||||
Ok(File::from_std(std))
|
||||
}
|
||||
@@ -182,7 +195,7 @@ impl File {
|
||||
/// [`AsyncWriteExt`]: trait@crate::io::AsyncWriteExt
|
||||
pub async fn create(path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std_file = asyncify(move || sys::File::create(path)).await?;
|
||||
let std_file = asyncify(move || StdFile::create(path)).await?;
|
||||
Ok(File::from_std(std_file))
|
||||
}
|
||||
|
||||
@@ -199,7 +212,7 @@ impl File {
|
||||
/// let std_file = std::fs::File::open("foo.txt").unwrap();
|
||||
/// let file = tokio::fs::File::from_std(std_file);
|
||||
/// ```
|
||||
pub fn from_std(std: sys::File) -> File {
|
||||
pub fn from_std(std: StdFile) -> File {
|
||||
File {
|
||||
std: Arc::new(std),
|
||||
inner: Mutex::new(Inner {
|
||||
@@ -323,7 +336,7 @@ impl File {
|
||||
|
||||
let std = self.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = if let Some(seek) = seek {
|
||||
(&*std).seek(seek).and_then(|_| std.set_len(size))
|
||||
} else {
|
||||
@@ -409,7 +422,7 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub async fn into_std(mut self) -> sys::File {
|
||||
pub async fn into_std(mut self) -> StdFile {
|
||||
self.inner.get_mut().complete_inflight().await;
|
||||
Arc::try_unwrap(self.std).expect("Arc::try_unwrap failed")
|
||||
}
|
||||
@@ -434,7 +447,7 @@ impl File {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn try_into_std(mut self) -> Result<sys::File, Self> {
|
||||
pub fn try_into_std(mut self) -> Result<StdFile, Self> {
|
||||
match Arc::try_unwrap(self.std) {
|
||||
Ok(file) => Ok(file),
|
||||
Err(std_file_arc) => {
|
||||
@@ -502,7 +515,7 @@ impl AsyncRead for File {
|
||||
buf.ensure_capacity_for(dst);
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = buf.read_from(&mut &*std);
|
||||
(Operation::Read(res), buf)
|
||||
}));
|
||||
@@ -569,7 +582,7 @@ impl AsyncSeek for File {
|
||||
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = (&*std).seek(pos);
|
||||
(Operation::Seek(res), buf)
|
||||
}));
|
||||
@@ -636,7 +649,7 @@ impl AsyncWrite for File {
|
||||
let n = buf.copy_from(src);
|
||||
let std = me.std.clone();
|
||||
|
||||
inner.state = Busy(sys::run(move || {
|
||||
inner.state = Busy(spawn_blocking(move || {
|
||||
let res = if let Some(seek) = seek {
|
||||
(&*std).seek(seek).and_then(|_| buf.write_to(&mut &*std))
|
||||
} else {
|
||||
@@ -685,8 +698,8 @@ impl AsyncWrite for File {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sys::File> for File {
|
||||
fn from(std: sys::File) -> Self {
|
||||
impl From<StdFile> for File {
|
||||
fn from(std: StdFile) -> Self {
|
||||
Self::from_std(std)
|
||||
}
|
||||
}
|
||||
@@ -709,7 +722,7 @@ impl std::os::unix::io::AsRawFd for File {
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
unsafe fn from_raw_fd(fd: std::os::unix::io::RawFd) -> Self {
|
||||
sys::File::from_raw_fd(fd).into()
|
||||
StdFile::from_raw_fd(fd).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,7 +736,7 @@ impl std::os::windows::io::AsRawHandle for File {
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(handle: std::os::windows::io::RawHandle) -> Self {
|
||||
sys::File::from_raw_handle(handle).into()
|
||||
StdFile::from_raw_handle(handle).into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -756,3 +769,6 @@ impl Inner {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@@ -1,80 +1,21 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![cfg(feature = "full")]
|
||||
|
||||
macro_rules! ready {
|
||||
($e:expr $(,)?) => {
|
||||
match $e {
|
||||
std::task::Poll::Ready(t) => t,
|
||||
std::task::Poll::Pending => return std::task::Poll::Pending,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cfg_fs {
|
||||
($($item:item)*) => { $($item)* }
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cfg_io_std {
|
||||
($($item:item)*) => { $($item)* }
|
||||
}
|
||||
|
||||
use futures::future;
|
||||
|
||||
// Load source
|
||||
#[allow(warnings)]
|
||||
#[path = "../src/fs/file.rs"]
|
||||
mod file;
|
||||
use file::File;
|
||||
|
||||
#[allow(warnings)]
|
||||
#[path = "../src/io/blocking.rs"]
|
||||
mod blocking;
|
||||
|
||||
// Load mocked types
|
||||
mod support {
|
||||
pub(crate) mod mock_file;
|
||||
pub(crate) mod mock_pool;
|
||||
}
|
||||
pub(crate) use support::mock_pool as pool;
|
||||
|
||||
// Place them where the source expects them
|
||||
pub(crate) mod io {
|
||||
pub(crate) use tokio::io::*;
|
||||
|
||||
pub(crate) use crate::blocking;
|
||||
|
||||
pub(crate) mod sys {
|
||||
pub(crate) use crate::support::mock_pool::{run, Blocking};
|
||||
}
|
||||
}
|
||||
pub(crate) mod fs {
|
||||
pub(crate) mod sys {
|
||||
pub(crate) use crate::support::mock_file::File;
|
||||
pub(crate) use crate::support::mock_pool::{run, Blocking};
|
||||
}
|
||||
|
||||
pub(crate) use crate::support::mock_pool::asyncify;
|
||||
}
|
||||
pub(crate) mod sync {
|
||||
pub(crate) use tokio::sync::Mutex;
|
||||
}
|
||||
use fs::sys;
|
||||
|
||||
use tokio::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt};
|
||||
use tokio_test::{assert_pending, assert_ready, assert_ready_err, assert_ready_ok, task};
|
||||
|
||||
use std::io::SeekFrom;
|
||||
use super::*;
|
||||
use crate::{
|
||||
fs::mocks::*,
|
||||
io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt},
|
||||
};
|
||||
use mockall::{predicate::eq, Sequence};
|
||||
use tokio_test::{assert_pending, assert_ready_err, assert_ready_ok, task};
|
||||
|
||||
const HELLO: &[u8] = b"hello world...";
|
||||
const FOO: &[u8] = b"foo bar baz...";
|
||||
|
||||
#[test]
|
||||
fn open_read() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO);
|
||||
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read().once().returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
@@ -83,12 +24,10 @@ fn open_read() {
|
||||
assert_eq!(0, pool::len());
|
||||
assert_pending!(t.poll());
|
||||
|
||||
assert_eq!(1, mock.remaining());
|
||||
assert_eq!(1, pool::len());
|
||||
|
||||
pool::run_one();
|
||||
|
||||
assert_eq!(0, mock.remaining());
|
||||
assert!(t.is_woken());
|
||||
|
||||
let n = assert_ready_ok!(t.poll());
|
||||
@@ -98,9 +37,11 @@ fn open_read() {
|
||||
|
||||
#[test]
|
||||
fn read_twice_before_dispatch() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO);
|
||||
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read().once().returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
@@ -120,8 +61,11 @@ fn read_twice_before_dispatch() {
|
||||
|
||||
#[test]
|
||||
fn read_with_smaller_buf() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read().once().returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -153,8 +97,22 @@ fn read_with_smaller_buf() {
|
||||
|
||||
#[test]
|
||||
fn read_with_bigger_buf() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(&HELLO[..4]).read(&HELLO[4..]);
|
||||
let mut seq = Sequence::new();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..4].copy_from_slice(&HELLO[..4]);
|
||||
Ok(4)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len() - 4].copy_from_slice(&HELLO[4..]);
|
||||
Ok(HELLO.len() - 4)
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -194,8 +152,19 @@ fn read_with_bigger_buf() {
|
||||
|
||||
#[test]
|
||||
fn read_err_then_read_success() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read_err().read(&HELLO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -225,8 +194,11 @@ fn read_err_then_read_success() {
|
||||
|
||||
#[test]
|
||||
fn open_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.with(eq(HELLO))
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -235,12 +207,10 @@ fn open_write() {
|
||||
assert_eq!(0, pool::len());
|
||||
assert_ready_ok!(t.poll());
|
||||
|
||||
assert_eq!(1, mock.remaining());
|
||||
assert_eq!(1, pool::len());
|
||||
|
||||
pool::run_one();
|
||||
|
||||
assert_eq!(0, mock.remaining());
|
||||
assert!(!t.is_woken());
|
||||
|
||||
let mut t = task::spawn(file.flush());
|
||||
@@ -249,7 +219,7 @@ fn open_write() {
|
||||
|
||||
#[test]
|
||||
fn flush_while_idle() {
|
||||
let (_mock, file) = sys::File::mock();
|
||||
let file = MockFile::default();
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -271,13 +241,42 @@ fn read_with_buffer_larger_than_max() {
|
||||
for i in 0..(chunk_d - 1) {
|
||||
data.push((i % 151) as u8);
|
||||
}
|
||||
let data = Arc::new(data);
|
||||
let d0 = data.clone();
|
||||
let d1 = data.clone();
|
||||
let d2 = data.clone();
|
||||
let d3 = data.clone();
|
||||
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(&data[0..chunk_a])
|
||||
.read(&data[chunk_a..chunk_b])
|
||||
.read(&data[chunk_b..chunk_c])
|
||||
.read(&data[chunk_c..]);
|
||||
|
||||
let mut seq = Sequence::new();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[0..chunk_a].copy_from_slice(&d0[0..chunk_a]);
|
||||
Ok(chunk_a)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[..chunk_a].copy_from_slice(&d1[chunk_a..chunk_b]);
|
||||
Ok(chunk_b - chunk_a)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[..chunk_a].copy_from_slice(&d2[chunk_b..chunk_c]);
|
||||
Ok(chunk_c - chunk_b)
|
||||
});
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(move |buf| {
|
||||
buf[..chunk_a - 1].copy_from_slice(&d3[chunk_c..]);
|
||||
Ok(chunk_a - 1)
|
||||
});
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
let mut actual = vec![0; chunk_d];
|
||||
@@ -296,8 +295,7 @@ fn read_with_buffer_larger_than_max() {
|
||||
pos += n;
|
||||
}
|
||||
|
||||
assert_eq!(mock.remaining(), 0);
|
||||
assert_eq!(data, &actual[..data.len()]);
|
||||
assert_eq!(&data[..], &actual[..data.len()]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -314,12 +312,34 @@ fn write_with_buffer_larger_than_max() {
|
||||
for i in 0..(chunk_d - 1) {
|
||||
data.push((i % 151) as u8);
|
||||
}
|
||||
let data = Arc::new(data);
|
||||
let d0 = data.clone();
|
||||
let d1 = data.clone();
|
||||
let d2 = data.clone();
|
||||
let d3 = data.clone();
|
||||
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(&data[0..chunk_a])
|
||||
.write(&data[chunk_a..chunk_b])
|
||||
.write(&data[chunk_b..chunk_c])
|
||||
.write(&data[chunk_c..]);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d0[0..chunk_a])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d1[chunk_a..chunk_b])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d2[chunk_b..chunk_c])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.withf(move |buf| buf == &d3[chunk_c..chunk_d - 1])
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -344,14 +364,22 @@ fn write_with_buffer_larger_than_max() {
|
||||
}
|
||||
|
||||
pool::run_one();
|
||||
|
||||
assert_eq!(mock.remaining(), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_twice_before_dispatch() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|buf| Ok(buf.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -380,10 +408,24 @@ fn write_twice_before_dispatch() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_read_followed_by_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO)
|
||||
.seek_current_ok(-(HELLO.len() as i64), 0)
|
||||
.write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Current(-(HELLO.len() as i64))))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -406,8 +448,25 @@ fn incomplete_read_followed_by_write() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_partial_read_followed_by_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO).seek_current_ok(-10, 0).write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(SeekFrom::Current(-10)))
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -433,10 +492,25 @@ fn incomplete_partial_read_followed_by_write() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_read_followed_by_flush() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO)
|
||||
.seek_current_ok(-(HELLO.len() as i64), 0)
|
||||
.write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(SeekFrom::Current(-(HELLO.len() as i64))))
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -458,8 +532,18 @@ fn incomplete_read_followed_by_flush() {
|
||||
|
||||
#[test]
|
||||
fn incomplete_flush_followed_by_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).write(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(FOO))
|
||||
.returning(|_| Ok(FOO.len()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -484,8 +568,10 @@ fn incomplete_flush_followed_by_write() {
|
||||
|
||||
#[test]
|
||||
fn read_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read_err();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -502,8 +588,10 @@ fn read_err() {
|
||||
|
||||
#[test]
|
||||
fn write_write_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err();
|
||||
let mut file = MockFile::default();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -518,8 +606,19 @@ fn write_write_err() {
|
||||
|
||||
#[test]
|
||||
fn write_read_write_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().read(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -541,8 +640,19 @@ fn write_read_write_err() {
|
||||
|
||||
#[test]
|
||||
fn write_read_flush_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().read(HELLO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -564,8 +674,17 @@ fn write_read_flush_err() {
|
||||
|
||||
#[test]
|
||||
fn write_seek_write_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().seek_start_ok(0);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Start(0)))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -587,8 +706,17 @@ fn write_seek_write_err() {
|
||||
|
||||
#[test]
|
||||
fn write_seek_flush_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write_err().seek_start_ok(0);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Start(0)))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
|
||||
@@ -610,8 +738,14 @@ fn write_seek_flush_err() {
|
||||
|
||||
#[test]
|
||||
fn sync_all_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_all();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_all().once().returning(|| Ok(()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -635,8 +769,16 @@ fn sync_all_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn sync_all_err_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_all_err();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_all()
|
||||
.once()
|
||||
.returning(|| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -660,8 +802,14 @@ fn sync_all_err_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn sync_data_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_data();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_data().once().returning(|| Ok(()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -685,8 +833,16 @@ fn sync_data_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn sync_data_err_ordered_after_write() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.write(HELLO).sync_data_err();
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_write()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(HELLO))
|
||||
.returning(|_| Ok(HELLO.len()));
|
||||
file.expect_sync_data()
|
||||
.once()
|
||||
.returning(|| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let mut file = File::from_std(file);
|
||||
let mut t = task::spawn(file.write(HELLO));
|
||||
@@ -710,17 +866,15 @@ fn sync_data_err_ordered_after_write() {
|
||||
|
||||
#[test]
|
||||
fn open_set_len_ok() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.set_len(123);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_set_len().with(eq(123)).returning(|_| Ok(()));
|
||||
|
||||
let file = File::from_std(file);
|
||||
let mut t = task::spawn(file.set_len(123));
|
||||
|
||||
assert_pending!(t.poll());
|
||||
assert_eq!(1, mock.remaining());
|
||||
|
||||
pool::run_one();
|
||||
assert_eq!(0, mock.remaining());
|
||||
|
||||
assert!(t.is_woken());
|
||||
assert_ready_ok!(t.poll());
|
||||
@@ -728,17 +882,17 @@ fn open_set_len_ok() {
|
||||
|
||||
#[test]
|
||||
fn open_set_len_err() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.set_len_err(123);
|
||||
let mut file = MockFile::default();
|
||||
file.expect_set_len()
|
||||
.with(eq(123))
|
||||
.returning(|_| Err(io::ErrorKind::Other.into()));
|
||||
|
||||
let file = File::from_std(file);
|
||||
let mut t = task::spawn(file.set_len(123));
|
||||
|
||||
assert_pending!(t.poll());
|
||||
assert_eq!(1, mock.remaining());
|
||||
|
||||
pool::run_one();
|
||||
assert_eq!(0, mock.remaining());
|
||||
|
||||
assert!(t.is_woken());
|
||||
assert_ready_err!(t.poll());
|
||||
@@ -746,11 +900,32 @@ fn open_set_len_err() {
|
||||
|
||||
#[test]
|
||||
fn partial_read_set_len_ok() {
|
||||
let (mock, file) = sys::File::mock();
|
||||
mock.read(HELLO)
|
||||
.seek_current_ok(-14, 0)
|
||||
.set_len(123)
|
||||
.read(FOO);
|
||||
let mut file = MockFile::default();
|
||||
let mut seq = Sequence::new();
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..HELLO.len()].copy_from_slice(HELLO);
|
||||
Ok(HELLO.len())
|
||||
});
|
||||
file.expect_inner_seek()
|
||||
.once()
|
||||
.with(eq(SeekFrom::Current(-(HELLO.len() as i64))))
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|_| Ok(0));
|
||||
file.expect_set_len()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.with(eq(123))
|
||||
.returning(|_| Ok(()));
|
||||
file.expect_inner_read()
|
||||
.once()
|
||||
.in_sequence(&mut seq)
|
||||
.returning(|buf| {
|
||||
buf[0..FOO.len()].copy_from_slice(FOO);
|
||||
Ok(FOO.len())
|
||||
});
|
||||
|
||||
let mut buf = [0; 32];
|
||||
let mut file = File::from_std(file);
|
||||
@@ -0,0 +1,136 @@
|
||||
//! Mock version of std::fs::File;
|
||||
use mockall::mock;
|
||||
|
||||
use crate::sync::oneshot;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::VecDeque,
|
||||
fs::{Metadata, Permissions},
|
||||
future::Future,
|
||||
io::{self, Read, Seek, SeekFrom, Write},
|
||||
path::PathBuf,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
|
||||
mock! {
|
||||
#[derive(Debug)]
|
||||
pub File {
|
||||
pub fn create(pb: PathBuf) -> io::Result<Self>;
|
||||
// These inner_ methods exist because std::fs::File has two
|
||||
// implementations for each of these methods: one on "&mut self" and
|
||||
// one on "&&self". Defining both of those in terms of an inner_ method
|
||||
// allows us to specify the expectation the same way, regardless of
|
||||
// which method is used.
|
||||
pub fn inner_flush(&self) -> io::Result<()>;
|
||||
pub fn inner_read(&self, dst: &mut [u8]) -> io::Result<usize>;
|
||||
pub fn inner_seek(&self, pos: SeekFrom) -> io::Result<u64>;
|
||||
pub fn inner_write(&self, src: &[u8]) -> io::Result<usize>;
|
||||
pub fn metadata(&self) -> io::Result<Metadata>;
|
||||
pub fn open(pb: PathBuf) -> io::Result<Self>;
|
||||
pub fn set_len(&self, size: u64) -> io::Result<()>;
|
||||
pub fn set_permissions(&self, _perm: Permissions) -> io::Result<()>;
|
||||
pub fn sync_all(&self) -> io::Result<()>;
|
||||
pub fn sync_data(&self) -> io::Result<()>;
|
||||
pub fn try_clone(&self) -> io::Result<Self>;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::AsRawHandle for File {
|
||||
fn as_raw_handle(&self) -> std::os::windows::io::RawHandle;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl std::os::windows::io::FromRawHandle for File {
|
||||
unsafe fn from_raw_handle(h: std::os::windows::io::RawHandle) -> Self;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::AsRawFd for File {
|
||||
fn as_raw_fd(&self) -> std::os::unix::io::RawFd;
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
impl std::os::unix::io::FromRawFd for File {
|
||||
unsafe fn from_raw_fd(h: std::os::unix::io::RawFd) -> Self;
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for MockFile {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner_read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for &'_ MockFile {
|
||||
fn read(&mut self, dst: &mut [u8]) -> io::Result<usize> {
|
||||
self.inner_read(dst)
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for &'_ MockFile {
|
||||
fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
|
||||
self.inner_seek(pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for &'_ MockFile {
|
||||
fn write(&mut self, src: &[u8]) -> io::Result<usize> {
|
||||
self.inner_write(src)
|
||||
}
|
||||
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.inner_flush()
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static QUEUE: RefCell<VecDeque<Box<dyn FnOnce() + Send>>> = RefCell::new(VecDeque::new())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct JoinHandle<T> {
|
||||
rx: oneshot::Receiver<T>,
|
||||
}
|
||||
|
||||
pub(super) fn spawn_blocking<F, R>(f: F) -> JoinHandle<R>
|
||||
where
|
||||
F: FnOnce() -> R + Send + 'static,
|
||||
R: Send + 'static,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let task = Box::new(move || {
|
||||
let _ = tx.send(f());
|
||||
});
|
||||
|
||||
QUEUE.with(|cell| cell.borrow_mut().push_back(task));
|
||||
|
||||
JoinHandle { rx }
|
||||
}
|
||||
|
||||
impl<T> Future for JoinHandle<T> {
|
||||
type Output = Result<T, io::Error>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
use std::task::Poll::*;
|
||||
|
||||
match Pin::new(&mut self.rx).poll(cx) {
|
||||
Ready(Ok(v)) => Ready(Ok(v)),
|
||||
Ready(Err(e)) => panic!("error = {:?}", e),
|
||||
Pending => Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) mod pool {
|
||||
use super::*;
|
||||
|
||||
pub(in super::super) fn len() -> usize {
|
||||
QUEUE.with(|cell| cell.borrow().len())
|
||||
}
|
||||
|
||||
pub(in super::super) fn run_one() {
|
||||
let task = QUEUE
|
||||
.with(|cell| cell.borrow_mut().pop_front())
|
||||
.expect("expected task to run, but none ready");
|
||||
|
||||
task();
|
||||
}
|
||||
}
|
||||
+9
-10
@@ -84,6 +84,9 @@ pub use self::write::write;
|
||||
mod copy;
|
||||
pub use self::copy::copy;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mocks;
|
||||
|
||||
feature! {
|
||||
#![unix]
|
||||
|
||||
@@ -103,12 +106,17 @@ feature! {
|
||||
|
||||
use std::io;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::spawn_blocking;
|
||||
#[cfg(test)]
|
||||
use mocks::spawn_blocking;
|
||||
|
||||
pub(crate) async fn asyncify<F, T>(f: F) -> io::Result<T>
|
||||
where
|
||||
F: FnOnce() -> io::Result<T> + Send + 'static,
|
||||
T: Send + 'static,
|
||||
{
|
||||
match sys::run(f).await {
|
||||
match spawn_blocking(f).await {
|
||||
Ok(res) => res,
|
||||
Err(_) => Err(io::Error::new(
|
||||
io::ErrorKind::Other,
|
||||
@@ -116,12 +124,3 @@ where
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Types in this module can be mocked out in tests.
|
||||
mod sys {
|
||||
pub(crate) use std::fs::File;
|
||||
|
||||
// TODO: don't rename
|
||||
pub(crate) use crate::blocking::spawn_blocking as run;
|
||||
pub(crate) use crate::blocking::JoinHandle as Blocking;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,13 @@ use crate::fs::{asyncify, File};
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(test)]
|
||||
mod mock_open_options;
|
||||
#[cfg(test)]
|
||||
use mock_open_options::MockOpenOptions as StdOpenOptions;
|
||||
#[cfg(not(test))]
|
||||
use std::fs::OpenOptions as StdOpenOptions;
|
||||
|
||||
/// Options and flags which can be used to configure how a file is opened.
|
||||
///
|
||||
/// This builder exposes the ability to configure how a [`File`] is opened and
|
||||
@@ -69,7 +76,7 @@ use std::path::Path;
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpenOptions(std::fs::OpenOptions);
|
||||
pub struct OpenOptions(StdOpenOptions);
|
||||
|
||||
impl OpenOptions {
|
||||
/// Creates a blank new set of options ready for configuration.
|
||||
@@ -89,7 +96,7 @@ impl OpenOptions {
|
||||
/// let future = options.read(true).open("foo.txt");
|
||||
/// ```
|
||||
pub fn new() -> OpenOptions {
|
||||
OpenOptions(std::fs::OpenOptions::new())
|
||||
OpenOptions(StdOpenOptions::new())
|
||||
}
|
||||
|
||||
/// Sets the option for read access.
|
||||
@@ -384,7 +391,7 @@ impl OpenOptions {
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the underlying `std::fs::OpenOptions`
|
||||
pub(super) fn as_inner_mut(&mut self) -> &mut std::fs::OpenOptions {
|
||||
pub(super) fn as_inner_mut(&mut self) -> &mut StdOpenOptions {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
@@ -645,8 +652,8 @@ feature! {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::fs::OpenOptions> for OpenOptions {
|
||||
fn from(options: std::fs::OpenOptions) -> OpenOptions {
|
||||
impl From<StdOpenOptions> for OpenOptions {
|
||||
fn from(options: StdOpenOptions) -> OpenOptions {
|
||||
OpenOptions(options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
//! Mock version of std::fs::OpenOptions;
|
||||
use mockall::mock;
|
||||
|
||||
use crate::fs::mocks::MockFile;
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::fs::OpenOptionsExt;
|
||||
#[cfg(windows)]
|
||||
use std::os::windows::fs::OpenOptionsExt;
|
||||
use std::{io, path::Path};
|
||||
|
||||
mock! {
|
||||
#[derive(Debug)]
|
||||
pub OpenOptions {
|
||||
pub fn append(&mut self, append: bool) -> &mut Self;
|
||||
pub fn create(&mut self, create: bool) -> &mut Self;
|
||||
pub fn create_new(&mut self, create_new: bool) -> &mut Self;
|
||||
pub fn open<P: AsRef<Path> + 'static>(&self, path: P) -> io::Result<MockFile>;
|
||||
pub fn read(&mut self, read: bool) -> &mut Self;
|
||||
pub fn truncate(&mut self, truncate: bool) -> &mut Self;
|
||||
pub fn write(&mut self, write: bool) -> &mut Self;
|
||||
}
|
||||
impl Clone for OpenOptions {
|
||||
fn clone(&self) -> Self;
|
||||
}
|
||||
#[cfg(unix)]
|
||||
impl OpenOptionsExt for OpenOptions {
|
||||
fn custom_flags(&mut self, flags: i32) -> &mut Self;
|
||||
fn mode(&mut self, mode: u32) -> &mut Self;
|
||||
}
|
||||
#[cfg(windows)]
|
||||
impl OpenOptionsExt for OpenOptions {
|
||||
fn access_mode(&mut self, access: u32) -> &mut Self;
|
||||
fn share_mode(&mut self, val: u32) -> &mut Self;
|
||||
fn custom_flags(&mut self, flags: u32) -> &mut Self;
|
||||
fn attributes(&mut self, val: u32) -> &mut Self;
|
||||
fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
|
||||
}
|
||||
}
|
||||
@@ -13,8 +13,12 @@ use std::{io, path::Path};
|
||||
/// buffer based on the file size when available, so it is generally faster than
|
||||
/// reading into a vector created with `Vec::new()`.
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking operation
|
||||
/// on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`File::open`]: super::File::open
|
||||
/// [`read_to_end`]: crate::io::AsyncReadExt::read_to_end
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::fs::{asyncify, sys};
|
||||
use crate::fs::asyncify;
|
||||
|
||||
use std::ffi::OsString;
|
||||
use std::fs::{FileType, Metadata};
|
||||
@@ -10,9 +10,23 @@ use std::sync::Arc;
|
||||
use std::task::Context;
|
||||
use std::task::Poll;
|
||||
|
||||
#[cfg(test)]
|
||||
use super::mocks::spawn_blocking;
|
||||
#[cfg(test)]
|
||||
use super::mocks::JoinHandle;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::spawn_blocking;
|
||||
#[cfg(not(test))]
|
||||
use crate::blocking::JoinHandle;
|
||||
|
||||
/// Returns a stream over the entries within a directory.
|
||||
///
|
||||
/// This is an async version of [`std::fs::read_dir`](std::fs::read_dir)
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking
|
||||
/// operation on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let std = asyncify(|| std::fs::read_dir(path)).await?;
|
||||
@@ -45,11 +59,15 @@ pub struct ReadDir(State);
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle(Option<std::fs::ReadDir>),
|
||||
Pending(sys::Blocking<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
|
||||
Pending(JoinHandle<(Option<io::Result<std::fs::DirEntry>>, std::fs::ReadDir)>),
|
||||
}
|
||||
|
||||
impl ReadDir {
|
||||
/// Returns the next entry in the directory stream.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe.
|
||||
pub async fn next_entry(&mut self) -> io::Result<Option<DirEntry>> {
|
||||
use crate::future::poll_fn;
|
||||
poll_fn(|cx| self.poll_next_entry(cx)).await
|
||||
@@ -79,7 +97,7 @@ impl ReadDir {
|
||||
State::Idle(ref mut std) => {
|
||||
let mut std = std.take().unwrap();
|
||||
|
||||
self.0 = State::Pending(sys::run(move || {
|
||||
self.0 = State::Pending(spawn_blocking(move || {
|
||||
let ret = std.next();
|
||||
(ret, std)
|
||||
}));
|
||||
|
||||
@@ -7,6 +7,10 @@ use std::{io, path::Path};
|
||||
///
|
||||
/// This is the async equivalent of [`std::fs::read_to_string`][std].
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking operation
|
||||
/// on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
/// [std]: fn@std::fs::read_to_string
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -7,6 +7,10 @@ use std::{io, path::Path};
|
||||
///
|
||||
/// This is the async equivalent of [`std::fs::write`][std].
|
||||
///
|
||||
/// This operation is implemented by running the equivalent blocking operation
|
||||
/// on a separate thread pool using [`spawn_blocking`].
|
||||
///
|
||||
/// [`spawn_blocking`]: crate::task::spawn_blocking
|
||||
/// [std]: fn@std::fs::write
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -22,3 +22,14 @@ cfg_sync! {
|
||||
mod block_on;
|
||||
pub(crate) use block_on::block_on;
|
||||
}
|
||||
|
||||
cfg_trace! {
|
||||
mod trace;
|
||||
pub(crate) use trace::InstrumentedFuture as Future;
|
||||
}
|
||||
|
||||
cfg_not_trace! {
|
||||
cfg_rt! {
|
||||
pub(crate) use std::future::Future;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
use std::future::Future;
|
||||
|
||||
pub(crate) trait InstrumentedFuture: Future {
|
||||
fn id(&self) -> Option<tracing::Id>;
|
||||
}
|
||||
|
||||
impl<F: Future> InstrumentedFuture for tracing::instrument::Instrumented<F> {
|
||||
fn id(&self) -> Option<tracing::Id> {
|
||||
self.span().id()
|
||||
}
|
||||
}
|
||||
@@ -540,6 +540,16 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyGuard<'a, Inner> {
|
||||
result => Ok(result),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner [`AsyncFd`].
|
||||
pub fn get_ref(&self) -> &AsyncFd<Inner> {
|
||||
self.async_fd
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
|
||||
pub fn get_inner(&self) -> &Inner {
|
||||
self.get_ref().get_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
|
||||
@@ -601,6 +611,26 @@ impl<'a, Inner: AsRawFd> AsyncFdReadyMutGuard<'a, Inner> {
|
||||
result => Ok(result),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the inner [`AsyncFd`].
|
||||
pub fn get_ref(&self) -> &AsyncFd<Inner> {
|
||||
self.async_fd
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the inner [`AsyncFd`].
|
||||
pub fn get_mut(&mut self) -> &mut AsyncFd<Inner> {
|
||||
self.async_fd
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the backing object of the inner [`AsyncFd`].
|
||||
pub fn get_inner(&self) -> &Inner {
|
||||
self.get_ref().get_ref()
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the backing object of the inner [`AsyncFd`].
|
||||
pub fn get_inner_mut(&mut self) -> &mut Inner {
|
||||
self.get_mut().get_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: std::fmt::Debug + AsRawFd> std::fmt::Debug for AsyncFdReadyGuard<'a, T> {
|
||||
|
||||
@@ -45,7 +45,11 @@ use std::task::{Context, Poll};
|
||||
pub trait AsyncWrite {
|
||||
/// Attempt to write bytes from `buf` into the object.
|
||||
///
|
||||
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`.
|
||||
/// On success, returns `Poll::Ready(Ok(num_bytes_written))`. If successful,
|
||||
/// then it must be guaranteed that `n <= buf.len()`. A return value of `0`
|
||||
/// typically means that the underlying object is no longer able to accept
|
||||
/// bytes and will likely not be able to in the future as well, or that the
|
||||
/// buffer provided is empty.
|
||||
///
|
||||
/// If the object is not ready for writing, the method returns
|
||||
/// `Poll::Pending` and arranges for the current task (via
|
||||
|
||||
@@ -58,7 +58,7 @@ impl Interest {
|
||||
self.0.is_writable()
|
||||
}
|
||||
|
||||
/// Add together two `Interst` values.
|
||||
/// Add together two `Interest` values.
|
||||
///
|
||||
/// This function works from a `const` context.
|
||||
///
|
||||
|
||||
@@ -96,7 +96,7 @@ const ADDRESS: bit::Pack = bit::Pack::least_significant(24);
|
||||
//
|
||||
// The generation prevents a race condition where a slab slot is reused for a
|
||||
// new socket while the I/O driver is about to apply a readiness event. The
|
||||
// generaton value is checked when setting new readiness. If the generation do
|
||||
// generation value is checked when setting new readiness. If the generation do
|
||||
// not match, then the readiness event is discarded.
|
||||
const GENERATION: bit::Pack = ADDRESS.then(7);
|
||||
|
||||
|
||||
@@ -84,9 +84,9 @@ cfg_io_readiness! {
|
||||
|
||||
// The `ScheduledIo::readiness` (`AtomicUsize`) is packed full of goodness.
|
||||
//
|
||||
// | reserved | generation | driver tick | readinesss |
|
||||
// |----------+------------+--------------+------------|
|
||||
// | 1 bit | 7 bits + 8 bits + 16 bits |
|
||||
// | reserved | generation | driver tick | readiness |
|
||||
// |----------+------------+--------------+-----------|
|
||||
// | 1 bit | 7 bits + 8 bits + 16 bits |
|
||||
|
||||
const READINESS: bit::Pack = bit::Pack::least_significant(16);
|
||||
|
||||
|
||||
@@ -40,9 +40,8 @@ cfg_io_driver! {
|
||||
/// [`poll_read_ready`] again will also indicate read readiness.
|
||||
///
|
||||
/// When the operation is attempted and is unable to succeed due to the I/O
|
||||
/// resource not being ready, the caller must call `clear_read_ready` or
|
||||
/// `clear_write_ready`. This clears the readiness state until a new
|
||||
/// readiness event is received.
|
||||
/// resource not being ready, the caller must call `clear_readiness`.
|
||||
/// This clears the readiness state until a new readiness event is received.
|
||||
///
|
||||
/// This allows the caller to implement additional functions. For example,
|
||||
/// [`TcpListener`] implements poll_accept by using [`poll_read_ready`] and
|
||||
|
||||
@@ -45,7 +45,7 @@ impl<'a> ReadBuf<'a> {
|
||||
|
||||
/// Creates a new `ReadBuf` from a fully uninitialized buffer.
|
||||
///
|
||||
/// Use `assume_init` if part of the buffer is known to be already inintialized.
|
||||
/// Use `assume_init` if part of the buffer is known to be already initialized.
|
||||
#[inline]
|
||||
pub fn uninit(buf: &'a mut [MaybeUninit<u8>]) -> ReadBuf<'a> {
|
||||
ReadBuf {
|
||||
@@ -85,7 +85,7 @@ impl<'a> ReadBuf<'a> {
|
||||
#[inline]
|
||||
pub fn take(&mut self, n: usize) -> ReadBuf<'_> {
|
||||
let max = std::cmp::min(self.remaining(), n);
|
||||
// Saftey: We don't set any of the `unfilled_mut` with `MaybeUninit::uninit`.
|
||||
// Safety: We don't set any of the `unfilled_mut` with `MaybeUninit::uninit`.
|
||||
unsafe { ReadBuf::uninit(&mut self.unfilled_mut()[..max]) }
|
||||
}
|
||||
|
||||
@@ -217,7 +217,7 @@ impl<'a> ReadBuf<'a> {
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if the filled region of the buffer would become larger than the intialized region.
|
||||
/// Panics if the filled region of the buffer would become larger than the initialized region.
|
||||
#[inline]
|
||||
pub fn set_filled(&mut self, n: usize) {
|
||||
assert!(
|
||||
|
||||
@@ -63,7 +63,7 @@ impl<T> ReadHalf<T> {
|
||||
/// Checks if this `ReadHalf` and some `WriteHalf` were split from the same
|
||||
/// stream.
|
||||
pub fn is_pair_of(&self, other: &WriteHalf<T>) -> bool {
|
||||
other.is_pair_of(&self)
|
||||
other.is_pair_of(self)
|
||||
}
|
||||
|
||||
/// Reunites with a previously split `WriteHalf`.
|
||||
|
||||
@@ -52,10 +52,10 @@ where
|
||||
|
||||
buf = &buf[..crate::io::blocking::MAX_BUF];
|
||||
|
||||
// Now there are two possibilites.
|
||||
// Now there are two possibilities.
|
||||
// If caller gave is binary buffer, we **should not** shrink it
|
||||
// anymore, because excessive shrinking hits performance.
|
||||
// If caller gave as binary buffer, we **must** additionaly
|
||||
// If caller gave as binary buffer, we **must** additionally
|
||||
// shrink it to strip incomplete char at the end of buffer.
|
||||
// that's why check we will perform now is allowed to have
|
||||
// false-positive.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::io::util::fill_buf::{fill_buf, FillBuf};
|
||||
use crate::io::util::lines::{lines, Lines};
|
||||
use crate::io::util::read_line::{read_line, ReadLine};
|
||||
use crate::io::util::read_until::{read_until, ReadUntil};
|
||||
@@ -36,6 +37,18 @@ cfg_io_util! {
|
||||
/// [`fill_buf`]: AsyncBufRead::poll_fill_buf
|
||||
/// [`ErrorKind::Interrupted`]: std::io::ErrorKind::Interrupted
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// If the method is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then some data may have been partially read. Any
|
||||
/// partially read bytes are appended to `buf`, and the method can be
|
||||
/// called again to continue reading until `byte`.
|
||||
///
|
||||
/// This method returns the total number of bytes read. If you cancel
|
||||
/// the call to `read_until` and then call it again to continue reading,
|
||||
/// the counter is reset.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`std::io::Cursor`][`Cursor`] is a type that implements `BufRead`. In
|
||||
@@ -114,6 +127,30 @@ cfg_io_util! {
|
||||
///
|
||||
/// [`read_until`]: AsyncBufReadExt::read_until
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may have been partially
|
||||
/// read, and this data is lost. There are no guarantees regarding the
|
||||
/// contents of `buf` when the call is cancelled. The current
|
||||
/// implementation replaces `buf` with the empty string, but this may
|
||||
/// change in the future.
|
||||
///
|
||||
/// This function does not behave like [`read_until`] because of the
|
||||
/// requirement that a string contains only valid utf-8. If you need a
|
||||
/// cancellation safe `read_line`, there are three options:
|
||||
///
|
||||
/// * Call [`read_until`] with a newline character and manually perform the utf-8 check.
|
||||
/// * The stream returned by [`lines`] has a cancellation safe
|
||||
/// [`next_line`] method.
|
||||
/// * Use [`tokio_util::codec::LinesCodec`][LinesCodec].
|
||||
///
|
||||
/// [LinesCodec]: https://docs.rs/tokio-util/0.6/tokio_util/codec/struct.LinesCodec.html
|
||||
/// [`read_until`]: Self::read_until
|
||||
/// [`lines`]: Self::lines
|
||||
/// [`next_line`]: crate::io::Lines::next_line
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`std::io::Cursor`][`Cursor`] is a type that implements
|
||||
@@ -173,10 +210,11 @@ cfg_io_util! {
|
||||
/// [`BufRead::split`](std::io::BufRead::split).
|
||||
///
|
||||
/// The stream returned from this function will yield instances of
|
||||
/// [`io::Result`]`<`[`Vec<u8>`]`>`. Each vector returned will *not* have
|
||||
/// [`io::Result`]`<`[`Option`]`<`[`Vec<u8>`]`>>`. Each vector returned will *not* have
|
||||
/// the delimiter byte at the end.
|
||||
///
|
||||
/// [`io::Result`]: std::io::Result
|
||||
/// [`Option`]: core::option::Option
|
||||
/// [`Vec<u8>`]: std::vec::Vec
|
||||
///
|
||||
/// # Errors
|
||||
@@ -206,14 +244,68 @@ cfg_io_util! {
|
||||
split(self, byte)
|
||||
}
|
||||
|
||||
/// Returns the contents of the internal buffer, filling it with more
|
||||
/// data from the inner reader if it is empty.
|
||||
///
|
||||
/// This function is a lower-level call. It needs to be paired with the
|
||||
/// [`consume`] method to function properly. When calling this method,
|
||||
/// none of the contents will be "read" in the sense that later calling
|
||||
/// `read` may return the same contents. As such, [`consume`] must be
|
||||
/// called with the number of bytes that are consumed from this buffer
|
||||
/// to ensure that the bytes are never returned twice.
|
||||
///
|
||||
/// An empty buffer returned indicates that the stream has reached EOF.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn fill_buf(&mut self) -> io::Result<&[u8]>;
|
||||
/// ```
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an I/O error if the underlying reader was
|
||||
/// read, but returned an error.
|
||||
///
|
||||
/// [`consume`]: crate::io::AsyncBufReadExt::consume
|
||||
fn fill_buf(&mut self) -> FillBuf<'_, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
fill_buf(self)
|
||||
}
|
||||
|
||||
/// Tells this buffer that `amt` bytes have been consumed from the
|
||||
/// buffer, so they should no longer be returned in calls to [`read`].
|
||||
///
|
||||
/// This function is a lower-level call. It needs to be paired with the
|
||||
/// [`fill_buf`] method to function properly. This function does not
|
||||
/// perform any I/O, it simply informs this object that some amount of
|
||||
/// its buffer, returned from [`fill_buf`], has been consumed and should
|
||||
/// no longer be returned. As such, this function may do odd things if
|
||||
/// [`fill_buf`] isn't called before calling it.
|
||||
///
|
||||
/// The `amt` must be less than the number of bytes in the buffer
|
||||
/// returned by [`fill_buf`].
|
||||
///
|
||||
/// [`read`]: crate::io::AsyncReadExt::read
|
||||
/// [`fill_buf`]: crate::io::AsyncBufReadExt::fill_buf
|
||||
fn consume(&mut self, amt: usize)
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
std::pin::Pin::new(self).consume(amt)
|
||||
}
|
||||
|
||||
/// Returns a stream over the lines of this reader.
|
||||
/// This method is the async equivalent to [`BufRead::lines`](std::io::BufRead::lines).
|
||||
///
|
||||
/// The stream returned from this function will yield instances of
|
||||
/// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline
|
||||
/// [`io::Result`]`<`[`Option`]`<`[`String`]`>>`. Each string returned will *not* have a newline
|
||||
/// byte (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end.
|
||||
///
|
||||
/// [`io::Result`]: std::io::Result
|
||||
/// [`Option`]: core::option::Option
|
||||
/// [`String`]: String
|
||||
///
|
||||
/// # Errors
|
||||
|
||||
@@ -2,6 +2,7 @@ use crate::io::util::chain::{chain, Chain};
|
||||
use crate::io::util::read::{read, Read};
|
||||
use crate::io::util::read_buf::{read_buf, ReadBuf};
|
||||
use crate::io::util::read_exact::{read_exact, ReadExact};
|
||||
use crate::io::util::read_int::{ReadF32, ReadF32Le, ReadF64, ReadF64Le};
|
||||
use crate::io::util::read_int::{
|
||||
ReadI128, ReadI128Le, ReadI16, ReadI16Le, ReadI32, ReadI32Le, ReadI64, ReadI64Le, ReadI8,
|
||||
};
|
||||
@@ -105,8 +106,10 @@ cfg_io_util! {
|
||||
/// async fn read(&mut self, buf: &mut [u8]) -> io::Result<usize>;
|
||||
/// ```
|
||||
///
|
||||
/// This function does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously
|
||||
/// This method does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// If the return value of this method is `Ok(n)`, then it must be
|
||||
/// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates
|
||||
@@ -136,6 +139,12 @@ cfg_io_util! {
|
||||
/// variant will be returned. If an error is returned then it must be
|
||||
/// guaranteed that no bytes were read.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`][crate::fs::File]s implement `Read`:
|
||||
@@ -175,14 +184,19 @@ cfg_io_util! {
|
||||
/// Usually, only a single `read` syscall is issued, even if there is
|
||||
/// more space in the supplied buffer.
|
||||
///
|
||||
/// This function does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously
|
||||
/// This method does not provide any guarantees about whether it
|
||||
/// completes immediately or asynchronously.
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// On a successful read, the number of read bytes is returned. If the
|
||||
/// supplied buffer is not empty and the function returns `Ok(0)` then
|
||||
/// the source has reached an "end-of-file" event.
|
||||
/// A nonzero `n` value indicates that the buffer `buf` has been filled
|
||||
/// in with `n` bytes of data from this source. If `n` is `0`, then it
|
||||
/// can indicate one of two scenarios:
|
||||
///
|
||||
/// 1. This reader has reached its "end of file" and will likely no longer
|
||||
/// be able to produce bytes. Note that this does not mean that the
|
||||
/// reader will *always* no longer be able to produce bytes.
|
||||
/// 2. The buffer specified had a remaining capacity of zero.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -190,6 +204,12 @@ cfg_io_util! {
|
||||
/// variant will be returned. If an error is returned then it must be
|
||||
/// guaranteed that no bytes were read.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If you use it as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then it is guaranteed that no data was read.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`] implements `Read` and [`BytesMut`] implements [`BufMut`]:
|
||||
@@ -254,6 +274,13 @@ cfg_io_util! {
|
||||
/// it has read, but it will never read more than would be necessary to
|
||||
/// completely fill the buffer.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If the method is used as the
|
||||
/// event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then some data may already have been
|
||||
/// read into `buf`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`][crate::fs::File]s implement `Read`:
|
||||
@@ -579,7 +606,7 @@ cfg_io_util! {
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0x80, 0, 0, 0, 0, 0, 0, 0]);
|
||||
///
|
||||
/// assert_eq!(i64::min_value(), reader.read_i64().await?);
|
||||
/// assert_eq!(i64::MIN, reader.read_i64().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
@@ -659,12 +686,88 @@ cfg_io_util! {
|
||||
/// 0, 0, 0, 0, 0, 0, 0, 0
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(i128::min_value(), reader.read_i128().await?);
|
||||
/// assert_eq!(i128::MIN, reader.read_i128().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i128(&mut self) -> ReadI128;
|
||||
|
||||
/// Reads an 32-bit floating point type in big-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f32(&mut self) -> io::Result<f32>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 32-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0xff, 0x7f, 0xff, 0xff]);
|
||||
///
|
||||
/// assert_eq!(f32::MIN, reader.read_f32().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f32(&mut self) -> ReadF32;
|
||||
|
||||
/// Reads an 64-bit floating point type in big-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f64(&mut self) -> io::Result<f64>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 64-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(f64::MIN, reader.read_f64().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f64(&mut self) -> ReadF64;
|
||||
|
||||
/// Reads an unsigned 16-bit integer in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
@@ -971,6 +1074,82 @@ cfg_io_util! {
|
||||
/// }
|
||||
/// ```
|
||||
fn read_i128_le(&mut self) -> ReadI128Le;
|
||||
|
||||
/// Reads an 32-bit floating point type in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f32_le(&mut self) -> io::Result<f32>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 32-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![0xff, 0xff, 0x7f, 0xff]);
|
||||
///
|
||||
/// assert_eq!(f32::MIN, reader.read_f32_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f32_le(&mut self) -> ReadF32Le;
|
||||
|
||||
/// Reads an 64-bit floating point type in little-endian order from the
|
||||
/// underlying reader.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn read_f64_le(&mut self) -> io::Result<f64>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered reader to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncReadExt::read_exact`].
|
||||
///
|
||||
/// [`AsyncReadExt::read_exact`]: AsyncReadExt::read_exact
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Read 64-bit floating point type from a `AsyncRead`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut reader = Cursor::new(vec![
|
||||
/// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff
|
||||
/// ]);
|
||||
///
|
||||
/// assert_eq!(f64::MIN, reader.read_f64_le().await?);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn read_f64_le(&mut self) -> ReadF64Le;
|
||||
}
|
||||
|
||||
/// Reads all bytes until EOF in this source, placing them into `buf`.
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::io::util::write::{write, Write};
|
||||
use crate::io::util::write_all::{write_all, WriteAll};
|
||||
use crate::io::util::write_all_buf::{write_all_buf, WriteAllBuf};
|
||||
use crate::io::util::write_buf::{write_buf, WriteBuf};
|
||||
use crate::io::util::write_int::{WriteF32, WriteF32Le, WriteF64, WriteF64Le};
|
||||
use crate::io::util::write_int::{
|
||||
WriteI128, WriteI128Le, WriteI16, WriteI16Le, WriteI32, WriteI32Le, WriteI64, WriteI64Le,
|
||||
WriteI8,
|
||||
@@ -97,6 +98,13 @@ cfg_io_util! {
|
||||
/// It is **not** considered an error if the entire buffer could not be
|
||||
/// written to this writer.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -129,6 +137,13 @@ cfg_io_util! {
|
||||
///
|
||||
/// See [`AsyncWrite::poll_write_vectored`] for more details.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -195,6 +210,13 @@ cfg_io_util! {
|
||||
/// It is **not** considered an error if the entire buffer could not be
|
||||
/// written to this writer.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe in the sense that if it is used as
|
||||
/// the event in a [`tokio::select!`](crate::select) statement and some
|
||||
/// other branch completes first, then it is guaranteed that no data was
|
||||
/// written to this `AsyncWrite`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`] implements [`AsyncWrite`] and [`Cursor`]`<&[u8]>` implements [`Buf`]:
|
||||
@@ -243,6 +265,7 @@ cfg_io_util! {
|
||||
/// while buf.has_remaining() {
|
||||
/// self.write_buf(&mut buf).await?;
|
||||
/// }
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
@@ -254,6 +277,15 @@ cfg_io_util! {
|
||||
/// The buffer is advanced after each chunk is successfully written. After failure,
|
||||
/// `src.chunk()` will return the chunk that failed to write.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// If `write_all_buf` is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then the data in the provided buffer may have been
|
||||
/// partially written. However, it is guaranteed that the provided
|
||||
/// buffer has been [advanced] by the amount of bytes that have been
|
||||
/// partially written.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// [`File`] implements [`AsyncWrite`] and [`Cursor`]`<&[u8]>` implements [`Buf`]:
|
||||
@@ -261,6 +293,7 @@ cfg_io_util! {
|
||||
/// [`File`]: crate::fs::File
|
||||
/// [`Buf`]: bytes::Buf
|
||||
/// [`Cursor`]: std::io::Cursor
|
||||
/// [advanced]: bytes::Buf::advance
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
@@ -300,6 +333,14 @@ cfg_io_util! {
|
||||
/// has been successfully written or such an error occurs. The first
|
||||
/// error generated from this method will be returned.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is not cancellation safe. If it is used as the event
|
||||
/// in a [`tokio::select!`](crate::select) statement and some other
|
||||
/// branch completes first, then the provided buffer may have been
|
||||
/// partially written, but future calls to `write_all` will start over
|
||||
/// from the beginning of the buffer.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return the first error that [`write`] returns.
|
||||
@@ -621,8 +662,8 @@ cfg_io_util! {
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_i64(i64::min_value()).await?;
|
||||
/// writer.write_i64(i64::max_value()).await?;
|
||||
/// writer.write_i64(i64::MIN).await?;
|
||||
/// writer.write_i64(i64::MAX).await?;
|
||||
///
|
||||
/// assert_eq!(writer, b"\x80\x00\x00\x00\x00\x00\x00\x00\x7f\xff\xff\xff\xff\xff\xff\xff");
|
||||
/// Ok(())
|
||||
@@ -699,7 +740,7 @@ cfg_io_util! {
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_i128(i128::min_value()).await?;
|
||||
/// writer.write_i128(i128::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![
|
||||
/// 0x80, 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -710,6 +751,81 @@ cfg_io_util! {
|
||||
/// ```
|
||||
fn write_i128(&mut self, n: i128) -> WriteI128;
|
||||
|
||||
/// Writes an 32-bit floating point type in big-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f32(&mut self, n: f32) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 32-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f32(f32::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![0xff, 0x7f, 0xff, 0xff]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f32(&mut self, n: f32) -> WriteF32;
|
||||
|
||||
/// Writes an 64-bit floating point type in big-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f64(&mut self, n: f64) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 64-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f64(f64::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![
|
||||
/// 0xff, 0xef, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
|
||||
/// ]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f64(&mut self, n: f64) -> WriteF64;
|
||||
|
||||
/// Writes an unsigned 16-bit integer in little-endian order to the
|
||||
/// underlying writer.
|
||||
@@ -930,8 +1046,8 @@ cfg_io_util! {
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_i64_le(i64::min_value()).await?;
|
||||
/// writer.write_i64_le(i64::max_value()).await?;
|
||||
/// writer.write_i64_le(i64::MIN).await?;
|
||||
/// writer.write_i64_le(i64::MAX).await?;
|
||||
///
|
||||
/// assert_eq!(writer, b"\x00\x00\x00\x00\x00\x00\x00\x80\xff\xff\xff\xff\xff\xff\xff\x7f");
|
||||
/// Ok(())
|
||||
@@ -1008,7 +1124,7 @@ cfg_io_util! {
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_i128_le(i128::min_value()).await?;
|
||||
/// writer.write_i128_le(i128::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![
|
||||
/// 0, 0, 0, 0, 0, 0, 0,
|
||||
@@ -1018,6 +1134,82 @@ cfg_io_util! {
|
||||
/// }
|
||||
/// ```
|
||||
fn write_i128_le(&mut self, n: i128) -> WriteI128Le;
|
||||
|
||||
/// Writes an 32-bit floating point type in little-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f32_le(&mut self, n: f32) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 32-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f32_le(f32::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![0xff, 0xff, 0x7f, 0xff]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f32_le(&mut self, n: f32) -> WriteF32Le;
|
||||
|
||||
/// Writes an 64-bit floating point type in little-endian order to the
|
||||
/// underlying writer.
|
||||
///
|
||||
/// Equivalent to:
|
||||
///
|
||||
/// ```ignore
|
||||
/// async fn write_f64_le(&mut self, n: f64) -> io::Result<()>;
|
||||
/// ```
|
||||
///
|
||||
/// It is recommended to use a buffered writer to avoid excessive
|
||||
/// syscalls.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This method returns the same errors as [`AsyncWriteExt::write_all`].
|
||||
///
|
||||
/// [`AsyncWriteExt::write_all`]: AsyncWriteExt::write_all
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Write 64-bit floating point type to a `AsyncWrite`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut writer = Vec::new();
|
||||
///
|
||||
/// writer.write_f64_le(f64::MIN).await?;
|
||||
///
|
||||
/// assert_eq!(writer, vec![
|
||||
/// 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xff
|
||||
/// ]);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
fn write_f64_le(&mut self, n: f64) -> WriteF64Le;
|
||||
}
|
||||
|
||||
/// Flushes this output stream, ensuring that all intermediately buffered
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::io::util::DEFAULT_BUF_SIZE;
|
||||
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io::{self, SeekFrom};
|
||||
use std::io::{self, IoSlice, SeekFrom};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::{cmp, fmt, mem};
|
||||
@@ -198,7 +198,7 @@ impl<R: AsyncRead + AsyncSeek> AsyncSeek for BufReader<R> {
|
||||
// it should be safe to assume that remainder fits within an i64 as the alternative
|
||||
// means we managed to allocate 8 exbibytes and that's absurd.
|
||||
// But it's not out of the realm of possibility for some weird underlying reader to
|
||||
// support seeking by i64::min_value() so we need to handle underflow when subtracting
|
||||
// support seeking by i64::MIN so we need to handle underflow when subtracting
|
||||
// remainder.
|
||||
if let Some(offset) = n.checked_sub(remainder) {
|
||||
self.as_mut()
|
||||
@@ -268,6 +268,18 @@ impl<R: AsyncRead + AsyncWrite> AsyncWrite for BufReader<R> {
|
||||
self.get_pin_mut().poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.get_pin_mut().poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.get_ref().is_write_vectored()
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.get_pin_mut().poll_flush(cx)
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::io::util::{BufReader, BufWriter};
|
||||
use crate::io::{AsyncBufRead, AsyncRead, AsyncWrite, ReadBuf};
|
||||
use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::io;
|
||||
use std::io::{self, IoSlice, SeekFrom};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -127,6 +127,18 @@ impl<RW: AsyncRead + AsyncWrite> AsyncWrite for BufStream<RW> {
|
||||
self.project().inner.poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
self.project().inner.poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
self.inner.is_write_vectored()
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
self.project().inner.poll_flush(cx)
|
||||
}
|
||||
@@ -146,6 +158,34 @@ impl<RW: AsyncRead + AsyncWrite> AsyncRead for BufStream<RW> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Seek to an offset, in bytes, in the underlying stream.
|
||||
///
|
||||
/// The position used for seeking with `SeekFrom::Current(_)` is the
|
||||
/// position the underlying stream would be at if the `BufStream` had no
|
||||
/// internal buffer.
|
||||
///
|
||||
/// Seeking always discards the internal buffer, even if the seek position
|
||||
/// would otherwise fall within it. This guarantees that calling
|
||||
/// `.into_inner()` immediately after a seek yields the underlying reader
|
||||
/// at the same position.
|
||||
///
|
||||
/// See [`AsyncSeek`] for more details.
|
||||
///
|
||||
/// Note: In the edge case where you're seeking with `SeekFrom::Current(n)`
|
||||
/// where `n` minus the internal buffer length overflows an `i64`, two
|
||||
/// seeks will be performed instead of one. If the second seek returns
|
||||
/// `Err`, the underlying reader will be left at the same position it would
|
||||
/// have if you called `seek` with `SeekFrom::Current(0)`.
|
||||
impl<RW: AsyncRead + AsyncWrite + AsyncSeek> AsyncSeek for BufStream<RW> {
|
||||
fn start_seek(self: Pin<&mut Self>, position: SeekFrom) -> io::Result<()> {
|
||||
self.project().inner.start_seek(position)
|
||||
}
|
||||
|
||||
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
self.project().inner.poll_complete(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<RW: AsyncRead + AsyncWrite> AsyncBufRead for BufStream<RW> {
|
||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
||||
self.project().inner.poll_fill_buf(cx)
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite, ReadBuf};
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
use std::io::{self, SeekFrom, Write};
|
||||
use std::io::{self, IoSlice, SeekFrom, Write};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -133,6 +133,72 @@ impl<W: AsyncWrite> AsyncWrite for BufWriter<W> {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
mut bufs: &[IoSlice<'_>],
|
||||
) -> Poll<io::Result<usize>> {
|
||||
if self.inner.is_write_vectored() {
|
||||
let total_len = bufs
|
||||
.iter()
|
||||
.fold(0usize, |acc, b| acc.saturating_add(b.len()));
|
||||
if total_len > self.buf.capacity() - self.buf.len() {
|
||||
ready!(self.as_mut().flush_buf(cx))?;
|
||||
}
|
||||
let me = self.as_mut().project();
|
||||
if total_len >= me.buf.capacity() {
|
||||
// It's more efficient to pass the slices directly to the
|
||||
// underlying writer than to buffer them.
|
||||
// The case when the total_len calculation saturates at
|
||||
// usize::MAX is also handled here.
|
||||
me.inner.poll_write_vectored(cx, bufs)
|
||||
} else {
|
||||
bufs.iter().for_each(|b| me.buf.extend_from_slice(b));
|
||||
Poll::Ready(Ok(total_len))
|
||||
}
|
||||
} else {
|
||||
// Remove empty buffers at the beginning of bufs.
|
||||
while bufs.first().map(|buf| buf.len()) == Some(0) {
|
||||
bufs = &bufs[1..];
|
||||
}
|
||||
if bufs.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
// Flush if the first buffer doesn't fit.
|
||||
let first_len = bufs[0].len();
|
||||
if first_len > self.buf.capacity() - self.buf.len() {
|
||||
ready!(self.as_mut().flush_buf(cx))?;
|
||||
debug_assert!(self.buf.is_empty());
|
||||
}
|
||||
let me = self.as_mut().project();
|
||||
if first_len >= me.buf.capacity() {
|
||||
// The slice is at least as large as the buffering capacity,
|
||||
// so it's better to write it directly, bypassing the buffer.
|
||||
debug_assert!(me.buf.is_empty());
|
||||
return me.inner.poll_write(cx, &bufs[0]);
|
||||
} else {
|
||||
me.buf.extend_from_slice(&bufs[0]);
|
||||
bufs = &bufs[1..];
|
||||
}
|
||||
let mut total_written = first_len;
|
||||
debug_assert!(total_written != 0);
|
||||
// Append the buffers that fit in the internal buffer.
|
||||
for buf in bufs {
|
||||
if buf.len() > me.buf.capacity() - me.buf.len() {
|
||||
break;
|
||||
} else {
|
||||
me.buf.extend_from_slice(buf);
|
||||
total_written += buf.len();
|
||||
}
|
||||
}
|
||||
Poll::Ready(Ok(total_written))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_write_vectored(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
||||
ready!(self.as_mut().flush_buf(cx))?;
|
||||
self.get_pin_mut().poll_flush(cx)
|
||||
|
||||
@@ -8,6 +8,7 @@ use std::task::{Context, Poll};
|
||||
#[derive(Debug)]
|
||||
pub(super) struct CopyBuffer {
|
||||
read_done: bool,
|
||||
need_flush: bool,
|
||||
pos: usize,
|
||||
cap: usize,
|
||||
amt: u64,
|
||||
@@ -18,6 +19,7 @@ impl CopyBuffer {
|
||||
pub(super) fn new() -> Self {
|
||||
Self {
|
||||
read_done: false,
|
||||
need_flush: false,
|
||||
pos: 0,
|
||||
cap: 0,
|
||||
amt: 0,
|
||||
@@ -41,7 +43,22 @@ impl CopyBuffer {
|
||||
if self.pos == self.cap && !self.read_done {
|
||||
let me = &mut *self;
|
||||
let mut buf = ReadBuf::new(&mut me.buf);
|
||||
ready!(reader.as_mut().poll_read(cx, &mut buf))?;
|
||||
|
||||
match reader.as_mut().poll_read(cx, &mut buf) {
|
||||
Poll::Ready(Ok(_)) => (),
|
||||
Poll::Ready(Err(err)) => return Poll::Ready(Err(err)),
|
||||
Poll::Pending => {
|
||||
// Try flushing when the reader has no progress to avoid deadlock
|
||||
// when the reader depends on buffered writer.
|
||||
if self.need_flush {
|
||||
ready!(writer.as_mut().poll_flush(cx))?;
|
||||
self.need_flush = false;
|
||||
}
|
||||
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
|
||||
let n = buf.filled().len();
|
||||
if n == 0 {
|
||||
self.read_done = true;
|
||||
@@ -63,6 +80,7 @@ impl CopyBuffer {
|
||||
} else {
|
||||
self.pos += i;
|
||||
self.amt += i as u64;
|
||||
self.need_flush = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
use crate::io::AsyncBufRead;
|
||||
|
||||
use pin_project_lite::pin_project;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::marker::PhantomPinned;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Future for the [`fill_buf`](crate::io::AsyncBufReadExt::fill_buf) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct FillBuf<'a, R: ?Sized> {
|
||||
reader: Option<&'a mut R>,
|
||||
#[pin]
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn fill_buf<R>(reader: &mut R) -> FillBuf<'_, R>
|
||||
where
|
||||
R: AsyncBufRead + ?Sized + Unpin,
|
||||
{
|
||||
FillBuf {
|
||||
reader: Some(reader),
|
||||
_pin: PhantomPinned,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, R: AsyncBufRead + ?Sized + Unpin> Future for FillBuf<'a, R> {
|
||||
type Output = io::Result<&'a [u8]>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let me = self.project();
|
||||
|
||||
// Due to a limitation in the borrow-checker, we cannot return the value
|
||||
// directly on Ready. Once Rust starts using the polonius borrow checker,
|
||||
// this can be simplified.
|
||||
let reader = me.reader.take().expect("Polled after completion.");
|
||||
match Pin::new(&mut *reader).poll_fill_buf(cx) {
|
||||
Poll::Ready(_) => match Pin::new(reader).poll_fill_buf(cx) {
|
||||
Poll::Ready(slice) => Poll::Ready(slice),
|
||||
Poll::Pending => panic!("poll_fill_buf returned Pending while having data"),
|
||||
},
|
||||
Poll::Pending => {
|
||||
*me.reader = Some(reader);
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,10 @@ where
|
||||
{
|
||||
/// Returns the next line in the stream.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancellation safe.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
@@ -102,11 +106,9 @@ where
|
||||
///
|
||||
/// When the method returns `Poll::Pending`, the `Waker` in the provided
|
||||
/// `Context` is scheduled to receive a wakeup when more bytes become
|
||||
/// available on the underlying IO resource.
|
||||
///
|
||||
/// Note that on multiple calls to `poll_next_line`, only the `Waker` from
|
||||
/// the `Context` passed to the most recent call is scheduled to receive a
|
||||
/// wakeup.
|
||||
/// available on the underlying IO resource. Note that on multiple calls to
|
||||
/// `poll_next_line`, only the `Waker` from the `Context` passed to the most
|
||||
/// recent call is scheduled to receive a wakeup.
|
||||
pub fn poll_next_line(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
|
||||
@@ -49,6 +49,7 @@ cfg_io_util! {
|
||||
mod read_exact;
|
||||
mod read_int;
|
||||
mod read_line;
|
||||
mod fill_buf;
|
||||
|
||||
mod read_to_end;
|
||||
mod vec_with_initialized;
|
||||
|
||||
@@ -142,6 +142,9 @@ reader!(ReadI32, i32, get_i32);
|
||||
reader!(ReadI64, i64, get_i64);
|
||||
reader!(ReadI128, i128, get_i128);
|
||||
|
||||
reader!(ReadF32, f32, get_f32);
|
||||
reader!(ReadF64, f64, get_f64);
|
||||
|
||||
reader!(ReadU16Le, u16, get_u16_le);
|
||||
reader!(ReadU32Le, u32, get_u32_le);
|
||||
reader!(ReadU64Le, u64, get_u64_le);
|
||||
@@ -151,3 +154,6 @@ reader!(ReadI16Le, i16, get_i16_le);
|
||||
reader!(ReadI32Le, i32, get_i32_le);
|
||||
reader!(ReadI64Le, i64, get_i64_le);
|
||||
reader!(ReadI128Le, i128, get_i128_le);
|
||||
|
||||
reader!(ReadF32Le, f32, get_f32_le);
|
||||
reader!(ReadF64Le, f64, get_f64_le);
|
||||
|
||||
@@ -10,12 +10,12 @@ use std::task::{Context, Poll};
|
||||
|
||||
pin_project! {
|
||||
/// Future for the [`read_until`](crate::io::AsyncBufReadExt::read_until) method.
|
||||
/// The delimeter is included in the resulting vector.
|
||||
/// The delimiter is included in the resulting vector.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct ReadUntil<'a, R: ?Sized> {
|
||||
reader: &'a mut R,
|
||||
delimeter: u8,
|
||||
delimiter: u8,
|
||||
buf: &'a mut Vec<u8>,
|
||||
// The number of bytes appended to buf. This can be less than buf.len() if
|
||||
// the buffer was not empty when the operation was started.
|
||||
@@ -28,7 +28,7 @@ pin_project! {
|
||||
|
||||
pub(crate) fn read_until<'a, R>(
|
||||
reader: &'a mut R,
|
||||
delimeter: u8,
|
||||
delimiter: u8,
|
||||
buf: &'a mut Vec<u8>,
|
||||
) -> ReadUntil<'a, R>
|
||||
where
|
||||
@@ -36,7 +36,7 @@ where
|
||||
{
|
||||
ReadUntil {
|
||||
reader,
|
||||
delimeter,
|
||||
delimiter,
|
||||
buf,
|
||||
read: 0,
|
||||
_pin: PhantomPinned,
|
||||
@@ -46,14 +46,14 @@ where
|
||||
pub(super) fn read_until_internal<R: AsyncBufRead + ?Sized>(
|
||||
mut reader: Pin<&mut R>,
|
||||
cx: &mut Context<'_>,
|
||||
delimeter: u8,
|
||||
delimiter: u8,
|
||||
buf: &mut Vec<u8>,
|
||||
read: &mut usize,
|
||||
) -> Poll<io::Result<usize>> {
|
||||
loop {
|
||||
let (done, used) = {
|
||||
let available = ready!(reader.as_mut().poll_fill_buf(cx))?;
|
||||
if let Some(i) = memchr::memchr(delimeter, available) {
|
||||
if let Some(i) = memchr::memchr(delimiter, available) {
|
||||
buf.extend_from_slice(&available[..=i]);
|
||||
(true, i + 1)
|
||||
} else {
|
||||
@@ -74,6 +74,6 @@ impl<R: AsyncBufRead + ?Sized + Unpin> Future for ReadUntil<'_, R> {
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let me = self.project();
|
||||
read_until_internal(Pin::new(*me.reader), cx, *me.delimeter, me.buf, me.read)
|
||||
read_until_internal(Pin::new(*me.reader), cx, *me.delimiter, me.buf, me.read)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ where
|
||||
let n = ready!(read_until_internal(
|
||||
me.reader, cx, *me.delim, me.buf, me.read,
|
||||
))?;
|
||||
// read_until_internal resets me.read to zero once it finds the delimeter
|
||||
// read_until_internal resets me.read to zero once it finds the delimiter
|
||||
debug_assert_eq!(*me.read, 0);
|
||||
|
||||
if n == 0 && me.buf.is_empty() {
|
||||
|
||||
@@ -135,6 +135,9 @@ writer!(WriteI32, i32, put_i32);
|
||||
writer!(WriteI64, i64, put_i64);
|
||||
writer!(WriteI128, i128, put_i128);
|
||||
|
||||
writer!(WriteF32, f32, put_f32);
|
||||
writer!(WriteF64, f64, put_f64);
|
||||
|
||||
writer!(WriteU16Le, u16, put_u16_le);
|
||||
writer!(WriteU32Le, u32, put_u32_le);
|
||||
writer!(WriteU64Le, u64, put_u64_le);
|
||||
@@ -144,3 +147,6 @@ writer!(WriteI16Le, i16, put_i16_le);
|
||||
writer!(WriteI32Le, i32, put_i32_le);
|
||||
writer!(WriteI64Le, i64, put_i64_le);
|
||||
writer!(WriteI128Le, i128, put_i128_le);
|
||||
|
||||
writer!(WriteF32Le, f32, put_f32_le);
|
||||
writer!(WriteF64Le, f64, put_f64_le);
|
||||
|
||||
+32
-1
@@ -9,7 +9,8 @@
|
||||
rust_2018_idioms,
|
||||
unreachable_pub
|
||||
)]
|
||||
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]
|
||||
#![deny(unused_must_use)]
|
||||
#![cfg_attr(docsrs, deny(rustdoc::broken_intra_doc_links))]
|
||||
#![doc(test(
|
||||
no_crate_inject,
|
||||
attr(deny(warnings, rust_2018_idioms), allow(dead_code, unused_variables))
|
||||
@@ -442,6 +443,28 @@ mod util;
|
||||
/// ```
|
||||
pub mod stream {}
|
||||
|
||||
// local re-exports of platform specific things, allowing for decent
|
||||
// documentation to be shimmed in on docs.rs
|
||||
|
||||
#[cfg(docsrs)]
|
||||
pub mod doc;
|
||||
|
||||
#[cfg(docsrs)]
|
||||
#[allow(unused)]
|
||||
pub(crate) use self::doc::os;
|
||||
|
||||
#[cfg(not(docsrs))]
|
||||
#[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
|
||||
@@ -453,15 +476,20 @@ cfg_macros! {
|
||||
#[cfg(feature = "rt-multi-thread")]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
#[doc(inline)]
|
||||
pub use tokio_macros::main;
|
||||
|
||||
#[cfg(feature = "rt-multi-thread")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
#[doc(inline)]
|
||||
pub use tokio_macros::test;
|
||||
|
||||
cfg_not_rt_multi_thread! {
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
#[doc(inline)]
|
||||
pub use tokio_macros::main_rt as main;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use tokio_macros::test_rt as test;
|
||||
}
|
||||
}
|
||||
@@ -469,7 +497,10 @@ cfg_macros! {
|
||||
// Always fail if rt is not enabled.
|
||||
cfg_not_rt! {
|
||||
#[cfg(not(test))]
|
||||
#[doc(inline)]
|
||||
pub use tokio_macros::main_fail as main;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use tokio_macros::test_fail as test;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,17 @@
|
||||
//! re-export of `AtomicU64`. On 32 bit platforms, this is implemented using a
|
||||
//! `Mutex`.
|
||||
|
||||
pub(crate) use self::imp::AtomicU64;
|
||||
|
||||
// `AtomicU64` can only be used on targets with `target_has_atomic` is 64 or greater.
|
||||
// Once `cfg_target_has_atomic` feature is stable, we can replace it with
|
||||
// `#[cfg(target_has_atomic = "64")]`.
|
||||
// Refs: https://github.com/rust-lang/rust/tree/master/src/librustc_target
|
||||
#[cfg(not(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc")))]
|
||||
mod imp {
|
||||
cfg_has_atomic_u64! {
|
||||
pub(crate) use std::sync::atomic::AtomicU64;
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "arm", target_arch = "mips", target_arch = "powerpc"))]
|
||||
mod imp {
|
||||
cfg_not_has_atomic_u64! {
|
||||
use crate::loom::sync::Mutex;
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct AtomicU64 {
|
||||
@@ -31,15 +27,15 @@ mod imp {
|
||||
}
|
||||
|
||||
pub(crate) fn load(&self, _: Ordering) -> u64 {
|
||||
*self.inner.lock().unwrap()
|
||||
*self.inner.lock()
|
||||
}
|
||||
|
||||
pub(crate) fn store(&self, val: u64, _: Ordering) {
|
||||
*self.inner.lock().unwrap() = val;
|
||||
*self.inner.lock() = val;
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_or(&self, val: u64, _: Ordering) -> u64 {
|
||||
let mut lock = self.inner.lock().unwrap();
|
||||
let mut lock = self.inner.lock();
|
||||
let prev = *lock;
|
||||
*lock = prev | val;
|
||||
prev
|
||||
@@ -52,7 +48,7 @@ mod imp {
|
||||
_success: Ordering,
|
||||
_failure: Ordering,
|
||||
) -> Result<u64, u64> {
|
||||
let mut lock = self.inner.lock().unwrap();
|
||||
let mut lock = self.inner.lock();
|
||||
|
||||
if *lock == current {
|
||||
*lock = new;
|
||||
|
||||
+36
-1
@@ -157,7 +157,6 @@ macro_rules! cfg_macros {
|
||||
$(
|
||||
#[cfg(feature = "macros")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
#[doc(inline)]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
@@ -183,6 +182,16 @@ macro_rules! cfg_net_unix {
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_net_windows {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(all(any(all(doc, docsrs), windows), feature = "net"))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(windows, feature = "net"))))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_process {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
@@ -375,3 +384,29 @@ macro_rules! cfg_not_coop {
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_has_atomic_u64 {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(not(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc"
|
||||
)))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! cfg_not_has_atomic_u64 {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(
|
||||
target_arch = "arm",
|
||||
target_arch = "mips",
|
||||
target_arch = "powerpc"
|
||||
))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
+136
-86
@@ -23,10 +23,10 @@
|
||||
/// returns the result of evaluating the completed branch's `<handler>`
|
||||
/// expression.
|
||||
///
|
||||
/// Additionally, each branch may include an optional `if` precondition. This
|
||||
/// precondition is evaluated **before** the `<async expression>`. If the
|
||||
/// precondition returns `false`, the branch is entirely disabled. This
|
||||
/// capability is useful when using `select!` within a loop.
|
||||
/// Additionally, each branch may include an optional `if` precondition. If the
|
||||
/// precondition returns `false`, then the branch is disabled. The provided
|
||||
/// `<async expression>` is still evaluated but the resulting future is never
|
||||
/// polled. This capability is useful when using `select!` within a loop.
|
||||
///
|
||||
/// The complete lifecycle of a `select!` expression is as follows:
|
||||
///
|
||||
@@ -42,12 +42,10 @@
|
||||
/// to the provided `<pattern>`, if the pattern matches, evaluate `<handler>`
|
||||
/// and return. If the pattern **does not** match, disable the current branch
|
||||
/// and for the remainder of the current call to `select!`. Continue from step 3.
|
||||
/// 5. If **all** branches are disabled, evaluate the `else` expression. If none
|
||||
/// is provided, panic.
|
||||
/// 5. If **all** branches are disabled, evaluate the `else` expression. If no
|
||||
/// else branch is provided, panic.
|
||||
///
|
||||
/// # Notes
|
||||
///
|
||||
/// ### Runtime characteristics
|
||||
/// # Runtime characteristics
|
||||
///
|
||||
/// By running all async expressions on the current task, the expressions are
|
||||
/// able to run **concurrently** but not in **parallel**. This means all
|
||||
@@ -58,76 +56,7 @@
|
||||
///
|
||||
/// [`tokio::spawn`]: crate::spawn
|
||||
///
|
||||
/// ### Avoid racy `if` preconditions
|
||||
///
|
||||
/// Given that `if` preconditions are used to disable `select!` branches, some
|
||||
/// caution must be used to avoid missing values.
|
||||
///
|
||||
/// For example, here is **incorrect** usage of `sleep` with `if`. The objective
|
||||
/// is to repeatedly run an asynchronous task for up to 50 milliseconds.
|
||||
/// However, there is a potential for the `sleep` completion to be missed.
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::time::{self, Duration};
|
||||
///
|
||||
/// async fn some_async_work() {
|
||||
/// // do work
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let sleep = time::sleep(Duration::from_millis(50));
|
||||
/// tokio::pin!(sleep);
|
||||
///
|
||||
/// while !sleep.is_elapsed() {
|
||||
/// tokio::select! {
|
||||
/// _ = &mut sleep, if !sleep.is_elapsed() => {
|
||||
/// println!("operation timed out");
|
||||
/// }
|
||||
/// _ = some_async_work() => {
|
||||
/// println!("operation completed");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In the above example, `sleep.is_elapsed()` may return `true` even if
|
||||
/// `sleep.poll()` never returned `Ready`. This opens up a potential race
|
||||
/// condition where `sleep` expires between the `while !sleep.is_elapsed()`
|
||||
/// check and the call to `select!` resulting in the `some_async_work()` call to
|
||||
/// run uninterrupted despite the sleep having elapsed.
|
||||
///
|
||||
/// One way to write the above example without the race would be:
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::time::{self, Duration};
|
||||
///
|
||||
/// async fn some_async_work() {
|
||||
/// # time::sleep(Duration::from_millis(10)).await;
|
||||
/// // do work
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let sleep = time::sleep(Duration::from_millis(50));
|
||||
/// tokio::pin!(sleep);
|
||||
///
|
||||
/// loop {
|
||||
/// tokio::select! {
|
||||
/// _ = &mut sleep => {
|
||||
/// println!("operation timed out");
|
||||
/// break;
|
||||
/// }
|
||||
/// _ = some_async_work() => {
|
||||
/// println!("operation completed");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Fairness
|
||||
/// # Fairness
|
||||
///
|
||||
/// By default, `select!` randomly picks a branch to check first. This provides
|
||||
/// some level of fairness when calling `select!` in a loop with branches that
|
||||
@@ -151,10 +80,60 @@
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// `select!` panics if all branches are disabled **and** there is no provided
|
||||
/// `else` branch. A branch is disabled when the provided `if` precondition
|
||||
/// returns `false` **or** when the pattern does not match the result of `<async
|
||||
/// expression>`.
|
||||
/// The `select!` macro panics if all branches are disabled **and** there is no
|
||||
/// provided `else` branch. A branch is disabled when the provided `if`
|
||||
/// precondition returns `false` **or** when the pattern does not match the
|
||||
/// result of `<async expression>`.
|
||||
///
|
||||
/// # Cancellation safety
|
||||
///
|
||||
/// When using `select!` in a loop to receive messages from multiple sources,
|
||||
/// you should make sure that the receive call is cancellation safe to avoid
|
||||
/// losing messages. This section goes through various common methods and
|
||||
/// describes whether they are cancel safe. The lists in this section are not
|
||||
/// exhaustive.
|
||||
///
|
||||
/// The following methods are cancellation safe:
|
||||
///
|
||||
/// * [`tokio::sync::mpsc::Receiver::recv`](crate::sync::mpsc::Receiver::recv)
|
||||
/// * [`tokio::sync::mpsc::UnboundedReceiver::recv`](crate::sync::mpsc::UnboundedReceiver::recv)
|
||||
/// * [`tokio::sync::broadcast::Receiver::recv`](crate::sync::broadcast::Receiver::recv)
|
||||
/// * [`tokio::sync::watch::Receiver::changed`](crate::sync::watch::Receiver::changed)
|
||||
/// * [`tokio::net::TcpListener::accept`](crate::net::TcpListener::accept)
|
||||
/// * [`tokio::net::UnixListener::accept`](crate::net::UnixListener::accept)
|
||||
/// * [`tokio::io::AsyncReadExt::read`](crate::io::AsyncReadExt::read) on any `AsyncRead`
|
||||
/// * [`tokio::io::AsyncReadExt::read_buf`](crate::io::AsyncReadExt::read_buf) on any `AsyncRead`
|
||||
/// * [`tokio::io::AsyncWriteExt::write`](crate::io::AsyncWriteExt::write) on any `AsyncWrite`
|
||||
/// * [`tokio::io::AsyncWriteExt::write_buf`](crate::io::AsyncWriteExt::write_buf) on any `AsyncWrite`
|
||||
/// * [`tokio_stream::StreamExt::next`](https://docs.rs/tokio-stream/0.1/tokio_stream/trait.StreamExt.html#method.next) on any `Stream`
|
||||
/// * [`futures::stream::StreamExt::next`](https://docs.rs/futures/0.3/futures/stream/trait.StreamExt.html#method.next) on any `Stream`
|
||||
///
|
||||
/// The following methods are not cancellation safe and can lead to loss of data:
|
||||
///
|
||||
/// * [`tokio::io::AsyncReadExt::read_exact`](crate::io::AsyncReadExt::read_exact)
|
||||
/// * [`tokio::io::AsyncReadExt::read_to_end`](crate::io::AsyncReadExt::read_to_end)
|
||||
/// * [`tokio::io::AsyncReadExt::read_to_string`](crate::io::AsyncReadExt::read_to_string)
|
||||
/// * [`tokio::io::AsyncWriteExt::write_all`](crate::io::AsyncWriteExt::write_all)
|
||||
///
|
||||
/// The following methods are not cancellation safe because they use a queue for
|
||||
/// fairness and cancellation makes you lose your place in the queue:
|
||||
///
|
||||
/// * [`tokio::sync::Mutex::lock`](crate::sync::Mutex::lock)
|
||||
/// * [`tokio::sync::RwLock::read`](crate::sync::RwLock::read)
|
||||
/// * [`tokio::sync::RwLock::write`](crate::sync::RwLock::write)
|
||||
/// * [`tokio::sync::Semaphore::acquire`](crate::sync::Semaphore::acquire)
|
||||
/// * [`tokio::sync::Notify::notified`](crate::sync::Notify::notified)
|
||||
///
|
||||
/// To determine whether your own methods are cancellation safe, look for the
|
||||
/// location of uses of `.await`. This is because when an asynchronous method is
|
||||
/// cancelled, that always happens at an `.await`. If your function behaves
|
||||
/// correctly even if it is restarted while waiting at an `.await`, then it is
|
||||
/// cancellation safe.
|
||||
///
|
||||
/// Be aware that cancelling something that is not cancellation safe is not
|
||||
/// necessarily wrong. For example, if you are cancelling a task because the
|
||||
/// application is shutting down, then you probably don't care that partially
|
||||
/// read data is lost.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -310,7 +289,7 @@
|
||||
/// loop {
|
||||
/// tokio::select! {
|
||||
/// // If you run this example without `biased;`, the polling order is
|
||||
/// // psuedo-random, and the assertions on the value of count will
|
||||
/// // pseudo-random, and the assertions on the value of count will
|
||||
/// // (probably) fail.
|
||||
/// biased;
|
||||
///
|
||||
@@ -338,6 +317,77 @@
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ## Avoid racy `if` preconditions
|
||||
///
|
||||
/// Given that `if` preconditions are used to disable `select!` branches, some
|
||||
/// caution must be used to avoid missing values.
|
||||
///
|
||||
/// For example, here is **incorrect** usage of `sleep` with `if`. The objective
|
||||
/// is to repeatedly run an asynchronous task for up to 50 milliseconds.
|
||||
/// However, there is a potential for the `sleep` completion to be missed.
|
||||
///
|
||||
/// ```no_run,should_panic
|
||||
/// use tokio::time::{self, Duration};
|
||||
///
|
||||
/// async fn some_async_work() {
|
||||
/// // do work
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let sleep = time::sleep(Duration::from_millis(50));
|
||||
/// tokio::pin!(sleep);
|
||||
///
|
||||
/// while !sleep.is_elapsed() {
|
||||
/// tokio::select! {
|
||||
/// _ = &mut sleep, if !sleep.is_elapsed() => {
|
||||
/// println!("operation timed out");
|
||||
/// }
|
||||
/// _ = some_async_work() => {
|
||||
/// println!("operation completed");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// panic!("This example shows how not to do it!");
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In the above example, `sleep.is_elapsed()` may return `true` even if
|
||||
/// `sleep.poll()` never returned `Ready`. This opens up a potential race
|
||||
/// condition where `sleep` expires between the `while !sleep.is_elapsed()`
|
||||
/// check and the call to `select!` resulting in the `some_async_work()` call to
|
||||
/// run uninterrupted despite the sleep having elapsed.
|
||||
///
|
||||
/// One way to write the above example without the race would be:
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::time::{self, Duration};
|
||||
///
|
||||
/// async fn some_async_work() {
|
||||
/// # time::sleep(Duration::from_millis(10)).await;
|
||||
/// // do work
|
||||
/// }
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let sleep = time::sleep(Duration::from_millis(50));
|
||||
/// tokio::pin!(sleep);
|
||||
///
|
||||
/// loop {
|
||||
/// tokio::select! {
|
||||
/// _ = &mut sleep => {
|
||||
/// println!("operation timed out");
|
||||
/// break;
|
||||
/// }
|
||||
/// _ = some_async_work() => {
|
||||
/// println!("operation completed");
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "macros")))]
|
||||
macro_rules! select {
|
||||
@@ -398,7 +448,7 @@ macro_rules! select {
|
||||
// set the appropriate bit in `disabled`.
|
||||
$(
|
||||
if !$c {
|
||||
let mask = 1 << $crate::count!( $($skip)* );
|
||||
let mask: util::Mask = 1 << $crate::count!( $($skip)* );
|
||||
disabled |= mask;
|
||||
}
|
||||
)*
|
||||
@@ -417,7 +467,7 @@ macro_rules! select {
|
||||
let mut is_pending = false;
|
||||
|
||||
// Choose a starting index to begin polling the futures at. In
|
||||
// practice, this will either be a psuedo-randomly generrated
|
||||
// practice, this will either be a pseudo-randomly generated
|
||||
// number by default, or the constant 0 if `biased;` is
|
||||
// supplied.
|
||||
let start = $start;
|
||||
|
||||
@@ -46,3 +46,7 @@ cfg_net_unix! {
|
||||
pub use unix::listener::UnixListener;
|
||||
pub use unix::stream::UnixStream;
|
||||
}
|
||||
|
||||
cfg_net_windows! {
|
||||
pub mod windows;
|
||||
}
|
||||
|
||||
@@ -125,6 +125,13 @@ impl TcpListener {
|
||||
/// established, the corresponding [`TcpStream`] and the remote peer's
|
||||
/// address will be returned.
|
||||
///
|
||||
/// # Cancel safety
|
||||
///
|
||||
/// This method is cancel safe. If the method is used as the event in a
|
||||
/// [`tokio::select!`](crate::select) statement and some other branch
|
||||
/// completes first, then it is guaranteed that no new connections were
|
||||
/// accepted by this method.
|
||||
///
|
||||
/// [`TcpStream`]: struct@crate::net::TcpStream
|
||||
///
|
||||
/// # Examples
|
||||
|
||||
@@ -482,6 +482,48 @@ impl TcpSocket {
|
||||
let mio = self.inner.listen(backlog)?;
|
||||
TcpListener::new(mio)
|
||||
}
|
||||
|
||||
/// Converts a [`std::net::TcpStream`] into a `TcpSocket`. The provided
|
||||
/// socket must not have been connected prior to calling this function. This
|
||||
/// function is typically used together with crates such as [`socket2`] to
|
||||
/// configure socket options that are not available on `TcpSocket`.
|
||||
///
|
||||
/// [`std::net::TcpStream`]: struct@std::net::TcpStream
|
||||
/// [`socket2`]: https://docs.rs/socket2/
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::net::TcpSocket;
|
||||
/// use socket2::{Domain, Socket, Type};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> std::io::Result<()> {
|
||||
///
|
||||
/// let socket2_socket = Socket::new(Domain::IPV4, Type::STREAM, None)?;
|
||||
///
|
||||
/// let socket = TcpSocket::from_std_stream(socket2_socket.into());
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_std_stream(std_stream: std::net::TcpStream) -> TcpSocket {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::io::{FromRawFd, IntoRawFd};
|
||||
|
||||
let raw_fd = std_stream.into_raw_fd();
|
||||
unsafe { TcpSocket::from_raw_fd(raw_fd) }
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
{
|
||||
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
|
||||
|
||||
let raw_socket = std_stream.into_raw_socket();
|
||||
unsafe { TcpSocket::from_raw_socket(raw_socket) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for TcpSocket {
|
||||
|
||||
@@ -30,7 +30,7 @@ pub struct ReadHalf<'a>(&'a TcpStream);
|
||||
|
||||
/// Borrowed write half of a [`TcpStream`], created by [`split`].
|
||||
///
|
||||
/// Note that in the [`AsyncWrite`] implemenation of this type, [`poll_shutdown`] will
|
||||
/// Note that in the [`AsyncWrite`] implementation of this type, [`poll_shutdown`] will
|
||||
/// shut down the TCP stream in the write direction.
|
||||
///
|
||||
/// Writing to an `WriteHalf` is usually done using the convenience methods found
|
||||
@@ -57,7 +57,7 @@ impl ReadHalf<'_> {
|
||||
/// `Waker` from the `Context` passed to the most recent call is scheduled
|
||||
/// to receive a wakeup.
|
||||
///
|
||||
/// See the [`TcpStream::poll_peek`] level documenation for more details.
|
||||
/// See the [`TcpStream::poll_peek`] level documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -95,7 +95,7 @@ impl ReadHalf<'_> {
|
||||
/// connected, without removing that data from the queue. On success,
|
||||
/// returns the number of bytes peeked.
|
||||
///
|
||||
/// See the [`TcpStream::peek`] level documenation for more details.
|
||||
/// See the [`TcpStream::peek`] level documentation for more details.
|
||||
///
|
||||
/// [`TcpStream::peek`]: TcpStream::peek
|
||||
///
|
||||
|
||||
@@ -112,7 +112,7 @@ impl OwnedReadHalf {
|
||||
/// `Waker` from the `Context` passed to the most recent call is scheduled
|
||||
/// to receive a wakeup.
|
||||
///
|
||||
/// See the [`TcpStream::poll_peek`] level documenation for more details.
|
||||
/// See the [`TcpStream::poll_peek`] level documentation for more details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -150,7 +150,7 @@ impl OwnedReadHalf {
|
||||
/// connected, without removing that data from the queue. On success,
|
||||
/// returns the number of bytes peeked.
|
||||
///
|
||||
/// See the [`TcpStream::peek`] level documenation for more details.
|
||||
/// See the [`TcpStream::peek`] level documentation for more details.
|
||||
///
|
||||
/// [`TcpStream::peek`]: TcpStream::peek
|
||||
///
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user