Merge remote-tracking branch 'upstream/master' into 342-stream-throttle

This commit is contained in:
Wouter Geraedts 2019-11-11 10:15:27 +01:00
commit 14d7d3bf9c
226 changed files with 8993 additions and 3084 deletions

View file

@ -7,6 +7,9 @@ on:
- staging - staging
- trying - trying
env:
RUSTFLAGS: -Dwarnings
jobs: jobs:
build_and_test: build_and_test:
name: Build and test name: Build and test
@ -14,7 +17,7 @@ jobs:
strategy: strategy:
matrix: matrix:
os: [ubuntu-latest, windows-latest, macOS-latest] os: [ubuntu-latest, windows-latest, macOS-latest]
rust: [nightly] rust: [nightly, beta, stable]
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
@ -29,13 +32,31 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --all --benches --bins --examples --tests args: --all --bins --tests
- name: check unstable - name: check unstable
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: check command: check
args: --features unstable --all --benches --bins --examples --tests args: --features unstable --all --bins --examples --tests
- name: check bench
uses: actions-rs/cargo@v1
if: matrix.rust == 'nightly'
with:
command: check
args: --benches
- name: check std only
uses: actions-rs/cargo@v1
with:
command: check
args: --no-default-features --features std
- name: check attributes
uses: actions-rs/cargo@v1
with:
command: check
args: --features attributes
- name: tests - name: tests
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
@ -49,15 +70,12 @@ jobs:
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- id: component
uses: actions-rs/components-nightly@v1
with:
component: rustfmt
- uses: actions-rs/toolchain@v1 - uses: actions-rs/toolchain@v1
with: with:
toolchain: ${{ steps.component.outputs.toolchain }} profile: minimal
toolchain: nightly
override: true override: true
components: rustfmt
- name: setup - name: setup
run: | run: |
@ -74,20 +92,14 @@ jobs:
- name: Docs - name: Docs
run: cargo doc --features docs run: cargo doc --features docs
clippy_check: # clippy_check:
name: Clippy check # name: Clippy check
runs-on: ubuntu-latest # runs-on: ubuntu-latest
steps: # steps:
- uses: actions/checkout@v1 # - uses: actions/checkout@v1
- id: component # - name: Install rust
uses: actions-rs/components-nightly@v1 # run: rustup update beta && rustup default beta
with: # - name: Install clippy
component: clippy # run: rustup component add clippy
- uses: actions-rs/toolchain@v1 # - name: clippy
with: # run: cargo clippy --all --features unstable
toolchain: ${{ steps.component.outputs.toolchain }}
override: true
- run: rustup component add clippy
- uses: actions-rs/clippy-check@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}

View file

