mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-09-09 00:00:08 +02:00
Compare commits
71
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bf4696f31 | ||
|
|
10398b20c0 | ||
|
|
780d6f91a0 | ||
|
|
45da5f3510 | ||
|
|
855d39f849 | ||
|
|
798e86821f | ||
|
|
0193df3a59 | ||
|
|
5930acef73 | ||
|
|
3540c5b9ee | ||
|
|
188fc6e0d2 | ||
|
|
d45f61c183 | ||
|
|
dcfa895b51 | ||
|
|
f0006006ed | ||
|
|
84ff73e687 | ||
|
|
32e15b3a24 | ||
|
|
efcbf9613f | ||
|
|
3736467dbb | ||
|
|
e43f28f6a8 | ||
|
|
3cf91db4b6 | ||
|
|
e8fcf55881 | ||
|
|
a515f9c459 | ||
|
|
50b91c0247 | ||
|
|
67bf9c36f3 | ||
|
|
101f770af3 | ||
|
|
6ff4e349e2 | ||
|
|
adc5186ebd | ||
|
|
7b53b7b659 | ||
|
|
99fa93bf0e | ||
|
|
0133bc1883 | ||
|
|
a854094825 | ||
|
|
3d1b4b3058 | ||
|
|
8656b7b8eb | ||
|
|
f309b295bb | ||
|
|
b1266a48c4 | ||
|
|
de5ec6e1bc | ||
|
|
3dcd76a38f | ||
|
|
3b9c7b1715 | ||
|
|
3bff5a3ffe | ||
|
|
248bf2144f | ||
|
|
93ab70a9a0 | ||
|
|
58b5abdb99 | ||
|
|
2d78cfe56a | ||
|
|
4c645866ef | ||
|
|
b0836ece7a | ||
|
|
0c0f682010 | ||
|
|
b24ad9fe86 | ||
|
|
7c010ed030 | ||
|
|
42c942de14 | ||
|
|
5e8f7eb03c | ||
|
|
9211adbe01 | ||
|
|
e5b99b0f7a | ||
|
|
83cd754bc8 | ||
|
|
17e424112d | ||
|
|
41d15ea212 | ||
|
|
8add90210b | ||
|
|
efb4b67a54 | ||
|
|
74d33a1b2f | ||
|
|
69885e214c | ||
|
|
4b85565bd7 | ||
|
|
d593c5b051 | ||
|
|
91ecb4b4c2 | ||
|
|
8abaf89e5f | ||
|
|
b560df9e66 | ||
|
|
df8278acb6 | ||
|
|
5862b9a2e0 | ||
|
|
c0953d41a5 | ||
|
|
24cd6d67f7 | ||
|
|
975576952f | ||
|
|
5d5755dca4 | ||
|
|
41ffdbb7d9 | ||
|
|
2450b5bfc9 |
@@ -8,6 +8,7 @@ members = [
|
||||
"tokio-util",
|
||||
|
||||
# Internal
|
||||
"benches",
|
||||
"examples",
|
||||
"tests-build",
|
||||
"tests-integration",
|
||||
|
||||
@@ -29,6 +29,7 @@ the Rust programming language. It is:
|
||||
[Website](https://tokio.rs) |
|
||||
[Guides](https://tokio.rs/docs/) |
|
||||
[API Docs](https://docs.rs/tokio/latest/tokio) |
|
||||
[Roadmap](https://github.com/tokio-rs/tokio/blob/master/ROADMAP.md) |
|
||||
[Chat](https://discord.gg/6yGkFeN)
|
||||
|
||||
## Overview
|
||||
@@ -87,7 +88,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
More examples can be found [here](examples). Note that the `master` branch
|
||||
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
# Tokio Roadmap
|
||||
|
||||
## A Roadmap to 1.0
|
||||
|
||||
The question of "why not 1.0?" has come up a few times. After all, Tokio 0.1 has
|
||||
been stable for three years. The short answer: because it isn't time. There is
|
||||
nobody who would rather ship a Tokio 1.0 than us. It also isn't something to rush.
|
||||
|
||||
After all, `async / await` only landed in the stable Rust channel weeks ago.
|
||||
There has been no significant production validation yet, except maybe fuchsia
|
||||
and that seems like a fairly specialized use case. This release of Tokio
|
||||
includes significant new code and new strategies with feature flags. Also, there
|
||||
are still big open questions, such as the [proposed changes][pr-1744] to
|
||||
`AsyncRead` and `AsyncWrite`.
|
||||
|
||||
Tokio 1.0 will be released as soon as the APIs are proven to handle real-world
|
||||
production cases.
|
||||
|
||||
### Tokio 1.0 in Q3 2020 with LTS support
|
||||
|
||||
The Tokio 1.0 release will be **no later** than Q3 2020. It will also come with
|
||||
"long-term support" guarantees:
|
||||
|
||||
* A minimum of 5 years of maintenance.
|
||||
* A minimum of 3 years before a hypothetical 2.0 release.
|
||||
|
||||
When Tokio 1.0 is released in Q3 2020, on-going support, security fixes, and
|
||||
critical bug fixes are guaranteed until **at least** Q3 2025. Tokio 2.0 will not
|
||||
be released until **at least** Q3 2023 (though, ideally there will never been a
|
||||
Tokio 2.0 release).
|
||||
|
||||
### How to get there
|
||||
|
||||
While Tokio 0.1 probably should have been a 1.0, Tokio 0.2 will be a **true**
|
||||
0.2 release. There will breaking change releases every 2 ~ 3 months until 1.0.
|
||||
These changes will be **much** smaller than going from 0.1 -> 0.2. It is
|
||||
expected that the 1.0 release will look a lot like 0.2.
|
||||
|
||||
### What is expected to change
|
||||
|
||||
The biggest change will be the `AsyncRead` and `AsyncWrite` traits. Based on
|
||||
experience gained over the past 3 years, there are a couple of issues to
|
||||
address:
|
||||
|
||||
* Be able to **safely** use uninitialized memory as a read buffer.
|
||||
* Practical read vectored and write vectored APIs.
|
||||
|
||||
There are a few strategies to solve these problems. These strategies need to be
|
||||
investigated and the solution validated. You can see [this comment][pr-1744-comment] for a
|
||||
detailed statement of the problem.
|
||||
|
||||
The other major change, which has been in the works for a while, is updating
|
||||
Mio. Mio 0.6 was first released almost 4 years ago and has not had a breaking
|
||||
change since. Mio 0.7 has been in the works for a while. It includes a full
|
||||
rewrite of the windows support as well as a refined API. More will be written
|
||||
about this shortly.
|
||||
|
||||
Finally, now that the API is starting to stabilize, effort will be put into
|
||||
documentation. Tokio 0.2 is being released before updating the website and many
|
||||
of the old content will no longer be relevant. In the coming weeks, expect to
|
||||
see updates there.
|
||||
|
||||
So, we have our work cut out for us. We hope you enjoy this 0.2 release and are
|
||||
looking forward to your feedback and help.
|
||||
|
||||
[pr-1744]: https://github.com/tokio-rs/tokio/pull/1744
|
||||
[pr-1744-comment]: https://github.com/tokio-rs/tokio/pull/1744#issuecomment-553575438
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "benches"
|
||||
version = "0.0.0"
|
||||
publish = false
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["full"] }
|
||||
bencher = "0.1.5"
|
||||
|
||||
[[bench]]
|
||||
name = "spawn"
|
||||
path = "spawn.rs"
|
||||
harness = false
|
||||
@@ -0,0 +1,70 @@
|
||||
//! Benchmark spawning a task onto the basic and threaded Tokio executors.
|
||||
//! This essentially measure the time to enqueue a task in the local and remote
|
||||
//! case.
|
||||
|
||||
use bencher::{black_box, Bencher};
|
||||
|
||||
async fn work() -> usize {
|
||||
let val = 1 + 1;
|
||||
black_box(val)
|
||||
}
|
||||
|
||||
fn basic_scheduler_local_spawn(bench: &mut Bencher) {
|
||||
let mut runtime = tokio::runtime::Builder::new()
|
||||
.basic_scheduler()
|
||||
.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 mut runtime = tokio::runtime::Builder::new()
|
||||
.threaded_scheduler()
|
||||
.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()
|
||||
.basic_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
let handle = runtime.handle();
|
||||
bench.iter(|| {
|
||||
let h = handle.spawn(work());
|
||||
black_box(h);
|
||||
});
|
||||
}
|
||||
|
||||
fn threaded_scheduler_remote_spawn(bench: &mut Bencher) {
|
||||
let runtime = tokio::runtime::Builder::new()
|
||||
.threaded_scheduler()
|
||||
.build()
|
||||
.unwrap();
|
||||
let handle = runtime.handle();
|
||||
bench.iter(|| {
|
||||
let h = handle.spawn(work());
|
||||
black_box(h);
|
||||
});
|
||||
}
|
||||
|
||||
bencher::benchmark_group!(
|
||||
benches,
|
||||
basic_scheduler_local_spawn,
|
||||
threaded_scheduler_local_spawn,
|
||||
basic_scheduler_remote_spawn,
|
||||
threaded_scheduler_remote_spawn
|
||||
);
|
||||
|
||||
bencher::benchmark_main!(benches);
|
||||
+1
-1
@@ -12,5 +12,5 @@ jobs:
|
||||
cargo clippy --version
|
||||
displayName: Install clippy
|
||||
- script: |
|
||||
cargo clippy --all --all-features -- -A clippy::mutex-atomic
|
||||
cargo clippy --all --all-features -- -A clippy::mutex-atomic -A clippy::needless-doctest-main
|
||||
displayName: cargo clippy --all
|
||||
|
||||
@@ -13,5 +13,6 @@ jobs:
|
||||
cargo fmt --version
|
||||
displayName: Install rustfmt
|
||||
- script: |
|
||||
cargo fmt --all -- --check
|
||||
# Workaround for rust-lang/cargo#7732
|
||||
rustfmt --check --edition 2018 $(find . -name '*.rs' -print)
|
||||
displayName: Check formatting
|
||||
|
||||
@@ -30,6 +30,11 @@ jobs:
|
||||
displayName: ${{ crate }} - cargo test --all-features
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
|
||||
# Check benches
|
||||
- script: cargo check --all-features --benches
|
||||
displayName: ${{ crate }} - cargo check --benches
|
||||
workingDirectory: $(Build.SourcesDirectory)/${{ crate }}
|
||||
|
||||
- template: azure-patch-crates.yml
|
||||
|
||||
- ${{ each crate in parameters.crates }}:
|
||||
|
||||
+7
-4
@@ -27,10 +27,11 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::stream::{Stream, StreamExt};
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
use tokio_util::codec::{Framed, LinesCodec, LinesCodecError};
|
||||
|
||||
use futures::{SinkExt, Stream, StreamExt};
|
||||
use futures::SinkExt;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
@@ -49,7 +50,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// client connection.
|
||||
let state = Arc::new(Mutex::new(Shared::new()));
|
||||
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:6142".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:6142".to_string());
|
||||
|
||||
// Bind a TCP listener to the socket address.
|
||||
//
|
||||
@@ -161,12 +164,12 @@ impl Stream for Peer {
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
// First poll the `UnboundedReceiver`.
|
||||
|
||||
if let Poll::Ready(Some(v)) = self.rx.poll_next_unpin(cx) {
|
||||
if let Poll::Ready(Some(v)) = Pin::new(&mut self.rx).poll_next(cx) {
|
||||
return Poll::Ready(Some(Ok(Message::Received(v))));
|
||||
}
|
||||
|
||||
// Secondly poll the `Framed` stream.
|
||||
let result: Option<_> = futures::ready!(self.lines.poll_next_unpin(cx));
|
||||
let result: Option<_> = futures::ready!(Pin::new(&mut self.lines).poll_next(cx));
|
||||
|
||||
Poll::Ready(match result {
|
||||
// We've received a message we should broadcast to others.
|
||||
|
||||
+27
-62
@@ -16,8 +16,9 @@
|
||||
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use futures::StreamExt;
|
||||
use tokio::io;
|
||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
@@ -36,14 +37,14 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
};
|
||||
|
||||
// Parse what address we're going to connect to
|
||||
let addr = match args.first() {
|
||||
Some(addr) => addr,
|
||||
None => Err("this program requires at least one argument")?,
|
||||
};
|
||||
let addr = args
|
||||
.first()
|
||||
.ok_or("this program requires at least one argument")?;
|
||||
let addr = addr.parse::<SocketAddr>()?;
|
||||
|
||||
let stdin = FramedRead::new(io::stdin(), codec::Bytes);
|
||||
let stdout = FramedWrite::new(io::stdout(), codec::Bytes);
|
||||
let stdin = FramedRead::new(io::stdin(), BytesCodec::new());
|
||||
let stdin = stdin.map(|i| i.map(|bytes| bytes.freeze()));
|
||||
let stdout = FramedWrite::new(io::stdout(), BytesCodec::new());
|
||||
|
||||
if tcp {
|
||||
tcp::connect(&addr, stdin, stdout).await?;
|
||||
@@ -55,23 +56,26 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
mod tcp {
|
||||
use super::codec;
|
||||
use bytes::Bytes;
|
||||
use futures::{future, Sink, SinkExt, Stream, StreamExt};
|
||||
use std::{error::Error, io, net::SocketAddr};
|
||||
use tokio::net::TcpStream;
|
||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||
use tokio_util::codec::{BytesCodec, FramedRead, FramedWrite};
|
||||
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: impl Stream<Item = Result<Vec<u8>, io::Error>> + Unpin,
|
||||
mut stdout: impl Sink<Vec<u8>, Error = io::Error> + Unpin,
|
||||
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
let mut stream = TcpStream::connect(addr).await?;
|
||||
let (r, w) = stream.split();
|
||||
let sink = FramedWrite::new(w, codec::Bytes);
|
||||
let mut stream = FramedRead::new(r, codec::Bytes)
|
||||
let mut sink = FramedWrite::new(w, BytesCodec::new());
|
||||
// filter map Result<BytesMut, Error> stream into just a Bytes stream to match stdout Sink
|
||||
// on the event of an Error, log the error and end the stream
|
||||
let mut stream = FramedRead::new(r, BytesCodec::new())
|
||||
.filter_map(|i| match i {
|
||||
Ok(i) => future::ready(Some(i)),
|
||||
//BytesMut into Bytes
|
||||
Ok(i) => future::ready(Some(i.freeze())),
|
||||
Err(e) => {
|
||||
println!("failed to read from socket; error={}", e);
|
||||
future::ready(None)
|
||||
@@ -79,7 +83,7 @@ mod tcp {
|
||||
})
|
||||
.map(Ok);
|
||||
|
||||
match future::join(stdin.forward(sink), stdout.send_all(&mut stream)).await {
|
||||
match future::join(sink.send_all(&mut stdin), stdout.send_all(&mut stream)).await {
|
||||
(Err(e), _) | (_, Err(e)) => Err(e.into()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
@@ -87,18 +91,18 @@ mod tcp {
|
||||
}
|
||||
|
||||
mod udp {
|
||||
use tokio::net::udp::{RecvHalf, SendHalf};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{future, Sink, SinkExt, Stream, StreamExt};
|
||||
use std::error::Error;
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use tokio::net::udp::{RecvHalf, SendHalf};
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
pub async fn connect(
|
||||
addr: &SocketAddr,
|
||||
stdin: impl Stream<Item = Result<Vec<u8>, io::Error>> + Unpin,
|
||||
stdout: impl Sink<Vec<u8>, Error = io::Error> + Unpin,
|
||||
stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
// We'll bind our UDP socket to a local IP/port, but for now we
|
||||
// basically let the OS pick both of those.
|
||||
@@ -118,7 +122,7 @@ mod udp {
|
||||
}
|
||||
|
||||
async fn send(
|
||||
mut stdin: impl Stream<Item = Result<Vec<u8>, io::Error>> + Unpin,
|
||||
mut stdin: impl Stream<Item = Result<Bytes, io::Error>> + Unpin,
|
||||
writer: &mut SendHalf,
|
||||
) -> Result<(), io::Error> {
|
||||
while let Some(item) = stdin.next().await {
|
||||
@@ -130,7 +134,7 @@ mod udp {
|
||||
}
|
||||
|
||||
async fn recv(
|
||||
mut stdout: impl Sink<Vec<u8>, Error = io::Error> + Unpin,
|
||||
mut stdout: impl Sink<Bytes, Error = io::Error> + Unpin,
|
||||
reader: &mut RecvHalf,
|
||||
) -> Result<(), io::Error> {
|
||||
loop {
|
||||
@@ -138,47 +142,8 @@ mod udp {
|
||||
let n = reader.recv(&mut buf[..]).await?;
|
||||
|
||||
if n > 0 {
|
||||
stdout.send(buf).await?;
|
||||
stdout.send(Bytes::from(buf)).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod codec {
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use std::io;
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
|
||||
/// A simple `Codec` implementation that just ships bytes around.
|
||||
///
|
||||
/// This type is used for "framing" a TCP/UDP stream of bytes but it's really
|
||||
/// just a convenient method for us to work with streams/sinks for now.
|
||||
/// This'll just take any data read and interpret it as a "frame" and
|
||||
/// conversely just shove data into the output location without looking at
|
||||
/// it.
|
||||
pub struct Bytes;
|
||||
|
||||
impl Decoder for Bytes {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn decode(&mut self, buf: &mut BytesMut) -> io::Result<Option<Vec<u8>>> {
|
||||
if buf.len() > 0 {
|
||||
let len = buf.len();
|
||||
Ok(Some(buf.split_to(len).into_iter().collect()))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Encoder for Bytes {
|
||||
type Item = Vec<u8>;
|
||||
type Error = io::Error;
|
||||
|
||||
fn encode(&mut self, data: Vec<u8>, buf: &mut BytesMut) -> io::Result<()> {
|
||||
buf.put(&data[..]);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,9 @@ impl Server {
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
let socket = UdpSocket::bind(&addr).await?;
|
||||
println!("Listening on: {}", socket.local_addr()?);
|
||||
|
||||
+3
-1
@@ -33,7 +33,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Allow passing an address to listen on as the first argument of this
|
||||
// program, but otherwise we'll just set up our TCP listener on
|
||||
// 127.0.0.1:8080 for connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
// Next up we create a TCP listener which will listen for incoming
|
||||
// connections. This TCP listener is bound to the address we determined
|
||||
|
||||
@@ -55,9 +55,9 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_util::codec::{BytesCodec, Decoder};
|
||||
|
||||
use futures::StreamExt;
|
||||
use std::env;
|
||||
|
||||
#[tokio::main]
|
||||
@@ -65,7 +65,9 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Allow passing an address to listen on as the first argument of this
|
||||
// program, but otherwise we'll just set up our TCP listener on
|
||||
// 127.0.0.1:8080 for connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
// Next up we create a TCP listener which will listen for incoming
|
||||
// connections. This TCP listener is bound to the address we determined
|
||||
|
||||
+6
-2
@@ -32,8 +32,12 @@ use std::error::Error;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let listen_addr = env::args().nth(1).unwrap_or("127.0.0.1:8081".to_string());
|
||||
let server_addr = env::args().nth(2).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let listen_addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8081".to_string());
|
||||
let server_addr = env::args()
|
||||
.nth(2)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
println!("Listening on: {}", listen_addr);
|
||||
println!("Proxying to: {}", server_addr);
|
||||
|
||||
+11
-11
@@ -42,9 +42,10 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_util::codec::{Framed, LinesCodec};
|
||||
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use futures::SinkExt;
|
||||
use std::collections::HashMap;
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
@@ -84,7 +85,9 @@ enum Response {
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Parse the address we're going to run this server on
|
||||
// and set up our TCP listener to accept connections.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
|
||||
let mut listener = TcpListener::bind(&addr).await?;
|
||||
println!("Listening on: {}", addr);
|
||||
@@ -175,15 +178,12 @@ fn handle_request(line: &str, db: &Arc<Database>) -> Response {
|
||||
|
||||
impl Request {
|
||||
fn parse(input: &str) -> Result<Request, String> {
|
||||
let mut parts = input.splitn(3, " ");
|
||||
let mut parts = input.splitn(3, ' ');
|
||||
match parts.next() {
|
||||
Some("GET") => {
|
||||
let key = match parts.next() {
|
||||
Some(key) => key,
|
||||
None => return Err(format!("GET must be followed by a key")),
|
||||
};
|
||||
let key = parts.next().ok_or("GET must be followed by a key")?;
|
||||
if parts.next().is_some() {
|
||||
return Err(format!("GET's key must not be followed by anything"));
|
||||
return Err("GET's key must not be followed by anything".into());
|
||||
}
|
||||
Ok(Request::Get {
|
||||
key: key.to_string(),
|
||||
@@ -192,11 +192,11 @@ impl Request {
|
||||
Some("SET") => {
|
||||
let key = match parts.next() {
|
||||
Some(key) => key,
|
||||
None => return Err(format!("SET must be followed by a key")),
|
||||
None => return Err("SET must be followed by a key".into()),
|
||||
};
|
||||
let value = match parts.next() {
|
||||
Some(value) => value,
|
||||
None => return Err(format!("SET needs a value")),
|
||||
None => return Err("SET needs a value".into()),
|
||||
};
|
||||
Ok(Request::Set {
|
||||
key: key.to_string(),
|
||||
@@ -204,7 +204,7 @@ impl Request {
|
||||
})
|
||||
}
|
||||
Some(cmd) => Err(format!("unknown command: {}", cmd)),
|
||||
None => Err(format!("empty input")),
|
||||
None => Err("empty input".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,20 +14,23 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures::{SinkExt, StreamExt};
|
||||
use futures::SinkExt;
|
||||
use http::{header::HeaderValue, Request, Response, StatusCode};
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
use serde_json;
|
||||
use std::{env, error::Error, fmt, io};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Parse the arguments, bind the TCP socket we'll be listening to, spin up
|
||||
// our worker threads, and start shipping sockets to those worker threads.
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:8080".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".to_string());
|
||||
let mut server = TcpListener::bind(&addr).await?;
|
||||
let mut incoming = server.incoming();
|
||||
println!("Listening on: {}", addr);
|
||||
|
||||
@@ -44,7 +44,7 @@ fn get_stdin_data() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let remote_addr: SocketAddr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or("127.0.0.1:8080".into())
|
||||
.unwrap_or_else(|| "127.0.0.1:8080".into())
|
||||
.parse()?;
|
||||
|
||||
// We use port 0 to let the operating system allocate an available port for us.
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio::{io, time};
|
||||
use tokio_util::codec::BytesCodec;
|
||||
use tokio_util::udp::UdpFramed;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures::{FutureExt, SinkExt, StreamExt};
|
||||
use futures::{FutureExt, SinkExt};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::net::SocketAddr;
|
||||
@@ -22,7 +23,9 @@ use std::time::Duration;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let addr = env::args().nth(1).unwrap_or("127.0.0.1:0".to_string());
|
||||
let addr = env::args()
|
||||
.nth(1)
|
||||
.unwrap_or_else(|| "127.0.0.1:0".to_string());
|
||||
|
||||
// Bind both our sockets and then figure out what ports we got.
|
||||
let a = UdpSocket::bind(&addr).await?;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
edition = "2018"
|
||||
@@ -25,8 +25,8 @@ fn cat() -> Command {
|
||||
}
|
||||
|
||||
async fn feed_cat(mut cat: Child, n: usize) -> io::Result<ExitStatus> {
|
||||
let mut stdin = cat.stdin().take().unwrap();
|
||||
let stdout = cat.stdout().take().unwrap();
|
||||
let mut stdin = cat.stdin.take().unwrap();
|
||||
let stdout = cat.stdout.take().unwrap();
|
||||
|
||||
// Produce n lines on the child's stdout.
|
||||
let write = async {
|
||||
@@ -97,7 +97,7 @@ async fn feed_a_lot() {
|
||||
#[tokio::test]
|
||||
async fn wait_with_output_captures() {
|
||||
let mut child = cat().spawn().unwrap();
|
||||
let mut stdin = child.stdin().take().unwrap();
|
||||
let mut stdin = child.stdin.take().unwrap();
|
||||
|
||||
let write_bytes = b"1234";
|
||||
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
# 0.2.1 (December 18, 2019)
|
||||
|
||||
### Fixes
|
||||
- inherit visibility when wrapping async fn (#1954).
|
||||
|
||||
# 0.2.0 (November 26, 2019)
|
||||
|
||||
- Initial release
|
||||
|
||||
@@ -7,13 +7,13 @@ name = "tokio-macros"
|
||||
# - Cargo.toml
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.1.x" git tag.
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
documentation = "https://docs.rs/tokio-macros/0.2.0/tokio_macros"
|
||||
documentation = "https://docs.rs/tokio-macros/0.2.1/tokio_macros"
|
||||
description = """
|
||||
Tokio's proc macros.
|
||||
"""
|
||||
|
||||
+227
-119
@@ -1,4 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.0")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-macros/0.2.1")]
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
@@ -17,19 +18,176 @@ extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq)]
|
||||
enum Runtime {
|
||||
Basic,
|
||||
Threaded,
|
||||
Auto,
|
||||
}
|
||||
|
||||
fn parse_knobs(
|
||||
input: syn::ItemFn,
|
||||
args: syn::AttributeArgs,
|
||||
is_test: bool,
|
||||
rt_threaded: bool,
|
||||
) -> Result<TokenStream, syn::Error> {
|
||||
let ret = &input.sig.output;
|
||||
let name = &input.sig.ident;
|
||||
let inputs = &input.sig.inputs;
|
||||
let body = &input.block;
|
||||
let attrs = &input.attrs;
|
||||
let vis = input.vis;
|
||||
|
||||
if input.sig.asyncness.is_none() {
|
||||
let msg = "the async keyword is missing from the function declaration";
|
||||
return Err(syn::Error::new_spanned(input.sig.fn_token, msg));
|
||||
}
|
||||
|
||||
let mut runtime = None;
|
||||
let mut core_threads = None;
|
||||
let mut max_threads = None;
|
||||
|
||||
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() {
|
||||
"core_threads" => {
|
||||
if rt_threaded {
|
||||
match &namevalue.lit {
|
||||
syn::Lit::Int(expr) => {
|
||||
let num = expr.base10_parse::<NonZeroUsize>().unwrap();
|
||||
if num.get() > 1 {
|
||||
runtime = Some(Runtime::Threaded);
|
||||
} else {
|
||||
runtime = Some(Runtime::Basic);
|
||||
}
|
||||
|
||||
if let Some(v) = max_threads {
|
||||
if v < num {
|
||||
return Err(syn::Error::new_spanned(
|
||||
namevalue,
|
||||
"max_threads cannot be less than core_threads",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
core_threads = Some(num);
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
namevalue,
|
||||
"core_threads argument must be an int",
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(syn::Error::new_spanned(
|
||||
namevalue,
|
||||
"core_threads can only be set with rt-threaded feature flag enabled",
|
||||
));
|
||||
}
|
||||
}
|
||||
"max_threads" => match &namevalue.lit {
|
||||
syn::Lit::Int(expr) => {
|
||||
let num = expr.base10_parse::<NonZeroUsize>().unwrap();
|
||||
|
||||
if let Some(v) = core_threads {
|
||||
if num < v {
|
||||
return Err(syn::Error::new_spanned(
|
||||
namevalue,
|
||||
"max_threads cannot be less than core_threads",
|
||||
));
|
||||
}
|
||||
}
|
||||
max_threads = Some(num);
|
||||
}
|
||||
_ => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
namevalue,
|
||||
"max_threads argument must be an int",
|
||||
))
|
||||
}
|
||||
},
|
||||
name => {
|
||||
let msg = format!("Unknown attribute pair {} is specified; expected one of: `core_threads`, `max_threads`", name);
|
||||
return Err(syn::Error::new_spanned(namevalue, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
match ident.unwrap().to_string().to_lowercase().as_str() {
|
||||
"threaded_scheduler" => {
|
||||
runtime = Some(runtime.unwrap_or_else(|| Runtime::Threaded))
|
||||
}
|
||||
"basic_scheduler" => runtime = Some(runtime.unwrap_or_else(|| Runtime::Basic)),
|
||||
name => {
|
||||
let msg = format!("Unknown attribute {} is specified; expected `basic_scheduler` or `threaded_scheduler`", name);
|
||||
return Err(syn::Error::new_spanned(path, msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
other => {
|
||||
return Err(syn::Error::new_spanned(
|
||||
other,
|
||||
"Unknown attribute inside the macro",
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut rt = quote! { tokio::runtime::Builder::new().basic_scheduler() };
|
||||
if rt_threaded && (runtime == Some(Runtime::Threaded) || (runtime.is_none() && !is_test)) {
|
||||
rt = quote! { #rt.threaded_scheduler() };
|
||||
}
|
||||
if let Some(v) = core_threads.map(|v| v.get()) {
|
||||
rt = quote! { #rt.core_threads(#v) };
|
||||
}
|
||||
if let Some(v) = max_threads.map(|v| v.get()) {
|
||||
rt = quote! { #rt.max_threads(#v) };
|
||||
}
|
||||
|
||||
let header = {
|
||||
if is_test {
|
||||
quote! {
|
||||
#[test]
|
||||
}
|
||||
} else {
|
||||
quote! {}
|
||||
}
|
||||
};
|
||||
|
||||
let result = quote! {
|
||||
#header
|
||||
#(#attrs)*
|
||||
#vis fn #name(#inputs) #ret {
|
||||
#rt
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async { #body })
|
||||
}
|
||||
};
|
||||
|
||||
Ok(result.into())
|
||||
}
|
||||
|
||||
/// Marks async function to be executed by selected runtime.
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
/// - `basic_scheduler` - All tasks are executed on the current thread.
|
||||
/// - `threaded_scheduler` - Uses the multi-threaded scheduler. Used by default.
|
||||
/// - `core_threads=n` - Sets core threads to `n`.
|
||||
/// - `max_threads=n` - Sets max threads to `n`.
|
||||
///
|
||||
/// ## Function arguments:
|
||||
///
|
||||
@@ -46,94 +204,73 @@ enum Runtime {
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// ### Select runtime
|
||||
/// ### Set number of core threads
|
||||
///
|
||||
/// ```rust
|
||||
/// #[tokio::main(basic_scheduler)]
|
||||
/// #[tokio::main(core_threads = 1)]
|
||||
/// async fn main() {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
pub fn main_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
main(args, item, true)
|
||||
}
|
||||
|
||||
/// Marks async function to be executed by selected runtime.
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
/// - `max_threads=n` - Sets max threads to `n`.
|
||||
///
|
||||
/// ## Function arguments:
|
||||
///
|
||||
/// Arguments are allowed for any functions aside from `main` which is special
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ### Using default
|
||||
///
|
||||
/// ```rust
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// println!("Hello world");
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub fn main_basic(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
main(args, item, false)
|
||||
}
|
||||
|
||||
fn main(args: TokenStream, item: TokenStream, rt_threaded: bool) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||
|
||||
let ret = &input.sig.output;
|
||||
let name = &input.sig.ident;
|
||||
let inputs = &input.sig.inputs;
|
||||
let body = &input.block;
|
||||
let attrs = &input.attrs;
|
||||
|
||||
if input.sig.asyncness.is_none() {
|
||||
let msg = "the async keyword is missing from the function declaration";
|
||||
return syn::Error::new_spanned(input.sig.fn_token, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
} else if name == "main" && !inputs.is_empty() {
|
||||
if input.sig.ident == "main" && !input.sig.inputs.is_empty() {
|
||||
let msg = "the main function cannot accept arguments";
|
||||
return syn::Error::new_spanned(&input.sig.inputs, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut runtime = Runtime::Auto;
|
||||
|
||||
for arg in args {
|
||||
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = arg {
|
||||
let ident = path.get_ident();
|
||||
if ident.is_none() {
|
||||
let msg = "Must have specified ident";
|
||||
return syn::Error::new_spanned(path, msg).to_compile_error().into();
|
||||
}
|
||||
match ident.unwrap().to_string().to_lowercase().as_str() {
|
||||
"threaded_scheduler" => runtime = Runtime::Threaded,
|
||||
"basic_scheduler" => runtime = Runtime::Basic,
|
||||
name => {
|
||||
let msg = format!("Unknown attribute {} is specified; expected `basic_scheduler` or `threaded_scheduler`", name);
|
||||
return syn::Error::new_spanned(path, msg).to_compile_error().into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = match runtime {
|
||||
Runtime::Threaded | Runtime::Auto => quote! {
|
||||
#(#attrs)*
|
||||
fn #name(#inputs) #ret {
|
||||
tokio::runtime::Runtime::new().unwrap().block_on(async { #body })
|
||||
}
|
||||
},
|
||||
Runtime::Basic => quote! {
|
||||
#(#attrs)*
|
||||
fn #name(#inputs) #ret {
|
||||
tokio::runtime::Builder::new()
|
||||
.basic_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async { #body })
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
result.into()
|
||||
parse_knobs(input, args, false, rt_threaded).unwrap_or_else(|e| e.to_compile_error().into())
|
||||
}
|
||||
|
||||
/// Marks async function to be executed by runtime, suitable to test enviornment
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
/// - `basic_scheduler` - All tasks are executed on the current thread. Used by default.
|
||||
/// - `threaded_scheduler` - Use multi-threaded scheduler.
|
||||
/// - `core_threads=n` - Sets core threads to `n`.
|
||||
/// - `max_threads=n` - Sets max threads to `n`.
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ### Select runtime
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[tokio::test(threaded_scheduler)]
|
||||
/// #[tokio::test(core_threads = 1)]
|
||||
/// async fn my_test() {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
@@ -148,16 +285,34 @@ pub fn main(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
pub fn test_threaded(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
test(args, item, true)
|
||||
}
|
||||
|
||||
/// Marks async function to be executed by runtime, suitable to test enviornment
|
||||
///
|
||||
/// ## Options:
|
||||
///
|
||||
/// - `max_threads=n` - Sets max threads to `n`.
|
||||
///
|
||||
/// ## Usage
|
||||
///
|
||||
/// ```no_run
|
||||
/// #[tokio::test]
|
||||
/// async fn my_test() {
|
||||
/// assert!(true);
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn test_basic(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
test(args, item, false)
|
||||
}
|
||||
|
||||
fn test(args: TokenStream, item: TokenStream, rt_threaded: bool) -> TokenStream {
|
||||
let input = syn::parse_macro_input!(item as syn::ItemFn);
|
||||
let args = syn::parse_macro_input!(args as syn::AttributeArgs);
|
||||
|
||||
let ret = &input.sig.output;
|
||||
let name = &input.sig.ident;
|
||||
let body = &input.block;
|
||||
let attrs = &input.attrs;
|
||||
|
||||
for attr in attrs {
|
||||
for attr in &input.attrs {
|
||||
if attr.path.is_ident("test") {
|
||||
let msg = "second test attribute is supplied";
|
||||
return syn::Error::new_spanned(&attr, msg)
|
||||
@@ -166,59 +321,12 @@ pub fn test(args: TokenStream, item: TokenStream) -> TokenStream {
|
||||
}
|
||||
}
|
||||
|
||||
if input.sig.asyncness.is_none() {
|
||||
let msg = "the async keyword is missing from the function declaration";
|
||||
return syn::Error::new_spanned(&input.sig.fn_token, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
} else if !input.sig.inputs.is_empty() {
|
||||
if !input.sig.inputs.is_empty() {
|
||||
let msg = "the test function cannot accept arguments";
|
||||
return syn::Error::new_spanned(&input.sig.inputs, msg)
|
||||
.to_compile_error()
|
||||
.into();
|
||||
}
|
||||
|
||||
let mut runtime = Runtime::Auto;
|
||||
|
||||
for arg in args {
|
||||
if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = arg {
|
||||
let ident = path.get_ident();
|
||||
if ident.is_none() {
|
||||
let msg = "Must have specified ident";
|
||||
return syn::Error::new_spanned(path, msg).to_compile_error().into();
|
||||
}
|
||||
match ident.unwrap().to_string().to_lowercase().as_str() {
|
||||
"threaded_scheduler" => runtime = Runtime::Threaded,
|
||||
"basic_scheduler" => runtime = Runtime::Basic,
|
||||
name => {
|
||||
let msg = format!("Unknown attribute {} is specified; expected `basic_scheduler` or `threaded_scheduler`", name);
|
||||
return syn::Error::new_spanned(path, msg).to_compile_error().into();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let result = match runtime {
|
||||
Runtime::Threaded => quote! {
|
||||
#[test]
|
||||
#(#attrs)*
|
||||
fn #name() #ret {
|
||||
tokio::runtime::Runtime::new().unwrap().block_on(async { #body })
|
||||
}
|
||||
},
|
||||
Runtime::Basic | Runtime::Auto => quote! {
|
||||
#[test]
|
||||
#(#attrs)*
|
||||
fn #name() #ret {
|
||||
tokio::runtime::Builder::new()
|
||||
.basic_scheduler()
|
||||
.enable_all()
|
||||
.build()
|
||||
.unwrap()
|
||||
.block_on(async { #body })
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
result.into()
|
||||
parse_knobs(input, args, true, rt_threaded).unwrap_or_else(|e| e.to_compile_error().into())
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ Testing utilities for Tokio- and futures-based code
|
||||
categories = ["asynchronous", "testing"]
|
||||
|
||||
[dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["rt-core", "sync", "time", "test-util"] }
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["rt-core", "stream", "sync", "time", "test-util"] }
|
||||
|
||||
bytes = "0.5.0"
|
||||
futures-core = "0.3.0"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Futures task based helpers
|
||||
|
||||
use futures_core::Stream;
|
||||
#![allow(clippy::mutex_atomic)]
|
||||
|
||||
use std::future::Future;
|
||||
use std::mem;
|
||||
use std::ops;
|
||||
@@ -8,6 +9,8 @@ use std::pin::Pin;
|
||||
use std::sync::{Arc, Condvar, Mutex};
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
use tokio::stream::Stream;
|
||||
|
||||
/// TOOD: dox
|
||||
pub fn spawn<T>(task: T) -> Spawn<T> {
|
||||
Spawn {
|
||||
|
||||
@@ -20,10 +20,8 @@ fn async_fn() {
|
||||
#[test]
|
||||
fn test_delay() {
|
||||
let deadline = Instant::now() + Duration::from_millis(100);
|
||||
assert_eq!(
|
||||
(),
|
||||
block_on(async {
|
||||
delay_until(deadline).await;
|
||||
})
|
||||
);
|
||||
|
||||
block_on(async {
|
||||
delay_until(deadline).await;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ tokio = { version = "0.2.0", path = "../tokio" }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "0.2.0", path = "../tokio", features = ["macros", "stream", "rt-core", "io-util", "net"] }
|
||||
tokio-util = { version = "0.2.0", path = "../tokio-util", features = ["full"] }
|
||||
|
||||
cfg-if = "0.1"
|
||||
env_logger = { version = "0.6", default-features = false }
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
// A tiny async TLS echo server with Tokio
|
||||
use native_tls;
|
||||
use native_tls::Identity;
|
||||
use tokio;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::TcpListener;
|
||||
use tokio_tls;
|
||||
|
||||
/**
|
||||
an example to setup a tls server.
|
||||
how to test:
|
||||
wget https://127.0.0.1:12345 --no-check-certificate
|
||||
*/
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Bind the server's socket
|
||||
let addr = "127.0.0.1:12345".to_string();
|
||||
let mut tcp: TcpListener = TcpListener::bind(&addr).await?;
|
||||
|
||||
// Create the TLS acceptor.
|
||||
let der = include_bytes!("identity.p12");
|
||||
let cert = Identity::from_pkcs12(der, "mypass")?;
|
||||
let tls_acceptor =
|
||||
tokio_tls::TlsAcceptor::from(native_tls::TlsAcceptor::builder(cert).build()?);
|
||||
loop {
|
||||
// Asynchronously wait for an inbound socket.
|
||||
let (socket, remote_addr) = tcp.accept().await?;
|
||||
let tls_acceptor = tls_acceptor.clone();
|
||||
println!("accept connection from {}", remote_addr);
|
||||
tokio::spawn(async move {
|
||||
// Accept the TLS connection.
|
||||
let mut tls_stream = tls_acceptor.accept(socket).await.expect("accept error");
|
||||
// In a loop, read data from the socket and write the data back.
|
||||
|
||||
let mut buf = [0; 1024];
|
||||
let n = tls_stream
|
||||
.read(&mut buf)
|
||||
.await
|
||||
.expect("failed to read data from socket");
|
||||
|
||||
if n == 0 {
|
||||
return;
|
||||
}
|
||||
println!("read={}", unsafe {
|
||||
String::from_utf8_unchecked(buf[0..n].into())
|
||||
});
|
||||
tls_stream
|
||||
.write_all(&buf[0..n])
|
||||
.await
|
||||
.expect("failed to write data to socket");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
// A tiny async TLS echo server with Tokio
|
||||
use native_tls;
|
||||
use native_tls::Identity;
|
||||
use tokio;
|
||||
use tokio::io;
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
use tokio_tls;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Bind the server's socket
|
||||
let addr = "127.0.0.1:12345".parse()?;
|
||||
let tcp = TcpListener::bind(&addr)?;
|
||||
|
||||
// Create the TLS acceptor.
|
||||
let der = include_bytes!("identity.p12");
|
||||
let cert = Identity::from_pkcs12(der, "mypass")?;
|
||||
let tls_acceptor =
|
||||
tokio_tls::TlsAcceptor::from(native_tls::TlsAcceptor::builder(cert).build()?);
|
||||
|
||||
// Iterate incoming connections
|
||||
let server = tcp
|
||||
.incoming()
|
||||
.for_each(move |tcp| {
|
||||
// Accept the TLS connection.
|
||||
let tls_accept = tls_acceptor
|
||||
.accept(tcp)
|
||||
.and_then(move |tls| {
|
||||
// Split up the read and write halves
|
||||
let (reader, writer) = tls.split();
|
||||
|
||||
// Copy the data back to the client
|
||||
let conn = io::copy(reader, writer)
|
||||
// print what happened
|
||||
.map(|(n, _, _)| println!("wrote {} bytes", n))
|
||||
// Handle any errors
|
||||
.map_err(|err| println!("IO error {:?}", err));
|
||||
|
||||
// Spawn the future as a concurrent task
|
||||
tokio::spawn(conn);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| {
|
||||
println!("TLS accept error: {:?}", err);
|
||||
});
|
||||
tokio::spawn(tls_accept);
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.map_err(|err| {
|
||||
println!("server error {:?}", err);
|
||||
});
|
||||
|
||||
// Start the runtime and spin up the server
|
||||
tokio::run(server);
|
||||
Ok(())
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
use cfg_if::cfg_if;
|
||||
use env_logger;
|
||||
use futures::join;
|
||||
use futures::stream::StreamExt;
|
||||
use native_tls;
|
||||
use native_tls::{Identity, TlsAcceptor, TlsConnector};
|
||||
use std::io::Write;
|
||||
@@ -12,6 +11,7 @@ use std::process::Command;
|
||||
use std::ptr;
|
||||
use tokio::io::{AsyncReadExt, AsyncWrite, AsyncWriteExt, Error, ErrorKind};
|
||||
use tokio::net::{TcpListener, TcpStream};
|
||||
use tokio::stream::StreamExt;
|
||||
use tokio_tls;
|
||||
|
||||
macro_rules! t {
|
||||
|
||||
@@ -26,7 +26,7 @@ default = []
|
||||
# Shorthand for enabling everything
|
||||
full = ["codec", "udp"]
|
||||
|
||||
codec = []
|
||||
codec = ["tokio/stream"]
|
||||
udp = ["tokio/udp"]
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -3,10 +3,12 @@ use crate::codec::encoder::Encoder;
|
||||
use crate::codec::framed_read::{framed_read2, framed_read2_with_buffer, FramedRead2};
|
||||
use crate::codec::framed_write::{framed_write2, framed_write2_with_buffer, FramedWrite2};
|
||||
|
||||
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite};
|
||||
use tokio::{
|
||||
io::{AsyncBufRead, AsyncRead, AsyncWrite},
|
||||
stream::Stream,
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures_core::Stream;
|
||||
use futures_sink::Sink;
|
||||
use pin_project_lite::pin_project;
|
||||
use std::fmt;
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use crate::codec::framed::{Fuse, ProjectFuse};
|
||||
use crate::codec::Decoder;
|
||||
|
||||
use tokio::io::AsyncRead;
|
||||
use tokio::{io::AsyncRead, stream::Stream};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures_core::Stream;
|
||||
use futures_sink::Sink;
|
||||
use log::trace;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
@@ -2,10 +2,13 @@ use crate::codec::decoder::Decoder;
|
||||
use crate::codec::encoder::Encoder;
|
||||
use crate::codec::framed::{Fuse, ProjectFuse};
|
||||
|
||||
use tokio::io::{AsyncBufRead, AsyncRead, AsyncWrite};
|
||||
use tokio::{
|
||||
io::{AsyncBufRead, AsyncRead, AsyncWrite},
|
||||
stream::Stream,
|
||||
};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use futures_core::{ready, Stream};
|
||||
use futures_core::ready;
|
||||
use futures_sink::Sink;
|
||||
use log::trace;
|
||||
use pin_project_lite::pin_project;
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
//!
|
||||
//! [`AsyncRead`]: https://docs.rs/tokio/*/tokio/io/trait.AsyncRead.html
|
||||
//! [`AsyncWrite`]: https://docs.rs/tokio/*/tokio/io/trait.AsyncWrite.html
|
||||
//! [`Stream`]: https://docs.rs/tokio/*/tokio/stream/trait.Stream.html
|
||||
//! [`Sink`]: https://docs.rs/futures-sink/*/futures_sink/trait.Sink.html
|
||||
//! [`Stream`]: https://docs.rs/futures-core/*/futures_core/stream/trait.Stream.html
|
||||
|
||||
mod bytes_codec;
|
||||
pub use self::bytes_codec::BytesCodec;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio-util/0.2.0")]
|
||||
#![allow(clippy::needless_doctest_main)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::codec::{Decoder, Encoder};
|
||||
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::{net::UdpSocket, stream::Stream};
|
||||
|
||||
use bytes::{BufMut, BytesMut};
|
||||
use futures_core::{ready, Stream};
|
||||
use futures_core::ready;
|
||||
use futures_sink::Sink;
|
||||
use std::io;
|
||||
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
|
||||
@@ -27,7 +27,7 @@ use std::task::{Context, Poll};
|
||||
/// calling `split` on the `UdpFramed` returned by this method, which will break
|
||||
/// them into separate objects, allowing them to interact more easily.
|
||||
#[must_use = "sinks do nothing unless polled"]
|
||||
#[cfg_attr(docsrs, doc(feature = "codec-udp"))]
|
||||
#[cfg_attr(docsrs, doc(all(feature = "codec", feature = "udp")))]
|
||||
#[derive(Debug)]
|
||||
pub struct UdpFramed<C> {
|
||||
socket: UdpSocket,
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use tokio::prelude::*;
|
||||
use tokio::{prelude::*, stream::StreamExt};
|
||||
use tokio_test::assert_ok;
|
||||
use tokio_util::codec::{Decoder, Encoder, Framed, FramedParts};
|
||||
|
||||
use bytes::{Buf, BufMut, BytesMut};
|
||||
use futures::StreamExt;
|
||||
use std::io::{self, Read};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
@@ -203,7 +203,7 @@ fn huge_size() {
|
||||
if buf.len() < 32 * 1024 {
|
||||
return Ok(None);
|
||||
}
|
||||
buf.split_to(32 * 1024);
|
||||
buf.advance(32 * 1024);
|
||||
Ok(Some(0))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ fn write_hits_backpressure() {
|
||||
|
||||
// Append to the end
|
||||
match mock.calls.back_mut().unwrap() {
|
||||
&mut Ok(ref mut data) => {
|
||||
Ok(ref mut data) => {
|
||||
// Write in 2kb chunks
|
||||
if data.len() < ITER {
|
||||
data.extend_from_slice(&b[..]);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use tokio::net::UdpSocket;
|
||||
use tokio::{net::UdpSocket, stream::StreamExt};
|
||||
use tokio_util::codec::{Decoder, Encoder};
|
||||
use tokio_util::udp::UdpFramed;
|
||||
|
||||
@@ -6,7 +6,6 @@ use bytes::{BufMut, BytesMut};
|
||||
use futures::future::try_join;
|
||||
use futures::future::FutureExt;
|
||||
use futures::sink::SinkExt;
|
||||
use futures::stream::StreamExt;
|
||||
use std::io;
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -1,3 +1,59 @@
|
||||
# 0.2.7 (January 7, 2019)
|
||||
|
||||
### Fixes
|
||||
- potential deadlock when dropping `basic_scheduler` Runtime.
|
||||
- calling `spawn_blocking` from within a `spawn_blocking` (#2006).
|
||||
- storing a `Runtime` instance in a thread-local (#2011).
|
||||
- miscellaneous documentation fixes.
|
||||
- rt: fix `Waker::will_wake` to return true when tasks match (#2045).
|
||||
- test-util: `time::advance` runs pending tasks before changing the time (#2059).
|
||||
|
||||
### Added
|
||||
- `net::lookup_host` maps a `T: ToSocketAddrs` to a stream of `SocketAddrs` (#1870).
|
||||
- `process::Child` fields are made public to match `std` (#2014).
|
||||
- impl `Stream` for `sync::broadcast::Receiver` (#2012).
|
||||
- `sync::RwLock` provides an asynchonous read-write lock (#1699).
|
||||
- `runtime::Handle::current` returns the handle for the current runtime (#2040).
|
||||
- `StreamExt::filter` filters stream values according to a predicate (#2001).
|
||||
- `StreamExt::filter_map` simultaneously filter and map stream values (#2001).
|
||||
- `StreamExt::try_next` convenience for streams of `Result<T, E>` (#2005).
|
||||
- `StreamExt::take` limits a stream to a specified number of values (#2025).
|
||||
- `StreamExt::take_while` limits a stream based on a predicate (#2029).
|
||||
- `StreamExt::all` tests if every element of the stream matches a predicate (#2035).
|
||||
- `StreamExt::any` tests if any element of the stream matches a predicate (#2034).
|
||||
- `task::LocalSet.await` runs spawned tasks until the set is idle (#1971).
|
||||
- `time::DelayQueue::len` returns the number entries in the queue (#1755).
|
||||
- expose runtime options from the `#[tokio::main]` and `#[tokio::test]` (#2022).
|
||||
|
||||
# 0.2.6 (December 19, 2019)
|
||||
|
||||
### Fixes
|
||||
- `fs::File::seek` API regression (#1991).
|
||||
|
||||
# 0.2.5 (December 18, 2019)
|
||||
|
||||
### Added
|
||||
- `io::AsyncSeek` trait (#1924).
|
||||
- `Mutex::try_lock` (#1939)
|
||||
- `mpsc::Receiver::try_recv` and `mpsc::UnboundedReceiver::try_recv` (#1939).
|
||||
- `writev` support for `TcpStream` (#1956).
|
||||
- `time::throttle` for throttling streams (#1949).
|
||||
- implement `Stream` for `time::DelayQueue` (#1975).
|
||||
- `sync::broadcast` provides a fan-out channel (#1943).
|
||||
- `sync::Semaphore` provides an async semaphore (#1973).
|
||||
- `stream::StreamExt` provides stream utilities (#1962).
|
||||
|
||||
### Fixes
|
||||
- deadlock risk while shutting down the runtime (#1972).
|
||||
- panic while shutting down the runtime (#1978).
|
||||
- `sync::MutexGuard` debug output (#1961).
|
||||
- misc doc improvements (#1933, #1934, #1940, #1942).
|
||||
|
||||
### Changes
|
||||
- runtime threads are configured with `runtime::Builder::core_threads` and
|
||||
`runtime::Builder::max_threads`. `runtime::Builder::num_threads` is
|
||||
deprecated (#1977).
|
||||
|
||||
# 0.2.4 (December 6, 2019)
|
||||
|
||||
### Fixes
|
||||
|
||||
+8
-4
@@ -8,12 +8,12 @@ name = "tokio"
|
||||
# - README.md
|
||||
# - Update CHANGELOG.md.
|
||||
# - Create "v0.2.x" git tag.
|
||||
version = "0.2.4"
|
||||
version = "0.2.7"
|
||||
edition = "2018"
|
||||
authors = ["Tokio Contributors <[email protected]>"]
|
||||
license = "MIT"
|
||||
readme = "README.md"
|
||||
documentation = "https://docs.rs/tokio/0.2.4/tokio/"
|
||||
documentation = "https://docs.rs/tokio/0.2.7/tokio/"
|
||||
repository = "https://github.com/tokio-rs/tokio"
|
||||
homepage = "https://tokio.rs"
|
||||
description = """
|
||||
@@ -85,14 +85,14 @@ signal = [
|
||||
stream = ["futures-core"]
|
||||
sync = ["fnv"]
|
||||
test-util = []
|
||||
tcp = ["io-driver"]
|
||||
tcp = ["io-driver", "iovec"]
|
||||
time = ["slab"]
|
||||
udp = ["io-driver"]
|
||||
uds = ["io-driver", "mio-uds", "libc"]
|
||||
|
||||
|
||||
[dependencies]
|
||||
tokio-macros = { version = "0.2.0", optional = true }
|
||||
tokio-macros = { version = "0.2.0", path = "../tokio-macros", optional = true }
|
||||
|
||||
bytes = "0.5.0"
|
||||
pin-project-lite = "0.1.1"
|
||||
@@ -103,6 +103,7 @@ futures-core = { version = "0.3.0", optional = true }
|
||||
lazy_static = { version = "1.0.2", optional = true }
|
||||
memchr = { version = "2.2", optional = true }
|
||||
mio = { version = "0.6.20", optional = true }
|
||||
iovec = { version = "0.1.4", optional = true }
|
||||
num_cpus = { version = "1.8.0", optional = true }
|
||||
# Backs `DelayQueue`
|
||||
slab = { version = "0.4.1", optional = true }
|
||||
@@ -130,3 +131,6 @@ tempfile = "3.1.0"
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[package.metadata.playground]
|
||||
features = ["full"]
|
||||
|
||||
+4
-6
@@ -61,15 +61,13 @@ shorthand, the `full` feature enables all components.
|
||||
|
||||
A basic TCP echo server with Tokio:
|
||||
|
||||
```rust
|
||||
|
||||
```rust,no_run
|
||||
use tokio::net::TcpListener;
|
||||
use tokio::prelude::*;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let addr = "127.0.0.1:8080".parse()?;
|
||||
let mut listener = TcpListener::bind(&addr).unwrap();
|
||||
let mut listener = TcpListener::bind("127.0.0.1:8080").await?;
|
||||
|
||||
loop {
|
||||
let (mut socket, _) = listener.accept().await?;
|
||||
@@ -84,14 +82,14 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(n) if n == 0 => return,
|
||||
Ok(n) => n,
|
||||
Err(e) => {
|
||||
println!("failed to read from socket; err = {:?}", e);
|
||||
eprintln!("failed to read from socket; err = {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Write the data back
|
||||
if let Err(e) = socket.write_all(&buf[0..n]).await {
|
||||
println!("failed to write to socket; err = {:?}", e);
|
||||
eprintln!("failed to write to socket; err = {:?}", e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
#![cfg(feature = "broken")]
|
||||
#![feature(test)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
use std::thread;
|
||||
|
||||
use futures::sync::mpsc;
|
||||
use futures::sync::oneshot;
|
||||
use futures::try_ready;
|
||||
use futures::{Future, Poll, Sink, Stream};
|
||||
use test::Bencher;
|
||||
use tokio::net::UdpSocket;
|
||||
|
||||
/// UDP echo server
|
||||
struct EchoServer {
|
||||
socket: UdpSocket,
|
||||
buf: Vec<u8>,
|
||||
to_send: Option<(usize, SocketAddr)>,
|
||||
}
|
||||
|
||||
impl EchoServer {
|
||||
fn new(s: UdpSocket) -> Self {
|
||||
EchoServer {
|
||||
socket: s,
|
||||
to_send: None,
|
||||
buf: vec![0u8; 1600],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for EchoServer {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
loop {
|
||||
if let Some(&(size, peer)) = self.to_send.as_ref() {
|
||||
try_ready!(self.socket.poll_send_to(&self.buf[..size], &peer));
|
||||
self.to_send = None;
|
||||
}
|
||||
self.to_send = Some(try_ready!(self.socket.poll_recv_from(&mut self.buf)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn udp_echo_latency(b: &mut Bencher) {
|
||||
let any_addr = "127.0.0.1:0".to_string();
|
||||
let any_addr = any_addr.parse::<SocketAddr>().unwrap();
|
||||
|
||||
let (stop_c, stop_p) = oneshot::channel::<()>();
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
let child = thread::spawn(move || {
|
||||
let socket = tokio::net::UdpSocket::bind(&any_addr).unwrap();
|
||||
tx.send(socket.local_addr().unwrap()).unwrap();
|
||||
|
||||
let server = EchoServer::new(socket);
|
||||
let server = server.select(stop_p.map_err(|_| panic!()));
|
||||
let server = server.map_err(|_| ());
|
||||
server.wait().unwrap();
|
||||
});
|
||||
|
||||
let client = std::net::UdpSocket::bind(&any_addr).unwrap();
|
||||
|
||||
let server_addr = rx.wait().unwrap();
|
||||
let mut buf = [0u8; 1000];
|
||||
|
||||
// warmup phase; for some reason initial couple of
|
||||
// runs are much slower
|
||||
//
|
||||
// TODO: Describe the exact reasons; caching? branch predictor? lazy closures?
|
||||
for _ in 0..8 {
|
||||
client.send_to(&buf, &server_addr).unwrap();
|
||||
let _ = client.recv_from(&mut buf).unwrap();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
client.send_to(&buf, &server_addr).unwrap();
|
||||
let _ = client.recv_from(&mut buf).unwrap();
|
||||
});
|
||||
|
||||
stop_c.send(()).unwrap();
|
||||
child.join().unwrap();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn futures_channel_latency(b: &mut Bencher) {
|
||||
let (mut in_tx, in_rx) = mpsc::channel(32);
|
||||
let (out_tx, out_rx) = mpsc::channel::<_>(32);
|
||||
|
||||
let child = thread::spawn(|| out_tx.send_all(in_rx.then(|r| r.unwrap())).wait());
|
||||
let mut rx_iter = out_rx.wait();
|
||||
|
||||
// warmup phase; for some reason initial couple of runs are much slower
|
||||
//
|
||||
// TODO: Describe the exact reasons; caching? branch predictor? lazy closures?
|
||||
for _ in 0..8 {
|
||||
in_tx.start_send(Ok(1usize)).unwrap();
|
||||
let _ = rx_iter.next();
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
in_tx.start_send(Ok(1usize)).unwrap();
|
||||
let _ = rx_iter.next();
|
||||
});
|
||||
|
||||
drop(in_tx);
|
||||
child.join().unwrap().unwrap();
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
// Measure cost of different operations
|
||||
// to get a sense of performance tradeoffs
|
||||
#![cfg(feature = "broken")]
|
||||
#![feature(test)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use test::Bencher;
|
||||
|
||||
use mio::tcp::TcpListener;
|
||||
use mio::{PollOpt, Ready, Token};
|
||||
|
||||
#[bench]
|
||||
fn mio_register_deregister(b: &mut Bencher) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
// Setup the server socket
|
||||
let sock = TcpListener::bind(&addr).unwrap();
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
|
||||
const CLIENT: Token = Token(1);
|
||||
|
||||
b.iter(|| {
|
||||
poll.register(&sock, CLIENT, Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
poll.deregister(&sock).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mio_reregister(b: &mut Bencher) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
// Setup the server socket
|
||||
let sock = TcpListener::bind(&addr).unwrap();
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
|
||||
const CLIENT: Token = Token(1);
|
||||
poll.register(&sock, CLIENT, Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
|
||||
b.iter(|| {
|
||||
poll.reregister(&sock, CLIENT, Ready::readable(), PollOpt::edge())
|
||||
.unwrap();
|
||||
});
|
||||
poll.deregister(&sock).unwrap();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn mio_poll(b: &mut Bencher) {
|
||||
let poll = mio::Poll::new().unwrap();
|
||||
let timeout = std::time::Duration::new(0, 0);
|
||||
let mut events = mio::Events::with_capacity(1024);
|
||||
|
||||
b.iter(|| {
|
||||
poll.poll(&mut events, Some(timeout)).unwrap();
|
||||
});
|
||||
}
|
||||
@@ -1,270 +0,0 @@
|
||||
#![feature(test)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use tokio::sync::mpsc::*;
|
||||
|
||||
use futures::{future, Async, Future, Sink, Stream};
|
||||
use std::thread;
|
||||
use test::Bencher;
|
||||
|
||||
type Medium = [usize; 64];
|
||||
type Large = [Medium; 64];
|
||||
|
||||
#[bench]
|
||||
fn bounded_new_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let _ = test::black_box(&channel::<Medium>(1_000));
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn unbounded_new_medium(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let _ = test::black_box(&unbounded_channel::<Medium>());
|
||||
})
|
||||
}
|
||||
#[bench]
|
||||
fn bounded_new_large(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let _ = test::black_box(&channel::<Large>(1_000));
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn unbounded_new_large(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let _ = test::black_box(&unbounded_channel::<Large>());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn send_one_message(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = channel(1_000);
|
||||
|
||||
// Send
|
||||
tx.try_send(1).unwrap();
|
||||
|
||||
// Receive
|
||||
assert_eq!(Async::Ready(Some(1)), rx.poll().unwrap());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn send_one_message_large(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = channel::<Large>(1_000);
|
||||
|
||||
// Send
|
||||
let _ = tx.try_send([[0; 64]; 64]);
|
||||
|
||||
// Receive
|
||||
let _ = test::black_box(&rx.poll());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bounded_rx_not_ready(b: &mut Bencher) {
|
||||
let (_tx, mut rx) = channel::<i32>(1_000);
|
||||
b.iter(|| {
|
||||
future::lazy(|| {
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bounded_tx_poll_ready(b: &mut Bencher) {
|
||||
let (mut tx, _rx) = channel::<i32>(1);
|
||||
b.iter(|| {
|
||||
future::lazy(|| {
|
||||
assert!(tx.poll_ready().unwrap().is_ready());
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bounded_tx_poll_not_ready(b: &mut Bencher) {
|
||||
let (mut tx, _rx) = channel::<i32>(1);
|
||||
tx.try_send(1).unwrap();
|
||||
b.iter(|| {
|
||||
future::lazy(|| {
|
||||
assert!(tx.poll_ready().unwrap().is_not_ready());
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn unbounded_rx_not_ready(b: &mut Bencher) {
|
||||
let (_tx, mut rx) = unbounded_channel::<i32>();
|
||||
b.iter(|| {
|
||||
future::lazy(|| {
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn unbounded_rx_not_ready_x5(b: &mut Bencher) {
|
||||
let (_tx, mut rx) = unbounded_channel::<i32>();
|
||||
b.iter(|| {
|
||||
future::lazy(|| {
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
assert!(rx.poll().unwrap().is_not_ready());
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bounded_uncontended_1(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = channel(1_000);
|
||||
|
||||
for i in 0..1000 {
|
||||
tx.try_send(i).unwrap();
|
||||
// No need to create a task, because poll is not going to park.
|
||||
assert_eq!(Async::Ready(Some(i)), rx.poll().unwrap());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bounded_uncontended_1_large(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = channel::<Large>(1_000);
|
||||
|
||||
for i in 0..1000 {
|
||||
let _ = tx.try_send([[i; 64]; 64]);
|
||||
// No need to create a task, because poll is not going to park.
|
||||
let _ = test::black_box(&rx.poll());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bounded_uncontended_2(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (mut tx, mut rx) = channel(1000);
|
||||
|
||||
for i in 0..1000 {
|
||||
tx.try_send(i).unwrap();
|
||||
}
|
||||
|
||||
for i in 0..1000 {
|
||||
// No need to create a task, because poll is not going to park.
|
||||
assert_eq!(Async::Ready(Some(i)), rx.poll().unwrap());
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn contended_unbounded_tx(b: &mut Bencher) {
|
||||
let mut threads = vec![];
|
||||
let mut txs = vec![];
|
||||
|
||||
for _ in 0..4 {
|
||||
let (tx, rx) = ::std::sync::mpsc::channel::<Sender<i32>>();
|
||||
txs.push(tx);
|
||||
|
||||
threads.push(thread::spawn(move || {
|
||||
for mut tx in rx.iter() {
|
||||
for i in 0..1_000 {
|
||||
tx.try_send(i).unwrap();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
// TODO make unbounded
|
||||
let (tx, rx) = channel::<i32>(1_000_000);
|
||||
|
||||
for th in &txs {
|
||||
th.send(tx.clone()).unwrap();
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
|
||||
let rx = rx.wait().take(4 * 1_000);
|
||||
|
||||
for v in rx {
|
||||
let _ = test::black_box(v);
|
||||
}
|
||||
});
|
||||
|
||||
drop(txs);
|
||||
|
||||
for th in threads {
|
||||
th.join().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn contended_bounded_tx(b: &mut Bencher) {
|
||||
const THREADS: usize = 4;
|
||||
const ITERS: usize = 100;
|
||||
|
||||
let mut threads = vec![];
|
||||
let mut txs = vec![];
|
||||
|
||||
for _ in 0..THREADS {
|
||||
let (tx, rx) = ::std::sync::mpsc::channel::<Sender<i32>>();
|
||||
txs.push(tx);
|
||||
|
||||
threads.push(thread::spawn(move || {
|
||||
for tx in rx.iter() {
|
||||
let mut tx = tx.wait();
|
||||
for i in 0..ITERS {
|
||||
tx.send(i as i32).unwrap();
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
b.iter(|| {
|
||||
let (tx, rx) = channel::<i32>(1);
|
||||
|
||||
for th in &txs {
|
||||
th.send(tx.clone()).unwrap();
|
||||
}
|
||||
|
||||
drop(tx);
|
||||
|
||||
let rx = rx.wait().take(THREADS * ITERS);
|
||||
|
||||
for v in rx {
|
||||
let _ = test::black_box(v);
|
||||
}
|
||||
});
|
||||
|
||||
drop(txs);
|
||||
|
||||
for th in threads {
|
||||
th.join().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
#![feature(test)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use futures::{future, Async, Future};
|
||||
use test::Bencher;
|
||||
|
||||
#[bench]
|
||||
fn new(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let _ = ::test::black_box(&oneshot::channel::<i32>());
|
||||
})
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn same_thread_send_recv(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (tx, mut rx) = oneshot::channel();
|
||||
|
||||
let _ = tx.send(1);
|
||||
|
||||
assert_eq!(Async::Ready(1), rx.poll().unwrap());
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn same_thread_recv_multi_send_recv(b: &mut Bencher) {
|
||||
b.iter(|| {
|
||||
let (tx, mut rx) = oneshot::channel();
|
||||
|
||||
future::lazy(|| {
|
||||
let _ = rx.poll();
|
||||
let _ = rx.poll();
|
||||
let _ = rx.poll();
|
||||
let _ = rx.poll();
|
||||
|
||||
let _ = tx.send(1);
|
||||
assert_eq!(Async::Ready(1), rx.poll().unwrap());
|
||||
|
||||
Ok::<_, ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn multi_thread_send_recv(b: &mut Bencher) {
|
||||
const MAX: usize = 10_000_000;
|
||||
|
||||
use std::thread;
|
||||
|
||||
fn spin<F: Future>(mut f: F) -> Result<F::Item, F::Error> {
|
||||
use futures::Async::Ready;
|
||||
loop {
|
||||
match f.poll() {
|
||||
Ok(Ready(v)) => return Ok(v),
|
||||
Ok(_) => {}
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut ping_txs = vec![];
|
||||
let mut ping_rxs = vec![];
|
||||
let mut pong_txs = vec![];
|
||||
let mut pong_rxs = vec![];
|
||||
|
||||
for _ in 0..MAX {
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
|
||||
ping_txs.push(Some(tx));
|
||||
ping_rxs.push(Some(rx));
|
||||
|
||||
let (tx, rx) = oneshot::channel::<()>();
|
||||
|
||||
pong_txs.push(Some(tx));
|
||||
pong_rxs.push(Some(rx));
|
||||
}
|
||||
|
||||
thread::spawn(move || {
|
||||
future::lazy(|| {
|
||||
for i in 0..MAX {
|
||||
let ping_rx = ping_rxs[i].take().unwrap();
|
||||
let pong_tx = pong_txs[i].take().unwrap();
|
||||
|
||||
if spin(ping_rx).is_err() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pong_tx.send(()).unwrap();
|
||||
}
|
||||
|
||||
Ok::<(), ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
future::lazy(|| {
|
||||
let mut i = 0;
|
||||
|
||||
b.iter(|| {
|
||||
let ping_tx = ping_txs[i].take().unwrap();
|
||||
let pong_rx = pong_rxs[i].take().unwrap();
|
||||
|
||||
ping_tx.send(()).unwrap();
|
||||
spin(pong_rx).unwrap();
|
||||
|
||||
i += 1;
|
||||
});
|
||||
|
||||
Ok::<(), ()>(())
|
||||
})
|
||||
.wait()
|
||||
.unwrap();
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
#![cfg(feature = "broken")]
|
||||
#![feature(test)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
pub extern crate test;
|
||||
|
||||
mod prelude {
|
||||
pub use futures::*;
|
||||
pub use tokio::net::{TcpListener, TcpStream};
|
||||
pub use tokio::reactor::Reactor;
|
||||
pub use tokio_io::io::read_to_end;
|
||||
|
||||
pub use std::io::{self, Read, Write};
|
||||
pub use std::thread;
|
||||
pub use std::time::Duration;
|
||||
pub use test::{self, Bencher};
|
||||
}
|
||||
|
||||
mod connect_churn {
|
||||
use crate::prelude::*;
|
||||
|
||||
const NUM: usize = 300;
|
||||
const CONCURRENT: usize = 8;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a single future that accepts & drops connections
|
||||
let serve_incomings = listener
|
||||
.incoming()
|
||||
.map_err(|e| panic!("server err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
let connects = stream::iter_result((0..NUM).map(|_| {
|
||||
Ok(TcpStream::connect(&addr).and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
|
||||
let connects_concurrent = connects
|
||||
.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
serve_incomings
|
||||
.select(connects_concurrent)
|
||||
.map(|_| ())
|
||||
.map_err(|_| ())
|
||||
.wait()
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
fn n_workers(n: usize, b: &mut Bencher) {
|
||||
let (shutdown_tx, shutdown_rx) = sync::oneshot::channel();
|
||||
let (addr_tx, addr_rx) = sync::oneshot::channel();
|
||||
|
||||
// Spawn reactor thread
|
||||
let server_thread = thread::spawn(move || {
|
||||
// Bind the TCP listener
|
||||
let listener = TcpListener::bind(&"127.0.0.1:0".parse().unwrap()).unwrap();
|
||||
|
||||
// Get the address being listened on.
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Send the remote & address back to the main thread
|
||||
addr_tx.send(addr).unwrap();
|
||||
|
||||
// Spawn a single future that accepts & drops connections
|
||||
let serve_incomings = listener
|
||||
.incoming()
|
||||
.map_err(|e| panic!("server err: {:?}", e))
|
||||
.for_each(|_| Ok(()));
|
||||
|
||||
// Run server
|
||||
serve_incomings
|
||||
.select(shutdown_rx)
|
||||
.map(|_| ())
|
||||
.map_err(|_| ())
|
||||
.wait()
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
// Get the bind addr of the server
|
||||
let addr = addr_rx.wait().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
use std::sync::{Arc, Barrier};
|
||||
|
||||
// Create a barrier to coordinate threads
|
||||
let barrier = Arc::new(Barrier::new(n + 1));
|
||||
|
||||
// Spawn worker threads
|
||||
let threads: Vec<_> = (0..n)
|
||||
.map(|_| {
|
||||
let barrier = barrier.clone();
|
||||
let addr = addr.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let connects = stream::iter_result((0..(NUM / n)).map(|_| {
|
||||
Ok(TcpStream::connect(&addr)
|
||||
.map_err(|e| panic!("connect err: {:?}", e))
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
read_to_end(sock, vec![])
|
||||
}))
|
||||
}));
|
||||
|
||||
barrier.wait();
|
||||
|
||||
connects
|
||||
.buffer_unordered(CONCURRENT)
|
||||
.map_err(|e| panic!("client err: {:?}", e))
|
||||
.for_each(|_| Ok(()))
|
||||
.wait()
|
||||
.unwrap();
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
barrier.wait();
|
||||
|
||||
for th in threads {
|
||||
th.join().unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
// Shutdown the server
|
||||
shutdown_tx.send(()).unwrap();
|
||||
server_thread.join().unwrap();
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn two_threads(b: &mut Bencher) {
|
||||
n_workers(1, b);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn multi_threads(b: &mut Bencher) {
|
||||
n_workers(4, b);
|
||||
}
|
||||
}
|
||||
|
||||
mod transfer {
|
||||
use crate::prelude::*;
|
||||
use std::{cmp, mem};
|
||||
use tokio_io::try_nb;
|
||||
|
||||
const MB: usize = 3 * 1024 * 1024;
|
||||
|
||||
struct Drain {
|
||||
sock: TcpStream,
|
||||
chunk: usize,
|
||||
}
|
||||
|
||||
impl Future for Drain {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
let mut buf: [u8; 1024] = unsafe { mem::uninitialized() };
|
||||
|
||||
loop {
|
||||
match try_nb!(self.sock.read(&mut buf[..self.chunk])) {
|
||||
0 => return Ok(Async::Ready(())),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Transfer {
|
||||
sock: TcpStream,
|
||||
rem: usize,
|
||||
chunk: usize,
|
||||
}
|
||||
|
||||
impl Future for Transfer {
|
||||
type Item = ();
|
||||
type Error = io::Error;
|
||||
|
||||
fn poll(&mut self) -> Poll<(), io::Error> {
|
||||
while self.rem > 0 {
|
||||
let len = cmp::min(self.rem, self.chunk);
|
||||
let buf = &DATA[..len];
|
||||
|
||||
let n = try_nb!(self.sock.write(&buf));
|
||||
self.rem -= n;
|
||||
}
|
||||
|
||||
Ok(Async::Ready(()))
|
||||
}
|
||||
}
|
||||
|
||||
static DATA: [u8; 1024] = [0; 1024];
|
||||
|
||||
fn one_thread(b: &mut Bencher, read_size: usize, write_size: usize) {
|
||||
let addr = "127.0.0.1:0".parse().unwrap();
|
||||
|
||||
b.iter(move || {
|
||||
let listener = TcpListener::bind(&addr).unwrap();
|
||||
let addr = listener.local_addr().unwrap();
|
||||
|
||||
// Spawn a single future that accepts 1 connection, Drain it and drops
|
||||
let server = listener
|
||||
.incoming()
|
||||
.into_future() // take the first connection
|
||||
.map_err(|(e, _other_incomings)| e)
|
||||
.map(|(connection, _other_incomings)| connection.unwrap())
|
||||
.and_then(|sock| {
|
||||
sock.set_linger(Some(Duration::from_secs(0))).unwrap();
|
||||
let drain = Drain {
|
||||
sock,
|
||||
chunk: read_size,
|
||||
};
|
||||
drain
|
||||
.map(|_| ())
|
||||
.map_err(|e| panic!("server error: {:?}", e))
|
||||
})
|
||||
.map_err(|e| panic!("server err: {:?}", e));
|
||||
|
||||
let client = TcpStream::connect(&addr)
|
||||
.and_then(move |sock| Transfer {
|
||||
sock,
|
||||
rem: MB,
|
||||
chunk: write_size,
|
||||
})
|
||||
.map_err(|e| panic!("client err: {:?}", e));
|
||||
|
||||
server.join(client).wait().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
mod small_chunks {
|
||||
use crate::prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
super::one_thread(b, 32, 32);
|
||||
}
|
||||
}
|
||||
|
||||
mod big_chunks {
|
||||
use crate::prelude::*;
|
||||
|
||||
#[bench]
|
||||
fn one_thread(b: &mut Bencher) {
|
||||
super::one_thread(b, 1_024, 1_024);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use tokio::executor::thread_pool::{Builder, Spawner};
|
||||
use tokio::sync::oneshot;
|
||||
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::Ordering::Relaxed;
|
||||
use std::sync::{mpsc, Arc};
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
struct Backoff(usize);
|
||||
|
||||
impl Future for Backoff {
|
||||
type Output = ();
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
|
||||
if self.0 == 0 {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
self.0 -= 1;
|
||||
cx.waker().wake_by_ref();
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const NUM_THREADS: usize = 6;
|
||||
|
||||
#[bench]
|
||||
fn spawn_many(b: &mut test::Bencher) {
|
||||
const NUM_SPAWN: usize = 10_000;
|
||||
|
||||
let threadpool = Builder::new().num_threads(NUM_THREADS).build();
|
||||
|
||||
let (tx, rx) = mpsc::sync_channel(1000);
|
||||
let rem = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
b.iter(|| {
|
||||
rem.store(NUM_SPAWN, Relaxed);
|
||||
|
||||
for _ in 0..NUM_SPAWN {
|
||||
let tx = tx.clone();
|
||||
let rem = rem.clone();
|
||||
|
||||
threadpool.spawn(async move {
|
||||
if 1 == rem.fetch_sub(1, Relaxed) {
|
||||
tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let _ = rx.recv().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn yield_many(b: &mut test::Bencher) {
|
||||
const NUM_YIELD: usize = 1_000;
|
||||
const TASKS_PER_CPU: usize = 50;
|
||||
|
||||
let threadpool = Builder::new().num_threads(NUM_THREADS).build();
|
||||
|
||||
let tasks = TASKS_PER_CPU * num_cpus::get_physical();
|
||||
let (tx, rx) = mpsc::sync_channel(tasks);
|
||||
|
||||
b.iter(move || {
|
||||
for _ in 0..tasks {
|
||||
let tx = tx.clone();
|
||||
|
||||
threadpool.spawn(async move {
|
||||
let backoff = Backoff(NUM_YIELD);
|
||||
backoff.await;
|
||||
tx.send(()).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
for _ in 0..tasks {
|
||||
let _ = rx.recv().unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn ping_pong(b: &mut test::Bencher) {
|
||||
const NUM_PINGS: usize = 1_000;
|
||||
|
||||
let threadpool = Builder::new().num_threads(NUM_THREADS).build();
|
||||
|
||||
let (done_tx, done_rx) = mpsc::sync_channel(1000);
|
||||
let rem = Arc::new(AtomicUsize::new(0));
|
||||
|
||||
b.iter(|| {
|
||||
let done_tx = done_tx.clone();
|
||||
let rem = rem.clone();
|
||||
rem.store(NUM_PINGS, Relaxed);
|
||||
|
||||
let spawner = threadpool.spawner().clone();
|
||||
|
||||
threadpool.spawn(async move {
|
||||
for _ in 0..NUM_PINGS {
|
||||
let rem = rem.clone();
|
||||
let done_tx = done_tx.clone();
|
||||
|
||||
let spawner2 = spawner.clone();
|
||||
|
||||
spawner.spawn(async move {
|
||||
let (tx1, rx1) = oneshot::channel();
|
||||
let (tx2, rx2) = oneshot::channel();
|
||||
|
||||
spawner2.spawn(async move {
|
||||
rx1.await.unwrap();
|
||||
tx2.send(()).unwrap();
|
||||
});
|
||||
|
||||
tx1.send(()).unwrap();
|
||||
rx2.await.unwrap();
|
||||
|
||||
if 1 == rem.fetch_sub(1, Relaxed) {
|
||||
done_tx.send(()).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
done_rx.recv().unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn chained_spawn(b: &mut test::Bencher) {
|
||||
const ITER: usize = 1_000;
|
||||
|
||||
let threadpool = Builder::new().num_threads(NUM_THREADS).build();
|
||||
|
||||
fn iter(spawner: Spawner, done_tx: mpsc::SyncSender<()>, n: usize) {
|
||||
if n == 0 {
|
||||
done_tx.send(()).unwrap();
|
||||
} else {
|
||||
let s2 = spawner.clone();
|
||||
spawner.spawn(async move {
|
||||
iter(s2, done_tx, n - 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let (done_tx, done_rx) = mpsc::sync_channel(1000);
|
||||
|
||||
b.iter(move || {
|
||||
let done_tx = done_tx.clone();
|
||||
let spawner = threadpool.spawner().clone();
|
||||
threadpool.spawn(async move {
|
||||
iter(spawner, done_tx, ITER);
|
||||
});
|
||||
|
||||
done_rx.recv().unwrap();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
use crate::fs::asyncify;
|
||||
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
/// Returns the canonical, absolute form of a path with all intermediate
|
||||
/// components normalized and symbolic links resolved.
|
||||
///
|
||||
/// This is an async version of [`std::fs::canonicalize`][std]
|
||||
///
|
||||
/// [std]: std::fs::canonicalize
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `realpath` function on Unix
|
||||
/// and the `CreateFile` and `GetFinalPathNameByHandle` functions on Windows.
|
||||
/// Note that, this [may change in the future][changes].
|
||||
///
|
||||
/// On Windows, this converts the path to use [extended length path][path]
|
||||
/// syntax, which allows your program to use longer path names, but means you
|
||||
/// can only join backslash-delimited paths to it, and it may be incompatible
|
||||
/// with other applications (if passed to the application on the command-line,
|
||||
/// or written to a file another application may read).
|
||||
///
|
||||
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
|
||||
/// [path]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx#maxpath
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error in the following situations, but is not
|
||||
/// limited to just these cases:
|
||||
///
|
||||
/// * `path` does not exist.
|
||||
/// * A non-final component in path is not a directory.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let path = fs::canonicalize("../a/../foo.txt").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn canonicalize(path: impl AsRef<Path>) -> io::Result<PathBuf> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(move || std::fs::canonicalize(path)).await
|
||||
}
|
||||
@@ -7,7 +7,45 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::fs::create_dir`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.create_dir.html
|
||||
/// [std]: std::fs::create_dir
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `mkdir` function on Unix
|
||||
/// and the `CreateDirectory` function on Windows.
|
||||
/// Note that, this [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
|
||||
///
|
||||
/// **NOTE**: If a parent of the given path doesn't exist, this function will
|
||||
/// return an error. To create a directory and all its missing parents at the
|
||||
/// same time, use the [`create_dir_all`] function.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error in the following situations, but is not
|
||||
/// limited to just these cases:
|
||||
///
|
||||
/// * User lacks permissions to create directory at `path`.
|
||||
/// * A parent of the given path doesn't exist. (To create a directory and all
|
||||
/// its missing parents at the same time, use the [`create_dir_all`]
|
||||
/// function.)
|
||||
/// * `path` already exists.
|
||||
///
|
||||
/// [`create_dir_all`]: super::create_dir_all()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// fs::create_dir("/some/dir").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn create_dir(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(move || std::fs::create_dir(path)).await
|
||||
|
||||
@@ -8,7 +8,45 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::fs::create_dir_all`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.create_dir_all.html
|
||||
/// [std]: std::fs::create_dir_all
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `mkdir` function on Unix
|
||||
/// and the `CreateDirectory` function on Windows.
|
||||
/// Note that, this [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error in the following situations, but is not
|
||||
/// limited to just these cases:
|
||||
///
|
||||
/// * If any directory in the path specified by `path` does not already exist
|
||||
/// and it could not be created otherwise. The specific error conditions for
|
||||
/// when a directory is being created (after it is determined to not exist) are
|
||||
/// outlined by [`fs::create_dir`].
|
||||
///
|
||||
/// Notable exception is made for situations where any of the directories
|
||||
/// specified in the `path` could not be created as it was being created concurrently.
|
||||
/// Such cases are considered to be successful. That is, calling `create_dir_all`
|
||||
/// concurrently from multiple threads or processes is guaranteed not to fail
|
||||
/// due to a race condition with itself.
|
||||
///
|
||||
/// [`fs::create_dir`]: std::fs::create_dir
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> std::io::Result<()> {
|
||||
/// fs::create_dir_all("/some/dir").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn create_dir_all(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(move || std::fs::create_dir_all(path)).await
|
||||
|
||||
+89
-9
@@ -1,11 +1,11 @@
|
||||
//! Types for working with [`File`].
|
||||
//!
|
||||
//! [`File`]: file/struct.File.html
|
||||
//! [`File`]: File
|
||||
|
||||
use self::State::*;
|
||||
use crate::fs::{asyncify, sys};
|
||||
use crate::io::blocking::Buf;
|
||||
use crate::io::{AsyncRead, AsyncWrite};
|
||||
use crate::io::{AsyncRead, AsyncSeek, AsyncWrite};
|
||||
|
||||
use std::fmt;
|
||||
use std::fs::{Metadata, Permissions};
|
||||
@@ -29,7 +29,7 @@ use std::task::Poll::*;
|
||||
///
|
||||
/// Files are automatically closed when they go out of scope.
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
|
||||
/// [std]: std::fs::File
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -90,7 +90,7 @@ impl File {
|
||||
///
|
||||
/// See [`OpenOptions`] for more details.
|
||||
///
|
||||
/// [`OpenOptions`]: struct.OpenOptions.html
|
||||
/// [`OpenOptions`]: super::OpenOptions
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
@@ -128,14 +128,14 @@ impl File {
|
||||
///
|
||||
/// See [`OpenOptions`] for more details.
|
||||
///
|
||||
/// [`OpenOptions`]: struct.OpenOptions.html
|
||||
/// [`OpenOptions`]: super::OpenOptions
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Results in an error if called from outside of the Tokio runtime or if
|
||||
/// the underlying [`create`] call results in an error.
|
||||
///
|
||||
/// [`create`]: https://doc.rust-lang.org/std/fs/struct.File.html#method.create
|
||||
/// [`create`]: std::fs::File::create
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -155,10 +155,10 @@ impl File {
|
||||
Ok(File::from_std(std_file))
|
||||
}
|
||||
|
||||
/// Convert a [`std::fs::File`][std] to a [`tokio_fs::File`][file].
|
||||
/// Convert a [`std::fs::File`][std] to a [`tokio::fs::File`][file].
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/struct.File.html
|
||||
/// [file]: struct.File.html
|
||||
/// [std]: std::fs::File
|
||||
/// [file]: File
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
@@ -399,6 +399,8 @@ impl File {
|
||||
///
|
||||
/// Use `File::try_into_std` to attempt conversion immediately.
|
||||
///
|
||||
/// [std]: std::fs::File
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
@@ -417,6 +419,8 @@ impl File {
|
||||
|
||||
/// Tries to immediately destructure `File` into a [`std::fs::File`][std].
|
||||
///
|
||||
/// [std]: std::fs::File
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error containing the file if some
|
||||
@@ -548,6 +552,82 @@ impl AsyncRead for File {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncSeek for File {
|
||||
fn start_seek(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
mut pos: SeekFrom,
|
||||
) -> Poll<io::Result<()>> {
|
||||
if let Some(e) = self.last_write_err.take() {
|
||||
return Ready(Err(e.into()));
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.state {
|
||||
Idle(ref mut buf_cell) => {
|
||||
let mut buf = buf_cell.take().unwrap();
|
||||
|
||||
// Factor in any unread data from the buf
|
||||
if !buf.is_empty() {
|
||||
let n = buf.discard_read();
|
||||
|
||||
if let SeekFrom::Current(ref mut offset) = pos {
|
||||
*offset += n;
|
||||
}
|
||||
}
|
||||
|
||||
let std = self.std.clone();
|
||||
|
||||
self.state = Busy(sys::run(move || {
|
||||
let res = (&*std).seek(pos);
|
||||
(Operation::Seek(res), buf)
|
||||
}));
|
||||
|
||||
return Ready(Ok(()));
|
||||
}
|
||||
Busy(ref mut rx) => {
|
||||
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
|
||||
self.state = Idle(Some(buf));
|
||||
|
||||
match op {
|
||||
Operation::Read(_) => {}
|
||||
Operation::Write(Err(e)) => {
|
||||
self.last_write_err = Some(e.kind());
|
||||
}
|
||||
Operation::Write(_) => {}
|
||||
Operation::Seek(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_complete(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
if let Some(e) = self.last_write_err.take() {
|
||||
return Ready(Err(e.into()));
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.state {
|
||||
Idle(_) => panic!("must call start_seek before calling poll_complete"),
|
||||
Busy(ref mut rx) => {
|
||||
let (op, buf) = ready!(Pin::new(rx).poll(cx))?;
|
||||
self.state = Idle(Some(buf));
|
||||
|
||||
match op {
|
||||
Operation::Read(_) => {}
|
||||
Operation::Write(Err(e)) => {
|
||||
self.last_write_err = Some(e.kind());
|
||||
}
|
||||
Operation::Write(_) => {}
|
||||
Operation::Seek(res) => return Ready(res),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsyncWrite for File {
|
||||
fn poll_write(
|
||||
mut self: Pin<&mut Self>,
|
||||
|
||||
@@ -5,12 +5,39 @@ use std::path::Path;
|
||||
|
||||
/// Creates a new hard link on the filesystem.
|
||||
///
|
||||
/// This is an async version of [`std::fs::hard_link`][std]
|
||||
///
|
||||
/// [std]: std::fs::hard_link
|
||||
///
|
||||
/// The `dst` path will be a link pointing to the `src` path. Note that systems
|
||||
/// often require these two paths to both be located on the same filesystem.
|
||||
///
|
||||
/// This is an async version of [`std::fs::hard_link`][std]
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.hard_link.html
|
||||
/// This function currently corresponds to the `link` function on Unix
|
||||
/// and the `CreateHardLink` function on Windows.
|
||||
/// Note that, this [may change in the future][changes].
|
||||
///
|
||||
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error in the following situations, but is not
|
||||
/// limited to just these cases:
|
||||
///
|
||||
/// * The `src` path is not a file or doesn't exist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> std::io::Result<()> {
|
||||
/// fs::hard_link("a.txt", "b.txt").await?; // Hard link a.txt to b.txt
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn hard_link(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
||||
@@ -4,7 +4,43 @@ use std::fs::Metadata;
|
||||
use std::io;
|
||||
use std::path::Path;
|
||||
|
||||
/// Queries the file system metadata for a path.
|
||||
/// Given a path, query the file system to get information about a file,
|
||||
/// directory, etc.
|
||||
///
|
||||
/// This is an async version of [`std::fs::metadata`][std]
|
||||
///
|
||||
/// This function will traverse symbolic links to query information about the
|
||||
/// destination file.
|
||||
///
|
||||
/// # Platform-specific behavior
|
||||
///
|
||||
/// This function currently corresponds to the `stat` function on Unix and the
|
||||
/// `GetFileAttributesEx` function on Windows. Note that, this [may change in
|
||||
/// the future][changes].
|
||||
///
|
||||
/// [std]: std::fs::metadata
|
||||
/// [changes]: https://doc.rust-lang.org/std/io/index.html#platform-specific-behavior
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error in the following situations, but is not
|
||||
/// limited to just these cases:
|
||||
///
|
||||
/// * The user lacks permissions to perform `metadata` call on `path`.
|
||||
/// * `path` does not exist.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust,no_run
|
||||
/// use tokio::fs;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> std::io::Result<()> {
|
||||
/// let attr = fs::metadata("/some/file/path.txt").await?;
|
||||
/// // inspect attr ...
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn metadata(path: impl AsRef<Path>) -> io::Result<Metadata> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(|| std::fs::metadata(path)).await
|
||||
|
||||
@@ -24,6 +24,9 @@
|
||||
//!
|
||||
//! [`AsyncRead`]: https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html
|
||||
|
||||
mod canonicalize;
|
||||
pub use self::canonicalize::canonicalize;
|
||||
|
||||
mod create_dir;
|
||||
pub use self::create_dir::create_dir;
|
||||
|
||||
|
||||
+304
-17
@@ -5,13 +5,69 @@ use std::path::Path;
|
||||
|
||||
/// 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
|
||||
/// what operations are permitted on the open file. The [`File::open`] and
|
||||
/// [`File::create`] methods are aliases for commonly used options using this
|
||||
/// builder.
|
||||
///
|
||||
/// Generally speaking, when using `OpenOptions`, you'll first call [`new`],
|
||||
/// then chain calls to methods to set each option, then call [`open`], passing
|
||||
/// the path of the file you're trying to open. This will give you a
|
||||
/// [`io::Result`][result] with a [`File`] inside that you can further operate
|
||||
/// on.
|
||||
///
|
||||
/// This is a specialized version of [`std::fs::OpenOptions`] for usage from
|
||||
/// the Tokio runtime.
|
||||
///
|
||||
/// `From<std::fs::OpenOptions>` is implemented for more advanced configuration
|
||||
/// than the methods provided here.
|
||||
///
|
||||
/// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html
|
||||
/// [`new`]: OpenOptions::new
|
||||
/// [`open`]: OpenOptions::open
|
||||
/// [result]: std::io::Result
|
||||
/// [`File`]: File
|
||||
/// [`File::open`]: File::open
|
||||
/// [`File::create`]: File::create
|
||||
/// [`std::fs::OpenOptions`]: std::fs::OpenOptions
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Opening a file to read:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .read(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Opening a file for both reading and writing, as well as creating it if it
|
||||
/// doesn't exist:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .read(true)
|
||||
/// .write(true)
|
||||
/// .create(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct OpenOptions(std::fs::OpenOptions);
|
||||
|
||||
@@ -20,9 +76,13 @@ impl OpenOptions {
|
||||
///
|
||||
/// All options are initially set to `false`.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::new`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::new
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```ignore
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
///
|
||||
/// let mut options = OpenOptions::new();
|
||||
@@ -32,49 +92,232 @@ impl OpenOptions {
|
||||
OpenOptions(std::fs::OpenOptions::new())
|
||||
}
|
||||
|
||||
/// See the underlying [`read`] call for details.
|
||||
/// Sets the option for read access.
|
||||
///
|
||||
/// [`read`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.read
|
||||
/// This option, when true, will indicate that the file should be
|
||||
/// `read`-able if opened.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::read`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::read
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .read(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn read(&mut self, read: bool) -> &mut OpenOptions {
|
||||
self.0.read(read);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`write`] call for details.
|
||||
/// Sets the option for write access.
|
||||
///
|
||||
/// [`write`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.write
|
||||
/// This option, when true, will indicate that the file should be
|
||||
/// `write`-able if opened.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::write`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::write
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .write(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn write(&mut self, write: bool) -> &mut OpenOptions {
|
||||
self.0.write(write);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`append`] call for details.
|
||||
/// Sets the option for the append mode.
|
||||
///
|
||||
/// [`append`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.append
|
||||
/// This option, when true, means that writes will append to a file instead
|
||||
/// of overwriting previous contents. Note that setting
|
||||
/// `.write(true).append(true)` has the same effect as setting only
|
||||
/// `.append(true)`.
|
||||
///
|
||||
/// For most filesystems, the operating system guarantees that all writes are
|
||||
/// atomic: no writes get mangled because another process writes at the same
|
||||
/// time.
|
||||
///
|
||||
/// One maybe obvious note when using append-mode: make sure that all data
|
||||
/// that belongs together is written to the file in one operation. This
|
||||
/// can be done by concatenating strings before passing them to [`write()`],
|
||||
/// or using a buffered writer (with a buffer of adequate size),
|
||||
/// and calling [`flush()`] when the message is complete.
|
||||
///
|
||||
/// If a file is opened with both read and append access, beware that after
|
||||
/// opening, and after every write, the position for reading may be set at the
|
||||
/// end of the file. So, before writing, save the current position (using
|
||||
/// [`seek`]`(`[`SeekFrom`]`::`[`Current`]`(0))`), and restore it before the next read.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::append`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::append
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// This function doesn't create the file if it doesn't exist. Use the [`create`]
|
||||
/// method to do so.
|
||||
///
|
||||
/// [`write()`]: crate::io::AsyncWriteExt::write
|
||||
/// [`flush()`]: crate::io::AsyncWriteExt::flush
|
||||
/// [`seek`]: crate::io::AsyncSeekExt::seek
|
||||
/// [`SeekFrom`]: std::io::SeekFrom
|
||||
/// [`Current`]: std::io::SeekFrom::Current
|
||||
/// [`create`]: OpenOptions::create
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .append(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn append(&mut self, append: bool) -> &mut OpenOptions {
|
||||
self.0.append(append);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`truncate`] call for details.
|
||||
/// Sets the option for truncating a previous file.
|
||||
///
|
||||
/// [`truncate`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.truncate
|
||||
/// If a file is successfully opened with this option set it will truncate
|
||||
/// the file to 0 length if it already exists.
|
||||
///
|
||||
/// The file must be opened with write access for truncate to work.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::truncate`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::truncate
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .write(true)
|
||||
/// .truncate(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn truncate(&mut self, truncate: bool) -> &mut OpenOptions {
|
||||
self.0.truncate(truncate);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`create`] call for details.
|
||||
/// Sets the option for creating a new file.
|
||||
///
|
||||
/// [`create`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create
|
||||
/// This option indicates whether a new file will be created if the file
|
||||
/// does not yet already exist.
|
||||
///
|
||||
/// In order for the file to be created, [`write`] or [`append`] access must
|
||||
/// be used.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::create`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::create
|
||||
/// [`write`]: OpenOptions::write
|
||||
/// [`append`]: OpenOptions::append
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .write(true)
|
||||
/// .create(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn create(&mut self, create: bool) -> &mut OpenOptions {
|
||||
self.0.create(create);
|
||||
self
|
||||
}
|
||||
|
||||
/// See the underlying [`create_new`] call for details.
|
||||
/// Sets the option to always create a new file.
|
||||
///
|
||||
/// [`create_new`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.create_new
|
||||
/// This option indicates whether a new file will be created. No file is
|
||||
/// allowed to exist at the target location, also no (dangling) symlink.
|
||||
///
|
||||
/// This option is useful because it is atomic. Otherwise between checking
|
||||
/// whether a file exists and creating a new one, the file may have been
|
||||
/// created by another process (a TOCTOU race condition / attack).
|
||||
///
|
||||
/// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are
|
||||
/// ignored.
|
||||
///
|
||||
/// The file must be opened with write or append access in order to create a
|
||||
/// new file.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::create_new`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::create_new
|
||||
/// [`.create()`]: OpenOptions::create
|
||||
/// [`.truncate()`]: OpenOptions::truncate
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new()
|
||||
/// .write(true)
|
||||
/// .create_new(true)
|
||||
/// .open("foo.txt")
|
||||
/// .await?;
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn create_new(&mut self, create_new: bool) -> &mut OpenOptions {
|
||||
self.0.create_new(create_new);
|
||||
self
|
||||
@@ -82,12 +325,56 @@ impl OpenOptions {
|
||||
|
||||
/// Opens a file at `path` with the options specified by `self`.
|
||||
///
|
||||
/// This is an async version of [`std::fs::OpenOptions::open`][std]
|
||||
///
|
||||
/// [std]: std::fs::OpenOptions::open
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `OpenOptionsFuture` results in an error if called from outside of the
|
||||
/// Tokio runtime or if the underlying [`open`] call results in an error.
|
||||
/// This function will return an error under a number of different
|
||||
/// circumstances. Some of these error conditions are listed here, together
|
||||
/// with their [`ErrorKind`]. The mapping to [`ErrorKind`]s is not part of
|
||||
/// the compatibility contract of the function, especially the `Other` kind
|
||||
/// might change to more specific kinds in the future.
|
||||
///
|
||||
/// [`open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html#method.open
|
||||
/// * [`NotFound`]: The specified file does not exist and neither `create`
|
||||
/// or `create_new` is set.
|
||||
/// * [`NotFound`]: One of the directory components of the file path does
|
||||
/// not exist.
|
||||
/// * [`PermissionDenied`]: The user lacks permission to get the specified
|
||||
/// access rights for the file.
|
||||
/// * [`PermissionDenied`]: The user lacks permission to open one of the
|
||||
/// directory components of the specified path.
|
||||
/// * [`AlreadyExists`]: `create_new` was specified and the file already
|
||||
/// exists.
|
||||
/// * [`InvalidInput`]: Invalid combinations of open options (truncate
|
||||
/// without write access, no access mode set, etc.).
|
||||
/// * [`Other`]: One of the directory components of the specified file path
|
||||
/// was not, in fact, a directory.
|
||||
/// * [`Other`]: Filesystem-level errors: full disk, write permission
|
||||
/// requested on a read-only file system, exceeded disk quota, too many
|
||||
/// open files, too long filename, too many symbolic links in the
|
||||
/// specified path (Unix-like systems only), etc.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::OpenOptions;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let file = OpenOptions::new().open("foo.txt").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// [`ErrorKind`]: std::io::ErrorKind
|
||||
/// [`AlreadyExists`]: std::io::ErrorKind::AlreadyExists
|
||||
/// [`InvalidInput`]: std::io::ErrorKind::InvalidInput
|
||||
/// [`NotFound`]: std::io::ErrorKind::NotFound
|
||||
/// [`Other`]: std::io::ErrorKind::Other
|
||||
/// [`PermissionDenied`]: std::io::ErrorKind::PermissionDenied
|
||||
pub async fn open(&self, path: impl AsRef<Path>) -> io::Result<File> {
|
||||
let path = path.as_ref().to_owned();
|
||||
let opts = self.0.clone();
|
||||
|
||||
@@ -9,7 +9,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::os::unix::fs::symlink`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/os/unix/fs/fn.symlink.html
|
||||
/// [std]: std::os::unix::fs::symlink
|
||||
pub async fn symlink(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_dir`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_dir.html
|
||||
/// [std]: std::os::windows::fs::symlink_dir
|
||||
pub async fn symlink_dir(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::path::Path;
|
||||
///
|
||||
/// This is an async version of [`std::os::windows::fs::symlink_file`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/os/windows/fs/fn.symlink_file.html
|
||||
/// [std]: std::os::windows::fs::symlink_file
|
||||
pub async fn symlink_file(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> io::Result<()> {
|
||||
let src = src.as_ref().to_owned();
|
||||
let dst = dst.as_ref().to_owned();
|
||||
|
||||
+31
-8
@@ -2,21 +2,44 @@ use crate::fs::asyncify;
|
||||
|
||||
use std::{io, path::Path};
|
||||
|
||||
/// Creates a future which will open a file for reading and read the entire
|
||||
/// contents into a buffer and return said buffer.
|
||||
/// Read the entire contents of a file into a bytes vector.
|
||||
///
|
||||
/// This is the async equivalent of `std::fs::read`.
|
||||
/// This is an async version of [`std::fs::read`][std]
|
||||
///
|
||||
/// [std]: std::fs::read
|
||||
///
|
||||
/// This is a convenience function for using [`File::open`] and [`read_to_end`]
|
||||
/// with fewer imports and without an intermediate variable. It pre-allocates a
|
||||
/// buffer based on the file size when available, so it is generally faster than
|
||||
/// reading into a vector created with `Vec::new()`.
|
||||
///
|
||||
/// [`File::open`]: super::File::open
|
||||
/// [`read_to_end`]: crate::io::AsyncReadExt::read_to_end
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if `path` does not already exist.
|
||||
/// Other errors may also be returned according to [`OpenOptions::open`].
|
||||
///
|
||||
/// [`OpenOptions::open`]: super::OpenOptions::open
|
||||
///
|
||||
/// It will also return an error if it encounters while reading an error
|
||||
/// of a kind other than [`ErrorKind::Interrupted`].
|
||||
///
|
||||
/// [`ErrorKind::Interrupted`]: std::io::ErrorKind::Interrupted
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs;
|
||||
/// use std::net::SocketAddr;
|
||||
///
|
||||
/// # async fn dox() -> std::io::Result<()> {
|
||||
/// let contents = fs::read("foo.txt").await?;
|
||||
/// println!("foo.txt contains {} bytes", contents.len());
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> Result<(), Box<dyn std::error::Error + 'static>> {
|
||||
/// let contents = fs::read("address.txt").await?;
|
||||
/// let foo: SocketAddr = String::from_utf8_lossy(&contents).parse()?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn read(path: impl AsRef<Path>) -> io::Result<Vec<u8>> {
|
||||
let path = path.as_ref().to_owned();
|
||||
|
||||
@@ -36,7 +36,7 @@ pub async fn read_dir(path: impl AsRef<Path>) -> io::Result<ReadDir> {
|
||||
///
|
||||
/// [`read_dir`]: read_dir
|
||||
/// [`DirEntry`]: DirEntry
|
||||
/// [`Stream`]: futures_core::Stream
|
||||
/// [`Stream`]: crate::stream::Stream
|
||||
/// [`Err`]: std::result::Result::Err
|
||||
#[derive(Debug)]
|
||||
#[must_use = "streams do nothing unless polled"]
|
||||
@@ -85,7 +85,7 @@ impl ReadDir {
|
||||
}
|
||||
|
||||
#[cfg(feature = "stream")]
|
||||
impl futures_core::Stream for ReadDir {
|
||||
impl crate::stream::Stream for ReadDir {
|
||||
type Item = io::Result<DirEntry>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
|
||||
@@ -5,13 +5,13 @@ use std::path::Path;
|
||||
|
||||
/// Removes a file from the filesystem.
|
||||
///
|
||||
/// Note that there is no
|
||||
/// guarantee that the file is immediately deleted (e.g. depending on
|
||||
/// platform, other open file descriptors may prevent immediate removal).
|
||||
/// Note that there is no guarantee that the file is immediately deleted (e.g.
|
||||
/// depending on platform, other open file descriptors may prevent immediate
|
||||
/// removal).
|
||||
///
|
||||
/// This is an async version of [`std::fs::remove_file`][std]
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/fs/fn.remove_file.html
|
||||
/// [std]: std::fs::remove_file
|
||||
pub async fn remove_file(path: impl AsRef<Path>) -> io::Result<()> {
|
||||
let path = path.as_ref().to_owned();
|
||||
asyncify(move || std::fs::remove_file(path)).await
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use sdt::pin::Pin;
|
||||
use std::future::Future;
|
||||
use std::marker;
|
||||
use sdt::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Future for the [`pending()`] function.
|
||||
@@ -29,7 +29,8 @@ struct Pending<T> {
|
||||
pub async fn pending() -> ! {
|
||||
Pending {
|
||||
_data: marker::PhantomData,
|
||||
}.await
|
||||
}
|
||||
.await
|
||||
}
|
||||
|
||||
impl<T> Future for Pending<T> {
|
||||
@@ -40,5 +41,4 @@ impl<T> Future for Pending<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Unpin for Pending<T> {
|
||||
}
|
||||
impl<T> Unpin for Pending<T> {}
|
||||
|
||||
@@ -7,9 +7,15 @@ use std::task::{Context, Poll};
|
||||
|
||||
/// Read bytes asynchronously.
|
||||
///
|
||||
/// This trait inherits from `std::io::BufRead` and indicates that an I/O object is
|
||||
/// This trait inherits from [`std::io::BufRead`] and indicates that an I/O object is
|
||||
/// **non-blocking**. All non-blocking I/O objects must return an error when
|
||||
/// bytes are unavailable instead of blocking the current thread.
|
||||
///
|
||||
/// Utilities for working with `AsyncBufRead` values are provided by
|
||||
/// [`AsyncBufReadExt`].
|
||||
///
|
||||
/// [`std::io::BufRead`]: std::io::BufRead
|
||||
/// [`AsyncBufReadExt`]: crate::io::AsyncBufReadExt
|
||||
pub trait AsyncBufRead: AsyncRead {
|
||||
/// Attempt to return the contents of the internal buffer, filling it with more data
|
||||
/// from the inner reader if it is empty.
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
use std::io::{self, SeekFrom};
|
||||
use std::ops::DerefMut;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Seek bytes asynchronously.
|
||||
///
|
||||
/// This trait is analogous to the [`std::io::Seek`] trait, but integrates
|
||||
/// with the asynchronous task system. In particular, the `start_seek`
|
||||
/// method, unlike [`Seek::seek`], will not block the calling thread.
|
||||
///
|
||||
/// Utilities for working with `AsyncSeek` values are provided by
|
||||
/// [`AsyncSeekExt`].
|
||||
///
|
||||
/// [`std::io::Seek`]: std::io::Seek
|
||||
/// [`Seek::seek`]: std::io::Seek::seek()
|
||||
/// [`AsyncSeekExt`]: crate::io::AsyncSeekExt
|
||||
pub trait AsyncSeek {
|
||||
/// Attempt to seek to an offset, in bytes, in a stream.
|
||||
///
|
||||
/// A seek beyond the end of a stream is allowed, but behavior is defined
|
||||
/// by the implementation.
|
||||
///
|
||||
/// If this function returns successfully, then the job has been submitted.
|
||||
/// To find out when it completes, call `poll_complete`.
|
||||
fn start_seek(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
position: SeekFrom,
|
||||
) -> Poll<io::Result<()>>;
|
||||
|
||||
/// Wait for a seek operation to complete.
|
||||
///
|
||||
/// If the seek operation completed successfully,
|
||||
/// this method returns the new position from the start of the stream.
|
||||
/// That position can be used later with [`SeekFrom::Start`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Seeking to a negative offset is considered an error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Calling this method without calling `start_seek` first is an error.
|
||||
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>>;
|
||||
}
|
||||
|
||||
macro_rules! deref_async_seek {
|
||||
() => {
|
||||
fn start_seek(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
pos: SeekFrom,
|
||||
) -> Poll<io::Result<()>> {
|
||||
Pin::new(&mut **self).start_seek(cx, pos)
|
||||
}
|
||||
|
||||
fn poll_complete(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<io::Result<u64>> {
|
||||
Pin::new(&mut **self).poll_complete(cx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncSeek + Unpin> AsyncSeek for Box<T> {
|
||||
deref_async_seek!();
|
||||
}
|
||||
|
||||
impl<T: ?Sized + AsyncSeek + Unpin> AsyncSeek for &mut T {
|
||||
deref_async_seek!();
|
||||
}
|
||||
|
||||
impl<P> AsyncSeek for Pin<P>
|
||||
where
|
||||
P: DerefMut + Unpin,
|
||||
P::Target: AsyncSeek,
|
||||
{
|
||||
fn start_seek(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
pos: SeekFrom,
|
||||
) -> Poll<io::Result<()>> {
|
||||
self.get_mut().as_mut().start_seek(cx, pos)
|
||||
}
|
||||
|
||||
fn poll_complete(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
self.get_mut().as_mut().poll_complete(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<[u8]> + Unpin> AsyncSeek for io::Cursor<T> {
|
||||
fn start_seek(
|
||||
mut self: Pin<&mut Self>,
|
||||
_: &mut Context<'_>,
|
||||
pos: SeekFrom,
|
||||
) -> Poll<io::Result<()>> {
|
||||
Poll::Ready(io::Seek::seek(&mut *self, pos).map(drop))
|
||||
}
|
||||
fn poll_complete(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<u64>> {
|
||||
Poll::Ready(Ok(self.get_mut().position()))
|
||||
}
|
||||
}
|
||||
@@ -6,11 +6,11 @@ use std::task::{Context, Poll};
|
||||
|
||||
/// Writes bytes asynchronously.
|
||||
///
|
||||
/// The trait inherits from `std::io::Write` and indicates that an I/O object is
|
||||
/// The trait inherits from [`std::io::Write`] and indicates that an I/O object is
|
||||
/// **nonblocking**. All non-blocking I/O objects must return an error when
|
||||
/// bytes cannot be written instead of blocking the current thread.
|
||||
///
|
||||
/// Specifically, this means that the `poll_write` function will return one of
|
||||
/// Specifically, this means that the [`poll_write`] function will return one of
|
||||
/// the following:
|
||||
///
|
||||
/// * `Poll::Ready(Ok(n))` means that `n` bytes of data was immediately
|
||||
@@ -26,14 +26,23 @@ use std::task::{Context, Poll};
|
||||
/// * `Poll::Ready(Err(e))` for other errors are standard I/O errors coming from the
|
||||
/// underlying object.
|
||||
///
|
||||
/// This trait importantly means that the `write` method only works in the
|
||||
/// context of a future's task. The object may panic if used outside of a task.
|
||||
/// This trait importantly means that the [`write`][stdwrite] method only works in
|
||||
/// the context of a future's task. The object may panic if used outside of a task.
|
||||
///
|
||||
/// Note that this trait also represents that the `Write::flush` method works
|
||||
/// very similarly to the `write` method, notably that `Ok(())` means that the
|
||||
/// Note that this trait also represents that the [`Write::flush`][stdflush] method
|
||||
/// works very similarly to the `write` method, notably that `Ok(())` means that the
|
||||
/// writer has successfully been flushed, a "would block" error means that the
|
||||
/// current task is ready to receive a notification when flushing can make more
|
||||
/// progress, and otherwise normal errors can happen as well.
|
||||
///
|
||||
/// Utilities for working with `AsyncWrite` values are provided by
|
||||
/// [`AsyncWriteExt`].
|
||||
///
|
||||
/// [`std::io::Write`]: std::io::Write
|
||||
/// [`poll_write`]: AsyncWrite::poll_write()
|
||||
/// [stdwrite]: std::io::Write::write()
|
||||
/// [stdflush]: std::io::Write::flush()
|
||||
/// [`AsyncWriteExt`]: crate::io::AsyncWriteExt
|
||||
pub trait AsyncWrite {
|
||||
/// Attempt to write bytes from `buf` into the object.
|
||||
///
|
||||
|
||||
@@ -5,15 +5,14 @@ pub(crate) use scheduled_io::ScheduledIo; // pub(crate) for tests
|
||||
|
||||
use crate::loom::sync::atomic::AtomicUsize;
|
||||
use crate::park::{Park, Unpark};
|
||||
use crate::runtime::context;
|
||||
use crate::util::slab::{Address, Slab};
|
||||
|
||||
use mio::event::Evented;
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
use std::sync::{Arc, Weak};
|
||||
use std::task::Waker;
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -54,11 +53,6 @@ pub(super) enum Direction {
|
||||
Write,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
/// Tracks the reactor for the current execution context.
|
||||
static CURRENT_REACTOR: RefCell<Option<Handle>> = RefCell::new(None)
|
||||
}
|
||||
|
||||
const TOKEN_WAKEUP: mio::Token = mio::Token(Address::NULL);
|
||||
|
||||
fn _assert_kinds() {
|
||||
@@ -69,40 +63,6 @@ fn _assert_kinds() {
|
||||
|
||||
// ===== impl Driver =====
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Guard that resets current reactor on drop.
|
||||
pub(crate) struct DefaultGuard<'a> {
|
||||
_lifetime: PhantomData<&'a u8>,
|
||||
}
|
||||
|
||||
impl Drop for DefaultGuard<'_> {
|
||||
fn drop(&mut self) {
|
||||
CURRENT_REACTOR.with(|current| {
|
||||
let mut current = current.borrow_mut();
|
||||
*current = None;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets handle for a default reactor, returning guard that unsets it on drop.
|
||||
pub(crate) fn set_default(handle: &Handle) -> DefaultGuard<'_> {
|
||||
CURRENT_REACTOR.with(|current| {
|
||||
let mut current = current.borrow_mut();
|
||||
|
||||
assert!(
|
||||
current.is_none(),
|
||||
"default Tokio reactor already set \
|
||||
for execution context"
|
||||
);
|
||||
|
||||
*current = Some(handle.clone());
|
||||
});
|
||||
|
||||
DefaultGuard {
|
||||
_lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
impl Driver {
|
||||
/// Creates a new event loop, returning any error that happened during the
|
||||
/// creation.
|
||||
@@ -238,10 +198,7 @@ impl Handle {
|
||||
///
|
||||
/// This function panics if there is no current reactor set.
|
||||
pub(super) fn current() -> Self {
|
||||
CURRENT_REACTOR.with(|current| match *current.borrow() {
|
||||
Some(ref handle) => handle.clone(),
|
||||
None => panic!("no current reactor"),
|
||||
})
|
||||
context::io_handle().expect("no current reactor")
|
||||
}
|
||||
|
||||
/// Forces a reactor blocked in a call to `turn` to wakeup, or otherwise
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::loom::sync::atomic::AtomicUsize;
|
||||
use crate::util::bit;
|
||||
use crate::util::slab::{Address, Entry, Generation};
|
||||
|
||||
use std::sync::atomic::Ordering::{Acquire, AcqRel, SeqCst};
|
||||
use std::sync::atomic::Ordering::{AcqRel, Acquire, SeqCst};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ScheduledIo {
|
||||
@@ -29,12 +29,10 @@ impl Entry for ScheduledIo {
|
||||
|
||||
let next = PACK.pack(generation.next().to_usize(), 0);
|
||||
|
||||
match self.readiness.compare_exchange(
|
||||
current,
|
||||
next,
|
||||
AcqRel,
|
||||
Acquire,
|
||||
) {
|
||||
match self
|
||||
.readiness
|
||||
.compare_exchange(current, next, AcqRel, Acquire)
|
||||
{
|
||||
Ok(_) => break,
|
||||
Err(actual) => current = actual,
|
||||
}
|
||||
|
||||
+8
-2
@@ -164,6 +164,9 @@ pub use self::async_buf_read::AsyncBufRead;
|
||||
mod async_read;
|
||||
pub use self::async_read::AsyncRead;
|
||||
|
||||
mod async_seek;
|
||||
pub use self::async_seek::AsyncSeek;
|
||||
|
||||
mod async_write;
|
||||
pub use self::async_write::AsyncWrite;
|
||||
|
||||
@@ -192,10 +195,13 @@ cfg_io_util! {
|
||||
mod split;
|
||||
pub use split::{split, ReadHalf, WriteHalf};
|
||||
|
||||
pub(crate) mod seek;
|
||||
pub use self::seek::Seek;
|
||||
|
||||
pub(crate) mod util;
|
||||
pub use util::{
|
||||
copy, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufStream,
|
||||
BufWriter, Copy, Empty, Lines, Repeat, Sink, Split, Take,
|
||||
copy, empty, repeat, sink, AsyncBufReadExt, AsyncReadExt, AsyncSeekExt, AsyncWriteExt, BufReader,
|
||||
BufStream, BufWriter, Copy, Empty, Lines, Repeat, Sink, Split, Take,
|
||||
};
|
||||
|
||||
// Re-export io::Error so that users don't have to deal with conflicts when
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::io::driver::platform;
|
||||
use crate::io::{AsyncRead, AsyncWrite, Registration};
|
||||
use crate::io::driver::{platform};
|
||||
|
||||
use mio::event::Evented;
|
||||
use std::fmt;
|
||||
@@ -166,6 +166,14 @@ where
|
||||
E: Evented,
|
||||
{
|
||||
/// Creates a new `PollEvented` associated with the default reactor.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn new(io: E) -> io::Result<Self> {
|
||||
let registration = Registration::new(&io)?;
|
||||
Ok(Self {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use crate::io::driver::{Direction, Handle, platform};
|
||||
use crate::io::driver::{platform, Direction, Handle};
|
||||
use crate::util::slab::Address;
|
||||
|
||||
use mio::{self, Evented};
|
||||
use std::task::{Context, Poll};
|
||||
use std::io;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_driver! {
|
||||
/// Associates an I/O resource with the reactor instance that drives it.
|
||||
@@ -53,6 +53,15 @@ impl Registration {
|
||||
///
|
||||
/// - `Ok` if the registration happened successfully
|
||||
/// - `Err` if an error was encountered during registration
|
||||
///
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn new<T>(io: &T) -> io::Result<Registration>
|
||||
where
|
||||
T: Evented,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
use crate::io::AsyncSeek;
|
||||
use std::future::Future;
|
||||
use std::io::{self, SeekFrom};
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
/// Future for the [`seek`](crate::io::AsyncSeekExt::seek) method.
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Seek<'a, S: ?Sized> {
|
||||
seek: &'a mut S,
|
||||
pos: Option<SeekFrom>,
|
||||
}
|
||||
|
||||
pub(crate) fn seek<S>(seek: &mut S, pos: SeekFrom) -> Seek<'_, S>
|
||||
where
|
||||
S: AsyncSeek + ?Sized + Unpin,
|
||||
{
|
||||
Seek {
|
||||
seek,
|
||||
pos: Some(pos),
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Future for Seek<'_, S>
|
||||
where
|
||||
S: AsyncSeek + ?Sized + Unpin,
|
||||
{
|
||||
type Output = io::Result<u64>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let me = &mut *self;
|
||||
match me.pos {
|
||||
Some(pos) => {
|
||||
match Pin::new(&mut me.seek).start_seek(cx, pos) {
|
||||
Poll::Ready(Ok(())) => me.pos = None,
|
||||
Poll::Ready(Err(e)) => return Poll::Ready(Err(e)),
|
||||
Poll::Pending => (),
|
||||
};
|
||||
Poll::Pending
|
||||
}
|
||||
None => Pin::new(&mut me.seek).poll_complete(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn assert_unpin() {
|
||||
use std::marker::PhantomPinned;
|
||||
crate::is_unpin::<Seek<'_, PhantomPinned>>();
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,12 @@ use std::sync::Arc;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_util! {
|
||||
/// The readable half of a value returned from `split`.
|
||||
/// The readable half of a value returned from [`split`](split()).
|
||||
pub struct ReadHalf<T> {
|
||||
inner: Arc<Inner<T>>,
|
||||
}
|
||||
|
||||
/// The writable half of a value returned from `split`.
|
||||
/// The writable half of a value returned from [`split`](split()).
|
||||
pub struct WriteHalf<T> {
|
||||
inner: Arc<Inner<T>>,
|
||||
}
|
||||
@@ -30,8 +30,8 @@ cfg_io_util! {
|
||||
/// Split a single value implementing `AsyncRead + AsyncWrite` into separate
|
||||
/// `AsyncRead` and `AsyncWrite` handles.
|
||||
///
|
||||
/// To restore this read/write object from its `split::ReadHalf` and
|
||||
/// `split::WriteHalf` use `unsplit`.
|
||||
/// To restore this read/write object from its `ReadHalf` and
|
||||
/// `WriteHalf` use [`unsplit`](ReadHalf::unsplit()).
|
||||
pub fn split<T>(stream: T) -> (ReadHalf<T>, WriteHalf<T>)
|
||||
where
|
||||
T: AsyncRead + AsyncWrite,
|
||||
@@ -139,7 +139,7 @@ impl<T> Inner<T> {
|
||||
} else {
|
||||
// Spin... but investigate a better strategy
|
||||
|
||||
::std::thread::yield_now();
|
||||
std::thread::yield_now();
|
||||
cx.waker().wake_by_ref();
|
||||
|
||||
Poll::Pending
|
||||
|
||||
+43
-4
@@ -9,13 +9,30 @@ use std::task::Poll;
|
||||
cfg_io_std! {
|
||||
/// A handle to the standard error stream of a process.
|
||||
///
|
||||
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent
|
||||
/// writes to `Stderr` must be executed with care.
|
||||
/// Concurrent writes to stderr must be executed with care: Only individual
|
||||
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
|
||||
/// you should be aware that writes using [`write_all`] are not guaranteed
|
||||
/// to occur as a single write, so multiple threads writing data with
|
||||
/// [`write_all`] may result in interleaved output.
|
||||
///
|
||||
/// Created by the [`stderr`] function.
|
||||
///
|
||||
/// [`stderr`]: fn.stderr.html
|
||||
/// [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
/// [`stderr`]: stderr()
|
||||
/// [`AsyncWrite`]: AsyncWrite
|
||||
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut stderr = io::stdout();
|
||||
/// stderr.write_all(b"Print some error here.").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Stderr {
|
||||
std: Blocking<std::io::Stderr>,
|
||||
@@ -25,6 +42,28 @@ cfg_io_std! {
|
||||
///
|
||||
/// The returned handle allows writing to standard error from the within the
|
||||
/// Tokio runtime.
|
||||
///
|
||||
/// Concurrent writes to stderr must be executed with care: Only individual
|
||||
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
|
||||
/// you should be aware that writes using [`write_all`] are not guaranteed
|
||||
/// to occur as a single write, so multiple threads writing data with
|
||||
/// [`write_all`] may result in interleaved output.
|
||||
///
|
||||
/// [`AsyncWrite`]: AsyncWrite
|
||||
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut stderr = io::stdout();
|
||||
/// stderr.write_all(b"Print some error here.").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn stderr() -> Stderr {
|
||||
let std = io::stderr();
|
||||
Stderr {
|
||||
|
||||
@@ -13,7 +13,7 @@ cfg_io_std! {
|
||||
/// reads of `Stdin` must be executed with care.
|
||||
///
|
||||
/// As an additional caveat, reading from the handle may block the calling
|
||||
/// future indefinitely, if there is not enough data available. This makes this
|
||||
/// future indefinitely if there is not enough data available. This makes this
|
||||
/// handle unsuitable for use in any circumstance where immediate reaction to
|
||||
/// available data is required, e.g. interactive use or when implementing a
|
||||
/// subprocess driven by requests on the standard input.
|
||||
@@ -31,6 +31,12 @@ cfg_io_std! {
|
||||
///
|
||||
/// The returned handle allows reading from standard input from the within the
|
||||
/// Tokio runtime.
|
||||
///
|
||||
/// As an additional caveat, reading from the handle may block the calling
|
||||
/// future indefinitely if there is not enough data available. This makes this
|
||||
/// handle unsuitable for use in any circumstance where immediate reaction to
|
||||
/// available data is required, e.g. interactive use or when implementing a
|
||||
/// subprocess driven by requests on the standard input.
|
||||
pub fn stdin() -> Stdin {
|
||||
let std = io::stdin();
|
||||
Stdin {
|
||||
|
||||
+45
-6
@@ -9,13 +9,30 @@ use std::task::Poll;
|
||||
cfg_io_std! {
|
||||
/// A handle to the standard output stream of a process.
|
||||
///
|
||||
/// The handle implements the [`AsyncWrite`] trait, but beware that concurrent
|
||||
/// writes to `Stdout` must be executed with care.
|
||||
/// Concurrent writes to stdout must be executed with care: Only individual
|
||||
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
|
||||
/// you should be aware that writes using [`write_all`] are not guaranteed
|
||||
/// to occur as a single write, so multiple threads writing data with
|
||||
/// [`write_all`] may result in interleaved output.
|
||||
///
|
||||
/// Created by the [`stdout`] function.
|
||||
///
|
||||
/// [`stdout`]: fn.stdout.html
|
||||
/// [`AsyncWrite`]: trait.AsyncWrite.html
|
||||
/// [`stdout`]: stdout()
|
||||
/// [`AsyncWrite`]: AsyncWrite
|
||||
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut stdout = io::stdout();
|
||||
/// stdout.write_all(b"Hello world!").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct Stdout {
|
||||
std: Blocking<std::io::Stdout>,
|
||||
@@ -23,8 +40,30 @@ cfg_io_std! {
|
||||
|
||||
/// Constructs a new handle to the standard output of the current process.
|
||||
///
|
||||
/// The returned handle allows writing to standard out from the within the Tokio
|
||||
/// runtime.
|
||||
/// The returned handle allows writing to standard out from the within the
|
||||
/// Tokio runtime.
|
||||
///
|
||||
/// Concurrent writes to stdout must be executed with care: Only individual
|
||||
/// writes to this [`AsyncWrite`] are guaranteed to be intact. In particular
|
||||
/// you should be aware that writes using [`write_all`] are not guaranteed
|
||||
/// to occur as a single write, so multiple threads writing data with
|
||||
/// [`write_all`] may result in interleaved output.
|
||||
///
|
||||
/// [`AsyncWrite`]: AsyncWrite
|
||||
/// [`write_all`]: crate::io::AsyncWriteExt::write_all()
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let mut stdout = io::stdout();
|
||||
/// stdout.write_all(b"Hello world!").await?;
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn stdout() -> Stdout {
|
||||
let std = io::stdout();
|
||||
Stdout {
|
||||
|
||||
@@ -5,7 +5,9 @@ use crate::io::util::split::{split, Split};
|
||||
use crate::io::AsyncBufRead;
|
||||
|
||||
cfg_io_util! {
|
||||
/// An extension trait which adds utility methods to `AsyncBufRead` types.
|
||||
/// An extension trait which adds utility methods to [`AsyncBufRead`] types.
|
||||
///
|
||||
/// [`AsyncBufRead`]: crate::io::AsyncBufRead
|
||||
pub trait AsyncBufReadExt: AsyncBufRead {
|
||||
/// Read all bytes into `buf` until the delimiter `byte` or EOF is reached.
|
||||
///
|
||||
@@ -226,8 +228,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// ```
|
||||
/// use tokio::io::AsyncBufReadExt;
|
||||
/// use tokio::stream::StreamExt;
|
||||
///
|
||||
/// use futures::{StreamExt};
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
|
||||
@@ -2,8 +2,8 @@ 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::{ReadU8, ReadU16, ReadU32, ReadU64, ReadU128};
|
||||
use crate::io::util::read_int::{ReadI8, ReadI16, ReadI32, ReadI64, ReadI128};
|
||||
use crate::io::util::read_int::{ReadI128, ReadI16, ReadI32, ReadI64, ReadI8};
|
||||
use crate::io::util::read_int::{ReadU128, ReadU16, ReadU32, ReadU64, ReadU8};
|
||||
use crate::io::util::read_to_end::{read_to_end, ReadToEnd};
|
||||
use crate::io::util::read_to_string::{read_to_string, ReadToString};
|
||||
use crate::io::util::take::{take, Take};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
use crate::io::seek::{seek, Seek};
|
||||
use crate::io::AsyncSeek;
|
||||
use std::io::SeekFrom;
|
||||
|
||||
/// An extension trait which adds utility methods to `AsyncSeek` types.
|
||||
pub trait AsyncSeekExt: AsyncSeek {
|
||||
/// Creates a future which will seek an IO object, and then yield the
|
||||
/// new position in the object and the object itself.
|
||||
///
|
||||
/// In the case of an error the buffer and the object will be discarded, with
|
||||
/// the error yielded.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::fs::File;
|
||||
/// use tokio::prelude::*;
|
||||
///
|
||||
/// use std::io::SeekFrom;
|
||||
///
|
||||
/// # async fn dox() -> std::io::Result<()> {
|
||||
/// let mut file = File::open("foo.txt").await?;
|
||||
/// file.seek(SeekFrom::Start(6)).await?;
|
||||
///
|
||||
/// let mut contents = vec![0u8; 10];
|
||||
/// file.read_exact(&mut contents).await?;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
fn seek(&mut self, pos: SeekFrom) -> Seek<'_, Self>
|
||||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
seek(self, pos)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsyncSeek + ?Sized> AsyncSeekExt for S {}
|
||||
@@ -3,8 +3,8 @@ use crate::io::util::shutdown::{shutdown, Shutdown};
|
||||
use crate::io::util::write::{write, Write};
|
||||
use crate::io::util::write_all::{write_all, WriteAll};
|
||||
use crate::io::util::write_buf::{write_buf, WriteBuf};
|
||||
use crate::io::util::write_int::{WriteU8, WriteU16, WriteU32, WriteU64, WriteU128};
|
||||
use crate::io::util::write_int::{WriteI8, WriteI16, WriteI32, WriteI64, WriteI128};
|
||||
use crate::io::util::write_int::{WriteI128, WriteI16, WriteI32, WriteI64, WriteI8};
|
||||
use crate::io::util::write_int::{WriteU128, WriteU16, WriteU32, WriteU64, WriteU8};
|
||||
use crate::io::AsyncWrite;
|
||||
|
||||
use bytes::Buf;
|
||||
|
||||
@@ -12,7 +12,7 @@ cfg_io_util! {
|
||||
/// This struct is generally created by calling [`copy`][copy]. Please
|
||||
/// see the documentation of `copy()` for more details.
|
||||
///
|
||||
/// [copy]: fn.copy.html
|
||||
/// [copy]: copy()
|
||||
#[derive(Debug)]
|
||||
#[must_use = "futures do nothing unless you `.await` or poll them"]
|
||||
pub struct Copy<'a, R: ?Sized, W: ?Sized> {
|
||||
@@ -36,6 +36,8 @@ cfg_io_util! {
|
||||
///
|
||||
/// This is an asynchronous version of [`std::io::copy`][std].
|
||||
///
|
||||
/// [std]: std::io::copy
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// The returned future will finish with an error will return an error
|
||||
@@ -56,8 +58,6 @@ cfg_io_util! {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/io/fn.copy.html
|
||||
pub fn copy<'a, R, W>(reader: &'a mut R, writer: &'a mut W) -> Copy<'a, R, W>
|
||||
where
|
||||
R: AsyncRead + Unpin + ?Sized,
|
||||
|
||||
+12
-10
@@ -6,7 +6,7 @@ use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
cfg_io_util! {
|
||||
// An async reader which is always at EOF.
|
||||
/// An async reader which is always at EOF.
|
||||
///
|
||||
/// This struct is generally created by calling [`empty`]. Please see
|
||||
/// the documentation of [`empty()`][`empty`] for more details.
|
||||
@@ -14,7 +14,7 @@ cfg_io_util! {
|
||||
/// This is an asynchronous version of [`std::io::empty`][std].
|
||||
///
|
||||
/// [`empty`]: fn.empty.html
|
||||
/// [std]: https://doc.rust-lang.org/std/io/struct.Empty.html
|
||||
/// [std]: std::io::empty
|
||||
pub struct Empty {
|
||||
_p: (),
|
||||
}
|
||||
@@ -25,20 +25,22 @@ cfg_io_util! {
|
||||
///
|
||||
/// This is an asynchronous version of [`std::io::empty`][std].
|
||||
///
|
||||
/// [std]: std::io::empty
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// A slightly sad example of not reading anything into a buffer:
|
||||
///
|
||||
/// ```rust
|
||||
/// # use tokio::io::{self, AsyncReadExt};
|
||||
/// # async fn dox() {
|
||||
/// let mut buffer = String::new();
|
||||
/// io::empty().read_to_string(&mut buffer).await.unwrap();
|
||||
/// assert!(buffer.is_empty());
|
||||
/// # }
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/io/fn.empty.html
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let mut buffer = String::new();
|
||||
/// io::empty().read_to_string(&mut buffer).await.unwrap();
|
||||
/// assert!(buffer.is_empty());
|
||||
/// }
|
||||
/// ```
|
||||
pub fn empty() -> Empty {
|
||||
Empty { _p: () }
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "stream")]
|
||||
impl<R: AsyncBufRead> futures_core::Stream for Lines<R> {
|
||||
impl<R: AsyncBufRead> crate::stream::Stream for Lines<R> {
|
||||
type Item = io::Result<String>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
|
||||
@@ -7,6 +7,9 @@ cfg_io_util! {
|
||||
mod async_read_ext;
|
||||
pub use async_read_ext::AsyncReadExt;
|
||||
|
||||
mod async_seek_ext;
|
||||
pub use async_seek_ext::AsyncSeekExt;
|
||||
|
||||
mod async_write_ext;
|
||||
pub use async_write_ext::AsyncWriteExt;
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ macro_rules! reader {
|
||||
}
|
||||
|
||||
while *me.read < $bytes as u8 {
|
||||
*me.read += match me.src
|
||||
*me.read += match me
|
||||
.src
|
||||
.as_mut()
|
||||
.poll_read(cx, &mut me.buf[*me.read as usize..])
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ cfg_io_util! {
|
||||
/// This is an asynchronous version of [`std::io::Repeat`][std].
|
||||
///
|
||||
/// [repeat]: fn.repeat.html
|
||||
/// [std]: https://doc.rust-lang.org/std/io/struct.Repeat.html
|
||||
/// [std]: std::io::Repeat
|
||||
#[derive(Debug)]
|
||||
pub struct Repeat {
|
||||
byte: u8,
|
||||
@@ -27,18 +27,20 @@ cfg_io_util! {
|
||||
///
|
||||
/// This is an asynchronous version of [`std::io::repeat`][std].
|
||||
///
|
||||
/// [std]: std::io::repeat
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use tokio::io::{self, AsyncReadExt};
|
||||
/// # async fn dox() {
|
||||
/// let mut buffer = [0; 3];
|
||||
/// io::repeat(0b101).read_exact(&mut buffer).await.unwrap();
|
||||
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||
/// # }
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncReadExt};
|
||||
///
|
||||
/// [std]: https://doc.rust-lang.org/std/io/fn.repeat.html
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
/// let mut buffer = [0; 3];
|
||||
/// io::repeat(0b101).read_exact(&mut buffer).await.unwrap();
|
||||
/// assert_eq!(buffer, [0b101, 0b101, 0b101]);
|
||||
/// }
|
||||
/// ```
|
||||
pub fn repeat(byte: u8) -> Repeat {
|
||||
Repeat { byte }
|
||||
}
|
||||
|
||||
+18
-11
@@ -11,9 +11,10 @@ cfg_io_util! {
|
||||
/// This struct is generally created by calling [`sink`][sink]. Please
|
||||
/// see the documentation of `sink()` for more details.
|
||||
///
|
||||
/// This is an asynchronous version of `std::io::Sink`.
|
||||
/// This is an asynchronous version of [`std::io::Sink`][std].
|
||||
///
|
||||
/// [sink]: fn.sink.html
|
||||
/// [sink]: sink()
|
||||
/// [std]: std::io::Sink
|
||||
pub struct Sink {
|
||||
_p: (),
|
||||
}
|
||||
@@ -21,21 +22,27 @@ cfg_io_util! {
|
||||
/// Creates an instance of an async writer which will successfully consume all
|
||||
/// data.
|
||||
///
|
||||
/// All calls to `poll_write` on the returned instance will return
|
||||
/// All calls to [`poll_write`] on the returned instance will return
|
||||
/// `Poll::Ready(Ok(buf.len()))` and the contents of the buffer will not be
|
||||
/// inspected.
|
||||
///
|
||||
/// This is an asynchronous version of `std::io::sink`.
|
||||
/// This is an asynchronous version of [`std::io::sink`][std].
|
||||
///
|
||||
/// [`poll_write`]: crate::io::AsyncWrite::poll_write()
|
||||
/// [std]: std::io::sink
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # use tokio::io::{self, AsyncWriteExt};
|
||||
/// # async fn dox() {
|
||||
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||
/// let num_bytes = io::sink().write(&buffer).await.unwrap();
|
||||
/// assert_eq!(num_bytes, 5);
|
||||
/// # }
|
||||
/// ```
|
||||
/// use tokio::io::{self, AsyncWriteExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// let buffer = vec![1, 2, 3, 5, 8];
|
||||
/// let num_bytes = io::sink().write(&buffer).await?;
|
||||
/// assert_eq!(num_bytes, 5);
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub fn sink() -> Sink {
|
||||
Sink { _p: () }
|
||||
|
||||
@@ -89,7 +89,7 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "stream")]
|
||||
impl<R: AsyncBufRead> futures_core::Stream for Split<R> {
|
||||
impl<R: AsyncBufRead> crate::stream::Stream for Split<R> {
|
||||
type Item = io::Result<Vec<u8>>;
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
|
||||
@@ -49,7 +49,8 @@ macro_rules! writer {
|
||||
}
|
||||
|
||||
while *me.written < $bytes as u8 {
|
||||
*me.written += match me.dst
|
||||
*me.written += match me
|
||||
.dst
|
||||
.as_mut()
|
||||
.poll_write(cx, &me.buf[*me.written as usize..])
|
||||
{
|
||||
@@ -77,10 +78,7 @@ macro_rules! writer8 {
|
||||
|
||||
impl<W> $name<W> {
|
||||
pub(crate) fn new(dst: W, byte: $ty) -> Self {
|
||||
Self {
|
||||
dst,
|
||||
byte,
|
||||
}
|
||||
Self { dst, byte }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+22
-7
@@ -1,4 +1,5 @@
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.2.4")]
|
||||
#![doc(html_root_url = "https://docs.rs/tokio/0.2.7")]
|
||||
#![allow(clippy::cognitive_complexity, clippy::needless_doctest_main)]
|
||||
#![warn(
|
||||
missing_debug_implementations,
|
||||
missing_docs,
|
||||
@@ -82,7 +83,7 @@
|
||||
//! [blocking]: task/index.html#blocking-and-yielding
|
||||
//!
|
||||
//! The [`tokio::sync`] module contains synchronization primitives to use when
|
||||
//! need to communicate or share data. These include:
|
||||
//! needing to communicate or share data. These include:
|
||||
//!
|
||||
//! * channels ([`oneshot`], [`mpsc`], and [`watch`]), for sending values
|
||||
//! between tasks,
|
||||
@@ -196,14 +197,14 @@
|
||||
//! Ok(n) if n == 0 => return,
|
||||
//! Ok(n) => n,
|
||||
//! Err(e) => {
|
||||
//! println!("failed to read from socket; err = {:?}", e);
|
||||
//! eprintln!("failed to read from socket; err = {:?}", e);
|
||||
//! return;
|
||||
//! }
|
||||
//! };
|
||||
//!
|
||||
//! // Write the data back
|
||||
//! if let Err(e) = socket.write_all(&buf[0..n]).await {
|
||||
//! println!("failed to write to socket; err = {:?}", e);
|
||||
//! eprintln!("failed to write to socket; err = {:?}", e);
|
||||
//! return;
|
||||
//! }
|
||||
//! }
|
||||
@@ -240,6 +241,10 @@ cfg_signal! {
|
||||
pub mod signal;
|
||||
}
|
||||
|
||||
cfg_stream! {
|
||||
pub mod stream;
|
||||
}
|
||||
|
||||
cfg_sync! {
|
||||
pub mod sync;
|
||||
}
|
||||
@@ -259,9 +264,19 @@ cfg_time! {
|
||||
mod util;
|
||||
|
||||
cfg_macros! {
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub use tokio_macros::main;
|
||||
pub use tokio_macros::test;
|
||||
doc_rt_core! {
|
||||
cfg_rt_threaded! {
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub use tokio_macros::main_threaded as main;
|
||||
pub use tokio_macros::test_threaded as test;
|
||||
}
|
||||
|
||||
cfg_not_rt_threaded! {
|
||||
#[cfg(not(test))] // Work around for rust-lang/rust#62127
|
||||
pub use tokio_macros::main_basic as main;
|
||||
pub use tokio_macros::test_basic as test;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests
|
||||
|
||||
@@ -11,9 +11,8 @@ macro_rules! assert_some {
|
||||
/// Assert option is none
|
||||
macro_rules! assert_none {
|
||||
($e:expr) => {{
|
||||
match $e {
|
||||
Some(v) => panic!("expected none, was {:?}", v),
|
||||
_ => {}
|
||||
if let Some(v) = $e {
|
||||
panic!("expected none, was {:?}", v);
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ macro_rules! cfg_resource_drivers {
|
||||
($($item:item)*) => {
|
||||
$(
|
||||
#[cfg(any(feature = "io-driver", feature = "time"))]
|
||||
#[cfg(not(loom))]
|
||||
$item
|
||||
)*
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
cfg_dns! {
|
||||
use crate::net::addr::ToSocketAddrs;
|
||||
|
||||
use std::io;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
/// Performs a DNS resolution.
|
||||
///
|
||||
/// The returned iterator may not actually yield any values depending on the
|
||||
/// outcome of any resolution performed.
|
||||
///
|
||||
/// This API is not intended to cover all DNS use cases. Anything beyond the
|
||||
/// basic use case should be done with a specialized library.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// To resolve a DNS entry:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net;
|
||||
/// use std::io;
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() -> io::Result<()> {
|
||||
/// for addr in net::lookup_host("localhost:3000").await? {
|
||||
/// println!("socket address is {}", addr);
|
||||
/// }
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
pub async fn lookup_host<T>(host: T) -> io::Result<impl Iterator<Item = SocketAddr>>
|
||||
where
|
||||
T: ToSocketAddrs
|
||||
{
|
||||
host.to_socket_addrs().await
|
||||
}
|
||||
}
|
||||
+11
-6
@@ -15,16 +15,21 @@
|
||||
//! over Unix Domain Datagram Socket **(available on Unix only)**
|
||||
|
||||
//!
|
||||
//! [`TcpListener`]: struct.TcpListener.html
|
||||
//! [`TcpStream`]: struct.TcpStream.html
|
||||
//! [`UdpSocket`]: struct.UdpSocket.html
|
||||
//! [`UnixListener`]: struct.UnixListener.html
|
||||
//! [`UnixStream`]: struct.UnixStream.html
|
||||
//! [`UnixDatagram`]: struct.UnixDatagram.html
|
||||
//! [`TcpListener`]: TcpListener
|
||||
//! [`TcpStream`]: TcpStream
|
||||
//! [`UdpSocket`]: UdpSocket
|
||||
//! [`UnixListener`]: UnixListener
|
||||
//! [`UnixStream`]: UnixStream
|
||||
//! [`UnixDatagram`]: UnixDatagram
|
||||
|
||||
mod addr;
|
||||
pub use addr::ToSocketAddrs;
|
||||
|
||||
cfg_dns! {
|
||||
mod lookup_host;
|
||||
pub use lookup_host::lookup_host;
|
||||
}
|
||||
|
||||
cfg_tcp! {
|
||||
pub mod tcp;
|
||||
pub use tcp::listener::TcpListener;
|
||||
|
||||
@@ -28,7 +28,7 @@ impl Incoming<'_> {
|
||||
}
|
||||
|
||||
#[cfg(feature = "stream")]
|
||||
impl futures_core::Stream for Incoming<'_> {
|
||||
impl crate::stream::Stream for Incoming<'_> {
|
||||
type Item = io::Result<TcpStream>;
|
||||
|
||||
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
|
||||
@@ -195,6 +195,14 @@ impl TcpListener {
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if thread-local runtime is not set.
|
||||
///
|
||||
/// The runtime is usually set implicitly when this function is called
|
||||
/// from a future driven by a tokio runtime, otherwise runtime can be set
|
||||
/// explicitly with [`Handle::enter`](crate::runtime::Handle::enter) function.
|
||||
pub fn from_std(listener: net::TcpListener) -> io::Result<TcpListener> {
|
||||
let io = mio::net::TcpListener::from_std(listener)?;
|
||||
let io = PollEvented::new(io)?;
|
||||
@@ -250,9 +258,7 @@ impl TcpListener {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
/// use tokio::net::TcpListener;
|
||||
///
|
||||
/// use futures::StreamExt;
|
||||
/// use tokio::{net::TcpListener, stream::StreamExt};
|
||||
///
|
||||
/// #[tokio::main]
|
||||
/// async fn main() {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user