Compare commits

...
71 Commits
Author SHA1 Message Date
Carl Lerche 8bf4696f31 chore: prepare v0.2.7 release (#2065) 2020-01-07 11:40:49 -08:00
Carl Lerche 10398b20c0 docs: minor tweaks to StreamExt API docs (#2066) 2020-01-07 11:40:37 -08:00
Alice Ryhl 780d6f91a0 docs: improve tokio::io API documentation (#2060)
* Links are added where missing and examples are improved.
* Improve `stdin`, `stdout`, and `stderr` documentation by going
  into more details regarding what can go wrong in concurrent
  situations and provide examples for `stdout` and `stderr`.
2020-01-07 09:17:01 -08:00
Carl Lerche 45da5f3510 rt: cleanup runtime::context (#2063)
Tweak context to remove more fns and usage of `Option`. Remove
`ThreadContext` struct as it is reduced to just `Handle`. Avoid passing
around individual driver handles and instead limit to the
`runtime::Handle` struct.
2020-01-07 07:53:40 -08:00
Sean McArthur 855d39f849 Fix basic_scheduler deadlock when waking during drop (#2062) 2020-01-06 15:37:03 -08:00
Eliza Weisman 798e86821f task: add ways to run a LocalSet from within a rt context (#1971)
Currently, the only way to run a `tokio::task::LocalSet` is to call its
`block_on` method with a `&mut Runtime`, like

```rust
let mut rt = tokio::runtime::Runtime::new();
let local = tokio::task::LocalSet::new();
local.block_on(&mut rt, async {
  // whatever...
});
```

Unfortunately, this means that `LocalSet` doesn't work with the 
`#[tokio::main]`  and `#[tokio::test]` macros, since the `main` 
function is _already_ inside of a call to `block_on`.

**Solution**

This branch adds a `LocalSet::run` method, which takes a future and
returns a new future that runs that future on the `LocalSet`. This
is analogous to `LocalSet::block_on`, except that it can be called in
an async context.

Additionally, this branch implements `Future` for `LocalSet`. Awaiting
a `LocalSet` will run all spawned local futures until they complete.
This allows code like

```rust
#[tokio::main] 
async fn main() {
    let local = tokio::task::LocalSet::new();

    local.spawn_local(async {
        // ...
    });

    local.spawn_local(async {
        // ...
        tokio::task::spawn_local(...);
        // ...
    });

    local.await;
}
```

The `LocalSet` docs have been updated to show the usage with 
`#[tokio::main]` rather than with manually created runtimes, where
applicable.

Closes #1906 
Closes #1908 
Fixes #2057
2020-01-06 14:44:30 -08:00
Benjamin Fry 0193df3a59 rt: add a Handle::current() (#2040)
Adds `Handle::current()` for accessing a handle to the runtime
associated with the current thread. This handle can then be
passed to other threads in order to spawn or perform other
runtime related tasks.
2020-01-06 11:32:21 -08:00
Tomasz Miąsko 5930acef73 rt: share vtable between waker and waker ref (#2045)
The `Waker::will_wake` compares both a data pointer and a vtable to
decide if wakers are equivalent. To avoid false negatives during
comparison, use the same vtable for a waker stored in `WakerRef`.
2020-01-06 10:39:48 -08:00
Artem Vorotnikov 3540c5b9ee stream: Add StreamExt::any (#2034) 2020-01-06 10:26:53 -08:00
Ivan Petkov 188fc6e0d2 process: deprecate Child stdio accessors in favor of pub fields (#2014)
Fixes #2009
2020-01-06 10:24:40 -08:00
Stepan Koltsov d45f61c183 doc: document from_std functions panic (#2056)
Document that conversion from `std` types must be done from within
the Tokio runtime context.
2020-01-06 10:06:39 -08:00
Linus Färnstrand dcfa895b51 chore: use just std instead of ::std in paths (#2049) 2020-01-06 10:04:21 -08:00
Carl Lerche f0006006ed time: advance frozen time in park_timeout (#2059)
This patch improves the behavior of frozen time (a testing utility made
available with the `test-util` feature flag). Instead of of requiring
`time::advance` to be called in order to advance the value returned by
`Instant::now`, calls to `time::Driver::park_timeout` will use the
provided duration to advance the time.

This is the desired behavior as the timeout is used to indicate when the
next scheduled delay needs to be fired.
2020-01-06 08:47:34 -08:00
John Van Enk 84ff73e687 tokio: remove documentation stating Receiver is clone-able. (#2037)
* tokio: remove documentation stating `Receiver` is clone-able.

The documentation for `broadcast` stated that both `Sender` and
`Receiver` are clonable. This isn't the case: `Receiver`s cannot be
cloned (and shouldn't be cloned).

In addition, mention that `Receiver` is `Sync`, and mention that both
`Receiver` and `Sender` are `Send`.

Fixes: #2032

* Clarify that Sender and Receiver are only Send and Sync if T is Send or Sync.
2020-01-06 11:28:26 -05:00
João Oliveira 32e15b3a24 sync: add RwLock (#1699)
Provides a `RwLock` based on a semaphore. The semaphore is initialized
with 32 permits. A read acquires a single permit and a write acquires all 32
permits. This ensures that reads (up to 32) may happen concurrently and
writes happen exclusively.
2020-01-03 21:03:26 -08:00
Carl Lerche efcbf9613f sync: add batch op support to internal semaphore (#2004)
Extend internal semaphore to support batch operations. With this PR,
consumers of the semaphore are able to atomically request more than one
permit. This is useful for implementing a RwLock.
2020-01-03 10:34:15 -08:00
Artem Vorotnikov 3736467dbb stream: correct trait bounds for all (#2043) 2020-01-02 15:03:53 -08:00
Artem Vorotnikov e43f28f6a8 macros: do not automatically pull rt-core (#2038) 2020-01-02 11:22:15 -08:00
Artem Vorotnikov 3cf91db4b6 stream: add StreamExt::all (#2035) 2020-01-02 11:36:38 -05:00
Artem Vorotnikov e8fcf55881 Refactor proc macros, add more knobs (#2022)
* Refactor proc macros, add more knobs

* make macros work with rt-core
2019-12-27 13:56:43 -05:00
Artem Vorotnikov a515f9c459 stream: add StreamExt::take_while (#2029) 2019-12-25 12:48:02 -08:00
Carl Lerche 50b91c0247 chore: move benches to separate crate (#2028)
This allows the `benches` crate to depend on `tokio` with all feature
flags. This is a similar strategy used for `examples`.
2019-12-24 20:53:20 -08:00
Gardner Vickers 67bf9c36f3 rt: coalesce thread-locals used by the runtime (#1925)
Previously, thread-locals used by the various drivers were situated
with the driver code. This resulted in state being spread out and many
thread-locals being required to run a runtime.

This PR coalesces the thread-locals into a single struct.
2019-12-24 15:34:47 -08:00
Artem Vorotnikov 101f770af3 stream: add StreamExt::take (#2025) 2019-12-24 08:20:02 -08:00
Stephen Carman 6ff4e349e2 doc: add additional Mutex example (#2019) 2019-12-23 10:18:30 -08:00
Carl Lerche adc5186ebd rt: fix storing Runtime in thread-local (#2011)
Storing a `Runtime` value in a thread-local resulted in a panic due to
the inability to access the parker.

This fixes the bug by skipping parking if it fails. In general, there
isn't much that we can do besides not parking.

Fixes #593
2019-12-22 13:03:44 -08:00
Carl Lerche 7b53b7b659 doc: fill out fs and remove html links (#2015)
also add an async version of `fs::canonicalize`
2019-12-22 12:55:09 -08:00
baizhenxuan 99fa93bf0e tokio-tls: fix examples build and run (#1963) 2019-12-22 11:48:08 -08:00
Ruben De Smet 0133bc1883 time: DelayQueue::len() (#1755) 2019-12-21 18:18:49 -08:00
Bhargav a854094825 sync: impl Stream for broadcast::Receiver (#2012) 2019-12-21 14:38:05 -08:00
Carl Lerche 3d1b4b3058 rt: fix spawn_blocking from spawn_blocking (#2006)
Nested spawn_blocking calls would result in a panic due to the necessary
context not being setup. This patch sets the blocking pool context from
within a blocking pool.

Fixes #1982
2019-12-21 13:19:52 -08:00
Artem Vorotnikov 8656b7b8eb chore: fix formatting, remove old rustfmt.toml (#2007)
`cargo fmt` has a bug where it does not format modules scoped with
feature flags.
2019-12-21 12:28:57 -08:00
fbucek f309b295bb doc: fix misleading comment in interval.rs 2019-12-21 09:31:02 -08:00
Artem Vorotnikov b1266a48c4 Fix UdpFramed doc cfg_attr (#2010) 2019-12-21 12:04:30 -05:00
David Barsky de5ec6e1bc dns: provide lookup_host function (#1870)
`ToSocketAddrs` is a sealed trait pending changes in Rust that will allow
defining async trait fns. Until then, `net::lookup_host` is provided as a way
to convert a `T: ToSocketAddrs` into `SocketAddr`s.
2019-12-21 08:30:00 -08:00
Artem Vorotnikov 3dcd76a38f stream: StreamExt::try_next (#2005) 2019-12-20 21:27:14 -08:00
Artem Vorotnikov 3b9c7b1715 stream: filtering utilities (#2001)
Adds `StreamExt::filter` and `StreamExt::filter_map`.
2019-12-20 20:17:05 -08:00
Artem Vorotnikov 3bff5a3ffe chore: formatting, docs and clippy (#2000) 2019-12-20 13:54:43 -08:00
Carl Lerche 248bf2144f prepare v0.2.6 release (#1995) 2019-12-19 14:02:07 -08:00
Carl Lerche 93ab70a9a0 fs: add deprecated fs::File::seek fn (#1991)
This fixes an API compatibility regression when `AsyncSeek` was added.

Fixes: #1989
2019-12-19 13:37:10 -08:00
João Oliveira 58b5abdb99 update connect example (#1787) 2019-12-18 19:54:06 -05:00
Carl Lerche 2d78cfe56a chore: prepare v0.2.5 release (#1984)
Also includes:
- `tokio-macros` v0.2.1
2019-12-18 13:07:27 -08:00
Artem Vorotnikov 4c645866ef stream: add next and map utility fn (#1962)
Introduces `StreamExt` trait. This trait will be used to add utility functions
to make working with streams easier. This patch includes two functions:

* `next`: a future returning the item in the stream.
* `map`: transform each item in the stream.
2019-12-18 11:57:22 -08:00
Carl Lerche b0836ece7a sync: encapsulate TryLockError variants (#1980)
As there is currently only one variant, make the error type an opaque
struct.
2019-12-18 11:50:56 -08:00
Douman 0c0f682010 Improve runtime threading options docs 2019-12-18 20:14:22 +01:00
Douman b24ad9fe86 rt: add configuration for core threads and max threads (#1977)
`num_threads` is deprecated. Instead, `core_threads` and `max_threads` are
introduced. `core_threads` specifies the number of "always on" threads used
for the async task executor and `max_threads` specifies the maximum number
of threads that the runtime may spawn.
2019-12-18 10:31:49 -08:00
Carl Lerche 7c010ed030 sync: add broadcast channel (#1943)
Adds a broadcast channel implementation. A broadcast channel is a
multi-producer, multi-consumer channel where each consumer receives a
clone of every value sent. This is useful for implementing pub / sub
style patterns.

Implemented as a ring buffer, a Vec of the specified capacity is
allocated on initialization of the channel. Values are pushed into
slots.

When the channel is full, a send overwrites the oldest value. Receivers
detect this and return an error on the next call to receive. This
prevents unbounded buffering and does not make the channel vulnerable to
the slowest consumer.

Closes: #1585
2019-12-18 10:13:15 -08:00
Dmitrii Goriunov 42c942de14 Fix ROADMAP link (#1981) 2019-12-18 09:54:35 -05:00
Kelly Thomas Kline 5e8f7eb03c docs: correct spelling (#1974) 2019-12-17 22:52:40 -08:00
Michael P. Jung 9211adbe01 sync: add Semaphore (#1973)
Provide an asynchronous Semaphore implementation. This is useful for
synchronizing concurrent access to a shared resource.
2019-12-17 22:32:12 -08:00
yim7 e5b99b0f7a sync: print MutexGuard inner value for debugging (#1961) 2019-12-17 22:02:31 -08:00
Carl Lerche 83cd754bc8 rt: fix blocking pool shutdown logic (#1978)
The blocking task queue was not explicitly drained as part of the
blocking pool shutdown logic. It was originally assumed that the
contents of the queue would be dropped when the blocking pool structure
is dropped. However, tasks must be explicitly shutdown, so we must drain
the queue can call `shutdown` on each task.

Fixes #1970, #1946
2019-12-17 21:24:26 -08:00
Ruben De Smet 17e424112d time: impl Stream for DelayQueue (#1975) 2019-12-17 21:01:29 -08:00
Carl Lerche 41d15ea212 rt: avoid dropping a task in calls to wake() (#1972)
Calls to tasks should not be nested. Currently, while a task is being
executed and the runtime is shutting down, a call to wake() can result
in the wake target to be dropped. This, in turn, results in the drop
handler being called.

If the user holds a ref cell borrow, a mutex guard, or any such value,
dropping the task inline can result in a deadlock.

The fix is to permit tasks to be scheduled during the shutdown process
and dropping the tasks once they are popped from the queue.

Fixes #1929, #1886
2019-12-17 20:52:09 -08:00
Kelly Thomas Kline 8add90210b docs: correct grammar (#1968) 2019-12-17 13:57:25 -08:00
Kelly Thomas Kline efb4b67a54 chore: add roadmap (#1965) 2019-12-16 09:39:49 -08:00
Vlad-Shcherbina 74d33a1b2f Fix typo in sync documentation (#1942) 2019-12-14 10:17:36 -08:00
Jake Goulding 69885e214c Enable the full feature when compiled for the playground (#1960)
Closes #1932
2019-12-14 10:16:20 -08:00
Artem Vorotnikov 4b85565bd7 time: stream throttle (#1949) 2019-12-13 22:06:41 -08:00
Artem Vorotnikov d593c5b051 chore: remove benches and fix/work around clippy lints (#1952) 2019-12-13 22:01:47 -08:00
Douman 91ecb4b4c2 macros: inherit visibility 2019-12-13 19:33:44 +01:00
Sean McArthur 8abaf89e5f Re-enable writev support in TcpStreams (#1956) 2019-12-13 10:25:27 -08:00
Carl Lerche b560df9e66 chore: fix warning in tokio-util tests (#1955)
`bytes` added a warning when using a fn that resulted in a useless
clone.
2019-12-13 10:03:10 -08:00
Mathspy df8278acb6 Fix typo with updated docs (#1920)
I think this is a typo
2019-12-12 14:55:25 -05:00
nickelc 5862b9a2e0 chore: fix the outdated example in README (#1930) 2019-12-11 14:12:53 -08:00
Carl Lerche c0953d41a5 chore: fix thread_pool benchmarks (#1947)
Update the rotted thread_pool benchmarks. These benchmarks are not the
greatest, but as of now it is all we have for micro benchmarks.

Adds a little yielding in the parker as it helps a bit.
2019-12-11 12:44:45 -08:00
Michael HowellandTaiki Endo 24cd6d67f7 io: add AsyncSeek trait (#1924)
Co-authored-by: Taiki Endo <[email protected]>
2019-12-10 21:48:24 -08:00
Michael P. Jung 975576952f Add Mutex::try_lock and (Unbounded)Receiver::try_recv (#1939) 2019-12-10 08:01:23 -08:00
Juan Alvarez 5d5755dca4 fix spawn function documentation (#1940) 2019-12-10 10:46:48 -05:00
Danilo Bargen 41ffdbb7d9 sync::Mutex: Fix typo in documentation (#1934) 2019-12-09 15:07:21 -05:00
Danilo Bargen 2450b5bfc9 sync::Mutex: Add note about the absence of poisoning (#1933) 2019-12-09 09:13:24 -08:00
218 changed files with 9497 additions and 4949 deletions
+1
View File
@@ -8,6 +8,7 @@ members = [
"tokio-util",
# Internal
"benches",
"examples",
"tests-build",
"tests-integration",
+1 -1
View File
@@ -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
View File
@@ -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
+14
View File
@@ -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
+70
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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
+5
View File
@@ -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
View File
@@ -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
View File
@@ -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(())
}
}
}
+3 -1
View File
@@ -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
View File
@@ -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
+4 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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()),
}
}
}
+5 -2
View File
@@ -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);
+1 -1
View File
@@ -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.
+5 -2
View File
@@ -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
View File
@@ -1 +0,0 @@
edition = "2018"
+3 -3
View File
@@ -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";
+5
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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())
}
+1 -1
View File
@@ -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"
+4 -1
View File
@@ -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 {
+4 -6
View File
@@ -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;
});
}
+1
View File
@@ -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 }
+55
View File
@@ -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");
});
}
}
-60
View File
@@ -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(())
}
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -26,7 +26,7 @@ default = []
# Shorthand for enabling everything
full = ["codec", "udp"]
codec = []
codec = ["tokio/stream"]
udp = ["tokio/udp"]
[dependencies]
+4 -2
View File
@@ -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 -2
View File
@@ -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;
+5 -2
View File
@@ -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;
+1 -1
View File
@@ -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
View File
@@ -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,
+3 -3
View File
@@ -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 -2
View File
@@ -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};
+1 -1
View File
@@ -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))
}
}
+1 -1
View File
@@ -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 -2
View File
@@ -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]
+56
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
}
-114
View File
@@ -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();
}
-57
View File
@@ -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();
});
}
-270
View File
@@ -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();
}
}
-120
View File
@@ -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();
}
-257
View File
@@ -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);
}
}
}
-161
View File
@@ -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();
});
}
+51
View File
@@ -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
}
+39 -1
View File
@@ -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
+39 -1
View File
@@ -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
View File
@@ -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>,
+29 -2
View File
@@ -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();
+37 -1
View File
@@ -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
+3
View File
@@ -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
View File
@@ -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();
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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();
+1 -1
View File
@@ -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
View File
@@ -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();
+2 -2
View File
@@ -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>> {
+4 -4
View File
@@ -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
+4 -4
View File
@@ -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 -1
View File
@@ -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.
+104
View File
@@ -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()))
}
}
+15 -6
View File
@@ -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.
///
+3 -46
View File
@@ -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
+5 -7
View File
@@ -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
View File
@@ -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
+9 -1
View File
@@ -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 {
+11 -2
View File
@@ -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,
+56
View File
@@ -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>>();
}
}
+5 -5
View File
@@ -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
View File
@@ -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 {
+7 -1
View File
@@ -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
View File
@@ -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 {
+4 -2
View File
@@ -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 -2
View File
@@ -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};
+38
View File
@@ -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 {}
+2 -2
View File
@@ -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;
+3 -3
View File
@@ -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
View File
@@ -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: () }
}
+1 -1
View File
@@ -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>> {
+3
View File
@@ -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;
+2 -1
View File
@@ -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..])
{
+11 -9
View File
@@ -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
View File
@@ -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: () }
+1 -1
View File
@@ -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>> {
+3 -5
View File
@@ -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
View File
@@ -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
+2 -3
View File
@@ -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);
}
}};
}
-1
View File
@@ -4,7 +4,6 @@ macro_rules! cfg_resource_drivers {
($($item:item)*) => {
$(
#[cfg(any(feature = "io-driver", feature = "time"))]
#[cfg(not(loom))]
$item
)*
}
+38
View File
@@ -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
View File
@@ -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;
+1 -1
View File
@@ -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>> {
+9 -3
View File
@@ -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