@ -7,6 +7,113 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
## [Unreleased] ## [Unreleased]
# [0.99.12] - 2019-11-07
[API Documentation](https://docs.rs/async-std/0.99.12/async-std)
This patch upgrades us to `futures` 0.3, support for `async/await` on Rust
Stable, performance improvements, and brand new module-level documentation.
## Added
- Added `Future::flatten` as "unstable".
- Added `Future::race` as "unstable" (replaces `future::select!`).
- Added `Future::try_race` as "unstable" (replaces `future::try_select!`).
- Added `Stderr::lock` as "unstable".
- Added `Stdin::lock` as "unstable".
- Added `Stdout::lock` as "unstable".
- Added `Stream::copied` as "unstable".
- Added `Stream::eq` as "unstable".
- Added `Stream::max_by_key` as "unstable".
- Added `Stream::min` as "unstable".
- Added `Stream::ne` as "unstable".
- Added `Stream::position` as "unstable".
- Added `StreamExt` and `FutureExt` as enumerable in the `prelude`.
- Added `TcpListener` and `TcpStream` integration tests.
- Added `stream::from_iter`.
- Added `sync::WakerSet` for internal use.
- Added an example to handle both `IP v4` and `IP v6` connections.
- Added the `default` Cargo feature.
- Added the `attributes` Cargo feature.
- Added the `std` Cargo feature.
## Changed
- Fixed a bug in the blocking threadpool where it didn't spawn more than one thread.
- Fixed a bug with `Stream::merge` where sometimes it ended too soon.
- Fixed a bug with our GitHub actions setup.
- Fixed an issue where our channels could spuriously deadlock.
- Refactored the `task` module.
- Removed a deprecated GitHub action.
- Replaced `futures-preview` with `futures`.
- Replaced `lazy_static` with `once_cell`.
- Replaced all uses of `VecDequeue` in the examples with `stream::from_iter`.
- Simplified `sync::RwLock` using the internal `sync::WakerSet` type.
- Updated the `path` submodule documentation to match std.
- Updated the mod-level documentation to match std.
## Removed
- Removed `future::select!` (replaced by `Future::race`).
- Removed `future::try_select!` (replaced by `Future::try_race`).
# [0.99.11] - 2019-10-29
This patch introduces `async_std::sync::channel`, a novel asynchronous port of
the ultra-fast Crossbeam channels. This has been one of the most anticipated
features for async-std, and we're excited to be providing a first version of
this!
In addition to channels, this patch has the regular list of new methods, types,
and doc fixes.
## Examples
__Send and receive items from a channel__
```rust
// Create a bounded channel with a max-size of 1
let (s, r) = channel(1);
// This call returns immediately because there is enough space in the channel.
s.send(1).await;
task::spawn(async move {
// This call blocks the current task because the channel is full.
// It will be able to complete only after the first message is received.
s.send(2).await;
});
// Receive items from the channel
task::sleep(Duration::from_secs(1)).await;
assert_eq!(r.recv().await, Some(1));
assert_eq!(r.recv().await, Some(2));
```
## Added
- Added `Future::delay` as "unstable"
- Added `Stream::flat_map` as "unstable"
- Added `Stream::flatten` as "unstable"
- Added `Stream::product` as "unstable"
- Added `Stream::sum` as "unstable"
- Added `Stream::min_by_key`
- Added `Stream::max_by`
- Added `Stream::timeout` as "unstable"
- Added `sync::channel` as "unstable".
- Added doc links from instantiated structs to the methods that create them.
- Implemented `Extend` + `FromStream` for `PathBuf`.
## Changed
- Fixed an issue with `block_on` so it works even when nested.
- Fixed issues with our Clippy check on CI.
- Replaced our uses of `cfg_if` with our own macros, simplifying the codebase.
- Updated the homepage link in `Cargo.toml` to point to [async.rs](https://async.rs).
- Updated the module-level documentation for `stream` and `sync`.
- Various typos and grammar fixes.
- Removed redundant file flushes, improving the performance of `File` operations
## Removed
Nothing was removed in this release.
# [0.99.10] - 2019-10-16 # [0.99.10] - 2019-10-16
This patch stabilizes several core concurrency macros, introduces async versions This patch stabilizes several core concurrency macros, introduces async versions
@ -281,7 +388,8 @@ task::blocking(async {
- Initial beta release - Initial beta release
[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.10...HEAD [Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.11...HEAD
[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.10...v0.99.11
[0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10 [0.99.10]: https://github.com/async-rs/async-std/compare/v0.99.9...v0.99.10
[0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9 [0.99.9]: https://github.com/async-rs/async-std/compare/v0.99.8...v0.99.9
[0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8 [0.99.8]: https://github.com/async-rs/async-std/compare/v0.99.7...v0.99.8

View file

@ -1,6 +1,6 @@
[package] [package]
name = "async-std" name = "async-std"
version = "0.99.10" version = "0.99.12"
authors = [ authors = [
"Stjepan Glavina <stjepang@gmail.com>", "Stjepan Glavina <stjepang@gmail.com>",
"Yoshua Wuyts <yoshuawuyts@gmail.com>", "Yoshua Wuyts <yoshuawuyts@gmail.com>",
@ -9,7 +9,7 @@ authors = [
edition = "2018" edition = "2018"
license = "Apache-2.0/MIT" license = "Apache-2.0/MIT"
repository = "https://github.com/async-rs/async-std" repository = "https://github.com/async-rs/async-std"
homepage = "https://github.com/async-rs/async-std" homepage = "https://async.rs"
documentation = "https://docs.rs/async-std" documentation = "https://docs.rs/async-std"
description = "Async version of the Rust standard library" description = "Async version of the Rust standard library"
keywords = ["async", "await", "future", "std", "task"] keywords = ["async", "await", "future", "std", "task"]
@ -21,35 +21,67 @@ features = ["docs"]
rustdoc-args = ["--cfg", "feature=\"docs\""] rustdoc-args = ["--cfg", "feature=\"docs\""]
[features] [features]
docs = ["unstable"] default = [
unstable = ["broadcaster"] "std",
"async-task",
"crossbeam-channel",
"crossbeam-deque",
"futures-timer",
"kv-log-macro",
"log",
"mio",
"mio-uds",
"num_cpus",
"pin-project-lite",
]
docs = ["attributes", "unstable"]
unstable = ["default", "broadcaster"]
attributes = ["async-attributes"]
std = [
"async-macros",
"crossbeam-utils",
"futures-core",
"futures-io",
"memchr",
"once_cell",
"pin-project-lite",
"pin-utils",
"slab",
]
[dependencies] [dependencies]
async-macros = "1.0.0" async-attributes = { version = "1.1.0", optional = true }
async-task = "1.0.0" async-macros = { version = "1.0.0", optional = true }
crossbeam-channel = "0.3.9" async-task = { version = "1.0.0", optional = true }
crossbeam-deque = "0.7.1"
crossbeam-utils = "0.6.6"
futures-core-preview = "=0.3.0-alpha.19"
futures-io-preview = "=0.3.0-alpha.19"
futures-timer = "1.0.2"
lazy_static = "1.4.0"
log = { version = "0.4.8", features = ["kv_unstable"] }
memchr = "2.2.1"
mio = "0.6.19"
mio-uds = "0.6.7"
num_cpus = "1.10.1"
pin-utils = "0.1.0-alpha.4"
slab = "0.4.2"
kv-log-macro = "1.0.4"
broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] }
crossbeam-channel = { version = "0.3.9", optional = true }
crossbeam-deque = { version = "0.7.1", optional = true }
crossbeam-utils = { version = "0.6.6", optional = true }
futures-core = { version = "0.3.0", optional = true }
futures-io = { version = "0.3.0", optional = true }
futures-timer = { version = "1.0.2", optional = true }
kv-log-macro = { version = "1.0.4", optional = true }
log = { version = "0.4.8", features = ["kv_unstable"], optional = true }
memchr = { version = "2.2.1", optional = true }
mio = { version = "0.6.19", optional = true }
mio-uds = { version = "0.6.7", optional = true }
num_cpus = { version = "1.10.1", optional = true }
once_cell = { version = "1.2.0", optional = true }
pin-project-lite = { version = "0.1", optional = true }
pin-utils = { version = "0.1.0-alpha.4", optional = true }
slab = { version = "0.4.2", optional = true }
[dev-dependencies] [dev-dependencies]
femme = "1.2.0" femme = "1.2.0"
rand = "0.7.2"
# surf = "1.0.2" # surf = "1.0.2"
tempdir = "0.3.7" tempdir = "0.3.7"
futures-preview = { version = "=0.3.0-alpha.19", features = ["async-await"] } futures = "0.3.0"
# These are used by the book for examples [[test]]
futures-channel-preview = "=0.3.0-alpha.19" name = "stream"
futures-util-preview = "=0.3.0-alpha.19" required-features = ["unstable"]
[[example]]
name = "tcp-ipv4-and-6-echo"
required-features = ["unstable"]

View file

@ -61,7 +61,7 @@ syntax.
## Features ## Features
- __Modern:__ Built from the ground up for `std::future` and `async/await` with - __Modern:__ Built from the ground up for `std::future` and `async/await` with
blazing fast compilation times. blazing fast compilation time.
- __Fast:__ Our robust allocator and threadpool designs provide ultra-high - __Fast:__ Our robust allocator and threadpool designs provide ultra-high
throughput with predictably low latency. throughput with predictably low latency.
- __Intuitive:__ Complete parity with the stdlib means you only need to learn - __Intuitive:__ Complete parity with the stdlib means you only need to learn

42
benches/mutex.rs Normal file
View file

@ -0,0 +1,42 @@
#![feature(test)]
extern crate test;
use std::sync::Arc;
use async_std::sync::Mutex;
use async_std::task;
use test::Bencher;
#[bench]
fn create(b: &mut Bencher) {
b.iter(|| Mutex::new(()));
}
#[bench]
fn contention(b: &mut Bencher) {
b.iter(|| task::block_on(run(10, 1000)));
}
#[bench]
fn no_contention(b: &mut Bencher) {
b.iter(|| task::block_on(run(1, 10000)));
}
async fn run(task: usize, iter: usize) {
let m = Arc::new(Mutex::new(()));
let mut tasks = Vec::new();
for _ in 0..task {
let m = m.clone();
tasks.push(task::spawn(async move {
for _ in 0..iter {
let _ = m.lock().await;
}
}));
}
for t in tasks {
t.await;
}
}

11
benches/task.rs Normal file
View file

@ -0,0 +1,11 @@
#![feature(test)]
extern crate test;
use async_std::task;
use test::Bencher;
#[bench]
fn block_on(b: &mut Bencher) {
b.iter(|| task::block_on(async {}));
}

View file

@ -24,11 +24,7 @@ To sum up: Rust gives us the ability to safely abstract over important propertie
## An easy view of computation ## An easy view of computation
While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: While computation is a subject to write a whole [book](https://computationbook.com/) about, a very simplified view suffices for us: A sequence of composable operations which can branch based on a decision, run to succession and yield a result or yield an error
- computation is a sequence of composable operations
- they can branch based on a decision
- they either run to succession and yield a result, or they can yield an error
## Deferring computation ## Deferring computation
@ -136,11 +132,11 @@ When executing 2 or more of these functions at the same time, our runtime system
## Conclusion ## Conclusion
Working from values, we searched for something that expresses *working towards a value available sometime later*. From there, we talked about the concept of polling. Working from values, we searched for something that expresses *working towards a value available later*. From there, we talked about the concept of polling.
A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple. A `Future` is any data type that does not represent a value, but the ability to *produce a value at some point in the future*. Implementations of this are very varied and detailed depending on use-case, but the interface is simple.
Next, we will introduce you to `tasks`, which we need to actually *run* Futures. Next, we will introduce you to `tasks`, which we will use to actually *run* Futures.
[^1]: Two parties reading while it is guaranteed that no one is writing is always safe. [^1]: Two parties reading while it is guaranteed that no one is writing is always safe.

View file

@ -80,7 +80,7 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread`
## Blocking ## Blocking
`Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this: `Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and of itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this:
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;

View file

@ -4,4 +4,4 @@
`async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`. `async-std` provides an interface to all important primitives: filesystem operations, network operations and concurrency basics like timers. It also exposes a `task` in a model similar to the `thread` module found in the Rust standard lib. But it does not only include I/O primitives, but also `async/await` compatible versions of primitives like `Mutex`.
[organization]: https://github.com/async-rs/async-std [organization]: https://github.com/async-rs

View file

@ -31,7 +31,7 @@ In general, this crate will be conservative with respect to the minimum supporte
## Security fixes ## Security fixes
Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give at least _3 month_ of ahead notice. Security fixes will be applied to _all_ minor branches of this library in all _supported_ major revisions. This policy might change in the future, in which case we give a notice at least _3 months_ ahead.
## Credits ## Credits

View file

@ -4,16 +4,15 @@ At this point, we only need to start the broker to get a fully-functioning (in t
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
use async_std::{ use async_std::{
io::{self, BufReader}, io::{self, BufReader},
net::{TcpListener, TcpStream, ToSocketAddrs}, net::{TcpListener, TcpStream, ToSocketAddrs},
prelude::*, prelude::*,
task, task,
}; };
use futures_channel::mpsc; use futures::channel::mpsc;
use futures_util::SinkExt; use futures::SinkExt;
use std::{ use std::{
collections::hash_map::{HashMap, Entry}, collections::hash_map::{HashMap, Entry},
sync::Arc, sync::Arc,

View file

@ -22,16 +22,15 @@ Let's add waiting to the server:
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
# use async_std::{ # use async_std::{
# io::{self, BufReader}, # io::{self, BufReader},
# net::{TcpListener, TcpStream, ToSocketAddrs}, # net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::*, # prelude::*,
# task, # task,
# }; # };
# use futures_channel::mpsc; # use futures::channel::mpsc;
# use futures_util::SinkExt; # use futures::SinkExt;
# use std::{ # use std::{
# collections::hash_map::{HashMap, Entry}, # collections::hash_map::{HashMap, Entry},
# sync::Arc, # sync::Arc,
@ -156,16 +155,15 @@ And to the broker:
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
# use async_std::{ # use async_std::{
# io::{self, BufReader}, # io::{self, BufReader},
# net::{TcpListener, TcpStream, ToSocketAddrs}, # net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::*, # prelude::*,
# task, # task,
# }; # };
# use futures_channel::mpsc; # use futures::channel::mpsc;
# use futures_util::SinkExt; # use futures::SinkExt;
# use std::{ # use std::{
# collections::hash_map::{HashMap, Entry}, # collections::hash_map::{HashMap, Entry},
# sync::Arc, # sync::Arc,

View file

@ -12,15 +12,14 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
# use async_std::{ # use async_std::{
# net::TcpStream, # net::TcpStream,
# prelude::*, # prelude::*,
# task, # task,
# }; # };
# use futures_channel::mpsc; # use futures::channel::mpsc;
# use futures_util::sink::SinkExt; # use futures::sink::SinkExt;
# use std::sync::Arc; # use std::sync::Arc;
# #
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

View file

@ -19,11 +19,10 @@ First, let's add a shutdown channel to the `connection_loop`:
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
# use async_std::net::TcpStream; # use async_std::net::TcpStream;
# use futures_channel::mpsc; # use futures::channel::mpsc;
# use futures_util::SinkExt; # use futures::SinkExt;
# use std::sync::Arc; # use std::sync::Arc;
# #
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
@ -70,11 +69,10 @@ We use the `select` macro for this purpose:
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
# use async_std::{net::TcpStream, prelude::*}; # use async_std::{net::TcpStream, prelude::*};
use futures_channel::mpsc; use futures::channel::mpsc;
use futures_util::{select, FutureExt}; use futures::{select, FutureExt};
# use std::sync::Arc; # use std::sync::Arc;
# type Receiver<T> = mpsc::UnboundedReceiver<T>; # type Receiver<T> = mpsc::UnboundedReceiver<T>;
@ -122,16 +120,15 @@ The final code looks like this:
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
use async_std::{ use async_std::{
io::BufReader, io::BufReader,
net::{TcpListener, TcpStream, ToSocketAddrs}, net::{TcpListener, TcpStream, ToSocketAddrs},
prelude::*, prelude::*,
task, task,
}; };
use futures_channel::mpsc; use futures::channel::mpsc;
use futures_util::{select, FutureExt, SinkExt}; use futures::{select, FutureExt, SinkExt};
use std::{ use std::{
collections::hash_map::{Entry, HashMap}, collections::hash_map::{Entry, HashMap},
future::Future, future::Future,

View file

@ -16,14 +16,14 @@ With async, we can just use the `select!` macro.
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_util; # extern crate futures;
use async_std::{ use async_std::{
io::{stdin, BufReader}, io::{stdin, BufReader},
net::{TcpStream, ToSocketAddrs}, net::{TcpStream, ToSocketAddrs},
prelude::*, prelude::*,
task, task,
}; };
use futures_util::{select, FutureExt}; use futures::{select, FutureExt};
type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

View file

@ -13,14 +13,13 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;
# extern crate futures_channel; # extern crate futures;
# extern crate futures_util;
# use async_std::{ # use async_std::{
# net::TcpStream, # net::TcpStream,
# prelude::*, # prelude::*,
# }; # };
use futures_channel::mpsc; // 1 use futures::channel::mpsc; // 1
use futures_util::sink::SinkExt; use futures::sink::SinkExt;
use std::sync::Arc; use std::sync::Arc;
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>; # type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;

View file

@ -12,7 +12,7 @@ After that, the client can send messages to other clients using the following sy
login1, login2, ... loginN: message login1, login2, ... loginN: message
``` ```
Each of the specified clients than receives a `from login: message` message. Each of the specified clients then receives a `from login: message` message.
A possible session might look like this A possible session might look like this

View file

@ -8,6 +8,6 @@ fn main() -> Result<()> {
match (args.nth(1).as_ref().map(String::as_str), args.next()) { match (args.nth(1).as_ref().map(String::as_str), args.next()) {
(Some("client"), None) => client::main(), (Some("client"), None) => client::main(),
(Some("server"), None) => server::main(), (Some("server"), None) => server::main(),
_ => Err("Usage: a-chat [client|server]")?, _ => Err("Usage: a-chat [client|server]".into()),
} }
} }

View file

@ -45,7 +45,7 @@ async fn connection_loop(mut broker: Sender<Event>, stream: TcpStream) -> Result
let mut lines = reader.lines(); let mut lines = reader.lines();
let name = match lines.next().await { let name = match lines.next().await {
None => Err("peer disconnected immediately")?, None => return Err("peer disconnected immediately".into()),
Some(line) => line?, Some(line) => line?,
}; };
let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>(); let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::<Void>();

View file

@ -0,0 +1,44 @@
//! TCP echo server, accepting connections both on both ipv4 and ipv6 sockets.
//!
//! To send messages, do:
//!
//! ```sh
//! $ nc 127.0.0.1 8080
//! $ nc ::1 8080
//! ```
use async_std::io;
use async_std::net::{TcpListener, TcpStream};
use async_std::prelude::*;
use async_std::task;
async fn process(stream: TcpStream) -> io::Result<()> {
println!("Accepted from: {}", stream.peer_addr()?);
let (reader, writer) = &mut (&stream, &stream);
io::copy(reader, writer).await?;
Ok(())
}
fn main() -> io::Result<()> {
task::block_on(async {
let ipv4_listener = TcpListener::bind("127.0.0.1:8080").await?;
println!("Listening on {}", ipv4_listener.local_addr()?);
let ipv6_listener = TcpListener::bind("[::1]:8080").await?;
println!("Listening on {}", ipv6_listener.local_addr()?);
let ipv4_incoming = ipv4_listener.incoming();
let ipv6_incoming = ipv6_listener.incoming();
let mut incoming = ipv4_incoming.merge(ipv6_incoming);
while let Some(stream) = incoming.next().await {
let stream = stream?;
task::spawn(async {
process(stream).await.unwrap();
});
}
Ok(())
})
}

View file

@ -2,10 +2,10 @@ use std::collections::BinaryHeap;
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<T: Ord> Extend<T> for BinaryHeap<T> { impl<T: Ord> stream::Extend<T> for BinaryHeap<T> {
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>( fn extend<'a, S: IntoStream<Item = T> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -1,23 +1,21 @@
use std::collections::BinaryHeap; use std::collections::BinaryHeap;
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<T: Ord> FromStream<T> for BinaryHeap<T> { impl<T: Ord> FromStream<T> for BinaryHeap<T> {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = T>>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = BinaryHeap::new(); let mut out = BinaryHeap::new();
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -2,10 +2,10 @@ use std::collections::BTreeMap;
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<K: Ord, V> Extend<(K, V)> for BTreeMap<K, V> { impl<K: Ord, V> stream::Extend<(K, V)> for BTreeMap<K, V> {
fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>( fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -1,23 +1,21 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> { impl<K: Ord, V> FromStream<(K, V)> for BTreeMap<K, V> {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = (K, V)>>( fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = BTreeMap::new(); let mut out = BTreeMap::new();
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -2,10 +2,10 @@ use std::collections::BTreeSet;
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<T: Ord> Extend<T> for BTreeSet<T> { impl<T: Ord> stream::Extend<T> for BTreeSet<T> {
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>( fn extend<'a, S: IntoStream<Item = T> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -1,23 +1,21 @@
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<T: Ord> FromStream<T> for BTreeSet<T> { impl<T: Ord> FromStream<T> for BTreeSet<T> {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = T>>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = BTreeSet::new(); let mut out = BTreeSet::new();
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -3,14 +3,14 @@ use std::hash::{BuildHasher, Hash};
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<K, V, H> Extend<(K, V)> for HashMap<K, V, H> impl<K, V, H> stream::Extend<(K, V)> for HashMap<K, V, H>
where where
K: Eq + Hash, K: Eq + Hash,
H: BuildHasher + Default, H: BuildHasher + Default,
{ {
fn stream_extend<'a, S: IntoStream<Item = (K, V)> + 'a>( fn extend<'a, S: IntoStream<Item = (K, V)> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -2,7 +2,8 @@ use std::collections::HashMap;
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H> impl<K, V, H> FromStream<(K, V)> for HashMap<K, V, H>
where where
@ -10,19 +11,16 @@ where
H: BuildHasher + Default, H: BuildHasher + Default,
{ {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = (K, V)>>( fn from_stream<'a, S: IntoStream<Item = (K, V)> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = HashMap::with_hasher(Default::default()); let mut out = HashMap::with_hasher(Default::default());
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -3,14 +3,14 @@ use std::hash::{BuildHasher, Hash};
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<T, H> Extend<T> for HashSet<T, H> impl<T, H> stream::Extend<T> for HashSet<T, H>
where where
T: Eq + Hash, T: Eq + Hash,
H: BuildHasher + Default, H: BuildHasher + Default,
{ {
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>( fn extend<'a, S: IntoStream<Item = T> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -2,7 +2,8 @@ use std::collections::HashSet;
use std::hash::{BuildHasher, Hash}; use std::hash::{BuildHasher, Hash};
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<T, H> FromStream<T> for HashSet<T, H> impl<T, H> FromStream<T> for HashSet<T, H>
where where
@ -10,19 +11,16 @@ where
H: BuildHasher + Default, H: BuildHasher + Default,
{ {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = T>>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = HashSet::with_hasher(Default::default()); let mut out = HashSet::with_hasher(Default::default());
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -2,10 +2,10 @@ use std::collections::LinkedList;
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<T> Extend<T> for LinkedList<T> { impl<T> stream::Extend<T> for LinkedList<T> {
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>( fn extend<'a, S: IntoStream<Item = T> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -1,23 +1,21 @@
use std::collections::LinkedList; use std::collections::LinkedList;
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<T> FromStream<T> for LinkedList<T> { impl<T> FromStream<T> for LinkedList<T> {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = T>>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = LinkedList::new(); let mut out = LinkedList::new();
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -2,10 +2,10 @@ use std::collections::VecDeque;
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Extend, IntoStream}; use crate::stream::{self, IntoStream};
impl<T> Extend<T> for VecDeque<T> { impl<T> stream::Extend<T> for VecDeque<T> {
fn stream_extend<'a, S: IntoStream<Item = T> + 'a>( fn extend<'a, S: IntoStream<Item = T> + 'a>(
&'a mut self, &'a mut self,
stream: S, stream: S,
) -> Pin<Box<dyn Future<Output = ()> + 'a>> { ) -> Pin<Box<dyn Future<Output = ()> + 'a>> {

View file

@ -1,23 +1,21 @@
use std::collections::VecDeque; use std::collections::VecDeque;
use std::pin::Pin; use std::pin::Pin;
use crate::stream::{Extend, FromStream, IntoStream}; use crate::prelude::*;
use crate::stream::{self, FromStream, IntoStream};
impl<T> FromStream<T> for VecDeque<T> { impl<T> FromStream<T> for VecDeque<T> {
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = T>>( fn from_stream<'a, S: IntoStream<Item = T> + 'a>(
stream: S, stream: S,
) -> Pin<Box<dyn core::future::Future<Output = Self> + 'a>> ) -> Pin<Box<dyn Future<Output = Self> + 'a>> {
where
<S as IntoStream>::IntoStream: 'a,
{
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
pin_utils::pin_mut!(stream); pin_utils::pin_mut!(stream);
let mut out = VecDeque::new(); let mut out = VecDeque::new();
out.stream_extend(stream).await; stream::extend(&mut out, stream).await;
out out
}) })
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::task::blocking; use crate::task::spawn_blocking;
/// Returns the canonical form of a path. /// Returns the canonical form of a path.
/// ///
@ -32,5 +32,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { pub async fn canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::canonicalize(&path).map(Into::into)).await spawn_blocking(move || std::fs::canonicalize(&path).map(Into::into)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Copies the contents and permissions of a file to a new location. /// Copies the contents and permissions of a file to a new location.
/// ///
@ -41,5 +41,5 @@ use crate::task::blocking;
pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> { pub async fn copy<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<u64> {
let from = from.as_ref().to_owned(); let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned(); let to = to.as_ref().to_owned();
blocking::spawn(move || std::fs::copy(&from, &to)).await spawn_blocking(move || std::fs::copy(&from, &to)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Creates a new directory. /// Creates a new directory.
/// ///
@ -34,5 +34,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn create_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::create_dir(path)).await spawn_blocking(move || std::fs::create_dir(path)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Creates a new directory and all of its parents if they are missing. /// Creates a new directory and all of its parents if they are missing.
/// ///
@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn create_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::create_dir_all(path)).await spawn_blocking(move || std::fs::create_dir_all(path)).await
} }

View file

@ -2,7 +2,7 @@ use std::future::Future;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// A builder for creating directories with configurable options. /// A builder for creating directories with configurable options.
/// ///
@ -107,7 +107,7 @@ impl DirBuilder {
} }
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
async move { blocking::spawn(move || builder.create(path)).await } async move { spawn_blocking(move || builder.create(path)).await }
} }
} }

View file

@ -5,7 +5,7 @@ use std::sync::Arc;
use crate::fs::{FileType, Metadata}; use crate::fs::{FileType, Metadata};
use crate::io; use crate::io;
use crate::path::PathBuf; use crate::path::PathBuf;
use crate::task::blocking; use crate::task::spawn_blocking;
/// An entry in a directory. /// An entry in a directory.
/// ///
@ -87,7 +87,7 @@ impl DirEntry {
/// ``` /// ```
pub async fn metadata(&self) -> io::Result<Metadata> { pub async fn metadata(&self) -> io::Result<Metadata> {
let inner = self.0.clone(); let inner = self.0.clone();
blocking::spawn(move || inner.metadata()).await spawn_blocking(move || inner.metadata()).await
} }
/// Reads the file type for this entry. /// Reads the file type for this entry.
@ -125,7 +125,7 @@ impl DirEntry {
/// ``` /// ```
pub async fn file_type(&self) -> io::Result<FileType> { pub async fn file_type(&self) -> io::Result<FileType> {
let inner = self.0.clone(); let inner = self.0.clone();
blocking::spawn(move || inner.file_type()).await spawn_blocking(move || inner.file_type()).await
} }
/// Returns the bare name of this entry without the leading path. /// Returns the bare name of this entry without the leading path.

View file

@ -12,7 +12,7 @@ use crate::future;
use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::io::{self, Read, Seek, SeekFrom, Write};
use crate::path::Path; use crate::path::Path;
use crate::prelude::*; use crate::prelude::*;
use crate::task::{self, blocking, Context, Poll, Waker}; use crate::task::{self, spawn_blocking, Context, Poll, Waker};
/// An open file on the filesystem. /// An open file on the filesystem.
/// ///
@ -66,6 +66,23 @@ pub struct File {
} }
impl File { impl File {
/// Creates an async file handle.
pub(crate) fn new(file: std::fs::File, is_flushed: bool) -> File {
let file = Arc::new(file);
File {
file: file.clone(),
lock: Lock::new(State {
file,
mode: Mode::Idle,
cache: Vec::new(),
is_flushed,
last_read_err: None,
last_write_err: None,
}),
}
}
/// Opens a file in read-only mode. /// Opens a file in read-only mode.
/// ///
/// See the [`OpenOptions::open`] function for more options. /// See the [`OpenOptions::open`] function for more options.
@ -95,8 +112,8 @@ impl File {
/// ``` /// ```
pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let file = blocking::spawn(move || std::fs::File::open(&path)).await?; let file = spawn_blocking(move || std::fs::File::open(&path)).await?;
Ok(file.into()) Ok(File::new(file, true))
} }
/// Opens a file in write-only mode. /// Opens a file in write-only mode.
@ -130,8 +147,8 @@ impl File {
/// ``` /// ```
pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let file = blocking::spawn(move || std::fs::File::create(&path)).await?; let file = spawn_blocking(move || std::fs::File::create(&path)).await?;
Ok(file.into()) Ok(File::new(file, true))
} }
/// Synchronizes OS-internal buffered contents and metadata to disk. /// Synchronizes OS-internal buffered contents and metadata to disk.
@ -163,7 +180,7 @@ impl File {
}) })
.await?; .await?;
blocking::spawn(move || state.file.sync_all()).await spawn_blocking(move || state.file.sync_all()).await
} }
/// Synchronizes OS-internal buffered contents to disk. /// Synchronizes OS-internal buffered contents to disk.
@ -199,7 +216,7 @@ impl File {
}) })
.await?; .await?;
blocking::spawn(move || state.file.sync_data()).await spawn_blocking(move || state.file.sync_data()).await
} }
/// Truncates or extends the file. /// Truncates or extends the file.
@ -232,7 +249,7 @@ impl File {
}) })
.await?; .await?;
blocking::spawn(move || state.file.set_len(size)).await spawn_blocking(move || state.file.set_len(size)).await
} }
/// Reads the file's metadata. /// Reads the file's metadata.
@ -251,7 +268,7 @@ impl File {
/// ``` /// ```
pub async fn metadata(&self) -> io::Result<Metadata> { pub async fn metadata(&self) -> io::Result<Metadata> {
let file = self.file.clone(); let file = self.file.clone();
blocking::spawn(move || file.metadata()).await spawn_blocking(move || file.metadata()).await
} }
/// Changes the permissions on the file. /// Changes the permissions on the file.
@ -280,7 +297,7 @@ impl File {
/// ``` /// ```
pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> {
let file = self.file.clone(); let file = self.file.clone();
blocking::spawn(move || file.set_permissions(perm)).await spawn_blocking(move || file.set_permissions(perm)).await
} }
} }
@ -383,19 +400,7 @@ impl Seek for &File {
impl From<std::fs::File> for File { impl From<std::fs::File> for File {
fn from(file: std::fs::File) -> File { fn from(file: std::fs::File) -> File {
let file = Arc::new(file); File::new(file, false)
File {
file: file.clone(),
lock: Lock::new(State {
file,
mode: Mode::Idle,
cache: Vec::new(),
is_flushed: false,
last_read_err: None,
last_write_err: None,
}),
}
} }
} }
@ -687,7 +692,7 @@ impl LockGuard<State> {
self.register(cx); self.register(cx);
// Start a read operation asynchronously. // Start a read operation asynchronously.
blocking::spawn(move || { spawn_blocking(move || {
// Read some data from the file into the cache. // Read some data from the file into the cache.
let res = { let res = {
let State { file, cache, .. } = &mut *self; let State { file, cache, .. } = &mut *self;
@ -796,7 +801,7 @@ impl LockGuard<State> {
self.register(cx); self.register(cx);
// Start a write operation asynchronously. // Start a write operation asynchronously.
blocking::spawn(move || { spawn_blocking(move || {
match (&*self.file).write_all(&self.cache) { match (&*self.file).write_all(&self.cache) {
Ok(_) => { Ok(_) => {
// Switch to idle mode. // Switch to idle mode.
@ -829,7 +834,7 @@ impl LockGuard<State> {
self.register(cx); self.register(cx);
// Start a flush operation asynchronously. // Start a flush operation asynchronously.
blocking::spawn(move || { spawn_blocking(move || {
match (&*self.file).flush() { match (&*self.file).flush() {
Ok(()) => { Ok(()) => {
// Mark the file as flushed. // Mark the file as flushed.

View file

@ -40,7 +40,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_dir(&self) -> bool { pub fn is_dir(&self) -> bool {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns `true` if this file type represents a regular file. /// Returns `true` if this file type represents a regular file.
@ -60,7 +60,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_file(&self) -> bool { pub fn is_file(&self) -> bool {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns `true` if this file type represents a symbolic link. /// Returns `true` if this file type represents a symbolic link.
@ -78,7 +78,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_symlink(&self) -> bool { pub fn is_symlink(&self) -> bool {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Creates a hard link on the filesystem. /// Creates a hard link on the filesystem.
/// ///
@ -32,5 +32,5 @@ use crate::task::blocking;
pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { pub async fn hard_link<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
let from = from.as_ref().to_owned(); let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned(); let to = to.as_ref().to_owned();
blocking::spawn(move || std::fs::hard_link(&from, &to)).await spawn_blocking(move || std::fs::hard_link(&from, &to)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Reads metadata for a path. /// Reads metadata for a path.
/// ///
@ -34,7 +34,7 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { pub async fn metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::metadata(path)).await spawn_blocking(move || std::fs::metadata(path)).await
} }
cfg_not_docs! { cfg_not_docs! {
@ -78,7 +78,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn file_type(&self) -> FileType { pub fn file_type(&self) -> FileType {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns `true` if this metadata is for a regular directory. /// Returns `true` if this metadata is for a regular directory.
@ -98,7 +98,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_dir(&self) -> bool { pub fn is_dir(&self) -> bool {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns `true` if this metadata is for a regular file. /// Returns `true` if this metadata is for a regular file.
@ -118,7 +118,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn is_file(&self) -> bool { pub fn is_file(&self) -> bool {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns the file size in bytes. /// Returns the file size in bytes.
@ -136,7 +136,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn len(&self) -> u64 { pub fn len(&self) -> u64 {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns the permissions from this metadata. /// Returns the permissions from this metadata.
@ -154,7 +154,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn permissions(&self) -> Permissions { pub fn permissions(&self) -> Permissions {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns the last modification time. /// Returns the last modification time.
@ -177,7 +177,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn modified(&self) -> io::Result<SystemTime> { pub fn modified(&self) -> io::Result<SystemTime> {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns the last access time. /// Returns the last access time.
@ -200,7 +200,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn accessed(&self) -> io::Result<SystemTime> { pub fn accessed(&self) -> io::Result<SystemTime> {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Returns the creation time. /// Returns the creation time.
@ -223,7 +223,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn created(&self) -> io::Result<SystemTime> { pub fn created(&self) -> io::Result<SystemTime> {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
} }
} }

View file

@ -3,7 +3,7 @@ use std::future::Future;
use crate::fs::File; use crate::fs::File;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// A builder for opening files with configurable options. /// A builder for opening files with configurable options.
/// ///
@ -284,7 +284,10 @@ impl OpenOptions {
pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> { pub fn open<P: AsRef<Path>>(&self, path: P) -> impl Future<Output = io::Result<File>> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let options = self.0.clone(); let options = self.0.clone();
async move { blocking::spawn(move || options.open(path).map(|f| f.into())).await } async move {
let file = spawn_blocking(move || options.open(path)).await?;
Ok(File::new(file, true))
}
} }
} }

View file

@ -29,7 +29,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn readonly(&self) -> bool { pub fn readonly(&self) -> bool {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
/// Configures the read-only flag. /// Configures the read-only flag.
@ -50,7 +50,7 @@ cfg_docs! {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn set_readonly(&mut self, readonly: bool) { pub fn set_readonly(&mut self, readonly: bool) {
unimplemented!() unreachable!("this impl only appears in the rendered docs")
} }
} }
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Reads the entire contents of a file as raw bytes. /// Reads the entire contents of a file as raw bytes.
/// ///
@ -36,5 +36,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> { pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::read(path)).await spawn_blocking(move || std::fs::read(path)).await
} }

View file

@ -1,11 +1,11 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::fs::DirEntry; use crate::fs::DirEntry;
use crate::future::Future;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
/// Returns a stream of entries in a directory. /// Returns a stream of entries in a directory.
/// ///
@ -45,7 +45,7 @@ use crate::task::{blocking, Context, JoinHandle, Poll};
/// ``` /// ```
pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { pub async fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::read_dir(path)) spawn_blocking(move || std::fs::read_dir(path))
.await .await
.map(ReadDir::new) .map(ReadDir::new)
} }
@ -91,7 +91,7 @@ impl Stream for ReadDir {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
self.0 = State::Busy(blocking::spawn(move || { self.0 = State::Busy(spawn_blocking(move || {
let next = inner.next(); let next = inner.next();
(inner, next) (inner, next)
})); }));

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::{Path, PathBuf}; use crate::path::{Path, PathBuf};
use crate::task::blocking; use crate::task::spawn_blocking;
/// Reads a symbolic link and returns the path it points to. /// Reads a symbolic link and returns the path it points to.
/// ///
@ -28,5 +28,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { pub async fn read_link<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::read_link(path).map(Into::into)).await spawn_blocking(move || std::fs::read_link(path).map(Into::into)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Reads the entire contents of a file as a string. /// Reads the entire contents of a file as a string.
/// ///
@ -37,5 +37,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> { pub async fn read_to_string<P: AsRef<Path>>(path: P) -> io::Result<String> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::read_to_string(path)).await spawn_blocking(move || std::fs::read_to_string(path)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Removes an empty directory. /// Removes an empty directory.
/// ///
@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn remove_dir<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::remove_dir(path)).await spawn_blocking(move || std::fs::remove_dir(path)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Removes a directory and all of its contents. /// Removes a directory and all of its contents.
/// ///
@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn remove_dir_all<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::remove_dir_all(path)).await spawn_blocking(move || std::fs::remove_dir_all(path)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Removes a file. /// Removes a file.
/// ///
@ -29,5 +29,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> { pub async fn remove_file<P: AsRef<Path>>(path: P) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::remove_file(path)).await spawn_blocking(move || std::fs::remove_file(path)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Renames a file or directory to a new location. /// Renames a file or directory to a new location.
/// ///
@ -34,5 +34,5 @@ use crate::task::blocking;
pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> { pub async fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> io::Result<()> {
let from = from.as_ref().to_owned(); let from = from.as_ref().to_owned();
let to = to.as_ref().to_owned(); let to = to.as_ref().to_owned();
blocking::spawn(move || std::fs::rename(&from, &to)).await spawn_blocking(move || std::fs::rename(&from, &to)).await
} }

View file

@ -1,7 +1,7 @@
use crate::fs::Permissions; use crate::fs::Permissions;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Changes the permissions of a file or directory. /// Changes the permissions of a file or directory.
/// ///
@ -32,5 +32,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> { pub async fn set_permissions<P: AsRef<Path>>(path: P, perm: Permissions) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::set_permissions(path, perm)).await spawn_blocking(move || std::fs::set_permissions(path, perm)).await
} }

View file

@ -1,7 +1,7 @@
use crate::fs::Metadata; use crate::fs::Metadata;
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Reads metadata for a path without following symbolic links. /// Reads metadata for a path without following symbolic links.
/// ///
@ -34,5 +34,5 @@ use crate::task::blocking;
/// ``` /// ```
pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> { pub async fn symlink_metadata<P: AsRef<Path>>(path: P) -> io::Result<Metadata> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
blocking::spawn(move || std::fs::symlink_metadata(path)).await spawn_blocking(move || std::fs::symlink_metadata(path)).await
} }

View file

@ -1,6 +1,6 @@
use crate::io; use crate::io;
use crate::path::Path; use crate::path::Path;
use crate::task::blocking; use crate::task::spawn_blocking;
/// Writes a slice of bytes as the new contents of a file. /// Writes a slice of bytes as the new contents of a file.
/// ///
@ -33,5 +33,5 @@ use crate::task::blocking;
pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { pub async fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let contents = contents.as_ref().to_owned(); let contents = contents.as_ref().to_owned();
blocking::spawn(move || std::fs::write(path, contents)).await spawn_blocking(move || std::fs::write(path, contents)).await
} }

View file

@ -1,139 +0,0 @@
extension_trait! {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
#[doc = r#"
A future represents an asynchronous computation.
A future is a value that may not have finished computing yet. This kind of
"asynchronous value" makes it possible for a thread to continue doing useful
work while it waits for the value to become available.
# The `poll` method
The core method of future, `poll`, *attempts* to resolve the future into a
final value. This method does not block if the value is not ready. Instead,
the current task is scheduled to be woken up when it's possible to make
further progress by `poll`ing again. The `context` passed to the `poll`
method can provide a [`Waker`], which is a handle for waking up the current
task.
When using a future, you generally won't call `poll` directly, but instead
`.await` the value.
[`Waker`]: ../task/struct.Waker.html
"#]
pub trait Future {
#[doc = r#"
The type of value produced on completion.
"#]
type Output;
#[doc = r#"
Attempt to resolve the future to a final value, registering
the current task for wakeup if the value is not yet available.
# Return value
This function returns:
- [`Poll::Pending`] if the future is not ready yet
- [`Poll::Ready(val)`] with the result `val` of this future if it
finished successfully.
Once a future has finished, clients should not `poll` it again.
When a future is not ready yet, `poll` returns `Poll::Pending` and
stores a clone of the [`Waker`] copied from the current [`Context`].
This [`Waker`] is then woken once the future can make progress.
For example, a future waiting for a socket to become
readable would call `.clone()` on the [`Waker`] and store it.
When a signal arrives elsewhere indicating that the socket is readable,
[`Waker::wake`] is called and the socket future's task is awoken.
Once a task has been woken up, it should attempt to `poll` the future
again, which may or may not produce a final value.
Note that on multiple calls to `poll`, only the [`Waker`] from the
[`Context`] passed to the most recent call should be scheduled to
receive a wakeup.
# Runtime characteristics
Futures alone are *inert*; they must be *actively* `poll`ed to make
progress, meaning that each time the current task is woken up, it should
actively re-`poll` pending futures that it still has an interest in.
The `poll` function is not called repeatedly in a tight loop -- instead,
it should only be called when the future indicates that it is ready to
make progress (by calling `wake()`). If you're familiar with the
`poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
typically do *not* suffer the same problems of "all wakeups must poll
all events"; they are more like `epoll(4)`.
An implementation of `poll` should strive to return quickly, and should
not block. Returning quickly prevents unnecessarily clogging up
threads or event loops. If it is known ahead of time that a call to
`poll` may end up taking awhile, the work should be offloaded to a
thread pool (or something similar) to ensure that `poll` can return
quickly.
# Panics
Once a future has completed (returned `Ready` from `poll`), calling its
`poll` method again may panic, block forever, or cause other kinds of
problems; the `Future` trait places no requirements on the effects of
such a call. However, as the `poll` method is not marked `unsafe`,
Rust's usual rules apply: calls must never cause undefined behavior
(memory corruption, incorrect use of `unsafe` functions, or the like),
regardless of the future's state.
[`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
[`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
[`Context`]: ../task/struct.Context.html
[`Waker`]: ../task/struct.Waker.html
[`Waker::wake`]: ../task/struct.Waker.html#method.wake
"#]
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
}
pub trait FutureExt: std::future::Future {
}
impl<F: Future + Unpin + ?Sized> Future for Box<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<F: Future + Unpin + ?Sized> Future for &mut F {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<P> Future for Pin<P>
where
P: DerefMut + Unpin,
<P as Deref>::Target: Future,
{
type Output = <<P as Deref>::Target as Future>::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<F: Future> Future for std::panic::AssertUnwindSafe<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
}

View file

@ -0,0 +1,43 @@
use std::future::Future;
use std::pin::Pin;
use std::time::Duration;
use futures_timer::Delay;
use pin_project_lite::pin_project;
use crate::task::{Context, Poll};
pin_project! {
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct DelayFuture<F> {
#[pin]
future: F,
#[pin]
delay: Delay,
}
}
impl<F> DelayFuture<F> {
pub fn new(future: F, dur: Duration) -> DelayFuture<F> {
let delay = Delay::new(dur);
DelayFuture { future, delay }
}
}
impl<F: Future> Future for DelayFuture<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
match this.delay.poll(cx) {
Poll::Pending => Poll::Pending,
Poll::Ready(_) => match this.future.poll(cx) {
Poll::Ready(v) => Poll::Ready(v),
Poll::Pending => Poll::Pending,
},
}
}
}

View file

@ -0,0 +1,52 @@
use std::future::Future;
use std::pin::Pin;
use crate::future::IntoFuture;
use crate::task::{ready, Context, Poll};
#[doc(hidden)]
#[allow(missing_debug_implementations)]
pub struct FlattenFuture<Fut1, Fut2> {
state: State<Fut1, Fut2>,
}
#[derive(Debug)]
enum State<Fut1, Fut2> {
First(Fut1),
Second(Fut2),
Empty,
}
impl<Fut1, Fut2> FlattenFuture<Fut1, Fut2> {
pub(crate) fn new(future: Fut1) -> FlattenFuture<Fut1, Fut2> {
FlattenFuture {
state: State::First(future),
}
}
}
impl<Fut1> Future for FlattenFuture<Fut1, <Fut1::Output as IntoFuture>::Future>
where
Fut1: Future,
Fut1::Output: IntoFuture,
{
type Output = <Fut1::Output as IntoFuture>::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let Self { state } = unsafe { self.get_unchecked_mut() };
loop {
match state {
State::First(fut1) => {
let fut2 = ready!(unsafe { Pin::new_unchecked(fut1) }.poll(cx)).into_future();
*state = State::Second(fut2);
}
State::Second(fut2) => {
let v = ready!(unsafe { Pin::new_unchecked(fut2) }.poll(cx));
*state = State::Empty;
return Poll::Ready(v);
}
State::Empty => panic!("polled a completed future"),
}
}
}
}

62
src/future/future/join.rs Normal file
View file

@ -0,0 +1,62 @@
use std::pin::Pin;
use async_macros::MaybeDone;
use pin_project_lite::pin_project;
use crate::task::{Context, Poll};
use std::future::Future;
pin_project! {
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
pub struct Join<L, R>
where
L: Future,
R: Future<Output = L::Output>
{
#[pin] left: MaybeDone<L>,
#[pin] right: MaybeDone<R>,
}
}
impl<L, R> Join<L, R>
where
L: Future,
R: Future<Output = L::Output>,
{
pub(crate) fn new(left: L, right: R) -> Self {
Self {
left: MaybeDone::new(left),
right: MaybeDone::new(right),
}
}
}
impl<L, R> Future for Join<L, R>
where
L: Future,
R: Future<Output = L::Output>,
{
type Output = (L::Output, R::Output);
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let mut left = this.left;
let mut right = this.right;
if Future::poll(Pin::new(&mut left), cx).is_ready() {
if right.as_ref().output().is_some() {
return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
}
}
if Future::poll(Pin::new(&mut right), cx).is_ready() {
if left.as_ref().output().is_some() {
return Poll::Ready((left.take().unwrap(), right.take().unwrap()));
}
}
Poll::Pending
}
}

395
src/future/future/mod.rs Normal file
View file

@ -0,0 +1,395 @@
cfg_unstable! {
mod delay;
mod flatten;
mod race;
mod try_race;
mod join;
mod try_join;
use std::time::Duration;
use delay::DelayFuture;
use flatten::FlattenFuture;
use crate::future::IntoFuture;
use race::Race;
use try_race::TryRace;
use join::Join;
use try_join::TryJoin;
}
extension_trait! {
use std::pin::Pin;
use std::ops::{Deref, DerefMut};
use crate::task::{Context, Poll};
#[doc = r#"
A future represents an asynchronous computation.
A future is a value that may not have finished computing yet. This kind of
"asynchronous value" makes it possible for a thread to continue doing useful
work while it waits for the value to become available.
The [provided methods] do not really exist in the trait itself, but they become
available when [`FutureExt`] from the [prelude] is imported:
```
# #[allow(unused_imports)]
use async_std::prelude::*;
```
# The `poll` method
The core method of future, `poll`, *attempts* to resolve the future into a
final value. This method does not block if the value is not ready. Instead,
the current task is scheduled to be woken up when it's possible to make
further progress by `poll`ing again. The `context` passed to the `poll`
method can provide a [`Waker`], which is a handle for waking up the current
task.
When using a future, you generally won't call `poll` directly, but instead
`.await` the value.
[`Waker`]: ../task/struct.Waker.html
[provided methods]: #provided-methods
[`FutureExt`]: ../prelude/trait.FutureExt.html
[prelude]: ../prelude/index.html
"#]
pub trait Future {
#[doc = r#"
The type of value produced on completion.
"#]
type Output;
#[doc = r#"
Attempt to resolve the future to a final value, registering
the current task for wakeup if the value is not yet available.
# Return value
This function returns:
- [`Poll::Pending`] if the future is not ready yet
- [`Poll::Ready(val)`] with the result `val` of this future if it
finished successfully.
Once a future has finished, clients should not `poll` it again.
When a future is not ready yet, `poll` returns `Poll::Pending` and
stores a clone of the [`Waker`] copied from the current [`Context`].
This [`Waker`] is then woken once the future can make progress.
For example, a future waiting for a socket to become
readable would call `.clone()` on the [`Waker`] and store it.
When a signal arrives elsewhere indicating that the socket is readable,
[`Waker::wake`] is called and the socket future's task is awoken.
Once a task has been woken up, it should attempt to `poll` the future
again, which may or may not produce a final value.
Note that on multiple calls to `poll`, only the [`Waker`] from the
[`Context`] passed to the most recent call should be scheduled to
receive a wakeup.
# Runtime characteristics
Futures alone are *inert*; they must be *actively* `poll`ed to make
progress, meaning that each time the current task is woken up, it should
actively re-`poll` pending futures that it still has an interest in.
The `poll` function is not called repeatedly in a tight loop -- instead,
it should only be called when the future indicates that it is ready to
make progress (by calling `wake()`). If you're familiar with the
`poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures
typically do *not* suffer the same problems of "all wakeups must poll
all events"; they are more like `epoll(4)`.
An implementation of `poll` should strive to return quickly, and should
not block. Returning quickly prevents unnecessarily clogging up
threads or event loops. If it is known ahead of time that a call to
`poll` may end up taking awhile, the work should be offloaded to a
thread pool (or something similar) to ensure that `poll` can return
quickly.
# Panics
Once a future has completed (returned `Ready` from `poll`), calling its
`poll` method again may panic, block forever, or cause other kinds of
problems; the `Future` trait places no requirements on the effects of
such a call. However, as the `poll` method is not marked `unsafe`,
Rust's usual rules apply: calls must never cause undefined behavior
(memory corruption, incorrect use of `unsafe` functions, or the like),
regardless of the future's state.
[`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending
[`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready
[`Context`]: ../task/struct.Context.html
[`Waker`]: ../task/struct.Waker.html
[`Waker::wake`]: ../task/struct.Waker.html#method.wake
"#]
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output>;
}
#[doc = r#"
Extension methods for [`Future`].
[`Future`]: ../future/trait.Future.html
"#]
pub trait FutureExt: std::future::Future {
/// Returns a Future that delays execution for a specified time.
///
/// # Examples
///
/// ```
/// # async_std::task::block_on(async {
/// use async_std::prelude::*;
/// use async_std::future;
/// use std::time::Duration;
///
/// let a = future::ready(1).delay(Duration::from_millis(2000));
/// dbg!(a.await);
/// # })
/// ```
#[cfg(all(feature = "default", feature = "unstable"))]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn delay(self, dur: Duration) -> impl Future<Output = Self::Output> [DelayFuture<Self>]
where
Self: Sized,
{
DelayFuture::new(self, dur)
}
/// Flatten out the execution of this future when the result itself
/// can be converted into another future.
///
/// # Examples
///
/// ```
/// # async_std::task::block_on(async {
/// use async_std::prelude::*;
///
/// let nested_future = async { async { 1 } };
/// let future = nested_future.flatten();
/// assert_eq!(future.await, 1);
/// # })
/// ```
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn flatten(
self,
) -> impl Future<Output = <Self::Output as IntoFuture>::Output>
[FlattenFuture<Self, <Self::Output as IntoFuture>::Future>]
where
Self: Sized,
<Self as Future>::Output: IntoFuture,
{
FlattenFuture::new(self)
}
#[doc = r#"
Waits for one of two similarly-typed futures to complete.
Awaits multiple futures simultaneously, returning the output of the
first future that completes.
This function will return a new future which awaits for either one of both
futures to complete. If multiple futures are completed at the same time,
resolution will occur in the order that they have been passed.
Note that this function consumes all futures passed, and once a future is
completed, all other futures are dropped.
# Examples
```
# async_std::task::block_on(async {
use async_std::prelude::*;
use async_std::future;
let a = future::pending();
let b = future::ready(1u8);
let c = future::ready(2u8);
let f = a.race(b).race(c);
assert_eq!(f.await, 1u8);
# });
```
"#]
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn race<F>(
self,
other: F,
) -> impl Future<Output = <Self as std::future::Future>::Output> [Race<Self, F>]
where
Self: std::future::Future + Sized,
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
{
Race::new(self, other)
}
#[doc = r#"
Waits for one of two similarly-typed fallible futures to complete.
Awaits multiple futures simultaneously, returning all results once complete.
`try_race` is similar to [`race`], but keeps going if a future
resolved to an error until all futures have been resolved. In which case
an error is returned.
The ordering of which value is yielded when two futures resolve
simultaneously is intentionally left unspecified.
[`race`]: #method.race
# Examples
```
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
#
use async_std::prelude::*;
use async_std::future;
use std::io::{Error, ErrorKind};
let a = future::pending::<Result<_, Error>>();
let b = future::ready(Err(Error::from(ErrorKind::Other)));
let c = future::ready(Ok(1u8));
let f = a.try_race(b).try_race(c);
assert_eq!(f.await?, 1u8);
#
# Ok(()) }) }
```
"#]
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn try_race<F, T, E>(
self,
other: F
) -> impl Future<Output = <Self as std::future::Future>::Output> [TryRace<Self, F>]
where
Self: std::future::Future<Output = Result<T, E>> + Sized,
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
{
TryRace::new(self, other)
}
#[doc = r#"
Waits for two similarly-typed futures to complete.
Awaits multiple futures simultaneously, returning the output of the
futures once both complete.
This function returns a new future which polls both futures
concurrently.
# Examples
```
# async_std::task::block_on(async {
use async_std::prelude::*;
use async_std::future;
let a = future::ready(1u8);
let b = future::ready(2u8);
let f = a.join(b);
assert_eq!(f.await, (1u8, 2u8));
# });
```
"#]
#[cfg(any(feature = "unstable", feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn join<F>(
self,
other: F
) -> impl Future<Output = (<Self as std::future::Future>::Output, <F as std::future::Future>::Output)> [Join<Self, F>]
where
Self: std::future::Future + Sized,
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
{
Join::new(self, other)
}
#[doc = r#"
Waits for two similarly-typed fallible futures to complete.
Awaits multiple futures simultaneously, returning all results once
complete.
`try_join` is similar to [`join`], but returns an error immediately
if a future resolves to an error.
[`join`]: #method.join
# Examples
```
# fn main() -> std::io::Result<()> { async_std::task::block_on(async {
#
use async_std::prelude::*;
use async_std::future;
let a = future::ready(Err("Error"));
let b = future::ready(Ok(1u8));
let f = a.try_join(b);
assert_eq!(f.await, Err("Error"));
let a = future::ready(Ok::<u8, String>(1u8));
let b = future::ready(Ok::<u8, String>(2u8));
let f = a.try_join(b);
assert_eq!(f.await, Ok((1u8, 2u8)));
#
# Ok(()) }) }
```
"#]
#[cfg(any(feature = "unstable", feature = "docs"))]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
fn try_join<F, T, E>(
self,
other: F
) -> impl Future<Output = Result<(T, T), E>> [TryJoin<Self, F>]
where
Self: std::future::Future<Output = Result<T, E>> + Sized,
F: std::future::Future<Output = <Self as std::future::Future>::Output>,
{
TryJoin::new(self, other)
}
}
impl<F: Future + Unpin + ?Sized> Future for Box<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<F: Future + Unpin + ?Sized> Future for &mut F {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<P> Future for Pin<P>
where
P: DerefMut + Unpin,
<P as Deref>::Target: Future,
{
type Output = <<P as Deref>::Target as Future>::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
impl<F: Future> Future for std::panic::AssertUnwindSafe<F> {
type Output = F::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
unreachable!("this impl only appears in the rendered docs")
}
}
}

57
src/future/future/race.rs Normal file
View file

@ -0,0 +1,57 @@
use std::future::Future;
use std::pin::Pin;
use async_macros::MaybeDone;
use pin_project_lite::pin_project;
use crate::task::{Context, Poll};
pin_project! {
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
pub struct Race<L, R>
where
L: Future,
R: Future<Output = L::Output>
{
#[pin] left: MaybeDone<L>,
#[pin] right: MaybeDone<R>,
}
}
impl<L, R> Race<L, R>
where
L: Future,
R: Future<Output = L::Output>,
{
pub(crate) fn new(left: L, right: R) -> Self {
Self {
left: MaybeDone::new(left),
right: MaybeDone::new(right),
}
}
}
impl<L, R> Future for Race<L, R>
where
L: Future,
R: Future<Output = L::Output>,
{
type Output = L::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let mut left = this.left;
if Future::poll(Pin::new(&mut left), cx).is_ready() {
return Poll::Ready(left.take().unwrap());
}
let mut right = this.right;
if Future::poll(Pin::new(&mut right), cx).is_ready() {
return Poll::Ready(right.take().unwrap());
}
Poll::Pending
}
}

View file

@ -0,0 +1,72 @@
use std::pin::Pin;
use async_macros::MaybeDone;
use pin_project_lite::pin_project;
use crate::task::{Context, Poll};
use std::future::Future;
pin_project! {
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
pub struct TryJoin<L, R>
where
L: Future,
R: Future<Output = L::Output>
{
#[pin] left: MaybeDone<L>,
#[pin] right: MaybeDone<R>,
}
}
impl<L, R> TryJoin<L, R>
where
L: Future,
R: Future<Output = L::Output>,
{
pub(crate) fn new(left: L, right: R) -> Self {
Self {
left: MaybeDone::new(left),
right: MaybeDone::new(right),
}
}
}
impl<L, R, T, E> Future for TryJoin<L, R>
where
L: Future<Output = Result<T, E>>,
R: Future<Output = L::Output>,
{
type Output = Result<(T, T), E>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let mut left = this.left;
let mut right = this.right;
if Future::poll(Pin::new(&mut left), cx).is_ready() {
if left.as_ref().output().unwrap().is_err() {
return Poll::Ready(Err(left.take().unwrap().err().unwrap()));
} else if right.as_ref().output().is_some() {
return Poll::Ready(Ok((
left.take().unwrap().ok().unwrap(),
right.take().unwrap().ok().unwrap(),
)));
}
}
if Future::poll(Pin::new(&mut right), cx).is_ready() {
if right.as_ref().output().unwrap().is_err() {
return Poll::Ready(Err(right.take().unwrap().err().unwrap()));
} else if left.as_ref().output().is_some() {
return Poll::Ready(Ok((
left.take().unwrap().ok().unwrap(),
right.take().unwrap().ok().unwrap(),
)));
}
}
Poll::Pending
}
}

View file

@ -0,0 +1,66 @@
use std::pin::Pin;
use async_macros::MaybeDone;
use pin_project_lite::pin_project;
use crate::task::{Context, Poll};
use std::future::Future;
pin_project! {
#[allow(missing_docs)]
#[allow(missing_debug_implementations)]
pub struct TryRace<L, R>
where
L: Future,
R: Future<Output = L::Output>
{
#[pin] left: MaybeDone<L>,
#[pin] right: MaybeDone<R>,
}
}
impl<L, R> TryRace<L, R>
where
L: Future,
R: Future<Output = L::Output>,
{
pub(crate) fn new(left: L, right: R) -> Self {
Self {
left: MaybeDone::new(left),
right: MaybeDone::new(right),
}
}
}
impl<L, R, T, E> Future for TryRace<L, R>
where
L: Future<Output = Result<T, E>>,
R: Future<Output = L::Output>,
{
type Output = L::Output;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.project();
let mut left_errored = false;
// Check if the left future is ready & successful. Continue if not.
let mut left = this.left;
if Future::poll(Pin::new(&mut left), cx).is_ready() {
if left.as_ref().output().unwrap().is_ok() {
return Poll::Ready(left.take().unwrap());
} else {
left_errored = true;
}
}
// Check if the right future is ready & successful. Return err if left
// future also resolved to err. Continue if not.
let mut right = this.right;
let is_ready = Future::poll(Pin::new(&mut right), cx).is_ready();
if is_ready && (right.as_ref().output().unwrap().is_ok() || left_errored) {
return Poll::Ready(right.take().unwrap());
}
Poll::Pending
}
}

View file

@ -1,4 +1,4 @@
use crate::future::Future; use std::future::Future;
/// Convert a type into a `Future`. /// Convert a type into a `Future`.
/// ///
@ -45,7 +45,6 @@ pub trait IntoFuture {
impl<T: Future> IntoFuture for T { impl<T: Future> IntoFuture for T {
type Output = T::Output; type Output = T::Output;
type Future = T; type Future = T;
fn into_future(self) -> Self::Future { fn into_future(self) -> Self::Future {

View file

@ -4,62 +4,64 @@
//! //!
//! Often it's desireable to await multiple futures as if it was a single //! Often it's desireable to await multiple futures as if it was a single
//! future. The `join` family of operations converts multiple futures into a //! future. The `join` family of operations converts multiple futures into a
//! single future that returns all of their outputs. The `select` family of //! single future that returns all of their outputs. The `race` family of
//! operations converts multiple future into a single future that returns the //! operations converts multiple future into a single future that returns the
//! first output. //! first output.
//! //!
//! For operating on futures the following macros can be used: //! For operating on futures the following functions can be used:
//! //!
//! | Name | Return signature | When does it return? | //! | Name | Return signature | When does it return? |
//! | --- | --- | --- | //! | --- | --- | --- |
//! | `future::join` | `(T1, T2)` | Wait for all to complete //! | [`Future::join`] | `(T1, T2)` | Wait for all to complete
//! | `future::select` | `T` | Return on first value //! | [`Future::race`] | `T` | Return on first value
//! //!
//! ## Fallible Futures Concurrency //! ## Fallible Futures Concurrency
//! //!
//! For operating on futures that return `Result` additional `try_` variants of //! For operating on futures that return `Result` additional `try_` variants of
//! the macros mentioned before can be used. These macros are aware of `Result`, //! the functions mentioned before can be used. These functions are aware of `Result`,
//! and will behave slightly differently from their base variants. //! and will behave slightly differently from their base variants.
//! //!
//! In the case of `try_join`, if any of the futures returns `Err` all //! In the case of `try_join`, if any of the futures returns `Err` all
//! futures are dropped and an error is returned. This is referred to as //! futures are dropped and an error is returned. This is referred to as
//! "short-circuiting". //! "short-circuiting".
//! //!
//! In the case of `try_select`, instead of returning the first future that //! In the case of `try_race`, instead of returning the first future that
//! completes it returns the first future that _successfully_ completes. This //! completes it returns the first future that _successfully_ completes. This
//! means `try_select` will keep going until any one of the futures returns //! means `try_race` will keep going until any one of the futures returns
//! `Ok`, or _all_ futures have returned `Err`. //! `Ok`, or _all_ futures have returned `Err`.
//! //!
//! However sometimes it can be useful to use the base variants of the macros //! However sometimes it can be useful to use the base variants of the functions
//! even on futures that return `Result`. Here is an overview of operations that //! even on futures that return `Result`. Here is an overview of operations that
//! work on `Result`, and their respective semantics: //! work on `Result`, and their respective semantics:
//! //!
//! | Name | Return signature | When does it return? | //! | Name | Return signature | When does it return? |
//! | --- | --- | --- | //! | --- | --- | --- |
//! | `future::join` | `(Result<T, E>, Result<T, E>)` | Wait for all to complete //! | [`Future::join`] | `(Result<T, E>, Result<T, E>)` | Wait for all to complete
//! | `future::try_join` | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete //! | [`Future::try_join`] | `Result<(T1, T2), E>` | Return on first `Err`, wait for all to complete
//! | `future::select` | `Result<T, E>` | Return on first value //! | [`Future::race`] | `Result<T, E>` | Return on first value
//! | `future::try_select` | `Result<T, E>` | Return on first `Ok`, reject on last Err //! | [`Future::try_race`] | `Result<T, E>` | Return on first `Ok`, reject on last Err
//!
#[doc(inline)] //! [`Future::join`]: trait.Future.html#method.join
pub use async_macros::{join, try_join}; //! [`Future::try_join`]: trait.Future.html#method.try_join
//! [`Future::race`]: trait.Future.html#method.race
//! [`Future::try_race`]: trait.Future.html#method.try_race
pub use future::Future; pub use future::Future;
pub use pending::pending; pub use pending::pending;
pub use poll_fn::poll_fn; pub use poll_fn::poll_fn;
pub use ready::ready; pub use ready::ready;
pub use timeout::{timeout, TimeoutError};
pub(crate) mod future; pub(crate) mod future;
mod pending; mod pending;
mod poll_fn; mod poll_fn;
mod ready; mod ready;
mod timeout;
cfg_default! {
pub use timeout::{timeout, TimeoutError};
mod timeout;
}
cfg_unstable! { cfg_unstable! {
#[doc(inline)]
pub use async_macros::{select, try_select};
pub use into_future::IntoFuture; pub use into_future::IntoFuture;
mod into_future; mod into_future;
} }

View file

@ -1,7 +1,7 @@
use std::future::Future;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::pin::Pin; use std::pin::Pin;
use crate::future::Future;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Never resolves to a value. /// Never resolves to a value.

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Creates a new future wrapping around a function returning [`Poll`]. /// Creates a new future wrapping around a function returning [`Poll`].

View file

@ -2,10 +2,11 @@ use std::error::Error;
use std::fmt; use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use std::future::Future;
use futures_timer::Delay; use futures_timer::Delay;
use pin_project_lite::pin_project;
use crate::future::Future;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Awaits a future or times out after a duration of time. /// Awaits a future or times out after a duration of time.
@ -39,24 +40,24 @@ where
f.await f.await
} }
/// A future that times out after a duration of time. pin_project! {
struct TimeoutFuture<F> { /// A future that times out after a duration of time.
future: F, struct TimeoutFuture<F> {
delay: Delay, #[pin]
} future: F,
#[pin]
impl<F> TimeoutFuture<F> { delay: Delay,
pin_utils::unsafe_pinned!(future: F); }
pin_utils::unsafe_pinned!(delay: Delay);
} }
impl<F: Future> Future for TimeoutFuture<F> { impl<F: Future> Future for TimeoutFuture<F> {
type Output = Result<F::Output, TimeoutError>; type Output = Result<F::Output, TimeoutError>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().future().poll(cx) { let this = self.project();
match this.future.poll(cx) {
Poll::Ready(v) => Poll::Ready(Ok(v)), Poll::Ready(v) => Poll::Ready(Ok(v)),
Poll::Pending => match self.delay().poll(cx) { Poll::Pending => match this.delay.poll(cx) {
Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })),
Poll::Pending => Poll::Pending, Poll::Pending => Poll::Pending,
}, },

View file

@ -2,50 +2,55 @@ use std::mem;
use std::pin::Pin; use std::pin::Pin;
use std::str; use std::str;
use pin_project_lite::pin_project;
use super::read_until_internal; use super::read_until_internal;
use crate::io::{self, BufRead}; use crate::io::{self, BufRead};
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream of lines in a byte stream. pin_project! {
/// /// A stream of lines in a byte stream.
/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. ///
/// /// This stream is created by the [`lines`] method on types that implement [`BufRead`].
/// This type is an async version of [`std::io::Lines`]. ///
/// /// This type is an async version of [`std::io::Lines`].
/// [`lines`]: trait.BufRead.html#method.lines ///
/// [`BufRead`]: trait.BufRead.html /// [`lines`]: trait.BufRead.html#method.lines
/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html /// [`BufRead`]: trait.BufRead.html
#[derive(Debug)] /// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html
pub struct Lines<R> { #[derive(Debug)]
pub(crate) reader: R, pub struct Lines<R> {
pub(crate) buf: String, #[pin]
pub(crate) bytes: Vec<u8>, pub(crate) reader: R,
pub(crate) read: usize, pub(crate) buf: String,
pub(crate) bytes: Vec<u8>,
pub(crate) read: usize,
}
} }
impl<R: BufRead> Stream for Lines<R> { impl<R: BufRead> Stream for Lines<R> {
type Item = io::Result<String>; type Item = io::Result<String>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let Self { let this = self.project();
reader, let n = futures_core::ready!(read_line_internal(
buf, this.reader,
bytes, cx,
read, this.buf,
} = unsafe { self.get_unchecked_mut() }; this.bytes,
let reader = unsafe { Pin::new_unchecked(reader) }; this.read
let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; ))?;
if n == 0 && buf.is_empty() { if n == 0 && this.buf.is_empty() {
return Poll::Ready(None); return Poll::Ready(None);
} }
if buf.ends_with('\n') { if this.buf.ends_with('\n') {
buf.pop(); this.buf.pop();
if buf.ends_with('\r') { if this.buf.ends_with('\r') {
buf.pop(); this.buf.pop();
} }
} }
Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) Poll::Ready(Some(Ok(mem::replace(this.buf, String::new()))))
} }
} }

View file

@ -25,7 +25,7 @@ extension_trait! {
[`std::io::BufRead`]. [`std::io::BufRead`].
The [provided methods] do not really exist in the trait itself, but they become The [provided methods] do not really exist in the trait itself, but they become
available when the prelude is imported: available when [`BufReadExt`] from the [prelude] is imported:
``` ```
# #[allow(unused_imports)] # #[allow(unused_imports)]
@ -36,6 +36,8 @@ extension_trait! {
[`futures::io::AsyncBufRead`]: [`futures::io::AsyncBufRead`]:
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html
[provided methods]: #provided-methods [provided methods]: #provided-methods
[`BufReadExt`]: ../io/prelude/trait.BufReadExt.html
[prelude]: ../prelude/index.html
"#] "#]
pub trait BufRead { pub trait BufRead {
#[doc = r#" #[doc = r#"
@ -62,6 +64,11 @@ extension_trait! {
fn consume(self: Pin<&mut Self>, amt: usize); fn consume(self: Pin<&mut Self>, amt: usize);
} }
#[doc = r#"
Extension methods for [`BufRead`].
[`BufRead`]: ../trait.BufRead.html
"#]
pub trait BufReadExt: futures_io::AsyncBufRead { pub trait BufReadExt: futures_io::AsyncBufRead {
#[doc = r#" #[doc = r#"
Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. Reads all bytes into `buf` until the delimiter `byte` or EOF is reached.

View file

@ -1,9 +1,9 @@
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use std::str; use std::str;
use std::future::Future;
use super::read_until_internal; use super::read_until_internal;
use crate::future::Future;
use crate::io::{self, BufRead}; use crate::io::{self, BufRead};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -37,8 +37,12 @@ impl<T: BufRead + Unpin + ?Sized> Future for ReadLineFuture<'_, T> {
)) ))
})) }))
} else { } else {
debug_assert!(buf.is_empty()); #[allow(clippy::debug_assert_with_mut_call)]
debug_assert_eq!(*read, 0); {
debug_assert!(buf.is_empty());
debug_assert_eq!(*read, 0);
}
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
mem::swap(unsafe { buf.as_mut_vec() }, bytes); mem::swap(unsafe { buf.as_mut_vec() }, bytes);
Poll::Ready(ret) Poll::Ready(ret)

View file

@ -1,7 +1,7 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use super::read_until_internal; use super::read_until_internal;
use crate::future::Future;
use crate::io::{self, BufRead}; use crate::io::{self, BufRead};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -1,46 +1,51 @@
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use super::read_until_internal; use super::read_until_internal;
use crate::io::{self, BufRead}; use crate::io::{self, BufRead};
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A stream over the contents of an instance of [`BufRead`] split on a particular byte. pin_project! {
/// /// A stream over the contents of an instance of [`BufRead`] split on a particular byte.
/// This stream is created by the [`split`] method on types that implement [`BufRead`]. ///
/// /// This stream is created by the [`split`] method on types that implement [`BufRead`].
/// This type is an async version of [`std::io::Split`]. ///
/// /// This type is an async version of [`std::io::Split`].
/// [`split`]: trait.BufRead.html#method.lines ///
/// [`BufRead`]: trait.BufRead.html /// [`split`]: trait.BufRead.html#method.lines
/// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html /// [`BufRead`]: trait.BufRead.html
#[derive(Debug)] /// [`std::io::Split`]: https://doc.rust-lang.org/std/io/struct.Split.html
pub struct Split<R> { #[derive(Debug)]
pub(crate) reader: R, pub struct Split<R> {
pub(crate) buf: Vec<u8>, #[pin]
pub(crate) read: usize, pub(crate) reader: R,
pub(crate) delim: u8, pub(crate) buf: Vec<u8>,
pub(crate) read: usize,
pub(crate) delim: u8,
}
} }
impl<R: BufRead> Stream for Split<R> { impl<R: BufRead> Stream for Split<R> {
type Item = io::Result<Vec<u8>>; type Item = io::Result<Vec<u8>>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> { fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let Self { let this = self.project();
reader, let n = futures_core::ready!(read_until_internal(
buf, this.reader,
read, cx,
delim, *this.delim,
} = unsafe { self.get_unchecked_mut() }; this.buf,
let reader = unsafe { Pin::new_unchecked(reader) }; this.read
let n = futures_core::ready!(read_until_internal(reader, cx, *delim, buf, read))?; ))?;
if n == 0 && buf.is_empty() { if n == 0 && this.buf.is_empty() {
return Poll::Ready(None); return Poll::Ready(None);
} }
if buf[buf.len() - 1] == *delim { if this.buf[this.buf.len() - 1] == *this.delim {
buf.pop(); this.buf.pop();
} }
Poll::Ready(Some(Ok(mem::replace(buf, vec![])))) Poll::Ready(Some(Ok(mem::replace(this.buf, vec![]))))
} }
} }

View file

@ -2,51 +2,56 @@ use std::io::{IoSliceMut, Read as _};
use std::pin::Pin; use std::pin::Pin;
use std::{cmp, fmt}; use std::{cmp, fmt};
use pin_project_lite::pin_project;
use crate::io::{self, BufRead, Read, Seek, SeekFrom}; use crate::io::{self, BufRead, Read, Seek, SeekFrom};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
const DEFAULT_CAPACITY: usize = 8 * 1024; const DEFAULT_CAPACITY: usize = 8 * 1024;
/// Adds buffering to any reader. pin_project! {
/// /// Adds buffering to any reader.
/// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader` ///
/// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer /// It can be excessively inefficient to work directly with a [`Read`] instance. A `BufReader`
/// of the incoming byte stream. /// performs large, infrequent reads on the underlying [`Read`] and maintains an in-memory buffer
/// /// of the incoming byte stream.
/// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to ///
/// the same file or network socket. It does not help when reading very large amounts at once, or /// `BufReader` can improve the speed of programs that make *small* and *repeated* read calls to
/// reading just one or a few times. It also provides no advantage when reading from a source that /// the same file or network socket. It does not help when reading very large amounts at once, or
/// is already in memory, like a `Vec<u8>`. /// reading just one or a few times. It also provides no advantage when reading from a source that
/// /// is already in memory, like a `Vec<u8>`.
/// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating ///
/// multiple instances of a `BufReader` on the same stream can cause data loss. /// When the `BufReader` is dropped, the contents of its buffer will be discarded. Creating
/// /// multiple instances of a `BufReader` on the same stream can cause data loss.
/// This type is an async version of [`std::io::BufReader`]. ///
/// /// This type is an async version of [`std::io::BufReader`].
/// [`Read`]: trait.Read.html ///
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html /// [`Read`]: trait.Read.html
/// /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// # Examples ///
/// /// # Examples
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// # /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::fs::File; /// #
/// use async_std::io::BufReader; /// use async_std::fs::File;
/// use async_std::prelude::*; /// use async_std::io::BufReader;
/// /// use async_std::prelude::*;
/// let mut file = BufReader::new(File::open("a.txt").await?); ///
/// /// let mut file = BufReader::new(File::open("a.txt").await?);
/// let mut line = String::new(); ///
/// file.read_line(&mut line).await?; /// let mut line = String::new();
/// # /// file.read_line(&mut line).await?;
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
pub struct BufReader<R> { /// ```
inner: R, pub struct BufReader<R> {
buf: Box<[u8]>, #[pin]
pos: usize, inner: R,
cap: usize, buf: Box<[u8]>,
pos: usize,
cap: usize,
}
} }
impl<R: io::Read> BufReader<R> { impl<R: io::Read> BufReader<R> {
@ -95,10 +100,6 @@ impl<R: io::Read> BufReader<R> {
} }
impl<R> BufReader<R> { impl<R> BufReader<R> {
pin_utils::unsafe_pinned!(inner: R);
pin_utils::unsafe_unpinned!(pos: usize);
pin_utils::unsafe_unpinned!(cap: usize);
/// Gets a reference to the underlying reader. /// Gets a reference to the underlying reader.
/// ///
/// It is inadvisable to directly read from the underlying reader. /// It is inadvisable to directly read from the underlying reader.
@ -141,6 +142,13 @@ impl<R> BufReader<R> {
&mut self.inner &mut self.inner
} }
/// Gets a pinned mutable reference to the underlying reader.
///
/// It is inadvisable to directly read from the underlying reader.
fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut R> {
self.project().inner
}
/// Returns a reference to the internal buffer. /// Returns a reference to the internal buffer.
/// ///
/// This function will not attempt to fill the buffer if it is empty. /// This function will not attempt to fill the buffer if it is empty.
@ -185,9 +193,10 @@ impl<R> BufReader<R> {
/// Invalidates all data in the internal buffer. /// Invalidates all data in the internal buffer.
#[inline] #[inline]
fn discard_buffer(mut self: Pin<&mut Self>) { fn discard_buffer(self: Pin<&mut Self>) {
*self.as_mut().pos() = 0; let this = self.project();
*self.cap() = 0; *this.pos = 0;
*this.cap = 0;
} }
} }
@ -201,7 +210,7 @@ impl<R: Read> Read for BufReader<R> {
// (larger than our internal buffer), bypass our internal buffer // (larger than our internal buffer), bypass our internal buffer
// entirely. // entirely.
if self.pos == self.cap && buf.len() >= self.buf.len() { if self.pos == self.cap && buf.len() >= self.buf.len() {
let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf)); let res = futures_core::ready!(self.as_mut().get_pin_mut().poll_read(cx, buf));
self.discard_buffer(); self.discard_buffer();
return Poll::Ready(res); return Poll::Ready(res);
} }
@ -218,7 +227,8 @@ impl<R: Read> Read for BufReader<R> {
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
let total_len = bufs.iter().map(|b| b.len()).sum::<usize>(); let total_len = bufs.iter().map(|b| b.len()).sum::<usize>();
if self.pos == self.cap && total_len >= self.buf.len() { if self.pos == self.cap && total_len >= self.buf.len() {
let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); let res =
futures_core::ready!(self.as_mut().get_pin_mut().poll_read_vectored(cx, bufs));
self.discard_buffer(); self.discard_buffer();
return Poll::Ready(res); return Poll::Ready(res);
} }
@ -234,28 +244,23 @@ impl<R: Read> BufRead for BufReader<R> {
self: Pin<&'a mut Self>, self: Pin<&'a mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
) -> Poll<io::Result<&'a [u8]>> { ) -> Poll<io::Result<&'a [u8]>> {
let Self { let mut this = self.project();
inner,
buf,
cap,
pos,
} = unsafe { self.get_unchecked_mut() };
let mut inner = unsafe { Pin::new_unchecked(inner) };
// If we've reached the end of our internal buffer then we need to fetch // If we've reached the end of our internal buffer then we need to fetch
// some more data from the underlying reader. // some more data from the underlying reader.
// Branch using `>=` instead of the more correct `==` // Branch using `>=` instead of the more correct `==`
// to tell the compiler that the pos..cap slice is always valid. // to tell the compiler that the pos..cap slice is always valid.
if *pos >= *cap { if *this.pos >= *this.cap {
debug_assert!(*pos == *cap); debug_assert!(*this.pos == *this.cap);
*cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?; *this.cap = futures_core::ready!(this.inner.as_mut().poll_read(cx, this.buf))?;
*pos = 0; *this.pos = 0;
} }
Poll::Ready(Ok(&buf[*pos..*cap])) Poll::Ready(Ok(&this.buf[*this.pos..*this.cap]))
} }
fn consume(mut self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
*self.as_mut().pos() = cmp::min(self.pos + amt, self.cap); let this = self.project();
*this.pos = cmp::min(*this.pos + amt, *this.cap);
} }
} }
@ -305,24 +310,26 @@ impl<R: Seek> Seek for BufReader<R> {
if let Some(offset) = n.checked_sub(remainder) { if let Some(offset) = n.checked_sub(remainder) {
result = futures_core::ready!( result = futures_core::ready!(
self.as_mut() self.as_mut()
.inner() .get_pin_mut()
.poll_seek(cx, SeekFrom::Current(offset)) .poll_seek(cx, SeekFrom::Current(offset))
)?; )?;
} else { } else {
// seek backwards by our remainder, and then by the offset // seek backwards by our remainder, and then by the offset
futures_core::ready!( futures_core::ready!(
self.as_mut() self.as_mut()
.inner() .get_pin_mut()
.poll_seek(cx, SeekFrom::Current(-remainder)) .poll_seek(cx, SeekFrom::Current(-remainder))
)?; )?;
self.as_mut().discard_buffer(); self.as_mut().discard_buffer();
result = futures_core::ready!( result = futures_core::ready!(
self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)) self.as_mut()
.get_pin_mut()
.poll_seek(cx, SeekFrom::Current(n))
)?; )?;
} }
} else { } else {
// Seeking with Start/End doesn't care about our buffer length. // Seeking with Start/End doesn't care about our buffer length.
result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?; result = futures_core::ready!(self.as_mut().get_pin_mut().poll_seek(cx, pos))?;
} }
self.discard_buffer(); self.discard_buffer();
Poll::Ready(Ok(result)) Poll::Ready(Ok(result))

View file

@ -1,96 +1,96 @@
use std::fmt; use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use futures_core::ready; use pin_project_lite::pin_project;
use crate::io::write::WriteExt; use crate::io::write::WriteExt;
use crate::io::{self, Seek, SeekFrom, Write}; use crate::io::{self, Seek, SeekFrom, Write};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll, ready};
const DEFAULT_CAPACITY: usize = 8 * 1024; const DEFAULT_CAPACITY: usize = 8 * 1024;
/// Wraps a writer and buffers its output. pin_project! {
/// /// Wraps a writer and buffers its output.
/// It can be excessively inefficient to work directly with something that ///
/// implements [`Write`]. For example, every call to /// It can be excessively inefficient to work directly with something that
/// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A /// implements [`Write`]. For example, every call to
/// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying /// [`write`][`TcpStream::write`] on [`TcpStream`] results in a system call. A
/// writer in large, infrequent batches. /// `BufWriter` keeps an in-memory buffer of data and writes it to an underlying
/// /// writer in large, infrequent batches.
/// `BufWriter` can improve the speed of programs that make *small* and ///
/// *repeated* write calls to the same file or network socket. It does not /// `BufWriter` can improve the speed of programs that make *small* and
/// help when writing very large amounts at once, or writing just one or a few /// *repeated* write calls to the same file or network socket. It does not
/// times. It also provides no advantage when writing to a destination that is /// help when writing very large amounts at once, or writing just one or a few
/// in memory, like a `Vec<u8>`. /// times. It also provides no advantage when writing to a destination that is
/// /// in memory, like a `Vec<u8>`.
/// When the `BufWriter` is dropped, the contents of its buffer will be written ///
/// out. However, any errors that happen in the process of flushing the buffer /// When the `BufWriter` is dropped, the contents of its buffer will be written
/// when the writer is dropped will be ignored. Code that wishes to handle such /// out. However, any errors that happen in the process of flushing the buffer
/// errors must manually call [`flush`] before the writer is dropped. /// when the writer is dropped will be ignored. Code that wishes to handle such
/// /// errors must manually call [`flush`] before the writer is dropped.
/// This type is an async version of [`std::io::BufReader`]. ///
/// /// This type is an async version of [`std::io::BufReader`].
/// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html ///
/// /// [`std::io::BufReader`]: https://doc.rust-lang.org/std/io/struct.BufReader.html
/// # Examples ///
/// /// # Examples
/// Let's write the numbers one through ten to a [`TcpStream`]: ///
/// /// Let's write the numbers one through ten to a [`TcpStream`]:
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// use async_std::net::TcpStream; /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::prelude::*; /// use async_std::net::TcpStream;
/// /// use async_std::prelude::*;
/// let mut stream = TcpStream::connect("127.0.0.1:34254").await?; ///
/// /// let mut stream = TcpStream::connect("127.0.0.1:34254").await?;
/// for i in 0..10 { ///
/// let arr = [i+1]; /// for i in 0..10 {
/// stream.write(&arr).await?; /// let arr = [i+1];
/// } /// stream.write(&arr).await?;
/// # /// }
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
/// /// ```
/// Because we're not buffering, we write each one in turn, incurring the ///
/// overhead of a system call per byte written. We can fix this with a /// Because we're not buffering, we write each one in turn, incurring the
/// `BufWriter`: /// overhead of a system call per byte written. We can fix this with a
/// /// `BufWriter`:
/// ```no_run ///
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// ```no_run
/// use async_std::io::BufWriter; /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// use async_std::net::TcpStream; /// use async_std::io::BufWriter;
/// use async_std::prelude::*; /// use async_std::net::TcpStream;
/// /// use async_std::prelude::*;
/// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?); ///
/// for i in 0..10 { /// let mut stream = BufWriter::new(TcpStream::connect("127.0.0.1:34254").await?);
/// let arr = [i+1]; /// for i in 0..10 {
/// stream.write(&arr).await?; /// let arr = [i+1];
/// }; /// stream.write(&arr).await?;
/// # /// };
/// # Ok(()) }) } /// #
/// ``` /// # Ok(()) }) }
/// /// ```
/// By wrapping the stream with a `BufWriter`, these ten writes are all grouped ///
/// together by the buffer, and will all be written out in one system call when /// By wrapping the stream with a `BufWriter`, these ten writes are all grouped
/// the `stream` is dropped. /// together by the buffer, and will all be written out in one system call when
/// /// the `stream` is dropped.
/// [`Write`]: trait.Write.html ///
/// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write /// [`Write`]: trait.Write.html
/// [`TcpStream`]: ../net/struct.TcpStream.html /// [`TcpStream::write`]: ../net/struct.TcpStream.html#method.write
/// [`flush`]: trait.Write.html#tymethod.flush /// [`TcpStream`]: ../net/struct.TcpStream.html
pub struct BufWriter<W> { /// [`flush`]: trait.Write.html#tymethod.flush
inner: W, pub struct BufWriter<W> {
buf: Vec<u8>, #[pin]
written: usize, inner: W,
buf: Vec<u8>,
written: usize,
}
} }
#[derive(Debug)] #[derive(Debug)]
pub struct IntoInnerError<W>(W, std::io::Error); pub struct IntoInnerError<W>(W, std::io::Error);
impl<W: Write> BufWriter<W> { impl<W: Write> BufWriter<W> {
pin_utils::unsafe_pinned!(inner: W);
pin_utils::unsafe_unpinned!(buf: Vec<u8>);
/// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB, /// Creates a new `BufWriter` with a default buffer capacity. The default is currently 8 KB,
/// but may change in the future. /// but may change in the future.
/// ///
@ -178,6 +178,13 @@ impl<W: Write> BufWriter<W> {
&mut self.inner &mut self.inner
} }
/// Gets a pinned mutable reference to the underlying writer.
///
/// It is inadvisable to directly write to the underlying writer.
fn get_pin_mut(self: Pin<&mut Self>) -> Pin<&mut W> {
self.project().inner
}
/// Consumes BufWriter, returning the underlying writer /// Consumes BufWriter, returning the underlying writer
/// ///
/// This method will not write leftover data, it will be lost. /// This method will not write leftover data, it will be lost.
@ -234,16 +241,15 @@ impl<W: Write> BufWriter<W> {
/// ///
/// [`LineWriter`]: struct.LineWriter.html /// [`LineWriter`]: struct.LineWriter.html
fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
let Self { let mut this = self.project();
inner, let len = this.buf.len();
buf,
written,
} = unsafe { Pin::get_unchecked_mut(self) };
let mut inner = unsafe { Pin::new_unchecked(inner) };
let len = buf.len();
let mut ret = Ok(()); let mut ret = Ok(());
while *written < len { while *this.written < len {
match inner.as_mut().poll_write(cx, &buf[*written..]) { match this
.inner
.as_mut()
.poll_write(cx, &this.buf[*this.written..])
{
Poll::Ready(Ok(0)) => { Poll::Ready(Ok(0)) => {
ret = Err(io::Error::new( ret = Err(io::Error::new(
io::ErrorKind::WriteZero, io::ErrorKind::WriteZero,
@ -251,7 +257,7 @@ impl<W: Write> BufWriter<W> {
)); ));
break; break;
} }
Poll::Ready(Ok(n)) => *written += n, Poll::Ready(Ok(n)) => *this.written += n,
Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {} Poll::Ready(Err(ref e)) if e.kind() == io::ErrorKind::Interrupted => {}
Poll::Ready(Err(e)) => { Poll::Ready(Err(e)) => {
ret = Err(e); ret = Err(e);
@ -260,10 +266,10 @@ impl<W: Write> BufWriter<W> {
Poll::Pending => return Poll::Pending, Poll::Pending => return Poll::Pending,
} }
} }
if *written > 0 { if *this.written > 0 {
buf.drain(..*written); this.buf.drain(..*this.written);
} }
*written = 0; *this.written = 0;
Poll::Ready(ret) Poll::Ready(ret)
} }
} }
@ -278,20 +284,20 @@ impl<W: Write> Write for BufWriter<W> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
} }
if buf.len() >= self.buf.capacity() { if buf.len() >= self.buf.capacity() {
self.inner().poll_write(cx, buf) self.get_pin_mut().poll_write(cx, buf)
} else { } else {
Pin::new(&mut *self.buf()).poll_write(cx, buf) Pin::new(&mut *self.project().buf).poll_write(cx, buf)
} }
} }
fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
self.inner().poll_flush(cx) self.get_pin_mut().poll_flush(cx)
} }
fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
self.inner().poll_close(cx) self.get_pin_mut().poll_close(cx)
} }
} }
@ -314,6 +320,6 @@ impl<W: Write + Seek> Seek for BufWriter<W> {
pos: SeekFrom, pos: SeekFrom,
) -> Poll<io::Result<u64>> { ) -> Poll<io::Result<u64>> {
ready!(self.as_mut().poll_flush_buf(cx))?; ready!(self.as_mut().poll_flush_buf(cx))?;
self.inner().poll_seek(cx, pos) self.get_pin_mut().poll_seek(cx, pos)
} }
} }

View file

@ -1,6 +1,8 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use pin_project_lite::pin_project;
use crate::future::Future;
use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::io::{self, BufRead, BufReader, Read, Write};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -41,52 +43,131 @@ use crate::task::{Context, Poll};
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(any(feature = "docs", not(feature = "unstable")))]
pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64> pub async fn copy<R, W>(reader: &mut R, writer: &mut W) -> io::Result<u64>
where where
R: Read + Unpin + ?Sized, R: Read + Unpin + ?Sized,
W: Write + Unpin + ?Sized, W: Write + Unpin + ?Sized,
{ {
pub struct CopyFuture<'a, R, W: ?Sized> { pin_project! {
reader: R, struct CopyFuture<R, W> {
writer: &'a mut W, #[pin]
amt: u64, reader: R,
} #[pin]
writer: W,
impl<R, W: Unpin + ?Sized> CopyFuture<'_, R, W> { amt: u64,
fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) {
unsafe {
let this = self.get_unchecked_mut();
(
Pin::new_unchecked(&mut this.reader),
Pin::new(&mut *this.writer),
&mut this.amt,
)
}
} }
} }
impl<R, W> Future for CopyFuture<'_, R, W> impl<R, W> Future for CopyFuture<R, W>
where where
R: BufRead, R: BufRead,
W: Write + Unpin + ?Sized, W: Write + Unpin,
{ {
type Output = io::Result<u64>; type Output = io::Result<u64>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let (mut reader, mut writer, amt) = self.project(); let mut this = self.project();
loop { loop {
let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
if buffer.is_empty() { if buffer.is_empty() {
futures_core::ready!(writer.as_mut().poll_flush(cx))?; futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
return Poll::Ready(Ok(*amt)); return Poll::Ready(Ok(*this.amt));
} }
let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?; let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
if i == 0 { if i == 0 {
return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
} }
*amt += i as u64; *this.amt += i as u64;
reader.as_mut().consume(i); this.reader.as_mut().consume(i);
}
}
}
let future = CopyFuture {
reader: BufReader::new(reader),
writer,
amt: 0,
};
future.await
}
/// Copies the entire contents of a reader into a writer.
///
/// This function will continuously read data from `reader` and then
/// write it into `writer` in a streaming fashion until `reader`
/// returns EOF.
///
/// On success, the total number of bytes that were copied from
/// `reader` to `writer` is returned.
///
/// If youre wanting to copy the contents of one file to another and youre
/// working with filesystem paths, see the [`fs::copy`] function.
///
/// This function is an async version of [`std::io::copy`].
///
/// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html
/// [`fs::copy`]: ../fs/fn.copy.html
///
/// # Errors
///
/// This function will return an error immediately if any call to `read` or
/// `write` returns an error. All instances of `ErrorKind::Interrupted` are
/// handled by this function and the underlying operation is retried.
///
/// # Examples
///
/// ```
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
///
/// let mut reader: &[u8] = b"hello";
/// let mut writer = io::stdout();
///
/// io::copy(&mut reader, &mut writer).await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg(all(feature = "unstable", not(feature = "docs")))]
pub async fn copy<R, W>(reader: R, writer: W) -> io::Result<u64>
where
R: Read + Unpin,
W: Write + Unpin,
{
pin_project! {
struct CopyFuture<R, W> {
#[pin]
reader: R,
#[pin]
writer: W,
amt: u64,
}
}
impl<R, W> Future for CopyFuture<R, W>
where
R: BufRead,
W: Write + Unpin,
{
type Output = io::Result<u64>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let mut this = self.project();
loop {
let buffer = futures_core::ready!(this.reader.as_mut().poll_fill_buf(cx))?;
if buffer.is_empty() {
futures_core::ready!(this.writer.as_mut().poll_flush(cx))?;
return Poll::Ready(Ok(*this.amt));
}
let i = futures_core::ready!(this.writer.as_mut().poll_write(cx, buffer))?;
if i == 0 {
return Poll::Ready(Err(io::ErrorKind::WriteZero.into()));
}
*this.amt += i as u64;
this.reader.as_mut().consume(i);
} }
} }
} }

View file

@ -28,9 +28,10 @@ pub fn empty() -> Empty {
/// A reader that contains no data. /// A reader that contains no data.
/// ///
/// This reader is constructed by the [`sink`] function. /// This reader is created by the [`empty`] function. See its
/// documentation for more.
/// ///
/// [`sink`]: fn.sink.html /// [`empty`]: fn.empty.html
pub struct Empty { pub struct Empty {
_private: (), _private: (),
} }

View file

@ -269,45 +269,57 @@
//! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
//! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap //! [`.unwrap()`]: https://doc.rust-lang.org/std/result/enum.Result.html#method.unwrap
#[doc(inline)] cfg_std! {
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; #[doc(inline)]
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
pub use buf_read::{BufRead, Lines}; pub use buf_read::{BufRead, Lines};
pub use buf_reader::BufReader; pub use buf_reader::BufReader;
pub use buf_writer::BufWriter; pub use buf_writer::BufWriter;
pub use copy::copy; pub use copy::copy;
pub use cursor::Cursor; pub use cursor::Cursor;
pub use empty::{empty, Empty}; pub use empty::{empty, Empty};
pub use read::Read; pub use read::Read;
pub use repeat::{repeat, Repeat}; pub use repeat::{repeat, Repeat};
pub use seek::Seek; pub use seek::Seek;
pub use sink::{sink, Sink}; pub use sink::{sink, Sink};
pub use stderr::{stderr, Stderr}; pub use write::Write;
pub use stdin::{stdin, Stdin};
pub use stdout::{stdout, Stdout};
pub use timeout::timeout;
pub use write::Write;
// For use in the print macros. pub mod prelude;
#[doc(hidden)]
pub use stdio::{_eprint, _print};
pub mod prelude; pub(crate) mod buf_read;
pub(crate) mod read;
pub(crate) mod seek;
pub(crate) mod write;
pub(crate) mod buf_read; mod buf_reader;
pub(crate) mod read; mod buf_writer;
pub(crate) mod seek; mod copy;
pub(crate) mod write; mod cursor;
mod empty;
mod repeat;
mod sink;
}
mod buf_reader; cfg_default! {
mod buf_writer; // For use in the print macros.
mod copy; #[doc(hidden)]
mod cursor; pub use stdio::{_eprint, _print};
mod empty;
mod repeat; pub use stderr::{stderr, Stderr};
mod sink; pub use stdin::{stdin, Stdin};
mod stderr; pub use stdout::{stdout, Stdout};
mod stdin; pub use timeout::timeout;
mod stdio;
mod stdout; mod timeout;
mod timeout; mod stderr;
mod stdin;
mod stdio;
mod stdout;
}
cfg_unstable! {
pub use stderr::StderrLock;
pub use stdin::StdinLock;
pub use stdout::StdoutLock;
}

View file

@ -1,4 +1,4 @@
//! The async I/O Prelude //! The async I/O prelude.
//! //!
//! The purpose of this module is to alleviate imports of many common I/O traits //! The purpose of this module is to alleviate imports of many common I/O traits
//! by adding a glob import to the top of I/O heavy modules: //! by adding a glob import to the top of I/O heavy modules:
@ -17,11 +17,11 @@ pub use crate::io::Seek;
#[doc(no_inline)] #[doc(no_inline)]
pub use crate::io::Write; pub use crate::io::Write;
#[doc(hidden)] #[doc(inline)]
pub use crate::io::buf_read::BufReadExt as _; pub use crate::io::buf_read::BufReadExt;
#[doc(hidden)] #[doc(inline)]
pub use crate::io::read::ReadExt as _; pub use crate::io::read::ReadExt;
#[doc(hidden)] #[doc(inline)]
pub use crate::io::seek::SeekExt as _; pub use crate::io::seek::SeekExt;
#[doc(hidden)] #[doc(inline)]
pub use crate::io::write::WriteExt as _; pub use crate::io::write::WriteExt;

View file

@ -1,20 +1,25 @@
use crate::io::IoSliceMut;
use std::fmt; use std::fmt;
use std::pin::Pin; use std::pin::Pin;
use crate::io::{self, BufRead, Read}; use pin_project_lite::pin_project;
use crate::io::{self, BufRead, IoSliceMut, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Adaptor to chain together two readers. pin_project! {
/// /// Adaptor to chain together two readers.
/// This struct is generally created by calling [`chain`] on a reader. ///
/// Please see the documentation of [`chain`] for more details. /// This struct is generally created by calling [`chain`] on a reader.
/// /// Please see the documentation of [`chain`] for more details.
/// [`chain`]: trait.Read.html#method.chain ///
pub struct Chain<T, U> { /// [`chain`]: trait.Read.html#method.chain
pub(crate) first: T, pub struct Chain<T, U> {
pub(crate) second: U, #[pin]
pub(crate) done_first: bool, pub(crate) first: T,
#[pin]
pub(crate) second: U,
pub(crate) done_first: bool,
}
} }
impl<T, U> Chain<T, U> { impl<T, U> Chain<T, U> {
@ -98,76 +103,64 @@ impl<T: fmt::Debug, U: fmt::Debug> fmt::Debug for Chain<T, U> {
} }
} }
impl<T: Read + Unpin, U: Read + Unpin> Read for Chain<T, U> { impl<T: Read, U: Read> Read for Chain<T, U> {
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
if !self.done_first { let this = self.project();
let rd = Pin::new(&mut self.first); if !*this.done_first {
match futures_core::ready!(this.first.poll_read(cx, buf)) {
match futures_core::ready!(rd.poll_read(cx, buf)) { Ok(0) if !buf.is_empty() => *this.done_first = true,
Ok(0) if !buf.is_empty() => self.done_first = true,
Ok(n) => return Poll::Ready(Ok(n)), Ok(n) => return Poll::Ready(Ok(n)),
Err(err) => return Poll::Ready(Err(err)), Err(err) => return Poll::Ready(Err(err)),
} }
} }
let rd = Pin::new(&mut self.second); this.second.poll_read(cx, buf)
rd.poll_read(cx, buf)
} }
fn poll_read_vectored( fn poll_read_vectored(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
bufs: &mut [IoSliceMut<'_>], bufs: &mut [IoSliceMut<'_>],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
if !self.done_first { let this = self.project();
let rd = Pin::new(&mut self.first); if !*this.done_first {
match futures_core::ready!(this.first.poll_read_vectored(cx, bufs)) {
match futures_core::ready!(rd.poll_read_vectored(cx, bufs)) { Ok(0) if !bufs.is_empty() => *this.done_first = true,
Ok(0) if !bufs.is_empty() => self.done_first = true,
Ok(n) => return Poll::Ready(Ok(n)), Ok(n) => return Poll::Ready(Ok(n)),
Err(err) => return Poll::Ready(Err(err)), Err(err) => return Poll::Ready(Err(err)),
} }
} }
let rd = Pin::new(&mut self.second); this.second.poll_read_vectored(cx, bufs)
rd.poll_read_vectored(cx, bufs)
} }
} }
impl<T: BufRead + Unpin, U: BufRead + Unpin> BufRead for Chain<T, U> { impl<T: BufRead, U: BufRead> BufRead for Chain<T, U> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let Self { let this = self.project();
first, if !*this.done_first {
second, match futures_core::ready!(this.first.poll_fill_buf(cx)) {
done_first,
} = unsafe { self.get_unchecked_mut() };
if !*done_first {
let first = unsafe { Pin::new_unchecked(first) };
match futures_core::ready!(first.poll_fill_buf(cx)) {
Ok(buf) if buf.is_empty() => { Ok(buf) if buf.is_empty() => {
*done_first = true; *this.done_first = true;
} }
Ok(buf) => return Poll::Ready(Ok(buf)), Ok(buf) => return Poll::Ready(Ok(buf)),
Err(err) => return Poll::Ready(Err(err)), Err(err) => return Poll::Ready(Err(err)),
} }
} }
let second = unsafe { Pin::new_unchecked(second) }; this.second.poll_fill_buf(cx)
second.poll_fill_buf(cx)
} }
fn consume(mut self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
if !self.done_first { let this = self.project();
let rd = Pin::new(&mut self.first); if !*this.done_first {
rd.consume(amt) this.first.consume(amt)
} else { } else {
let rd = Pin::new(&mut self.second); this.second.consume(amt)
rd.consume(amt)
} }
} }
} }

View file

@ -31,7 +31,7 @@ extension_trait! {
[`std::io::Read`]. [`std::io::Read`].
Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the
trait itself, but they become available when the prelude is imported: trait itself, but they become available when [`ReadExt`] from the [prelude] is imported:
``` ```
# #[allow(unused_imports)] # #[allow(unused_imports)]
@ -43,6 +43,8 @@ extension_trait! {
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html
[`poll_read`]: #tymethod.poll_read [`poll_read`]: #tymethod.poll_read
[`poll_read_vectored`]: #method.poll_read_vectored [`poll_read_vectored`]: #method.poll_read_vectored
[`ReadExt`]: ../io/prelude/trait.ReadExt.html
[prelude]: ../prelude/index.html
"#] "#]
pub trait Read { pub trait Read {
#[doc = r#" #[doc = r#"
@ -66,6 +68,11 @@ extension_trait! {
} }
} }
#[doc = r#"
Extension methods for [`Read`].
[`Read`]: ../trait.Read.html
"#]
pub trait ReadExt: futures_io::AsyncRead { pub trait ReadExt: futures_io::AsyncRead {
#[doc = r#" #[doc = r#"
Reads some bytes from the byte stream. Reads some bytes from the byte stream.
@ -267,7 +274,7 @@ extension_trait! {
This function returns a new instance of `Read` which will read at most This function returns a new instance of `Read` which will read at most
`limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any `limit` bytes, after which it will always return EOF ([`Ok(0)`]). Any
read errors will not count towards the number of bytes read and future read errors will not count towards the number of bytes read and future
calls to [`read()`] may succeed. calls to [`read`] may succeed.
# Examples # Examples
@ -275,7 +282,7 @@ extension_trait! {
[`File`]: ../fs/struct.File.html [`File`]: ../fs/struct.File.html
[`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok [`Ok(0)`]: ../../std/result/enum.Result.html#variant.Ok
[`read()`]: tymethod.read [`read`]: tymethod.read
```no_run ```no_run
# fn main() -> std::io::Result<()> { async_std::task::block_on(async { # fn main() -> std::io::Result<()> { async_std::task::block_on(async {

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -1,7 +1,7 @@
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -1,9 +1,9 @@
use std::mem; use std::mem;
use std::pin::Pin; use std::pin::Pin;
use std::str; use std::str;
use std::future::Future;
use super::read_to_end_internal; use super::read_to_end_internal;
use crate::future::Future;
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -37,7 +37,11 @@ impl<T: Read + Unpin + ?Sized> Future for ReadToStringFuture<'_, T> {
)) ))
})) }))
} else { } else {
debug_assert!(buf.is_empty()); #[allow(clippy::debug_assert_with_mut_call)]
{
debug_assert!(buf.is_empty());
}
// Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`.
mem::swap(unsafe { buf.as_mut_vec() }, bytes); mem::swap(unsafe { buf.as_mut_vec() }, bytes);
Poll::Ready(ret) Poll::Ready(ret)

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, IoSliceMut, Read}; use crate::io::{self, IoSliceMut, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -1,19 +1,24 @@
use std::cmp; use std::cmp;
use std::pin::Pin; use std::pin::Pin;
use pin_project_lite::pin_project;
use crate::io::{self, BufRead, Read}; use crate::io::{self, BufRead, Read};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// Reader adaptor which limits the bytes read from an underlying reader. pin_project! {
/// /// Reader adaptor which limits the bytes read from an underlying reader.
/// This struct is generally created by calling [`take`] on a reader. ///
/// Please see the documentation of [`take`] for more details. /// This struct is generally created by calling [`take`] on a reader.
/// /// Please see the documentation of [`take`] for more details.
/// [`take`]: trait.Read.html#method.take ///
#[derive(Debug)] /// [`take`]: trait.Read.html#method.take
pub struct Take<T> { #[derive(Debug)]
pub(crate) inner: T, pub struct Take<T> {
pub(crate) limit: u64, #[pin]
pub(crate) inner: T,
pub(crate) limit: u64,
}
} }
impl<T> Take<T> { impl<T> Take<T> {
@ -152,15 +157,15 @@ impl<T> Take<T> {
} }
} }
impl<T: Read + Unpin> Read for Take<T> { impl<T: Read> Read for Take<T> {
/// Attempt to read from the `AsyncRead` into `buf`. /// Attempt to read from the `AsyncRead` into `buf`.
fn poll_read( fn poll_read(
mut self: Pin<&mut Self>, self: Pin<&mut Self>,
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
let Self { inner, limit } = &mut *self; let this = self.project();
take_read_internal(Pin::new(inner), cx, buf, limit) take_read_internal(this.inner, cx, buf, this.limit)
} }
} }
@ -186,31 +191,30 @@ pub fn take_read_internal<R: Read + ?Sized>(
} }
} }
impl<T: BufRead + Unpin> BufRead for Take<T> { impl<T: BufRead> BufRead for Take<T> {
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> { fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
let Self { inner, limit } = unsafe { self.get_unchecked_mut() }; let this = self.project();
let inner = unsafe { Pin::new_unchecked(inner) };
if *limit == 0 { if *this.limit == 0 {
return Poll::Ready(Ok(&[])); return Poll::Ready(Ok(&[]));
} }
match futures_core::ready!(inner.poll_fill_buf(cx)) { match futures_core::ready!(this.inner.poll_fill_buf(cx)) {
Ok(buf) => { Ok(buf) => {
let cap = cmp::min(buf.len() as u64, *limit) as usize; let cap = cmp::min(buf.len() as u64, *this.limit) as usize;
Poll::Ready(Ok(&buf[..cap])) Poll::Ready(Ok(&buf[..cap]))
} }
Err(e) => Poll::Ready(Err(e)), Err(e) => Poll::Ready(Err(e)),
} }
} }
fn consume(mut self: Pin<&mut Self>, amt: usize) { fn consume(self: Pin<&mut Self>, amt: usize) {
let this = self.project();
// Don't let callers reset the limit by passing an overlarge value // Don't let callers reset the limit by passing an overlarge value
let amt = cmp::min(amt as u64, self.limit) as usize; let amt = cmp::min(amt as u64, *this.limit) as usize;
self.limit -= amt as u64; *this.limit -= amt as u64;
let rd = Pin::new(&mut self.inner); this.inner.consume(amt);
rd.consume(amt);
} }
} }

View file

@ -29,7 +29,8 @@ pub fn repeat(byte: u8) -> Repeat {
/// A reader which yields one byte over and over and over and over and over and... /// A reader which yields one byte over and over and over and over and over and...
/// ///
/// This reader is constructed by the [`repeat`] function. /// This reader is created by the [`repeat`] function. See its
/// documentation for more.
/// ///
/// [`repeat`]: fn.repeat.html /// [`repeat`]: fn.repeat.html
pub struct Repeat { pub struct Repeat {

View file

@ -18,7 +18,7 @@ extension_trait! {
[`std::io::Seek`]. [`std::io::Seek`].
The [provided methods] do not really exist in the trait itself, but they become The [provided methods] do not really exist in the trait itself, but they become
available when the prelude is imported: available when [`SeekExt`] the [prelude] is imported:
``` ```
# #[allow(unused_imports)] # #[allow(unused_imports)]
@ -29,6 +29,8 @@ extension_trait! {
[`futures::io::AsyncSeek`]: [`futures::io::AsyncSeek`]:
https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html
[provided methods]: #provided-methods [provided methods]: #provided-methods
[`SeekExt`]: ../io/prelude/trait.SeekExt.html
[prelude]: ../prelude/index.html
"#] "#]
pub trait Seek { pub trait Seek {
#[doc = r#" #[doc = r#"
@ -41,6 +43,11 @@ extension_trait! {
) -> Poll<io::Result<u64>>; ) -> Poll<io::Result<u64>>;
} }
#[doc = r#"
Extension methods for [`Seek`].
[`Seek`]: ../trait.Seek.html
"#]
pub trait SeekExt: futures_io::AsyncSeek { pub trait SeekExt: futures_io::AsyncSeek {
#[doc = r#" #[doc = r#"
Seeks to a new position in a byte stream. Seeks to a new position in a byte stream.

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Seek, SeekFrom}; use crate::io::{self, Seek, SeekFrom};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -25,7 +25,8 @@ pub fn sink() -> Sink {
/// A writer that consumes and drops all data. /// A writer that consumes and drops all data.
/// ///
/// This writer is constructed by the [`sink`] function. /// This writer is constructed by the [`sink`] function. See its documentation
/// for more.
/// ///
/// [`sink`]: fn.sink.html /// [`sink`]: fn.sink.html
pub struct Sink { pub struct Sink {

View file

@ -1,9 +1,14 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
cfg_unstable! {
use once_cell::sync::Lazy;
use std::io::Write as _;
}
/// Constructs a new handle to the standard error of the current process. /// Constructs a new handle to the standard error of the current process.
/// ///
@ -11,6 +16,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll};
/// ///
/// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html /// [`std::io::stderr`]: https://doc.rust-lang.org/std/io/fn.stderr.html
/// ///
/// ### Note: Windows Portability Consideration
///
/// When operating in a console, the Windows implementation of this stream does not support
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
/// an error.
///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
@ -34,15 +45,35 @@ pub fn stderr() -> Stderr {
/// A handle to the standard error of the current process. /// A handle to the standard error of the current process.
/// ///
/// Created by the [`stderr`] function. /// This writer is created by the [`stderr`] function. See its documentation for
/// more.
/// ///
/// This type is an async version of [`std::io::Stderr`]. /// ### Note: Windows Portability Consideration
///
/// When operating in a console, the Windows implementation of this stream does not support
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
/// an error.
/// ///
/// [`stderr`]: fn.stderr.html /// [`stderr`]: fn.stderr.html
/// [`std::io::Stderr`]: https://doc.rust-lang.org/std/io/struct.Stderr.html
#[derive(Debug)] #[derive(Debug)]
pub struct Stderr(Mutex<State>); pub struct Stderr(Mutex<State>);
/// A locked reference to the Stderr handle.
///
/// This handle implements the [`Write`] traits, and is constructed via the [`Stderr::lock`]
/// method.
///
/// [`Write`]: trait.Read.html
/// [`Stderr::lock`]: struct.Stderr.html#method.lock
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)]
pub struct StderrLock<'a>(std::io::StderrLock<'a>);
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
unsafe impl Send for StderrLock<'_> {}
/// The state of the asynchronous stderr. /// The state of the asynchronous stderr.
/// ///
/// The stderr can be either idle or busy performing an asynchronous operation. /// The stderr can be either idle or busy performing an asynchronous operation.
@ -77,6 +108,35 @@ enum Operation {
Flush(io::Result<()>), Flush(io::Result<()>),
} }
impl Stderr {
/// Locks this handle to the standard error stream, returning a writable guard.
///
/// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
/// use async_std::prelude::*;
///
/// let stderr = io::stderr();
/// let mut handle = stderr.lock().await;
///
/// handle.write_all(b"hello world").await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn lock(&self) -> StderrLock<'static> {
static STDERR: Lazy<std::io::Stderr> = Lazy::new(std::io::stderr);
spawn_blocking(move || StderrLock(STDERR.lock())).await
}
}
impl Write for Stderr { impl Write for Stderr {
fn poll_write( fn poll_write(
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
@ -114,7 +174,7 @@ impl Write for Stderr {
inner.buf[..buf.len()].copy_from_slice(buf); inner.buf[..buf.len()].copy_from_slice(buf);
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(move || { *state = State::Busy(spawn_blocking(move || {
let res = std::io::Write::write(&mut inner.stderr, &inner.buf); let res = std::io::Write::write(&mut inner.stderr, &inner.buf);
inner.last_op = Some(Operation::Write(res)); inner.last_op = Some(Operation::Write(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -142,7 +202,7 @@ impl Write for Stderr {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(move || { *state = State::Busy(spawn_blocking(move || {
let res = std::io::Write::flush(&mut inner.stderr); let res = std::io::Write::flush(&mut inner.stderr);
inner.last_op = Some(Operation::Flush(res)); inner.last_op = Some(Operation::Flush(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -179,3 +239,23 @@ cfg_windows! {
} }
} }
} }
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
impl io::Write for StderrLock<'_> {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.write(buf))
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(self.0.flush())
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}

View file

@ -1,9 +1,15 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use std::future::Future;
use crate::future::{self, Future}; use crate::future;
use crate::io::{self, Read}; use crate::io::{self, Read};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
cfg_unstable! {
use once_cell::sync::Lazy;
use std::io::Read as _;
}
/// Constructs a new handle to the standard input of the current process. /// Constructs a new handle to the standard input of the current process.
/// ///
@ -11,6 +17,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll};
/// ///
/// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html /// [`std::io::stdin`]: https://doc.rust-lang.org/std/io/fn.stdin.html
/// ///
/// ### Note: Windows Portability Consideration
///
/// When operating in a console, the Windows implementation of this stream does not support
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
/// an error.
///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
@ -35,15 +47,34 @@ pub fn stdin() -> Stdin {
/// A handle to the standard input of the current process. /// A handle to the standard input of the current process.
/// ///
/// Created by the [`stdin`] function. /// This reader is created by the [`stdin`] function. See its documentation for
/// more.
/// ///
/// This type is an async version of [`std::io::Stdin`]. /// ### Note: Windows Portability Consideration
///
/// When operating in a console, the Windows implementation of this stream does not support
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
/// an error.
/// ///
/// [`stdin`]: fn.stdin.html /// [`stdin`]: fn.stdin.html
/// [`std::io::Stdin`]: https://doc.rust-lang.org/std/io/struct.Stdin.html
#[derive(Debug)] #[derive(Debug)]
pub struct Stdin(Mutex<State>); pub struct Stdin(Mutex<State>);
/// A locked reference to the Stdin handle.
///
/// This handle implements the [`Read`] traits, and is constructed via the [`Stdin::lock`] method.
///
/// [`Read`]: trait.Read.html
/// [`Stdin::lock`]: struct.Stdin.html#method.lock
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(feature = "unstable")]
#[derive(Debug)]
pub struct StdinLock<'a>(std::io::StdinLock<'a>);
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
unsafe impl Send for StdinLock<'_> {}
/// The state of the asynchronous stdin. /// The state of the asynchronous stdin.
/// ///
/// The stdin can be either idle or busy performing an asynchronous operation. /// The stdin can be either idle or busy performing an asynchronous operation.
@ -117,7 +148,7 @@ impl Stdin {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(move || { *state = State::Busy(spawn_blocking(move || {
inner.line.clear(); inner.line.clear();
let res = inner.stdin.read_line(&mut inner.line); let res = inner.stdin.read_line(&mut inner.line);
inner.last_op = Some(Operation::ReadLine(res)); inner.last_op = Some(Operation::ReadLine(res));
@ -132,6 +163,35 @@ impl Stdin {
}) })
.await .await
} }
/// Locks this handle to the standard input stream, returning a readable guard.
///
/// The lock is released when the returned lock goes out of scope. The returned guard also implements the Read trait for accessing the underlying data.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
/// use async_std::prelude::*;
///
/// let mut buffer = String::new();
///
/// let stdin = io::stdin();
/// let mut handle = stdin.lock().await;
///
/// handle.read_to_string(&mut buffer).await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn lock(&self) -> StdinLock<'static> {
static STDIN: Lazy<std::io::Stdin> = Lazy::new(std::io::stdin);
spawn_blocking(move || StdinLock(STDIN.lock())).await
}
} }
impl Read for Stdin { impl Read for Stdin {
@ -170,7 +230,7 @@ impl Read for Stdin {
} }
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(move || { *state = State::Busy(spawn_blocking(move || {
let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf);
inner.last_op = Some(Operation::Read(res)); inner.last_op = Some(Operation::Read(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -203,3 +263,15 @@ cfg_windows! {
} }
} }
} }
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
impl Read for StdinLock<'_> {
fn poll_read(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.read(buf))
}
}

View file

@ -1,9 +1,14 @@
use std::pin::Pin; use std::pin::Pin;
use std::sync::Mutex; use std::sync::Mutex;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
cfg_unstable! {
use once_cell::sync::Lazy;
use std::io::Write as _;
}
/// Constructs a new handle to the standard output of the current process. /// Constructs a new handle to the standard output of the current process.
/// ///
@ -11,6 +16,12 @@ use crate::task::{blocking, Context, JoinHandle, Poll};
/// ///
/// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html /// [`std::io::stdout`]: https://doc.rust-lang.org/std/io/fn.stdout.html
/// ///
/// ### Note: Windows Portability Consideration
///
/// When operating in a console, the Windows implementation of this stream does not support
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
/// an error.
///
/// # Examples /// # Examples
/// ///
/// ```no_run /// ```no_run
@ -34,15 +45,35 @@ pub fn stdout() -> Stdout {
/// A handle to the standard output of the current process. /// A handle to the standard output of the current process.
/// ///
/// Created by the [`stdout`] function. /// This writer is created by the [`stdout`] function. See its documentation
/// for more.
/// ///
/// This type is an async version of [`std::io::Stdout`]. /// ### Note: Windows Portability Consideration
///
/// When operating in a console, the Windows implementation of this stream does not support
/// non-UTF-8 byte sequences. Attempting to write bytes that are not valid UTF-8 will return
/// an error.
/// ///
/// [`stdout`]: fn.stdout.html /// [`stdout`]: fn.stdout.html
/// [`std::io::Stdout`]: https://doc.rust-lang.org/std/io/struct.Stdout.html
#[derive(Debug)] #[derive(Debug)]
pub struct Stdout(Mutex<State>); pub struct Stdout(Mutex<State>);
/// A locked reference to the Stderr handle.
///
/// This handle implements the [`Write`] traits, and is constructed via the [`Stdout::lock`]
/// method.
///
/// [`Write`]: trait.Read.html
/// [`Stdout::lock`]: struct.Stdout.html#method.lock
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)]
pub struct StdoutLock<'a>(std::io::StdoutLock<'a>);
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
unsafe impl Send for StdoutLock<'_> {}
/// The state of the asynchronous stdout. /// The state of the asynchronous stdout.
/// ///
/// The stdout can be either idle or busy performing an asynchronous operation. /// The stdout can be either idle or busy performing an asynchronous operation.
@ -77,6 +108,35 @@ enum Operation {
Flush(io::Result<()>), Flush(io::Result<()>),
} }
impl Stdout {
/// Locks this handle to the standard error stream, returning a writable guard.
///
/// The lock is released when the returned lock goes out of scope. The returned guard also implements the Write trait for writing data.
///
/// # Examples
///
/// ```no_run
/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async {
/// #
/// use async_std::io;
/// use async_std::prelude::*;
///
/// let stdout = io::stdout();
/// let mut handle = stdout.lock().await;
///
/// handle.write_all(b"hello world").await?;
/// #
/// # Ok(()) }) }
/// ```
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[cfg(any(feature = "unstable", feature = "docs"))]
pub async fn lock(&self) -> StdoutLock<'static> {
static STDOUT: Lazy<std::io::Stdout> = Lazy::new(std::io::stdout);
spawn_blocking(move || StdoutLock(STDOUT.lock())).await
}
}
impl Write for Stdout { impl Write for Stdout {
fn poll_write( fn poll_write(
mut self: Pin<&mut Self>, mut self: Pin<&mut Self>,
@ -114,7 +174,7 @@ impl Write for Stdout {
inner.buf[..buf.len()].copy_from_slice(buf); inner.buf[..buf.len()].copy_from_slice(buf);
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(move || { *state = State::Busy(spawn_blocking(move || {
let res = std::io::Write::write(&mut inner.stdout, &inner.buf); let res = std::io::Write::write(&mut inner.stdout, &inner.buf);
inner.last_op = Some(Operation::Write(res)); inner.last_op = Some(Operation::Write(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -142,7 +202,7 @@ impl Write for Stdout {
let mut inner = opt.take().unwrap(); let mut inner = opt.take().unwrap();
// Start the operation asynchronously. // Start the operation asynchronously.
*state = State::Busy(blocking::spawn(move || { *state = State::Busy(spawn_blocking(move || {
let res = std::io::Write::flush(&mut inner.stdout); let res = std::io::Write::flush(&mut inner.stdout);
inner.last_op = Some(Operation::Flush(res)); inner.last_op = Some(Operation::Flush(res));
State::Idle(Some(inner)) State::Idle(Some(inner))
@ -179,3 +239,23 @@ cfg_windows! {
} }
} }
} }
#[cfg(feature = "unstable")]
#[cfg_attr(feature = "docs", doc(cfg(unstable)))]
impl Write for StdoutLock<'_> {
fn poll_write(
mut self: Pin<&mut Self>,
_cx: &mut Context<'_>,
buf: &[u8],
) -> Poll<io::Result<usize>> {
Poll::Ready(self.0.write(buf))
}
fn poll_flush(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(self.0.flush())
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.poll_flush(cx)
}
}

View file

@ -1,11 +1,11 @@
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::Duration; use std::time::Duration;
use std::future::Future;
use futures_timer::Delay; use futures_timer::Delay;
use pin_utils::unsafe_pinned; use pin_project_lite::pin_project;
use crate::future::Future;
use crate::io; use crate::io;
/// Awaits an I/O future or times out after a duration of time. /// Awaits an I/O future or times out after a duration of time.
@ -43,22 +43,18 @@ where
.await .await
} }
/// Future returned by the `FutureExt::timeout` method. pin_project! {
#[derive(Debug)] /// Future returned by the `FutureExt::timeout` method.
pub struct Timeout<F, T> #[derive(Debug)]
where pub struct Timeout<F, T>
F: Future<Output = io::Result<T>>, where
{ F: Future<Output = io::Result<T>>,
future: F, {
timeout: Delay, #[pin]
} future: F,
#[pin]
impl<F, T> Timeout<F, T> timeout: Delay,
where }
F: Future<Output = io::Result<T>>,
{
unsafe_pinned!(future: F);
unsafe_pinned!(timeout: Delay);
} }
impl<F, T> Future for Timeout<F, T> impl<F, T> Future for Timeout<F, T>
@ -67,14 +63,15 @@ where
{ {
type Output = io::Result<T>; type Output = io::Result<T>;
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
match self.as_mut().future().poll(cx) { let this = self.project();
match this.future.poll(cx) {
Poll::Pending => {} Poll::Pending => {}
other => return other, other => return other,
} }
if self.timeout().poll(cx).is_ready() { if this.timeout.poll(cx).is_ready() {
let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out").into()); let err = Err(io::Error::new(io::ErrorKind::TimedOut, "future timed out"));
Poll::Ready(err) Poll::Ready(err)
} else { } else {
Poll::Pending Poll::Pending

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

View file

@ -26,7 +26,7 @@ extension_trait! {
Methods other than [`poll_write`], [`poll_write_vectored`], [`poll_flush`], and Methods other than [`poll_write`], [`poll_write_vectored`], [`poll_flush`], and
[`poll_close`] do not really exist in the trait itself, but they become available when [`poll_close`] do not really exist in the trait itself, but they become available when
the prelude is imported: [`WriteExt`] from the [prelude] is imported:
``` ```
# #[allow(unused_imports)] # #[allow(unused_imports)]
@ -40,6 +40,8 @@ extension_trait! {
[`poll_write_vectored`]: #method.poll_write_vectored [`poll_write_vectored`]: #method.poll_write_vectored
[`poll_flush`]: #tymethod.poll_flush [`poll_flush`]: #tymethod.poll_flush
[`poll_close`]: #tymethod.poll_close [`poll_close`]: #tymethod.poll_close
[`WriteExt`]: ../io/prelude/trait.WriteExt.html
[prelude]: ../prelude/index.html
"#] "#]
pub trait Write { pub trait Write {
#[doc = r#" #[doc = r#"
@ -74,6 +76,11 @@ extension_trait! {
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>; fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>;
} }
#[doc = r#"
Extension methods for [`Write`].
[`Write`]: ../trait.Write.html
"#]
pub trait WriteExt: futures_io::AsyncWrite { pub trait WriteExt: futures_io::AsyncWrite {
#[doc = r#" #[doc = r#"
Writes some bytes into the byte stream. Writes some bytes into the byte stream.

View file

@ -1,6 +1,6 @@
use std::pin::Pin; use std::pin::Pin;
use std::future::Future;
use crate::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

Some files were not shown because too many files have changed in this diff Show more