Merge remote-tracking branch 'origin/master' into flat_map_fixed

master
Oleg Nosov 5 years ago
commit 924e5a3f41
No known key found for this signature in database
GPG Key ID: DE90B83800644E24

@ -29,6 +29,24 @@ jobs:
toolchain: ${{ matrix.rust }} toolchain: ${{ matrix.rust }}
override: true override: true
- name: Cache cargo registry
uses: actions/cache@v2
with:
path: ~/.cargo/registry
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-registry-${{ hashFiles('**/Cargo.toml') }}
- name: Cache cargo index
uses: actions/cache@v2
with:
path: ~/.cargo/git
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-index-${{ hashFiles('**/Cargo.toml') }}
- name: Cache cargo build
uses: actions/cache@v2
with:
path: target
key: ${{ matrix.os }}-${{ matrix.rust }}-cargo-build-target-${{ hashFiles('**/Cargo.toml') }}
- name: check - name: check
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
@ -40,6 +58,15 @@ jobs:
with: with:
command: check command: check
args: --features unstable --all --bins --examples --tests args: --features unstable --all --bins --examples --tests
- name: check wasm
uses: actions-rs/cargo@v1
with:
command: check
target: wasm32-unknown-unknown
override: true
args: --features unstable --all --bins --tests
- name: check bench - name: check bench
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
if: matrix.rust == 'nightly' if: matrix.rust == 'nightly'
@ -65,11 +92,67 @@ jobs:
command: test command: test
args: --all --features "unstable attributes" args: --all --features "unstable attributes"
- name: documentation test build__with_no_std:
name: Build with no-std
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: setup
run: |
rustup default nightly
rustup target add thumbv7m-none-eabi
- name: check no_std
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: test command: check
args: --doc --features "unstable attributes" args: --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps
check_tokio_02_feature:
name: Check tokio02 feature
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: check tokio02
uses: actions-rs/cargo@v1
with:
command: check
args: --all --features tokio02
cross:
name: Cross compile
runs-on: ubuntu-latest
strategy:
matrix:
target:
- i686-unknown-linux-gnu
- powerpc-unknown-linux-gnu
- powerpc64-unknown-linux-gnu
- mips-unknown-linux-gnu
- arm-linux-androideabi
steps:
- uses: actions/checkout@master
- name: Install nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
- name: Install cross
run: cargo install cross
- name: check
run: cross check --all --target ${{ matrix.target }}
- name: check unstable
run: cross check --all --features unstable --target ${{ matrix.target }}
- name: test
run: cross test --all --features unstable --target ${{ matrix.target }}
check_fmt_and_docs: check_fmt_and_docs:
name: Checking fmt and docs name: Checking fmt and docs
@ -98,15 +181,3 @@ jobs:
- name: Docs - name: Docs
run: cargo doc --features docs run: cargo doc --features docs
# clippy_check:
# name: Clippy check
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v1
# - name: Install rust
# run: rustup update beta && rustup default beta
# - name: Install clippy
# run: rustup component add clippy
# - name: clippy
# run: cargo clippy --all --features unstable

@ -7,6 +7,101 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview
## [Unreleased] ## [Unreleased]
# [1.6.1] - 2020-06-11
## Added
- Added `tokio02` feature flag, to allow compatability usage with tokio@0.2 ([#804](https://github.com/async-rs/async-std/pull/804)).
## Changed
- Removed unstable `stdio` lock methods, due to their unsoundness ([#807](https://github.com/async-rs/async-std/pull/807)).
## Fixed
- Fixed wrong slice index for file reading ([#802](https://github.com/async-rs/async-std/pull/802)).
- Fixed recursive calls to `block_on` ([#799](https://github.com/async-rs/async-std/pull/799)) and ([#809](https://github.com/async-rs/async-std/pull/809)).
- Remove `default` feature requirement for the `unstable` feature ([#806](https://github.com/async-rs/async-std/pull/806)).
# [1.6.0] - 2020-05-22
See `1.6.0-beta.1` and `1.6.0-beta.2`.
# [1.6.0-beta.2] - 2020-05-19
## Added
- Added an environment variable to configure the thread pool size of the runtime. ([#774](https://github.com/async-rs/async-std/pull/774))
- Implement `Clone` for `UnixStream` ([#772](https://github.com/async-rs/async-std/pull/772))
## Changed
- For `wasm`, switched underlying `Timer` implementation to [`futures-timer`](https://github.com/async-rs/futures-timer). ([#776](https://github.com/async-rs/async-std/pull/776))
## Fixed
- Use `smol::block_on` to handle drop of `File`, avoiding nested executor panic. ([#768](https://github.com/async-rs/async-std/pull/768))
# [1.6.0-beta.1] - 2020-05-07
## Added
- Added `task::spawn_local`. ([#757](https://github.com/async-rs/async-std/pull/757))
- Added out of the box support for `wasm`. ([#757](https://github.com/async-rs/async-std/pull/757))
- Added `JoinHandle::cancel` ([#757](https://github.com/async-rs/async-std/pull/757))
- Added `sync::Condvar` ([#369](https://github.com/async-rs/async-std/pull/369))
- Added `sync::Sender::try_send` and `sync::Receiver::try_recv` ([#585](https://github.com/async-rs/async-std/pull/585))
- Added `no_std` support for `task`, `future` and `stream` ([#680](https://github.com/async-rs/async-std/pull/680))
## Changed
- Switched underlying runtime to [`smol`](https://github.com/stjepang/smol/). ([#757](https://github.com/async-rs/async-std/pull/757))
- Switched implementation of `sync::Barrier` to use `sync::Condvar` like `std` does. ([#581](https://github.com/async-rs/async-std/pull/581))
## Fixed
- Allow compilation on 32 bit targets, by using `AtomicUsize` for `TaskId`. ([#756](https://github.com/async-rs/async-std/pull/756))
# [1.5.0] - 2020-02-03
[API Documentation](https://docs.rs/async-std/1.5.0/async-std)
This patch includes various quality of life improvements to async-std.
Including improved performance, stability, and the addition of various
`Clone` impls that replace the use of `Arc` in many cases.
## Added
- Added links to various ecosystem projects from the README ([#660](https://github.com/async-rs/async-std/pull/660))
- Added an example on `FromStream` for `Result<T, E>` ([#643](https://github.com/async-rs/async-std/pull/643))
- Added `stream::pending` as "unstable" ([#615](https://github.com/async-rs/async-std/pull/615))
- Added an example of `stream::timeout` to document the error flow ([#675](https://github.com/async-rs/async-std/pull/675))
- Implement `Clone` for `DirEntry` ([#682](https://github.com/async-rs/async-std/pull/682))
- Implement `Clone` for `TcpStream` ([#689](https://github.com/async-rs/async-std/pull/689))
## Changed
- Removed internal comment on `stream::Interval` ([#645](https://github.com/async-rs/async-std/pull/645))
- The "unstable" feature can now be used without requiring the "default" feature ([#647](https://github.com/async-rs/async-std/pull/647))
- Removed unnecessary trait bound on `stream::FlatMap` ([#651](https://github.com/async-rs/async-std/pull/651))
- Updated the "broadcaster" dependency used by "unstable" to `1.0.0` ([#681](https://github.com/async-rs/async-std/pull/681))
- Updated `async-task` to 1.2.1 ([#676](https://github.com/async-rs/async-std/pull/676))
- `task::block_on` now parks after a single poll, improving performance in many cases ([#684](https://github.com/async-rs/async-std/pull/684))
- Improved reading flow of the "client" part of the async-std tutorial ([#550](https://github.com/async-rs/async-std/pull/550))
- Use `take_while` instead of `scan` in `impl` of `Product`, `Sum` and `FromStream` ([#667](https://github.com/async-rs/async-std/pull/667))
- `TcpStream::connect` no longer uses a thread from the threadpool, improving performance ([#687](https://github.com/async-rs/async-std/pull/687))
## Fixed
- Fixed crate documentation typo ([#655](https://github.com/async-rs/async-std/pull/655))
- Fixed documentation for `UdpSocket::recv` ([#648](https://github.com/async-rs/async-std/pull/648))
- Fixed documentation for `UdpSocket::send` ([#671](https://github.com/async-rs/async-std/pull/671))
- Fixed typo in stream documentation ([#650](https://github.com/async-rs/async-std/pull/650))
- Fixed typo on `sync::JoinHandle` documentation ([#659](https://github.com/async-rs/async-std/pull/659))
- Removed use of `std::error::Error::description` which failed CI ([#661](https://github.com/async-rs/async-std/pull/662))
- Removed the use of rustfmt's unstable `format_code_in_doc_comments` option which failed CI ([#685](https://github.com/async-rs/async-std/pull/685))
- Fixed a code typo in the `task::sleep` example ([#688](https://github.com/async-rs/async-std/pull/688))
# [1.4.0] - 2019-12-20 # [1.4.0] - 2019-12-20
[API Documentation](https://docs.rs/async-std/1.4.0/async-std) [API Documentation](https://docs.rs/async-std/1.4.0/async-std)
@ -637,7 +732,12 @@ task::blocking(async {
- Initial beta release - Initial beta release
[Unreleased]: https://github.com/async-rs/async-std/compare/v1.3.0...HEAD [Unreleased]: https://github.com/async-rs/async-std/compare/v1.6.1...HEAD
[1.6.1]: https://github.com/async-rs/async-std/compare/v1.6.0...v1.6.1
[1.6.0]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0
[1.6.0-beta.2]: https://github.com/async-rs/async-std/compare/v1.6.0-beta.1...v1.6.0-beta.2
[1.6.0-beta.1]: https://github.com/async-rs/async-std/compare/v1.5.0...v1.6.0-beta.1
[1.5.0]: https://github.com/async-rs/async-std/compare/v1.4.0...v1.5.0
[1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0 [1.4.0]: https://github.com/async-rs/async-std/compare/v1.3.0...v1.4.0
[1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0 [1.3.0]: https://github.com/async-rs/async-std/compare/v1.2.0...v1.3.0
[1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0 [1.2.0]: https://github.com/async-rs/async-std/compare/v1.1.0...v1.2.0

@ -1,9 +1,10 @@
[package] [package]
name = "async-std" name = "async-std"
version = "1.4.0" version = "1.6.1"
authors = [ authors = [
"Stjepan Glavina <stjepang@gmail.com>", "Stjepan Glavina <stjepang@gmail.com>",
"Yoshua Wuyts <yoshuawuyts@gmail.com>", "Yoshua Wuyts <yoshuawuyts@gmail.com>",
"Friedel Ziegelmayer <me@dignifiedquire.com>",
"Contributors to async-std", "Contributors to async-std",
] ]
edition = "2018" edition = "2018"
@ -24,57 +25,72 @@ rustdoc-args = ["--cfg", "feature=\"docs\""]
default = [ default = [
"std", "std",
"async-task", "async-task",
"crossbeam-channel",
"crossbeam-deque",
"futures-timer",
"kv-log-macro", "kv-log-macro",
"log", "log",
"mio",
"mio-uds",
"num_cpus", "num_cpus",
"pin-project-lite", "pin-project-lite",
"smol",
] ]
docs = ["attributes", "unstable", "default"] docs = ["attributes", "unstable", "default"]
unstable = ["std", "broadcaster", "futures-timer"] unstable = [
"std",
"futures-timer",
]
attributes = ["async-attributes"] attributes = ["async-attributes"]
std = [ std = [
"alloc",
"crossbeam-utils", "crossbeam-utils",
"futures-core", "futures-core/std",
"futures-io", "futures-io",
"memchr", "memchr",
"once_cell", "once_cell",
"pin-project-lite",
"pin-utils", "pin-utils",
"slab", "slab",
"wasm-bindgen-futures",
"futures-channel",
] ]
alloc = [
"futures-core/alloc",
"pin-project-lite",
]
tokio02 = ["smol/tokio02"]
[dependencies] [dependencies]
async-attributes = { version = "1.1.1", optional = true } async-attributes = { version = "1.1.1", optional = true }
async-task = { version = "1.0.0", optional = true } async-task = { version = "3.0.0", optional = true }
broadcaster = { version = "0.2.6", optional = true, default-features = false, features = ["default-channels"] } crossbeam-utils = { version = "0.7.2", optional = true }
crossbeam-channel = { version = "0.4.0", optional = true } futures-core = { version = "0.3.4", optional = true, default-features = false }
crossbeam-deque = { version = "0.7.2", optional = true } futures-io = { version = "0.3.4", optional = true }
crossbeam-utils = { version = "0.7.0", optional = true }
futures-core = { version = "0.3.1", optional = true }
futures-io = { version = "0.3.1", optional = true }
futures-timer = { version = "2.0.2", optional = true }
kv-log-macro = { version = "1.0.4", optional = true } kv-log-macro = { version = "1.0.4", optional = true }
log = { version = "0.4.8", features = ["kv_unstable"], optional = true } log = { version = "0.4.8", features = ["kv_unstable"], optional = true }
memchr = { version = "2.2.1", optional = true } memchr = { version = "2.3.3", optional = true }
mio = { version = "0.6.19", optional = true } num_cpus = { version = "1.12.0", optional = true }
mio-uds = { version = "0.6.7", optional = true } once_cell = { version = "1.3.1", optional = true }
num_cpus = { version = "1.11.1", optional = true } pin-project-lite = { version = "0.1.4", optional = true }
once_cell = { version = "1.2.0", optional = true }
pin-project-lite = { version = "0.1.1", optional = true }
pin-utils = { version = "0.1.0-alpha.4", optional = true } pin-utils = { version = "0.1.0-alpha.4", optional = true }
slab = { version = "0.4.2", optional = true } slab = { version = "0.4.2", optional = true }
futures-timer = { version = "3.0.2", optional = true }
# Devdepencency, but they are not allowed to be optional :/
surf = { version = "1.0.3", optional = true }
[target.'cfg(not(target_os = "unknown"))'.dependencies]
smol = { version = "0.1.11", optional = true }
[target.'cfg(target_arch = "wasm32")'.dependencies]
futures-timer = { version = "3.0.2", optional = true, features = ["wasm-bindgen"] }
wasm-bindgen-futures = { version = "0.4.10", optional = true }
futures-channel = { version = "0.3.4", optional = true }
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.10"
[dev-dependencies] [dev-dependencies]
femme = "1.3.0" femme = "1.3.0"
rand = "0.7.2" rand = "0.7.3"
surf = "1.0.3"
tempdir = "0.3.7" tempdir = "0.3.7"
futures = "0.3.1" futures = "0.3.4"
rand_xorshift = "0.2.0"
[[test]] [[test]]
name = "stream" name = "stream"
@ -83,3 +99,8 @@ required-features = ["unstable"]
[[example]] [[example]]
name = "tcp-ipv4-and-6-echo" name = "tcp-ipv4-and-6-echo"
required-features = ["unstable"] required-features = ["unstable"]
[[example]]
name = "surf-web"
required-features = ["surf"]

@ -8,6 +8,11 @@
<br /> <br />
<div align="center"> <div align="center">
<!-- CI status -->
<a href="https://github.com/async-rs/async-std/actions">
<img src="https://github.com/async-rs/async-std/workflows/CI/badge.svg"
alt="CI Status" />
</a>
<!-- Crates version --> <!-- Crates version -->
<a href="https://crates.io/crates/async-std"> <a href="https://crates.io/crates/async-std">
<img src="https://img.shields.io/crates/v/async-std.svg?style=flat-square" <img src="https://img.shields.io/crates/v/async-std.svg?style=flat-square"
@ -134,6 +139,10 @@ documentation] on how to enable them.
* [Surf](https://crates.io/crates/surf) — Surf the web. Surf is a friendly **HTTP client** built for casual Rustaceans and veterans alike. * [Surf](https://crates.io/crates/surf) — Surf the web. Surf is a friendly **HTTP client** built for casual Rustaceans and veterans alike.
* [Xactor](https://crates.io/crates/xactor) — Xactor is a rust actors framework based on async-std.
* [async-graphql](https://crates.io/crates/async-graphql) — A GraphQL server library implemented in rust, with full support for async/await.
## License ## License
<sup> <sup>

@ -19,8 +19,9 @@
- [Clean Shutdown](./tutorial/clean_shutdown.md) - [Clean Shutdown](./tutorial/clean_shutdown.md)
- [Handling Disconnection](./tutorial/handling_disconnection.md) - [Handling Disconnection](./tutorial/handling_disconnection.md)
- [Implementing a Client](./tutorial/implementing_a_client.md) - [Implementing a Client](./tutorial/implementing_a_client.md)
- [TODO: Async Patterns](./patterns.md) - [Async Patterns](./patterns.md)
- [TODO: Collected Small Patterns](./patterns/small-patterns.md) - [TODO: Collected Small Patterns](./patterns/small-patterns.md)
- [Production-Ready Accept Loop](./patterns/accept-loop.md)
- [Security practices](./security/index.md) - [Security practices](./security/index.md)
- [Security Disclosures and Policy](./security/policy.md) - [Security Disclosures and Policy](./security/policy.md)
- [Glossary](./glossary.md) - [Glossary](./glossary.md)

@ -0,0 +1,266 @@
# Production-Ready Accept Loop
A production-ready accept loop needs the following things:
1. Handling errors
2. Limiting the number of simultanteous connections to avoid deny-of-service
(DoS) attacks
## Handling errors
There are two kinds of errors in an accept loop:
1. Per-connection errors. The system uses them to notify that there was a
connection in the queue and it's dropped by the peer. Subsequent connections
can be already queued so next connection must be accepted immediately.
2. Resource shortages. When these are encountered it doesn't make sense to
accept the next socket immediately. But the listener stays active, so you server
should try to accept socket later.
Here is the example of a per-connection error (printed in normal and debug mode):
```
Error: Connection reset by peer (os error 104)
Error: Os { code: 104, kind: ConnectionReset, message: "Connection reset by peer" }
```
And the following is the most common example of a resource shortage error:
```
Error: Too many open files (os error 24)
Error: Os { code: 24, kind: Other, message: "Too many open files" }
```
### Testing Application
To test your application for these errors try the following (this works
on unixes only).
Lower limits and start the application:
```
$ ulimit -n 100
$ cargo run --example your_app
Compiling your_app v0.1.0 (/work)
Finished dev [unoptimized + debuginfo] target(s) in 5.47s
Running `target/debug/examples/your_app`
Server is listening on: http://127.0.0.1:1234
```
Then in another console run the [`wrk`] benchmark tool:
```
$ wrk -c 1000 http://127.0.0.1:1234
Running 10s test @ http://localhost:8080/
2 threads and 1000 connections
$ telnet localhost 1234
Trying ::1...
Connected to localhost.
```
Important is to check the following things:
1. The application doesn't crash on error (but may log errors, see below)
2. It's possible to connect to the application again once load is stopped
(few seconds after `wrk`). This is what `telnet` does in example above,
make sure it prints `Connected to <hostname>`.
3. The `Too many open files` error is logged in the appropriate log. This
requires to set "maximum number of simultaneous connections" parameter (see
below) of your application to a value greater then `100` for this example.
4. Check CPU usage of the app while doing a test. It should not occupy 100%
of a single CPU core (it's unlikely that you can exhaust CPU by 1000
connections in Rust, so this means error handling is not right).
#### Testing non-HTTP applications
If it's possible, use the appropriate benchmark tool and set the appropriate
number of connections. For example `redis-benchmark` has a `-c` parameter for
that, if you implement redis protocol.
Alternatively, can still use `wrk`, just make sure that connection is not
immediately closed. If it is, put a temporary timeout before handing
the connection to the protocol handler, like this:
```rust,edition2018
# extern crate async_std;
# use std::time::Duration;
# use async_std::{
# net::{TcpListener, ToSocketAddrs},
# prelude::*,
# };
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
#async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
# let listener = TcpListener::bind(addr).await?;
# let mut incoming = listener.incoming();
while let Some(stream) = incoming.next().await {
task::spawn(async {
task::sleep(Duration::from_secs(10)).await; // 1
connection_loop(stream).await;
});
}
# Ok(())
# }
```
1. Make sure the sleep coroutine is inside the spawned task, not in the loop.
[`wrk`]: https://github.com/wg/wrk
### Handling Errors Manually
Here is how basic accept loop could look like:
```rust,edition2018
# extern crate async_std;
# use std::time::Duration;
# use async_std::{
# net::{TcpListener, ToSocketAddrs},
# prelude::*,
# };
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
let listener = TcpListener::bind(addr).await?;
let mut incoming = listener.incoming();
while let Some(result) = incoming.next().await {
let stream = match stream {
Err(ref e) if is_connection_error(e) => continue, // 1
Err(e) => {
eprintln!("Error: {}. Pausing for 500ms."); // 3
task::sleep(Duration::from_millis(500)).await; // 2
continue;
}
Ok(s) => s,
};
// body
}
Ok(())
}
```
1. Ignore per-connection errors.
2. Sleep and continue on resource shortage.
3. It's important to log the message, because these errors commonly mean the
misconfiguration of the system and are helpful for operations people running
the application.
Be sure to [test your application](#testing-application).
### External Crates
The crate [`async-listen`] has a helper to achieve this task:
```rust,edition2018
# extern crate async_std;
# extern crate async_listen;
# use std::time::Duration;
# use async_std::{
# net::{TcpListener, ToSocketAddrs},
# prelude::*,
# };
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
use async_listen::{ListenExt, error_hint};
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
let listener = TcpListener::bind(addr).await?;
let mut incoming = listener
.incoming()
.log_warnings(log_accept_error) // 1
.handle_errors(Duration::from_millis(500));
while let Some(socket) = incoming.next().await { // 2
// body
}
Ok(())
}
fn log_accept_error(e: &io::Error) {
eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e)) // 3
}
```
1. Logs resource shortages (`async-listen` calls them warnings). If you use
`log` crate or any other in your app this should go to the log.
2. Stream yields sockets without `Result` wrapper after `handle_errors` because
all errors are already handled.
3. Together with the error we print a hint, which explains some errors for end
users. For example, it recommends increasing open file limit and gives
a link.
[`async-listen`]: https://crates.io/crates/async-listen/
Be sure to [test your application](#testing-application).
## Connections Limit
Even if you've applied everything described in
[Handling Errors](#handling-errors) section, there is still a problem.
Let's imagine you have a server that needs to open a file to process
client request. At some point, you might encounter the following situation:
1. There are as many client connection as max file descriptors allowed for
the application.
2. Listener gets `Too many open files` error so it sleeps.
3. Some client sends a request via the previously open connection.
4. Opening a file to serve request fails, because of the same
`Too many open files` error, until some other client drops a connection.
There are many more possible situations, this is just a small illustation that
limiting number of connections is very useful. Generally, it's one of the ways
to control resources used by a server and avoiding some kinds of deny of
service (DoS) attacks.
### `async-listen` crate
Limiting maximum number of simultaneous connections with [`async-listen`]
looks like the following:
```rust,edition2018
# extern crate async_std;
# extern crate async_listen;
# use std::time::Duration;
# use async_std::{
# net::{TcpListener, TcpStream, ToSocketAddrs},
# prelude::*,
# };
#
# type Result<T> = std::result::Result<T, Box<dyn std::error::Error + Send + Sync>>;
#
use async_listen::{ListenExt, Token, error_hint};
async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> {
let listener = TcpListener::bind(addr).await?;
let mut incoming = listener
.incoming()
.log_warnings(log_accept_error)
.handle_errors(Duration::from_millis(500)) // 1
.backpressure(100);
while let Some((token, socket)) = incoming.next().await { // 2
task::spawn(async move {
connection_loop(&token, stream).await; // 3
});
}
Ok(())
}
async fn connection_loop(_token: &Token, stream: TcpStream) { // 4
// ...
}
# fn log_accept_error(e: &io::Error) {
# eprintln!("Error: {}. Listener paused for 0.5s. {}", e, error_hint(e));
# }
```
1. We need to handle errors first, because [`backpressure`] helper expects
stream of `TcpStream` rather than `Result`.
2. The token yielded by a new stream is what is counted by backpressure helper.
I.e. if you drop a token, new connection can be established.
3. We give the connection loop a reference to token to bind token's lifetime to
the lifetime of the connection.
4. The token itsellf in the function can be ignored, hence `_token`
[`backpressure`]: https://docs.rs/async-listen/0.1.2/async_listen/trait.ListenExt.html#method.backpressure
Be sure to [test this behavior](#testing-application).

@ -1,18 +1,16 @@
## Implementing a client ## Implementing a client
Let's now implement the client for the chat. Since the protocol is line-based, implementing a client for the chat is straightforward:
Because the protocol is line-based, the implementation is pretty straightforward:
* Lines read from stdin should be sent over the socket. * Lines read from stdin should be sent over the socket.
* Lines read from the socket should be echoed to stdout. * Lines read from the socket should be echoed to stdout.
Unlike the server, the client needs only limited concurrency, as it interacts with only a single user. Although async does not significantly affect client performance (as unlike the server, the client interacts solely with one user and only needs limited concurrency), async is still useful for managing concurrency!
For this reason, async doesn't bring a lot of performance benefits in this case.
The client has to read from stdin and the socket *simultaneously*.
Programming this with threads is cumbersome, especially when implementing a clean shutdown.
With async, the `select!` macro is all that is needed.
However, async is still useful for managing concurrency!
Specifically, the client should *simultaneously* read from stdin and from the socket.
Programming this with threads is cumbersome, especially when implementing clean shutdown.
With async, we can just use the `select!` macro.
```rust,edition2018 ```rust,edition2018
# extern crate async_std; # extern crate async_std;

@ -111,7 +111,7 @@ We can "fix" it by waiting for the task to be joined, like this:
# #
# async move |stream| { # async move |stream| {
let handle = task::spawn(connection_loop(stream)); let handle = task::spawn(connection_loop(stream));
handle.await handle.await?
# }; # };
``` ```

@ -14,8 +14,9 @@ use async_std::task;
async fn process(stream: TcpStream) -> io::Result<()> { async fn process(stream: TcpStream) -> io::Result<()> {
println!("Accepted from: {}", stream.peer_addr()?); println!("Accepted from: {}", stream.peer_addr()?);
let (reader, writer) = &mut (&stream, &stream); let mut reader = stream.clone();
io::copy(reader, writer).await?; let mut writer = stream;
io::copy(&mut reader, &mut writer).await?;
Ok(()) Ok(())
} }

@ -15,8 +15,9 @@ use async_std::task;
async fn process(stream: TcpStream) -> io::Result<()> { async fn process(stream: TcpStream) -> io::Result<()> {
println!("Accepted from: {}", stream.peer_addr()?); println!("Accepted from: {}", stream.peer_addr()?);
let (reader, writer) = &mut (&stream, &stream); let mut reader = stream.clone();
io::copy(reader, writer).await?; let mut writer = stream;
io::copy(&mut reader, &mut writer).await?;
Ok(()) Ok(())
} }

@ -1,2 +1 @@
version = "Two" version = "Two"
format_code_in_doc_comments = true

@ -158,6 +158,12 @@ impl fmt::Debug for DirEntry {
} }
} }
impl Clone for DirEntry {
fn clone(&self) -> Self {
DirEntry(self.0.clone())
}
}
cfg_unix! { cfg_unix! {
use crate::os::unix::fs::DirEntryExt; use crate::os::unix::fs::DirEntryExt;

@ -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, spawn_blocking, Context, Poll, Waker}; use crate::task::{spawn_blocking, Context, Poll, Waker};
use crate::utils::Context as _; use crate::utils::Context as _;
/// An open file on the filesystem. /// An open file on the filesystem.
@ -315,7 +315,7 @@ impl Drop for File {
// non-blocking fashion, but our only other option here is losing data remaining in the // non-blocking fashion, but our only other option here is losing data remaining in the
// write cache. Good task schedulers should be resilient to occasional blocking hiccups in // write cache. Good task schedulers should be resilient to occasional blocking hiccups in
// file destructors so we don't expect this to be a common problem in practice. // file destructors so we don't expect this to be a common problem in practice.
let _ = task::block_on(self.flush()); let _ = smol::block_on(self.flush());
} }
} }
@ -673,7 +673,7 @@ impl LockGuard<State> {
if available > 0 || self.cache.is_empty() { if available > 0 || self.cache.is_empty() {
// Copy data from the cache into the buffer. // Copy data from the cache into the buffer.
let n = cmp::min(available, buf.len()); let n = cmp::min(available, buf.len());
buf[..n].copy_from_slice(&self.cache[start..n]); buf[..n].copy_from_slice(&self.cache[start..(start + n)]);
// Move the read cursor forward. // Move the read cursor forward.
self.mode = Mode::Reading(start + n); self.mode = Mode::Reading(start + n);
@ -867,3 +867,15 @@ impl LockGuard<State> {
Poll::Ready(Ok(())) Poll::Ready(Ok(()))
} }
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn async_file_drop() {
crate::task::block_on(async move {
File::open(file!()).await.unwrap();
});
}
}

@ -2,10 +2,10 @@ use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::time::Duration; use std::time::Duration;
use futures_timer::Delay;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use crate::utils::Timer;
pin_project! { pin_project! {
#[doc(hidden)] #[doc(hidden)]
@ -14,13 +14,13 @@ pin_project! {
#[pin] #[pin]
future: F, future: F,
#[pin] #[pin]
delay: Delay, delay: Timer,
} }
} }
impl<F> DelayFuture<F> { impl<F> DelayFuture<F> {
pub fn new(future: F, dur: Duration) -> DelayFuture<F> { pub fn new(future: F, dur: Duration) -> DelayFuture<F> {
let delay = Delay::new(dur); let delay = Timer::after(dur);
DelayFuture { future, delay } DelayFuture { future, delay }
} }

@ -21,8 +21,8 @@ cfg_unstable_default! {
} }
extension_trait! { extension_trait! {
use std::pin::Pin; use core::pin::Pin;
use std::ops::{Deref, DerefMut}; use core::ops::{Deref, DerefMut};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -136,7 +136,7 @@ extension_trait! {
[`Future`]: ../future/trait.Future.html [`Future`]: ../future/trait.Future.html
"#] "#]
pub trait FutureExt: std::future::Future { pub trait FutureExt: core::future::Future {
/// Returns a Future that delays execution for a specified time. /// Returns a Future that delays execution for a specified time.
/// ///
/// # Examples /// # Examples

@ -46,21 +46,26 @@
//! [`Future::race`]: trait.Future.html#method.race //! [`Future::race`]: trait.Future.html#method.race
//! [`Future::try_race`]: trait.Future.html#method.try_race //! [`Future::try_race`]: trait.Future.html#method.try_race
pub use future::Future; cfg_alloc! {
pub use pending::pending; pub use future::Future;
pub use poll_fn::poll_fn; pub(crate) mod future;
pub use ready::ready; }
pub(crate) mod future; cfg_std! {
mod pending; pub use pending::pending;
mod poll_fn; pub use poll_fn::poll_fn;
mod ready; pub use ready::ready;
cfg_default! { mod pending;
pub use timeout::{timeout, TimeoutError}; mod poll_fn;
mod timeout; mod ready;
} }
#[cfg(any(feature = "unstable", feature = "default"))]
pub use timeout::{timeout, TimeoutError};
#[cfg(any(feature = "unstable", feature = "default"))]
mod timeout;
cfg_unstable! { cfg_unstable! {
pub use into_future::IntoFuture; pub use into_future::IntoFuture;
pub(crate) use maybe_done::MaybeDone; pub(crate) use maybe_done::MaybeDone;

@ -1,13 +1,13 @@
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::future::Future;
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 pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use crate::utils::Timer;
/// Awaits a future or times out after a duration of time. /// Awaits a future or times out after a duration of time.
/// ///
@ -33,11 +33,7 @@ pub async fn timeout<F, T>(dur: Duration, f: F) -> Result<T, TimeoutError>
where where
F: Future<Output = T>, F: Future<Output = T>,
{ {
let f = TimeoutFuture { TimeoutFuture::new(f, dur).await
future: f,
delay: Delay::new(dur),
};
f.await
} }
pin_project! { pin_project! {
@ -46,14 +42,17 @@ pin_project! {
#[pin] #[pin]
future: F, future: F,
#[pin] #[pin]
delay: Delay, delay: Timer,
} }
} }
impl<F> TimeoutFuture<F> { impl<F> TimeoutFuture<F> {
#[allow(dead_code)] #[allow(dead_code)]
pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture<F> { pub(super) fn new(future: F, dur: Duration) -> TimeoutFuture<F> {
TimeoutFuture { future: future, delay: Delay::new(dur) } TimeoutFuture {
future,
delay: Timer::after(dur),
}
} }
} }

@ -29,7 +29,7 @@ extension_trait! {
``` ```
# #[allow(unused_imports)] # #[allow(unused_imports)]
use async_std::prelude::*; use async_std::io::prelude::*;
``` ```
[`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html

@ -275,7 +275,7 @@ cfg_std! {
#[doc(inline)] #[doc(inline)]
pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom};
pub use buf_read::{BufRead, Lines}; pub use buf_read::{BufRead, Lines, Split};
pub use buf_reader::BufReader; pub use buf_reader::BufReader;
pub use buf_writer::{BufWriter, IntoInnerError}; pub use buf_writer::{BufWriter, IntoInnerError};
pub use copy::copy; pub use copy::copy;
@ -307,22 +307,24 @@ cfg_std! {
cfg_default! { cfg_default! {
// For use in the print macros. // For use in the print macros.
#[doc(hidden)] #[doc(hidden)]
#[cfg(not(target_os = "unknown"))]
pub use stdio::{_eprint, _print}; pub use stdio::{_eprint, _print};
#[cfg(not(target_os = "unknown"))]
pub use stderr::{stderr, Stderr}; pub use stderr::{stderr, Stderr};
#[cfg(not(target_os = "unknown"))]
pub use stdin::{stdin, Stdin}; pub use stdin::{stdin, Stdin};
#[cfg(not(target_os = "unknown"))]
pub use stdout::{stdout, Stdout}; pub use stdout::{stdout, Stdout};
pub use timeout::timeout; pub use timeout::timeout;
mod timeout; mod timeout;
#[cfg(not(target_os = "unknown"))]
mod stderr; mod stderr;
#[cfg(not(target_os = "unknown"))]
mod stdin; mod stdin;
#[cfg(not(target_os = "unknown"))]
mod stdio; mod stdio;
#[cfg(not(target_os = "unknown"))]
mod stdout; mod stdout;
} }
cfg_unstable_default! {
pub use stderr::StderrLock;
pub use stdin::StdinLock;
pub use stdout::StdoutLock;
}

@ -17,9 +17,9 @@ use std::mem;
use crate::io::IoSliceMut; use crate::io::IoSliceMut;
pub use take::Take;
pub use bytes::Bytes; pub use bytes::Bytes;
pub use chain::Chain; pub use chain::Chain;
pub use take::Take;
extension_trait! { extension_trait! {
use std::pin::Pin; use std::pin::Pin;
@ -477,13 +477,13 @@ unsafe fn initialize<R: futures_io::AsyncRead>(_reader: &R, buf: &mut [u8]) {
std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len()) std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len())
} }
#[cfg(test)] #[cfg(all(test, not(target_os = "unknown")))]
mod tests { mod tests {
use crate::io; use crate::io;
use crate::prelude::*; use crate::prelude::*;
#[test] #[test]
fn test_read_by_ref() -> io::Result<()> { fn test_read_by_ref() {
crate::task::block_on(async { crate::task::block_on(async {
let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]); let mut f = io::Cursor::new(vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8]);
let mut buffer = Vec::new(); let mut buffer = Vec::new();
@ -493,14 +493,13 @@ mod tests {
let reference = f.by_ref(); let reference = f.by_ref();
// read at most 5 bytes // read at most 5 bytes
assert_eq!(reference.take(5).read_to_end(&mut buffer).await?, 5); assert_eq!(reference.take(5).read_to_end(&mut buffer).await.unwrap(), 5);
assert_eq!(&buffer, &[0, 1, 2, 3, 4]) assert_eq!(&buffer, &[0, 1, 2, 3, 4])
} // drop our &mut reference so we can use f again } // drop our &mut reference so we can use f again
// original file still usable, read the rest // original file still usable, read the rest
assert_eq!(f.read_to_end(&mut other_buffer).await?, 4); assert_eq!(f.read_to_end(&mut other_buffer).await.unwrap(), 4);
assert_eq!(&other_buffer, &[5, 6, 7, 8]); assert_eq!(&other_buffer, &[5, 6, 7, 8]);
Ok(()) });
})
} }
} }

@ -218,7 +218,7 @@ impl<T: BufRead> BufRead for Take<T> {
} }
} }
#[cfg(test)] #[cfg(all(test, not(target_os = "unknown")))]
mod tests { mod tests {
use crate::io; use crate::io;
use crate::prelude::*; use crate::prelude::*;

@ -5,11 +5,6 @@ use std::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{spawn_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.
/// ///
/// This function is an async version of [`std::io::stderr`]. /// This function is an async version of [`std::io::stderr`].
@ -58,22 +53,6 @@ pub fn stderr() -> Stderr {
#[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.
@ -108,35 +87,6 @@ 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>,
@ -239,23 +189,3 @@ 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)
}
}

@ -7,11 +7,6 @@ use crate::io::{self, Read};
use crate::task::{spawn_blocking, Context, JoinHandle, Poll}; use crate::task::{spawn_blocking, Context, JoinHandle, Poll};
use crate::utils::Context as _; use crate::utils::Context as _;
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.
/// ///
/// This function is an async version of [`std::io::stdin`]. /// This function is an async version of [`std::io::stdin`].
@ -61,21 +56,6 @@ pub fn stdin() -> Stdin {
#[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.
@ -165,35 +145,6 @@ impl Stdin {
.await .await
.context(|| String::from("could not read line on stdin")) .context(|| String::from("could not read line on stdin"))
} }
/// 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 {
@ -265,15 +216,3 @@ 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))
}
}

@ -5,11 +5,6 @@ use std::future::Future;
use crate::io::{self, Write}; use crate::io::{self, Write};
use crate::task::{spawn_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.
/// ///
/// This function is an async version of [`std::io::stdout`]. /// This function is an async version of [`std::io::stdout`].
@ -58,22 +53,6 @@ pub fn stdout() -> Stdout {
#[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.
@ -108,35 +87,6 @@ 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>,
@ -239,23 +189,3 @@ 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)
}
}

@ -1,12 +1,12 @@
use std::future::Future;
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 pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::io; use crate::io;
use crate::utils::Timer;
/// 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.
/// ///
@ -37,7 +37,7 @@ where
F: Future<Output = io::Result<T>>, F: Future<Output = io::Result<T>>,
{ {
Timeout { Timeout {
timeout: Delay::new(dur), timeout: Timer::after(dur),
future: f, future: f,
} }
.await .await
@ -53,7 +53,7 @@ pin_project! {
#[pin] #[pin]
future: F, future: F,
#[pin] #[pin]
timeout: Delay, timeout: Timer,
} }
} }

@ -138,7 +138,8 @@
//! //!
//! Call an async function from the main function: //! Call an async function from the main function:
//! //!
//! ``` #![cfg_attr(feature = "attributes", doc = "```")]
#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
//! async fn say_hello() { //! async fn say_hello() {
//! println!("Hello, world!"); //! println!("Hello, world!");
//! } //! }
@ -151,7 +152,8 @@
//! //!
//! Await two futures concurrently, and return a tuple of their output: //! Await two futures concurrently, and return a tuple of their output:
//! //!
//! ``` #![cfg_attr(feature = "attributes", doc = "```")]
#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
//! use async_std::prelude::*; //! use async_std::prelude::*;
//! //!
//! #[async_std::main] //! #[async_std::main]
@ -164,7 +166,8 @@
//! //!
//! Create a UDP server that echoes back each received message to the sender: //! Create a UDP server that echoes back each received message to the sender:
//! //!
//! ```no_run #![cfg_attr(feature = "attributes", doc = "```no_run")]
#![cfg_attr(not(feature = "attributes"), doc = "```ignore")]
//! use async_std::net::UdpSocket; //! use async_std::net::UdpSocket;
//! //!
//! #[async_std::main] //! #[async_std::main]
@ -194,7 +197,7 @@
//! //!
//! ```toml //! ```toml
//! [dependencies.async-std] //! [dependencies.async-std]
//! version = "1.0.0" //! version = "1.6.1"
//! features = ["unstable"] //! features = ["unstable"]
//! ``` //! ```
//! //!
@ -207,20 +210,55 @@
//! //!
//! ```toml //! ```toml
//! [dependencies.async-std] //! [dependencies.async-std]
//! version = "1.0.0" //! version = "1.6.1"
//! features = ["attributes"] //! features = ["attributes"]
//! ``` //! ```
//! //!
//! Compatibility with the `tokio` runtime is possible using the `tokio02`
//! Cargo feature:
//!
//! ```toml
//! [dependencies.async-std]
//! version = "1.6.1"
//! features = ["tokio02"]
//! ```
//!
//! Additionally it's possible to only use the core traits and combinators by //! Additionally it's possible to only use the core traits and combinators by
//! only enabling the `std` Cargo feature: //! only enabling the `std` Cargo feature:
//! //!
//! ```toml //! ```toml
//! [dependencies.async-std] //! [dependencies.async-std]
//! version = "1.0.0" //! version = "1.6.1"
//! default-features = false //! default-features = false
//! features = ["std"] //! features = ["std"]
//! ``` //! ```
//!
//! And to use async-std on `no_std` targets that only support `alloc` only
//! enable the `alloc` Cargo feature:
//!
//! ```toml
//! [dependencies.async-std]
//! version = "1.6.1"
//! default-features = false
//! features = ["alloc"]
//! ```
//!
//! # Runtime configuration
//!
//! Several environment variables are available to tune the async-std
//! runtime:
//!
//! * `ASYNC_STD_THREAD_COUNT`: The number of threads that the
//! async-std runtime will start. By default, this is one per logical
//! cpu as reported by the [num_cpus](num_cpus) crate, which may be
//! different than the number of physical cpus. Async-std _will panic_
//! if this is set to any value other than a positive integer.
//! * `ASYNC_STD_THREAD_NAME`: The name that async-std's runtime
//! threads report to the operating system. The default value is
//! `"async-std/runtime"`.
//!
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "docs", feature(doc_cfg))] #![cfg_attr(feature = "docs", feature(doc_cfg))]
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
#![allow(clippy::mutex_atomic, clippy::module_inception)] #![allow(clippy::mutex_atomic, clippy::module_inception)]
@ -229,6 +267,8 @@
#![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")]
#![recursion_limit = "2048"] #![recursion_limit = "2048"]
extern crate alloc;
#[macro_use] #[macro_use]
mod utils; mod utils;
@ -240,24 +280,31 @@ pub use async_attributes::{main, test};
#[cfg(feature = "std")] #[cfg(feature = "std")]
mod macros; mod macros;
cfg_std! { cfg_alloc! {
pub mod task;
pub mod future; pub mod future;
pub mod stream;
}
cfg_std! {
pub mod io; pub mod io;
pub mod os; pub mod os;
pub mod prelude; pub mod prelude;
pub mod stream;
pub mod sync; pub mod sync;
pub mod task;
} }
cfg_default! { cfg_default! {
#[cfg(not(target_os = "unknown"))]
pub mod fs; pub mod fs;
pub mod path; pub mod path;
pub mod net; pub mod net;
#[cfg(not(target_os = "unknown"))]
pub(crate) mod rt;
} }
cfg_unstable! { cfg_unstable! {
pub mod pin; pub mod pin;
#[cfg(not(target_os = "unknown"))]
pub mod process; pub mod process;
mod unit; mod unit;

@ -1,315 +0,0 @@
use std::fmt;
use std::sync::{Arc, Mutex};
use mio::{self, Evented};
use once_cell::sync::Lazy;
use slab::Slab;
use crate::io;
use crate::task::{Context, Poll, Waker};
use crate::utils::abort_on_panic;
/// Data associated with a registered I/O handle.
#[derive(Debug)]
struct Entry {
/// A unique identifier.
token: mio::Token,
/// Tasks that are blocked on reading from this I/O handle.
readers: Mutex<Vec<Waker>>,
/// Thasks that are blocked on writing to this I/O handle.
writers: Mutex<Vec<Waker>>,
}
/// The state of a networking driver.
struct Reactor {
/// A mio instance that polls for new events.
poller: mio::Poll,
/// A collection of registered I/O handles.
entries: Mutex<Slab<Arc<Entry>>>,
/// Dummy I/O handle that is only used to wake up the polling thread.
notify_reg: (mio::Registration, mio::SetReadiness),
/// An identifier for the notification handle.
notify_token: mio::Token,
}
impl Reactor {
/// Creates a new reactor for polling I/O events.
fn new() -> io::Result<Reactor> {
let poller = mio::Poll::new()?;
let notify_reg = mio::Registration::new2();
let mut reactor = Reactor {
poller,
entries: Mutex::new(Slab::new()),
notify_reg,
notify_token: mio::Token(0),
};
// Register a dummy I/O handle for waking up the polling thread.
let entry = reactor.register(&reactor.notify_reg.0)?;
reactor.notify_token = entry.token;
Ok(reactor)
}
/// Registers an I/O event source and returns its associated entry.
fn register(&self, source: &dyn Evented) -> io::Result<Arc<Entry>> {
let mut entries = self.entries.lock().unwrap();
// Reserve a vacant spot in the slab and use its key as the token value.
let vacant = entries.vacant_entry();
let token = mio::Token(vacant.key());
// Allocate an entry and insert it into the slab.
let entry = Arc::new(Entry {
token,
readers: Mutex::new(Vec::new()),
writers: Mutex::new(Vec::new()),
});
vacant.insert(entry.clone());
// Register the I/O event source in the poller.
let interest = mio::Ready::all();
let opts = mio::PollOpt::edge();
self.poller.register(source, token, interest, opts)?;
Ok(entry)
}
/// Deregisters an I/O event source associated with an entry.
fn deregister(&self, source: &dyn Evented, entry: &Entry) -> io::Result<()> {
// Deregister the I/O object from the mio instance.
self.poller.deregister(source)?;
// Remove the entry associated with the I/O object.
self.entries.lock().unwrap().remove(entry.token.0);
Ok(())
}
// fn notify(&self) {
// self.notify_reg
// .1
// .set_readiness(mio::Ready::readable())
// .unwrap();
// }
}
/// The state of the global networking driver.
static REACTOR: Lazy<Reactor> = Lazy::new(|| {
// Spawn a thread that waits on the poller for new events and wakes up tasks blocked on I/O
// handles.
std::thread::Builder::new()
.name("async-std/net".to_string())
.spawn(move || {
// If the driver thread panics, there's not much we can do. It is not a
// recoverable error and there is no place to propagate it into so we just abort.
abort_on_panic(|| {
main_loop().expect("async networking thread has panicked");
})
})
.expect("cannot start a thread driving blocking tasks");
Reactor::new().expect("cannot initialize reactor")
});
/// Waits on the poller for new events and wakes up tasks blocked on I/O handles.
fn main_loop() -> io::Result<()> {
let reactor = &REACTOR;
let mut events = mio::Events::with_capacity(1000);
loop {
// Block on the poller until at least one new event comes in.
reactor.poller.poll(&mut events, None)?;
// Lock the entire entry table while we're processing new events.
let entries = reactor.entries.lock().unwrap();
for event in events.iter() {
let token = event.token();
if token == reactor.notify_token {
// If this is the notification token, we just need the notification state.
reactor.notify_reg.1.set_readiness(mio::Ready::empty())?;
} else {
// Otherwise, look for the entry associated with this token.
if let Some(entry) = entries.get(token.0) {
// Set the readiness flags from this I/O event.
let readiness = event.readiness();
// Wake up reader tasks blocked on this I/O handle.
if !(readiness & reader_interests()).is_empty() {
for w in entry.readers.lock().unwrap().drain(..) {
w.wake();
}
}
// Wake up writer tasks blocked on this I/O handle.
if !(readiness & writer_interests()).is_empty() {
for w in entry.writers.lock().unwrap().drain(..) {
w.wake();
}
}
}
}
}
}
}
/// An I/O handle powered by the networking driver.
///
/// This handle wraps an I/O event source and exposes a "futurized" interface on top of it,
/// implementing traits `AsyncRead` and `AsyncWrite`.
pub struct Watcher<T: Evented> {
/// Data associated with the I/O handle.
entry: Arc<Entry>,
/// The I/O event source.
source: Option<T>,
}
impl<T: Evented> Watcher<T> {
/// Creates a new I/O handle.
///
/// The provided I/O event source will be kept registered inside the reactor's poller for the
/// lifetime of the returned I/O handle.
pub fn new(source: T) -> Watcher<T> {
Watcher {
entry: REACTOR
.register(&source)
.expect("cannot register an I/O event source"),
source: Some(source),
}
}
/// Returns a reference to the inner I/O event source.
pub fn get_ref(&self) -> &T {
self.source.as_ref().unwrap()
}
/// Polls the inner I/O source for a non-blocking read operation.
///
/// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task
/// will be registered for wakeup when the I/O source becomes readable.
pub fn poll_read_with<'a, F, R>(&'a self, cx: &mut Context<'_>, mut f: F) -> Poll<io::Result<R>>
where
F: FnMut(&'a T) -> io::Result<R>,
{
// If the operation isn't blocked, return its result.
match f(self.source.as_ref().unwrap()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
// Lock the waker list.
let mut list = self.entry.readers.lock().unwrap();
// Try running the operation again.
match f(self.source.as_ref().unwrap()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
// Register the task if it isn't registered already.
if list.iter().all(|w| !w.will_wake(cx.waker())) {
list.push(cx.waker().clone());
}
Poll::Pending
}
/// Polls the inner I/O source for a non-blocking write operation.
///
/// If the operation returns an error of the `io::ErrorKind::WouldBlock` kind, the current task
/// will be registered for wakeup when the I/O source becomes writable.
pub fn poll_write_with<'a, F, R>(
&'a self,
cx: &mut Context<'_>,
mut f: F,
) -> Poll<io::Result<R>>
where
F: FnMut(&'a T) -> io::Result<R>,
{
// If the operation isn't blocked, return its result.
match f(self.source.as_ref().unwrap()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
// Lock the waker list.
let mut list = self.entry.writers.lock().unwrap();
// Try running the operation again.
match f(self.source.as_ref().unwrap()) {
Err(err) if err.kind() == io::ErrorKind::WouldBlock => {}
res => return Poll::Ready(res),
}
// Register the task if it isn't registered already.
if list.iter().all(|w| !w.will_wake(cx.waker())) {
list.push(cx.waker().clone());
}
Poll::Pending
}
/// Deregisters and returns the inner I/O source.
///
/// This method is typically used to convert `Watcher`s to raw file descriptors/handles.
#[allow(dead_code)]
pub fn into_inner(mut self) -> T {
let source = self.source.take().unwrap();
REACTOR
.deregister(&source, &self.entry)
.expect("cannot deregister I/O event source");
source
}
}
impl<T: Evented> Drop for Watcher<T> {
fn drop(&mut self) {
if let Some(ref source) = self.source {
REACTOR
.deregister(source, &self.entry)
.expect("cannot deregister I/O event source");
}
}
}
impl<T: Evented + fmt::Debug> fmt::Debug for Watcher<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Watcher")
.field("entry", &self.entry)
.field("source", &self.source)
.finish()
}
}
/// Returns a mask containing flags that interest tasks reading from I/O handles.
#[inline]
fn reader_interests() -> mio::Ready {
mio::Ready::all() - mio::Ready::writable()
}
/// Returns a mask containing flags that interest tasks writing into I/O handles.
#[inline]
fn writer_interests() -> mio::Ready {
mio::Ready::writable() | hup()
}
/// Returns a flag containing the hangup status.
#[inline]
fn hup() -> mio::Ready {
#[cfg(unix)]
let ready = mio::unix::UnixReady::hup().into();
#[cfg(not(unix))]
let ready = mio::Ready::empty();
ready
}

@ -61,11 +61,16 @@ pub use std::net::Shutdown;
pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; pub use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; pub use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6};
#[cfg(not(target_os = "unknown"))]
pub use addr::ToSocketAddrs; pub use addr::ToSocketAddrs;
#[cfg(not(target_os = "unknown"))]
pub use tcp::{Incoming, TcpListener, TcpStream}; pub use tcp::{Incoming, TcpListener, TcpStream};
#[cfg(not(target_os = "unknown"))]
pub use udp::UdpSocket; pub use udp::UdpSocket;
#[cfg(not(target_os = "unknown"))]
mod addr; mod addr;
pub(crate) mod driver; #[cfg(not(target_os = "unknown"))]
mod tcp; mod tcp;
#[cfg(not(target_os = "unknown"))]
mod udp; mod udp;

@ -2,11 +2,12 @@ use std::future::Future;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use crate::future; use smol::Async;
use crate::io; use crate::io;
use crate::net::driver::Watcher;
use crate::net::{TcpStream, ToSocketAddrs}; use crate::net::{TcpStream, ToSocketAddrs};
use crate::stream::Stream; use crate::stream::Stream;
use crate::sync::Arc;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
/// A TCP socket server, listening for connections. /// A TCP socket server, listening for connections.
@ -48,7 +49,7 @@ use crate::task::{Context, Poll};
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct TcpListener { pub struct TcpListener {
watcher: Watcher<mio::net::TcpListener>, watcher: Async<std::net::TcpListener>,
} }
impl TcpListener { impl TcpListener {
@ -75,16 +76,12 @@ impl TcpListener {
/// [`local_addr`]: #method.local_addr /// [`local_addr`]: #method.local_addr
pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpListener> { pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpListener> {
let mut last_err = None; let mut last_err = None;
let addrs = addrs let addrs = addrs.to_socket_addrs().await?;
.to_socket_addrs()
.await?;
for addr in addrs { for addr in addrs {
match mio::net::TcpListener::bind(&addr) { match Async::<std::net::TcpListener>::bind(&addr) {
Ok(mio_listener) => { Ok(listener) => {
return Ok(TcpListener { return Ok(TcpListener { watcher: listener });
watcher: Watcher::new(mio_listener),
});
} }
Err(err) => last_err = Some(err), Err(err) => last_err = Some(err),
} }
@ -115,13 +112,9 @@ impl TcpListener {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> {
let (io, addr) = let (stream, addr) = self.watcher.accept().await?;
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.accept_std()))
.await?;
let mio_stream = mio::net::TcpStream::from_stream(io)?;
let stream = TcpStream { let stream = TcpStream {
watcher: Watcher::new(mio_stream), watcher: Arc::new(stream),
}; };
Ok((stream, addr)) Ok((stream, addr))
} }
@ -207,9 +200,8 @@ impl<'a> Stream for Incoming<'a> {
impl From<std::net::TcpListener> for TcpListener { impl From<std::net::TcpListener> for TcpListener {
/// Converts a `std::net::TcpListener` into its asynchronous equivalent. /// Converts a `std::net::TcpListener` into its asynchronous equivalent.
fn from(listener: std::net::TcpListener) -> TcpListener { fn from(listener: std::net::TcpListener) -> TcpListener {
let mio_listener = mio::net::TcpListener::from_std(listener).unwrap();
TcpListener { TcpListener {
watcher: Watcher::new(mio_listener), watcher: Async::new(listener).expect("TcpListener is known to be good"),
} }
} }
} }
@ -231,29 +223,31 @@ cfg_unix! {
impl IntoRawFd for TcpListener { impl IntoRawFd for TcpListener {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }
} }
cfg_windows! { cfg_windows! {
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::os::windows::io::{
// AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket,
// impl AsRawSocket for TcpListener { };
// fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket impl AsRawSocket for TcpListener {
// } fn as_raw_socket(&self) -> RawSocket {
// } self.watcher.as_raw_socket()
// }
// impl FromRawSocket for TcpListener { }
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
// net::TcpListener::from_raw_socket(handle).try_into().unwrap() impl FromRawSocket for TcpListener {
// } unsafe fn from_raw_socket(handle: RawSocket) -> TcpListener {
// } std::net::TcpListener::from_raw_socket(handle).into()
// }
// impl IntoRawSocket for TcpListener { }
// fn into_raw_socket(self) -> RawSocket {
// self.raw_socket impl IntoRawSocket for TcpListener {
// } fn into_raw_socket(self) -> RawSocket {
// } self.watcher.into_raw_socket()
}
}
} }

@ -1,13 +1,13 @@
use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; use std::io::{IoSlice, IoSliceMut};
use std::net::SocketAddr; use std::net::SocketAddr;
use std::pin::Pin; use std::pin::Pin;
use crate::future; use smol::Async;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::net::driver::Watcher;
use crate::net::ToSocketAddrs; use crate::net::ToSocketAddrs;
use crate::task::{spawn_blocking, Context, Poll}; use crate::sync::Arc;
use crate::utils::Context as _; use crate::task::{Context, Poll};
/// A TCP stream between a local and a remote socket. /// A TCP stream between a local and a remote socket.
/// ///
@ -45,9 +45,9 @@ use crate::utils::Context as _;
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct TcpStream { pub struct TcpStream {
pub(super) watcher: Watcher<mio::net::TcpStream>, pub(super) watcher: Arc<Async<std::net::TcpStream>>,
} }
impl TcpStream { impl TcpStream {
@ -72,25 +72,19 @@ impl TcpStream {
/// ``` /// ```
pub async fn connect<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpStream> { pub async fn connect<A: ToSocketAddrs>(addrs: A) -> io::Result<TcpStream> {
let mut last_err = None; let mut last_err = None;
let addrs = addrs let addrs = addrs.to_socket_addrs().await?;
.to_socket_addrs()
.await?;
for addr in addrs { for addr in addrs {
let res = spawn_blocking(move || { match Async::<std::net::TcpStream>::connect(&addr).await {
let std_stream = std::net::TcpStream::connect(addr) Ok(stream) => {
.context(|| format!("could not connect to {}", addr))?; return Ok(TcpStream {
let mio_stream = mio::net::TcpStream::from_stream(std_stream) watcher: Arc::new(stream),
.context(|| format!("could not open async connection to {}", addr))?; });
Ok(TcpStream { }
watcher: Watcher::new(mio_stream), Err(e) => {
}) last_err = Some(e);
}) continue;
.await; }
match res {
Ok(stream) => return Ok(stream),
Err(err) => last_err = Some(err),
} }
} }
@ -208,7 +202,7 @@ impl TcpStream {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn peek(&self, buf: &mut [u8]) -> io::Result<usize> { pub async fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.peek(buf))).await self.watcher.peek(buf).await
} }
/// Gets the value of the `TCP_NODELAY` option on this socket. /// Gets the value of the `TCP_NODELAY` option on this socket.
@ -311,7 +305,7 @@ impl Read for &TcpStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) Pin::new(&mut &*self.watcher).poll_read(cx, buf)
} }
} }
@ -347,26 +341,23 @@ impl Write for &TcpStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher Pin::new(&mut &*self.watcher).poll_write(cx, buf)
.poll_write_with(cx, |mut inner| inner.write(buf))
} }
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.watcher.poll_write_with(cx, |mut inner| inner.flush()) Pin::new(&mut &*self.watcher).poll_flush(cx)
} }
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.shutdown(std::net::Shutdown::Write)?; Pin::new(&mut &*self.watcher).poll_close(cx)
Poll::Ready(Ok(()))
} }
} }
impl From<std::net::TcpStream> for TcpStream { impl From<std::net::TcpStream> for TcpStream {
/// Converts a `std::net::TcpStream` into its asynchronous equivalent. /// Converts a `std::net::TcpStream` into its asynchronous equivalent.
fn from(stream: std::net::TcpStream) -> TcpStream { fn from(stream: std::net::TcpStream) -> TcpStream {
let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap();
TcpStream { TcpStream {
watcher: Watcher::new(mio_stream), watcher: Arc::new(Async::new(stream).expect("TcpStream is known to be good")),
} }
} }
} }
@ -388,29 +379,37 @@ cfg_unix! {
impl IntoRawFd for TcpStream { impl IntoRawFd for TcpStream {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file
// descriptor because it's possible that there are other clones of this `TcpStream`
// using it at the same time. We should probably document that behavior.
self.as_raw_fd()
} }
} }
} }
cfg_windows! { cfg_windows! {
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::os::windows::io::{
// RawSocket, AsRawSocket, FromRawSocket, IntoRawSocket
// impl AsRawSocket for TcpStream { };
// fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket impl AsRawSocket for TcpStream {
// } fn as_raw_socket(&self) -> RawSocket {
// } self.watcher.get_ref().as_raw_socket()
// }
// impl FromRawSocket for TcpStream { }
// unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
// net::TcpStream::from_raw_socket(handle).try_into().unwrap() impl FromRawSocket for TcpStream {
// } unsafe fn from_raw_socket(handle: RawSocket) -> TcpStream {
// } std::net::TcpStream::from_raw_socket(handle).into()
// }
// impl IntoRawSocket for TcpListener { }
// fn into_raw_socket(self) -> RawSocket {
// self.raw_socket impl IntoRawSocket for TcpStream {
// } fn into_raw_socket(self) -> RawSocket {
// } // TODO(stjepang): This does not mean `RawFd` is now the sole owner of the file
// descriptor because it's possible that there are other clones of this `TcpStream`
// using it at the same time. We should probably document that behavior.
self.as_raw_socket()
}
}
} }

@ -2,8 +2,8 @@ use std::io;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::net::{Ipv4Addr, Ipv6Addr}; use std::net::{Ipv4Addr, Ipv6Addr};
use crate::future; use smol::Async;
use crate::net::driver::Watcher;
use crate::net::ToSocketAddrs; use crate::net::ToSocketAddrs;
use crate::utils::Context as _; use crate::utils::Context as _;
@ -45,7 +45,7 @@ use crate::utils::Context as _;
/// ``` /// ```
#[derive(Debug)] #[derive(Debug)]
pub struct UdpSocket { pub struct UdpSocket {
watcher: Watcher<mio::net::UdpSocket>, watcher: Async<std::net::UdpSocket>,
} }
impl UdpSocket { impl UdpSocket {
@ -69,16 +69,12 @@ impl UdpSocket {
/// ``` /// ```
pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<UdpSocket> { pub async fn bind<A: ToSocketAddrs>(addrs: A) -> io::Result<UdpSocket> {
let mut last_err = None; let mut last_err = None;
let addrs = addrs let addrs = addrs.to_socket_addrs().await?;
.to_socket_addrs()
.await?;
for addr in addrs { for addr in addrs {
match mio::net::UdpSocket::bind(&addr) { match Async::<std::net::UdpSocket>::bind(&addr) {
Ok(mio_socket) => { Ok(socket) => {
return Ok(UdpSocket { return Ok(UdpSocket { watcher: socket });
watcher: Watcher::new(mio_socket),
});
} }
Err(err) => last_err = Some(err), Err(err) => last_err = Some(err),
} }
@ -102,7 +98,7 @@ impl UdpSocket {
/// ```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 {
/// # /// #
/// use async_std::net::UdpSocket; /// use async_std::net::UdpSocket;
/// ///
/// let socket = UdpSocket::bind("127.0.0.1:0").await?; /// let socket = UdpSocket::bind("127.0.0.1:0").await?;
/// let addr = socket.local_addr()?; /// let addr = socket.local_addr()?;
@ -153,12 +149,10 @@ impl UdpSocket {
} }
}; };
future::poll_fn(|cx| { self.watcher
self.watcher .send_to(buf, addr)
.poll_write_with(cx, |inner| inner.send_to(buf, &addr)) .await
}) .context(|| format!("could not send packet to {}", addr))
.await
.context(|| format!("could not send packet to {}", addr))
} }
/// Receives data from the socket. /// Receives data from the socket.
@ -181,22 +175,7 @@ impl UdpSocket {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
future::poll_fn(|cx| { self.watcher.recv_from(buf).await
self.watcher
.poll_read_with(cx, |inner| inner.recv_from(buf))
})
.await
.context(|| {
use std::fmt::Write;
let mut error = String::from("could not receive data on ");
if let Ok(addr) = self.local_addr() {
let _ = write!(&mut error, "{}", addr);
} else {
error.push_str("socket");
}
error
})
} }
/// Connects the UDP socket to a remote address. /// Connects the UDP socket to a remote address.
@ -244,9 +223,12 @@ impl UdpSocket {
})) }))
} }
/// Sends data on the socket to the given address. /// Sends data on the socket to the remote address to which it is connected.
/// ///
/// On success, returns the number of bytes written. /// The [`connect`] method will connect this socket to a remote address.
/// This method will fail if the socket is not connected.
///
/// [`connect`]: #method.connect
/// ///
/// # Examples /// # Examples
/// ///
@ -255,35 +237,16 @@ impl UdpSocket {
/// # /// #
/// use async_std::net::UdpSocket; /// use async_std::net::UdpSocket;
/// ///
/// const THE_MERCHANT_OF_VENICE: &[u8] = b" /// let socket = UdpSocket::bind("127.0.0.1:34254").await?;
/// If you prick us, do we not bleed? /// socket.connect("127.0.0.1:8080").await?;
/// If you tickle us, do we not laugh? /// let bytes = socket.send(b"Hi there!").await?;
/// If you poison us, do we not die?
/// And if you wrong us, shall we not revenge?
/// ";
///
/// let socket = UdpSocket::bind("127.0.0.1:0").await?;
/// ///
/// let addr = "127.0.0.1:7878"; /// println!("Sent {} bytes", bytes);
/// let sent = socket.send_to(THE_MERCHANT_OF_VENICE, &addr).await?;
/// println!("Sent {} bytes to {}", sent, addr);
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn send(&self, buf: &[u8]) -> io::Result<usize> { pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))) self.watcher.send(buf).await
.await
.context(|| {
use std::fmt::Write;
let mut error = String::from("could not send data on ");
if let Ok(addr) = self.local_addr() {
let _ = write!(&mut error, "{}", addr);
} else {
error.push_str("socket");
}
error
})
} }
/// Receives data from the socket. /// Receives data from the socket.
@ -307,19 +270,7 @@ impl UdpSocket {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))) self.watcher.recv(buf).await
.await
.context(|| {
use std::fmt::Write;
let mut error = String::from("could not receive data on ");
if let Ok(addr) = self.local_addr() {
let _ = write!(&mut error, "{}", addr);
} else {
error.push_str("socket");
}
error
})
} }
/// Gets the value of the `SO_BROADCAST` option for this socket. /// Gets the value of the `SO_BROADCAST` option for this socket.
@ -502,9 +453,8 @@ impl UdpSocket {
impl From<std::net::UdpSocket> for UdpSocket { impl From<std::net::UdpSocket> for UdpSocket {
/// Converts a `std::net::UdpSocket` into its asynchronous equivalent. /// Converts a `std::net::UdpSocket` into its asynchronous equivalent.
fn from(socket: std::net::UdpSocket) -> UdpSocket { fn from(socket: std::net::UdpSocket) -> UdpSocket {
let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap();
UdpSocket { UdpSocket {
watcher: Watcher::new(mio_socket), watcher: Async::new(socket).expect("UdpSocket is known to be good"),
} }
} }
} }
@ -526,29 +476,31 @@ cfg_unix! {
impl IntoRawFd for UdpSocket { impl IntoRawFd for UdpSocket {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }
} }
cfg_windows! { cfg_windows! {
// use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle}; use crate::os::windows::io::{
// RawSocket, AsRawSocket, IntoRawSocket, FromRawSocket
// impl AsRawSocket for UdpSocket { };
// fn as_raw_socket(&self) -> RawSocket {
// self.raw_socket impl AsRawSocket for UdpSocket {
// } fn as_raw_socket(&self) -> RawSocket {
// } self.watcher.get_ref().as_raw_socket()
// }
// impl FromRawSocket for UdpSocket { }
// unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
// net::UdpSocket::from_raw_socket(handle).into() impl FromRawSocket for UdpSocket {
// } unsafe fn from_raw_socket(handle: RawSocket) -> UdpSocket {
// } std::net::UdpSocket::from_raw_socket(handle).into()
// }
// impl IntoRawSocket for UdpSocket { }
// fn into_raw_socket(self) -> RawSocket {
// self.raw_socket impl IntoRawSocket for UdpSocket {
// } fn into_raw_socket(self) -> RawSocket {
// } self.watcher.into_raw_socket()
}
}
} }

@ -2,6 +2,7 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{FromStream, IntoStream}; use crate::stream::{FromStream, IntoStream};
use std::convert::identity;
impl<T, V> FromStream<Option<T>> for Option<V> impl<T, V> FromStream<Option<T>> for Option<V>
where where
@ -24,18 +25,15 @@ where
.take_while(|elem| { .take_while(|elem| {
elem.is_some() || { elem.is_some() || {
found_none = true; found_none = true;
// Stop processing the stream on `None`
false false
} }
}) })
.map(Option::unwrap) .filter_map(identity)
.collect() .collect()
.await; .await;
if found_none { if found_none { None } else { Some(out) }
None
} else {
Some(out)
}
}) })
} }
} }

@ -2,6 +2,7 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Product, Stream}; use crate::stream::{Product, Stream};
use std::convert::identity;
impl<T, U> Product<Option<U>> for Option<T> impl<T, U> Product<Option<U>> for Option<T>
where where
@ -48,18 +49,15 @@ where
.take_while(|elem| { .take_while(|elem| {
elem.is_some() || { elem.is_some() || {
found_none = true; found_none = true;
// Stop processing the stream on `None`
false false
} }
}) })
.map(Option::unwrap), .filter_map(identity),
) )
.await; .await;
if found_none { if found_none { None } else { Some(out) }
None
} else {
Some(out)
}
}) })
} }
} }

@ -2,6 +2,7 @@ use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Stream, Sum}; use crate::stream::{Stream, Sum};
use std::convert::identity;
impl<T, U> Sum<Option<U>> for Option<T> impl<T, U> Sum<Option<U>> for Option<T>
where where
@ -43,18 +44,15 @@ where
.take_while(|elem| { .take_while(|elem| {
elem.is_some() || { elem.is_some() || {
found_none = true; found_none = true;
// Stop processing the stream on `None`
false false
} }
}) })
.map(Option::unwrap), .filter_map(identity),
) )
.await; .await;
if found_none { if found_none { None } else { Some(out) }
None
} else {
Some(out)
}
}) })
} }
} }

@ -2,16 +2,14 @@
use std::fmt; use std::fmt;
use std::net::Shutdown; use std::net::Shutdown;
use std::os::unix::net::UnixDatagram as StdUnixDatagram;
use mio_uds; use smol::Async;
use super::SocketAddr; use super::SocketAddr;
use crate::future;
use crate::io; use crate::io;
use crate::net::driver::Watcher;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::Path; use crate::path::Path;
use crate::task::spawn_blocking;
/// A Unix datagram socket. /// A Unix datagram socket.
/// ///
@ -42,13 +40,13 @@ use crate::task::spawn_blocking;
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub struct UnixDatagram { pub struct UnixDatagram {
watcher: Watcher<mio_uds::UnixDatagram>, watcher: Async<StdUnixDatagram>,
} }
impl UnixDatagram { impl UnixDatagram {
fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram { fn new(socket: StdUnixDatagram) -> UnixDatagram {
UnixDatagram { UnixDatagram {
watcher: Watcher::new(socket), watcher: Async::new(socket).expect("UnixDatagram is known to be good"),
} }
} }
@ -67,8 +65,8 @@ impl UnixDatagram {
/// ``` /// ```
pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> { pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixDatagram> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let socket = spawn_blocking(move || mio_uds::UnixDatagram::bind(path)).await?; let socket = Async::<StdUnixDatagram>::bind(path)?;
Ok(UnixDatagram::new(socket)) Ok(UnixDatagram { watcher: socket })
} }
/// Creates a Unix datagram which is not bound to any address. /// Creates a Unix datagram which is not bound to any address.
@ -85,7 +83,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn unbound() -> io::Result<UnixDatagram> { pub fn unbound() -> io::Result<UnixDatagram> {
let socket = mio_uds::UnixDatagram::unbound()?; let socket = StdUnixDatagram::unbound()?;
Ok(UnixDatagram::new(socket)) Ok(UnixDatagram::new(socket))
} }
@ -105,7 +103,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> { pub fn pair() -> io::Result<(UnixDatagram, UnixDatagram)> {
let (a, b) = mio_uds::UnixDatagram::pair()?; let (a, b) = StdUnixDatagram::pair()?;
let a = UnixDatagram::new(a); let a = UnixDatagram::new(a);
let b = UnixDatagram::new(b); let b = UnixDatagram::new(b);
Ok((a, b)) Ok((a, b))
@ -197,11 +195,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> {
future::poll_fn(|cx| { self.watcher.recv_from(buf).await
self.watcher
.poll_read_with(cx, |inner| inner.recv_from(buf))
})
.await
} }
/// Receives data from the socket. /// Receives data from the socket.
@ -222,7 +216,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> { pub async fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await self.watcher.recv(buf).await
} }
/// Sends data on the socket to the specified address. /// Sends data on the socket to the specified address.
@ -242,11 +236,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> { pub async fn send_to<P: AsRef<Path>>(&self, buf: &[u8], path: P) -> io::Result<usize> {
future::poll_fn(|cx| { self.watcher.send_to(buf, path.as_ref()).await
self.watcher
.poll_write_with(cx, |inner| inner.send_to(buf, path.as_ref()))
})
.await
} }
/// Sends data on the socket to the socket's peer. /// Sends data on the socket to the socket's peer.
@ -267,7 +257,7 @@ impl UnixDatagram {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn send(&self, buf: &[u8]) -> io::Result<usize> { pub async fn send(&self, buf: &[u8]) -> io::Result<usize> {
future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await self.watcher.send(buf).await
} }
/// Shut down the read, write, or both halves of this connection. /// Shut down the read, write, or both halves of this connection.
@ -312,31 +302,31 @@ impl fmt::Debug for UnixDatagram {
} }
} }
impl From<std::os::unix::net::UnixDatagram> for UnixDatagram { impl From<StdUnixDatagram> for UnixDatagram {
/// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent. /// Converts a `std::os::unix::net::UnixDatagram` into its asynchronous equivalent.
fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram { fn from(datagram: StdUnixDatagram) -> UnixDatagram {
let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap();
UnixDatagram { UnixDatagram {
watcher: Watcher::new(mio_datagram), watcher: Async::new(datagram).expect("UnixDatagram is known to be good"),
} }
} }
} }
impl AsRawFd for UnixDatagram { impl AsRawFd for UnixDatagram {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd() self.watcher.as_raw_fd()
} }
} }
impl FromRawFd for UnixDatagram { impl FromRawFd for UnixDatagram {
unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram { unsafe fn from_raw_fd(fd: RawFd) -> UnixDatagram {
let datagram = std::os::unix::net::UnixDatagram::from_raw_fd(fd); let raw = StdUnixDatagram::from_raw_fd(fd);
datagram.into() let datagram = Async::<StdUnixDatagram>::new(raw).expect("invalid file descriptor");
UnixDatagram { watcher: datagram }
} }
} }
impl IntoRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }

@ -1,20 +1,20 @@
//! Unix-specific networking extensions. //! Unix-specific networking extensions.
use std::fmt; use std::fmt;
use std::pin::Pin;
use std::future::Future; use std::future::Future;
use std::os::unix::net::UnixListener as StdUnixListener;
use std::pin::Pin;
use mio_uds; use smol::Async;
use super::SocketAddr; use super::SocketAddr;
use super::UnixStream; use super::UnixStream;
use crate::future;
use crate::io; use crate::io;
use crate::net::driver::Watcher;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::Path; use crate::path::Path;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{spawn_blocking, Context, Poll}; use crate::sync::Arc;
use crate::task::{Context, Poll};
/// A Unix domain socket server, listening for connections. /// A Unix domain socket server, listening for connections.
/// ///
@ -50,7 +50,7 @@ use crate::task::{spawn_blocking, Context, Poll};
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub struct UnixListener { pub struct UnixListener {
watcher: Watcher<mio_uds::UnixListener>, watcher: Async<StdUnixListener>,
} }
impl UnixListener { impl UnixListener {
@ -69,11 +69,9 @@ impl UnixListener {
/// ``` /// ```
pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> { pub async fn bind<P: AsRef<Path>>(path: P) -> io::Result<UnixListener> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let listener = spawn_blocking(move || mio_uds::UnixListener::bind(path)).await?; let listener = Async::<StdUnixListener>::bind(path)?;
Ok(UnixListener { Ok(UnixListener { watcher: listener })
watcher: Watcher::new(listener),
})
} }
/// Accepts a new incoming connection to this listener. /// Accepts a new incoming connection to this listener.
@ -93,29 +91,9 @@ impl UnixListener {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> {
future::poll_fn(|cx| { let (stream, addr) = self.watcher.accept().await?;
let res = futures_core::ready!(self.watcher.poll_read_with(cx, |inner| {
match inner.accept_std() { Ok((UnixStream { watcher: Arc::new(stream) }, addr))
// Converting to `WouldBlock` so that the watcher will
// add the waker of this task to a list of readers.
Ok(None) => Err(io::ErrorKind::WouldBlock.into()),
res => res,
}
}));
match res? {
Some((io, addr)) => {
let mio_stream = mio_uds::UnixStream::from_stream(io)?;
let stream = UnixStream {
watcher: Watcher::new(mio_stream),
};
Poll::Ready(Ok((stream, addr)))
}
// This should never happen since `None` is converted to `WouldBlock`
None => unreachable!(),
}
})
.await
} }
/// Returns a stream of incoming connections. /// Returns a stream of incoming connections.
@ -206,19 +184,18 @@ impl Stream for Incoming<'_> {
} }
} }
impl From<std::os::unix::net::UnixListener> for UnixListener { impl From<StdUnixListener> for UnixListener {
/// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent. /// Converts a `std::os::unix::net::UnixListener` into its asynchronous equivalent.
fn from(listener: std::os::unix::net::UnixListener) -> UnixListener { fn from(listener: StdUnixListener) -> UnixListener {
let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap();
UnixListener { UnixListener {
watcher: Watcher::new(mio_listener), watcher: Async::new(listener).expect("UnixListener is known to be good"),
} }
} }
} }
impl AsRawFd for UnixListener { impl AsRawFd for UnixListener {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd() self.watcher.as_raw_fd()
} }
} }
@ -231,6 +208,6 @@ impl FromRawFd for UnixListener {
impl IntoRawFd for UnixListener { impl IntoRawFd for UnixListener {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.watcher.into_raw_fd()
} }
} }

@ -1,18 +1,18 @@
//! Unix-specific networking extensions. //! Unix-specific networking extensions.
use std::fmt; use std::fmt;
use std::io::{Read as _, Write as _};
use std::net::Shutdown; use std::net::Shutdown;
use std::os::unix::net::UnixStream as StdUnixStream;
use std::pin::Pin; use std::pin::Pin;
use mio_uds; use smol::Async;
use super::SocketAddr; use super::SocketAddr;
use crate::io::{self, Read, Write}; use crate::io::{self, Read, Write};
use crate::net::driver::Watcher;
use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use crate::path::Path; use crate::path::Path;
use crate::task::{spawn_blocking, Context, Poll}; use crate::sync::Arc;
use crate::task::{Context, Poll};
/// A Unix stream socket. /// A Unix stream socket.
/// ///
@ -37,8 +37,9 @@ use crate::task::{spawn_blocking, Context, Poll};
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[derive(Clone)]
pub struct UnixStream { pub struct UnixStream {
pub(super) watcher: Watcher<mio_uds::UnixStream>, pub(super) watcher: Arc<Async<StdUnixStream>>,
} }
impl UnixStream { impl UnixStream {
@ -57,15 +58,9 @@ impl UnixStream {
/// ``` /// ```
pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> { pub async fn connect<P: AsRef<Path>>(path: P) -> io::Result<UnixStream> {
let path = path.as_ref().to_owned(); let path = path.as_ref().to_owned();
let stream = Arc::new(Async::<StdUnixStream>::connect(path).await?);
spawn_blocking(move || { Ok(UnixStream { watcher: stream })
let std_stream = std::os::unix::net::UnixStream::connect(path)?;
let mio_stream = mio_uds::UnixStream::from_stream(std_stream)?;
Ok(UnixStream {
watcher: Watcher::new(mio_stream),
})
})
.await
} }
/// Creates an unnamed pair of connected sockets. /// Creates an unnamed pair of connected sockets.
@ -84,12 +79,12 @@ impl UnixStream {
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
pub fn pair() -> io::Result<(UnixStream, UnixStream)> { pub fn pair() -> io::Result<(UnixStream, UnixStream)> {
let (a, b) = mio_uds::UnixStream::pair()?; let (a, b) = Async::<StdUnixStream>::pair()?;
let a = UnixStream { let a = UnixStream {
watcher: Watcher::new(a), watcher: Arc::new(a),
}; };
let b = UnixStream { let b = UnixStream {
watcher: Watcher::new(b), watcher: Arc::new(b),
}; };
Ok((a, b)) Ok((a, b))
} }
@ -169,7 +164,7 @@ impl Read for &UnixStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &mut [u8], buf: &mut [u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) Pin::new(&mut &*self.watcher).poll_read(cx, buf)
} }
} }
@ -197,16 +192,15 @@ impl Write for &UnixStream {
cx: &mut Context<'_>, cx: &mut Context<'_>,
buf: &[u8], buf: &[u8],
) -> Poll<io::Result<usize>> { ) -> Poll<io::Result<usize>> {
self.watcher Pin::new(&mut &*self.watcher).poll_write(cx, buf)
.poll_write_with(cx, |mut inner| inner.write(buf))
} }
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
self.watcher.poll_write_with(cx, |mut inner| inner.flush()) Pin::new(&mut &*self.watcher).poll_flush(cx)
} }
fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<io::Result<()>> { fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
Poll::Ready(Ok(())) Pin::new(&mut &*self.watcher).poll_close(cx)
} }
} }
@ -227,19 +221,17 @@ impl fmt::Debug for UnixStream {
} }
} }
impl From<std::os::unix::net::UnixStream> for UnixStream { impl From<StdUnixStream> for UnixStream {
/// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent. /// Converts a `std::os::unix::net::UnixStream` into its asynchronous equivalent.
fn from(stream: std::os::unix::net::UnixStream) -> UnixStream { fn from(stream: StdUnixStream) -> UnixStream {
let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap(); let stream = Async::new(stream).expect("UnixStream is known to be good");
UnixStream { UnixStream { watcher: Arc::new(stream) }
watcher: Watcher::new(mio_stream),
}
} }
} }
impl AsRawFd for UnixStream { impl AsRawFd for UnixStream {
fn as_raw_fd(&self) -> RawFd { fn as_raw_fd(&self) -> RawFd {
self.watcher.get_ref().as_raw_fd() self.watcher.as_raw_fd()
} }
} }
@ -252,6 +244,6 @@ impl FromRawFd for UnixStream {
impl IntoRawFd for UnixStream { impl IntoRawFd for UnixStream {
fn into_raw_fd(self) -> RawFd { fn into_raw_fd(self) -> RawFd {
self.watcher.into_inner().into_raw_fd() self.as_raw_fd()
} }
} }

@ -2,7 +2,8 @@
cfg_not_docs! { cfg_not_docs! {
pub use std::os::windows::io::{ pub use std::os::windows::io::{
AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle,
AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket,
}; };
} }
@ -45,4 +46,33 @@ cfg_docs! {
/// it once it's no longer needed. /// it once it's no longer needed.
fn into_raw_handle(self) -> RawHandle; fn into_raw_handle(self) -> RawHandle;
} }
/// Creates I/O objects from raw sockets.
pub trait FromRawSocket {
/// Creates a new I/O object from the given raw socket.
///
/// This function will consume ownership of the socket provided and it will be closed when the returned object goes out of scope.
///
/// This function is also unsafe as the primitives currently returned have the contract that they are the sole owner of the
/// file descriptor they are wrapping. Usage of this function could accidentally allow violating this contract which can cause
/// memory unsafety in code that relies on it being true.
unsafe fn from_raw_socket(sock: RawSocket) -> Self;
}
/// Extracts raw sockets.
pub trait AsRawSocket {
/// Extracts the underlying raw socket from this object.
fn as_raw_socket(&self) -> RawSocket;
}
/// A trait to express the ability to consume an object and acquire ownership of
/// its raw `SOCKET`.
pub trait IntoRawSocket {
/// Consumes this object, returning the raw underlying socket.
///
/// This function **transfers ownership** of the underlying socket to the
/// caller. Callers are then the unique owners of the socket and must close
/// it once it's no longer needed.
fn into_raw_socket(self) -> RawSocket;
}
} }

@ -4,9 +4,9 @@ use std::ffi::{OsStr, OsString};
use std::rc::Rc; use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use crate::fs;
use crate::io;
use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError}; use crate::path::{Ancestors, Components, Display, Iter, PathBuf, StripPrefixError};
#[cfg(not(target_os = "unknown"))]
use crate::{fs, io};
/// A slice of a path. /// A slice of a path.
/// ///
@ -584,6 +584,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn metadata(&self) -> io::Result<fs::Metadata> { pub async fn metadata(&self) -> io::Result<fs::Metadata> {
fs::metadata(self).await fs::metadata(self).await
} }
@ -607,6 +608,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn symlink_metadata(&self) -> io::Result<fs::Metadata> { pub async fn symlink_metadata(&self) -> io::Result<fs::Metadata> {
fs::symlink_metadata(self).await fs::symlink_metadata(self).await
} }
@ -632,6 +634,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn canonicalize(&self) -> io::Result<PathBuf> { pub async fn canonicalize(&self) -> io::Result<PathBuf> {
fs::canonicalize(self).await fs::canonicalize(self).await
} }
@ -654,6 +657,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn read_link(&self) -> io::Result<PathBuf> { pub async fn read_link(&self) -> io::Result<PathBuf> {
fs::read_link(self).await fs::read_link(self).await
} }
@ -688,6 +692,7 @@ impl Path {
/// # /// #
/// # Ok(()) }) } /// # Ok(()) }) }
/// ``` /// ```
#[cfg(not(target_os = "unknown"))]
pub async fn read_dir(&self) -> io::Result<fs::ReadDir> { pub async fn read_dir(&self) -> io::Result<fs::ReadDir> {
fs::read_dir(self).await fs::read_dir(self).await
} }
@ -717,6 +722,7 @@ impl Path {
/// check errors, call [fs::metadata]. /// check errors, call [fs::metadata].
/// ///
/// [fs::metadata]: ../fs/fn.metadata.html /// [fs::metadata]: ../fs/fn.metadata.html
#[cfg(not(target_os = "unknown"))]
pub async fn exists(&self) -> bool { pub async fn exists(&self) -> bool {
fs::metadata(self).await.is_ok() fs::metadata(self).await.is_ok()
} }
@ -749,6 +755,7 @@ impl Path {
/// ///
/// [fs::metadata]: ../fs/fn.metadata.html /// [fs::metadata]: ../fs/fn.metadata.html
/// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file /// [fs::Metadata::is_file]: ../fs/struct.Metadata.html#method.is_file
#[cfg(not(target_os = "unknown"))]
pub async fn is_file(&self) -> bool { pub async fn is_file(&self) -> bool {
fs::metadata(self) fs::metadata(self)
.await .await
@ -785,6 +792,7 @@ impl Path {
/// ///
/// [fs::metadata]: ../fs/fn.metadata.html /// [fs::metadata]: ../fs/fn.metadata.html
/// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir /// [fs::Metadata::is_dir]: ../fs/struct.Metadata.html#method.is_dir
#[cfg(not(target_os = "unknown"))]
pub async fn is_dir(&self) -> bool { pub async fn is_dir(&self) -> bool {
fs::metadata(self) fs::metadata(self)
.await .await

@ -10,6 +10,23 @@ where
/// Takes each element in the stream: if it is an `Err`, no further /// Takes each element in the stream: if it is an `Err`, no further
/// elements are taken, and the `Err` is returned. Should no `Err` /// elements are taken, and the `Err` is returned. Should no `Err`
/// occur, a container with the values of each `Result` is returned. /// occur, a container with the values of each `Result` is returned.
///
/// # Examples
///
/// ```
/// # fn main() { async_std::task::block_on(async {
/// #
/// use async_std::prelude::*;
/// use async_std::stream;
///
/// let v = stream::from_iter(vec![1, 2]);
/// let res: Result<Vec<u32>, &'static str> = v.map(|x: u32|
/// x.checked_add(1).ok_or("Overflow!")
/// ).collect().await;
/// assert_eq!(res, Ok(vec![2, 3]));
/// #
/// # }) }
/// ```
#[inline] #[inline]
fn from_stream<'a, S: IntoStream<Item = Result<T, E>> + 'a>( fn from_stream<'a, S: IntoStream<Item = Result<T, E>> + 'a>(
stream: S, stream: S,
@ -17,26 +34,34 @@ where
let stream = stream.into_stream(); let stream = stream.into_stream();
Box::pin(async move { Box::pin(async move {
// Using `scan` here because it is able to stop the stream early // Using `take_while` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut is_error = false;
let mut found_error = None; let mut found_error = None;
let out: V = stream let out: V = stream
.scan((), |_, elem| { .take_while(|elem| {
match elem { // Stop processing the stream on `Err`
Ok(elem) => Some(elem), !is_error
Err(err) => { && (elem.is_ok() || {
found_error = Some(err); is_error = true;
// Stop processing the stream on error // Capture first `Err`
None true
} })
})
.filter_map(|elem| match elem {
Ok(value) => Some(value),
Err(err) => {
found_error = Some(err);
None
} }
}) })
.collect() .collect()
.await; .await;
match found_error { if is_error {
Some(err) => Err(err), Err(found_error.unwrap())
None => Ok(out), } else {
Ok(out)
} }
}) })
} }

@ -1,7 +1,7 @@
use std::pin::Pin; use std::pin::Pin;
use crate::prelude::*; use crate::prelude::*;
use crate::stream::{Stream, Product}; use crate::stream::{Product, Stream};
impl<T, U, E> Product<Result<U, E>> for Result<T, E> impl<T, U, E> Product<Result<U, E>> for Result<T, E>
where where
@ -36,26 +36,39 @@ where
``` ```
"#] "#]
fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>> fn product<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>
where S: Stream<Item = Result<U, E>> + 'a where
S: Stream<Item = Result<U, E>> + 'a,
{ {
Box::pin(async move { Box::pin(async move {
// Using `scan` here because it is able to stop the stream early // Using `take_while` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut is_error = false;
let mut found_error = None; let mut found_error = None;
let out = <T as Product<U>>::product(stream let out = <T as Product<U>>::product(
.scan((), |_, elem| { stream
match elem { .take_while(|elem| {
Ok(elem) => Some(elem), // Stop processing the stream on `Err`
!is_error
&& (elem.is_ok() || {
is_error = true;
// Capture first `Err`
true
})
})
.filter_map(|elem| match elem {
Ok(value) => Some(value),
Err(err) => { Err(err) => {
found_error = Some(err); found_error = Some(err);
// Stop processing the stream on error
None None
} }
} }),
})).await; )
match found_error { .await;
Some(err) => Err(err),
None => Ok(out) if is_error {
Err(found_error.unwrap())
} else {
Ok(out)
} }
}) })
} }

@ -36,26 +36,39 @@ where
``` ```
"#] "#]
fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>> fn sum<'a, S>(stream: S) -> Pin<Box<dyn Future<Output = Result<T, E>> + 'a>>
where S: Stream<Item = Result<U, E>> + 'a where
S: Stream<Item = Result<U, E>> + 'a,
{ {
Box::pin(async move { Box::pin(async move {
// Using `scan` here because it is able to stop the stream early // Using `take_while` here because it is able to stop the stream early
// if a failure occurs // if a failure occurs
let mut is_error = false;
let mut found_error = None; let mut found_error = None;
let out = <T as Sum<U>>::sum(stream let out = <T as Sum<U>>::sum(
.scan((), |_, elem| { stream
match elem { .take_while(|elem| {
Ok(elem) => Some(elem), // Stop processing the stream on `Err`
!is_error
&& (elem.is_ok() || {
is_error = true;
// Capture first `Err`
true
})
})
.filter_map(|elem| match elem {
Ok(value) => Some(value),
Err(err) => { Err(err) => {
found_error = Some(err); found_error = Some(err);
// Stop processing the stream on error
None None
} }
} }),
})).await; )
match found_error { .await;
Some(err) => Err(err),
None => Ok(out) if is_error {
Err(found_error.unwrap())
} else {
Ok(out)
} }
}) })
} }

@ -0,0 +1,34 @@
//! The runtime.
use std::env;
use std::thread;
use once_cell::sync::Lazy;
use crate::future;
/// Dummy runtime struct.
pub struct Runtime {}
/// The global runtime.
pub static RUNTIME: Lazy<Runtime> = Lazy::new(|| {
// Create an executor thread pool.
let thread_count = env::var("ASYNC_STD_THREAD_COUNT")
.map(|env| {
env.parse()
.expect("ASYNC_STD_THREAD_COUNT must be a number")
})
.unwrap_or_else(|_| num_cpus::get())
.max(1);
let thread_name = env::var("ASYNC_STD_THREAD_NAME").unwrap_or("async-std/runtime".to_string());
for _ in 0..thread_count {
thread::Builder::new()
.name(thread_name.clone())
.spawn(|| crate::task::block_on(future::pending::<()>()))
.expect("cannot start a runtime thread");
}
Runtime {}
});

@ -1,5 +1,5 @@
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use crate::stream::DoubleEndedStream; use crate::stream::DoubleEndedStream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,6 +1,6 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use crate::stream::DoubleEndedStream; use crate::stream::DoubleEndedStream;

@ -1,6 +1,6 @@
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use crate::stream::DoubleEndedStream; use crate::stream::DoubleEndedStream;

@ -1,6 +1,6 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use crate::future::Future; use crate::future::Future;
use std::pin::Pin; use core::pin::Pin;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::marker::PhantomData; use core::marker::PhantomData;
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,6 +1,6 @@
use std::pin::Pin; use core::pin::Pin;
use core::future::Future;
use crate::prelude::*;
use crate::stream::IntoStream; use crate::stream::IntoStream;
/// Extends a collection with the contents of a stream. /// Extends a collection with the contents of a stream.

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use crate::stream::IntoStream; use crate::stream::IntoStream;

@ -1,10 +1,10 @@
use std::future::Future;
use std::pin::Pin; use std::pin::Pin;
use std::task::{Context, Poll}; use std::task::{Context, Poll};
use std::time::{Duration, Instant}; use std::time::Duration;
use futures_timer::Delay; use crate::stream::Stream;
use crate::utils::Timer;
use crate::prelude::*;
/// Creates a new stream that yields at a set interval. /// Creates a new stream that yields at a set interval.
/// ///
@ -45,7 +45,7 @@ use crate::prelude::*;
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
pub fn interval(dur: Duration) -> Interval { pub fn interval(dur: Duration) -> Interval {
Interval { Interval {
delay: Delay::new(dur), delay: Timer::after(dur),
interval: dur, interval: dur,
} }
} }
@ -60,7 +60,7 @@ pub fn interval(dur: Duration) -> Interval {
#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))]
#[derive(Debug)] #[derive(Debug)]
pub struct Interval { pub struct Interval {
delay: Delay, delay: Timer,
interval: Duration, interval: Duration,
} }
@ -71,125 +71,8 @@ impl Stream for Interval {
if Pin::new(&mut self.delay).poll(cx).is_pending() { if Pin::new(&mut self.delay).poll(cx).is_pending() {
return Poll::Pending; return Poll::Pending;
} }
let when = Instant::now(); let interval = self.interval;
let next = next_interval(when, Instant::now(), self.interval); let _ = std::mem::replace(&mut self.delay, Timer::after(interval));
self.delay.reset(next);
Poll::Ready(Some(())) Poll::Ready(Some(()))
} }
} }
/// Converts Duration object to raw nanoseconds if possible
///
/// This is useful to divide intervals.
///
/// While technically for large duration it's impossible to represent any
/// duration as nanoseconds, the largest duration we can represent is about
/// 427_000 years. Large enough for any interval we would use or calculate in
/// async-std.
fn duration_to_nanos(dur: Duration) -> Option<u64> {
dur.as_secs()
.checked_mul(1_000_000_000)
.and_then(|v| v.checked_add(u64::from(dur.subsec_nanos())))
}
fn next_interval(prev: Instant, now: Instant, interval: Duration) -> Instant {
let new = prev + interval;
if new > now {
return new;
}
let spent_ns = duration_to_nanos(now.duration_since(prev)).expect("interval should be expired");
let interval_ns =
duration_to_nanos(interval).expect("interval is less that 427 thousand years");
let mult = spent_ns / interval_ns + 1;
assert!(
mult < (1 << 32),
"can't skip more than 4 billion intervals of {:?} \
(trying to skip {})",
interval,
mult
);
prev + interval * (mult as u32)
}
#[cfg(test)]
mod test {
use super::next_interval;
use std::cmp::Ordering;
use std::time::{Duration, Instant};
struct Timeline(Instant);
impl Timeline {
fn new() -> Timeline {
Timeline(Instant::now())
}
fn at(&self, millis: u64) -> Instant {
self.0 + Duration::from_millis(millis)
}
fn at_ns(&self, sec: u64, nanos: u32) -> Instant {
self.0 + Duration::new(sec, nanos)
}
}
fn dur(millis: u64) -> Duration {
Duration::from_millis(millis)
}
// The math around Instant/Duration isn't 100% precise due to rounding
// errors, see #249 for more info
fn almost_eq(a: Instant, b: Instant) -> bool {
match a.cmp(&b) {
Ordering::Equal => true,
Ordering::Greater => a - b < Duration::from_millis(1),
Ordering::Less => b - a < Duration::from_millis(1),
}
}
#[test]
fn norm_next() {
let tm = Timeline::new();
assert!(almost_eq(
next_interval(tm.at(1), tm.at(2), dur(10)),
tm.at(11)
));
assert!(almost_eq(
next_interval(tm.at(7777), tm.at(7788), dur(100)),
tm.at(7877)
));
assert!(almost_eq(
next_interval(tm.at(1), tm.at(1000), dur(2100)),
tm.at(2101)
));
}
#[test]
fn fast_forward() {
let tm = Timeline::new();
assert!(almost_eq(
next_interval(tm.at(1), tm.at(1000), dur(10)),
tm.at(1001)
));
assert!(almost_eq(
next_interval(tm.at(7777), tm.at(8888), dur(100)),
tm.at(8977)
));
assert!(almost_eq(
next_interval(tm.at(1), tm.at(10000), dur(2100)),
tm.at(10501)
));
}
/// TODO: this test actually should be successful, but since we can't
/// multiply Duration on anything larger than u32 easily we decided
/// to allow it to fail for now
#[test]
#[should_panic(expected = "can't skip more than 4 billion intervals")]
fn large_skip() {
let tm = Timeline::new();
assert_eq!(
next_interval(tm.at_ns(0, 1), tm.at_ns(25, 0), Duration::new(0, 2)),
tm.at_ns(25, 1)
);
}
}

@ -325,6 +325,7 @@ cfg_unstable! {
mod fused_stream; mod fused_stream;
mod interval; mod interval;
mod into_stream; mod into_stream;
mod pending;
mod product; mod product;
mod successors; mod successors;
mod sum; mod sum;
@ -336,6 +337,7 @@ cfg_unstable! {
pub use fused_stream::FusedStream; pub use fused_stream::FusedStream;
pub use interval::{interval, Interval}; pub use interval::{interval, Interval};
pub use into_stream::IntoStream; pub use into_stream::IntoStream;
pub use pending::{pending, Pending};
pub use product::Product; pub use product::Product;
pub use stream::Merge; pub use stream::Merge;
pub use successors::{successors, Successors}; pub use successors::{successors, Successors};

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -0,0 +1,68 @@
use core::marker::PhantomData;
use core::pin::Pin;
use core::task::{Context, Poll};
use crate::stream::{DoubleEndedStream, ExactSizeStream, FusedStream, Stream};
/// A stream that never returns any items.
///
/// This stream is created by the [`pending`] function. See its
/// documentation for more.
///
/// [`pending`]: fn.pending.html
#[derive(Debug)]
pub struct Pending<T> {
_marker: PhantomData<T>,
}
/// Creates a stream that never returns any items.
///
/// The returned stream will always return `Pending` when polled.
/// # Examples
///
/// ```
/// # async_std::task::block_on(async {
/// #
/// use std::time::Duration;
///
/// use async_std::prelude::*;
/// use async_std::stream;
///
/// let dur = Duration::from_millis(100);
/// let mut s = stream::pending::<()>().timeout(dur);
///
/// let item = s.next().await;
///
/// assert!(item.is_some());
/// assert!(item.unwrap().is_err());
///
/// #
/// # })
/// ```
pub fn pending<T>() -> Pending<T> {
Pending {
_marker: PhantomData,
}
}
impl<T> Stream for Pending<T> {
type Item = T;
fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
Poll::Pending
}
}
impl<T> DoubleEndedStream for Pending<T> {
fn poll_next_back(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Option<T>> {
Poll::Pending
}
}
impl<T> FusedStream for Pending<T> {}
impl<T> ExactSizeStream for Pending<T> {
fn len(&self) -> usize {
0
}
}

@ -1,5 +1,5 @@
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use crate::stream::Stream; use crate::stream::Stream;

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,6 +1,6 @@
use std::marker::PhantomData; use core::marker::PhantomData;
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,6 +1,6 @@
use std::marker::PhantomData; use core::marker::PhantomData;
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,9 +1,10 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
pin_project! { pin_project! {

@ -1,7 +1,7 @@
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use std::pin::Pin; use core::pin::Pin;
pin_project! { pin_project! {
/// A stream that clones the elements of an underlying stream. /// A stream that clones the elements of an underlying stream.

@ -1,11 +1,11 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,7 +1,7 @@
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use std::pin::Pin; use core::pin::Pin;
pin_project! { pin_project! {
/// A stream that copies the elements of an underlying stream. /// A stream that copies the elements of an underlying stream.

@ -1,5 +1,5 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::mem::ManuallyDrop; use core::mem::ManuallyDrop;
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,11 +1,12 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::time::Duration; use core::time::Duration;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
use crate::utils::Timer;
pin_project! { pin_project! {
#[doc(hidden)] #[doc(hidden)]
@ -14,7 +15,7 @@ pin_project! {
#[pin] #[pin]
stream: S, stream: S,
#[pin] #[pin]
delay: futures_timer::Delay, delay: Timer,
delay_done: bool, delay_done: bool,
} }
} }
@ -23,7 +24,7 @@ impl<S> Delay<S> {
pub(super) fn new(stream: S, dur: Duration) -> Self { pub(super) fn new(stream: S, dur: Duration) -> Self {
Delay { Delay {
stream, stream,
delay: futures_timer::Delay::new(dur), delay: Timer::after(dur),
delay_done: false, delay_done: false,
} }
} }

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,10 +1,10 @@
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::pin::Pin; use core::pin::Pin;
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,6 +1,6 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use crate::stream::Stream; use crate::stream::Stream;

@ -1,9 +1,9 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::prelude::*;
use crate::stream::stream::map::Map; use crate::stream::stream::map::Map;
use crate::stream::stream::StreamExt;
use crate::stream::{IntoStream, Stream}; use crate::stream::{IntoStream, Stream};
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};
@ -41,7 +41,6 @@ where
impl<S, U, F> Stream for FlatMap<S, U, F> impl<S, U, F> Stream for FlatMap<S, U, F>
where where
S: Stream, S: Stream,
S::Item: IntoStream<IntoStream = U, Item = U::Item>,
U: Stream, U: Stream,
F: FnMut(S::Item) -> U, F: FnMut(S::Item) -> U,
{ {

@ -1,5 +1,5 @@
use std::fmt; use core::fmt;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,11 +1,11 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture; use super::partial_cmp::PartialCmpFuture;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,11 +1,11 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture; use super::partial_cmp::PartialCmpFuture;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,5 +1,5 @@
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,11 +1,11 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture; use super::partial_cmp::PartialCmpFuture;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,11 +1,11 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::partial_cmp::PartialCmpFuture; use super::partial_cmp::PartialCmpFuture;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,4 +1,4 @@
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,7 +1,6 @@
use std::cmp::{Ord, Ordering}; use core::cmp::{Ord, Ordering};
use std::marker::PhantomData; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::future::Future;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
@ -11,29 +10,23 @@ use crate::task::{Context, Poll};
pin_project! { pin_project! {
#[doc(hidden)] #[doc(hidden)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct MaxFuture<S, F, T> { pub struct MaxFuture<S, T> {
#[pin] #[pin]
stream: S, stream: S,
_compare: PhantomData<F>,
max: Option<T>, max: Option<T>,
} }
} }
impl<S, F, T> MaxFuture<S, F, T> { impl<S, T> MaxFuture<S, T> {
pub(super) fn new(stream: S) -> Self { pub(super) fn new(stream: S) -> Self {
Self { Self { stream, max: None }
stream,
_compare: PhantomData,
max: None,
}
} }
} }
impl<S, F> Future for MaxFuture<S, F, S::Item> impl<S> Future for MaxFuture<S, S::Item>
where where
S: Stream, S: Stream,
S::Item: Ord, S::Item: Ord,
F: FnMut(&S::Item, &S::Item) -> Ordering,
{ {
type Output = Option<S::Item>; type Output = Option<S::Item>;

@ -1,6 +1,6 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,6 +1,6 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,10 +1,11 @@
use std::pin::Pin; use core::pin::Pin;
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Fuse; use crate::stream::Fuse;
use crate::stream::Stream;
use crate::utils; use crate::utils;
pin_project! { pin_project! {

@ -1,7 +1,6 @@
use std::cmp::{Ord, Ordering}; use core::cmp::{Ord, Ordering};
use std::marker::PhantomData; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::future::Future;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
@ -11,29 +10,23 @@ use crate::task::{Context, Poll};
pin_project! { pin_project! {
#[doc(hidden)] #[doc(hidden)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
pub struct MinFuture<S, F, T> { pub struct MinFuture<S, T> {
#[pin] #[pin]
stream: S, stream: S,
_compare: PhantomData<F>,
min: Option<T>, min: Option<T>,
} }
} }
impl<S, F, T> MinFuture<S, F, T> { impl<S, T> MinFuture<S, T> {
pub(super) fn new(stream: S) -> Self { pub(super) fn new(stream: S) -> Self {
Self { Self { stream, min: None }
stream,
_compare: PhantomData,
min: None,
}
} }
} }
impl<S, F> Future for MinFuture<S, F, S::Item> impl<S> Future for MinFuture<S, S::Item>
where where
S: Stream, S: Stream,
S::Item: Ord, S::Item: Ord,
F: FnMut(&S::Item, &S::Item) -> Ordering,
{ {
type Output = Option<S::Item>; type Output = Option<S::Item>;

@ -1,6 +1,6 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -1,6 +1,6 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;

@ -110,12 +110,12 @@ pub use take::Take;
pub use take_while::TakeWhile; pub use take_while::TakeWhile;
pub use zip::Zip; pub use zip::Zip;
use std::cmp::Ordering; use core::cmp::Ordering;
cfg_unstable! { cfg_unstable! {
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use std::time::Duration; use core::time::Duration;
use crate::stream::into_stream::IntoStream; use crate::stream::into_stream::IntoStream;
use crate::stream::{FromStream, Product, Sum}; use crate::stream::{FromStream, Product, Sum};
@ -791,18 +791,22 @@ extension_trait! {
# async_std::task::block_on(async { # async_std::task::block_on(async {
use async_std::prelude::*; use async_std::prelude::*;
use async_std::stream::IntoStream;
use async_std::stream; use async_std::stream;
let inner1 = stream::from_iter(vec![1,2,3]); let words = stream::from_iter(&["alpha", "beta", "gamma"]);
let inner2 = stream::from_iter(vec![4,5,6]);
let s = stream::from_iter(vec![inner1, inner2]); let merged: String = words
.flat_map(|s| stream::from_iter(s.chars()))
.collect().await;
assert_eq!(merged, "alphabetagamma");
let v :Vec<_> = s.flat_map(|s| s.into_stream()).collect().await; let d3 = stream::from_iter(&[[[1, 2], [3, 4]], [[5, 6], [7, 8]]]);
let d1: Vec<_> = d3
assert_eq!(v, vec![1,2,3,4,5,6]); .flat_map(|item| stream::from_iter(item))
.flat_map(|item| stream::from_iter(item))
.collect().await;
assert_eq!(d1, [&1, &2, &3, &4, &5, &6, &7, &8]);
# }); # });
``` ```
"#] "#]
@ -1007,7 +1011,7 @@ extension_trait! {
# Examples # Examples
```ignore ```
# fn main() { async_std::task::block_on(async { # fn main() { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
@ -1024,12 +1028,12 @@ extension_trait! {
# }) } # }) }
``` ```
"#] "#]
fn max<F>( fn max(
self, self,
) -> impl Future<Output = Option<Self::Item>> [MaxFuture<Self, F, Self::Item>] ) -> impl Future<Output = Option<Self::Item>> [MaxFuture<Self, Self::Item>]
where where
Self: Sized, Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Ordering, Self::Item: Ord,
{ {
MaxFuture::new(self) MaxFuture::new(self)
} }
@ -1040,7 +1044,7 @@ extension_trait! {
# Examples # Examples
```ignore ```
# fn main() { async_std::task::block_on(async { # fn main() { async_std::task::block_on(async {
# #
use async_std::prelude::*; use async_std::prelude::*;
@ -1057,12 +1061,12 @@ extension_trait! {
# }) } # }) }
``` ```
"#] "#]
fn min<F>( fn min(
self, self,
) -> impl Future<Output = Option<Self::Item>> [MinFuture<Self, F, Self::Item>] ) -> impl Future<Output = Option<Self::Item>> [MinFuture<Self, Self::Item>]
where where
Self: Sized, Self: Sized,
F: FnMut(&Self::Item, &Self::Item) -> Ordering, Self::Item: Ord,
{ {
MinFuture::new(self) MinFuture::new(self)
} }
@ -1641,6 +1645,13 @@ extension_trait! {
while let Some(v) = s.next().await { while let Some(v) = s.next().await {
assert_eq!(v, Ok(1)); assert_eq!(v, Ok(1));
} }
// when timeout
let mut s = stream::pending::<()>().timeout(Duration::from_millis(10));
match s.next().await {
Some(item) => assert!(item.is_err()),
None => panic!()
};
# #
# Ok(()) }) } # Ok(()) }) }
``` ```

@ -1,10 +1,10 @@
use std::pin::Pin; use core::future::Future;
use std::future::Future; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,5 +1,5 @@
use std::pin::Pin; use core::pin::Pin;
use std::future::Future; use core::future::Future;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,6 +1,6 @@
use std::pin::Pin; use core::pin::Pin;
use std::task::{Context, Poll}; use core::task::{Context, Poll};
use std::future::Future; use core::future::Future;
use crate::stream::Stream; use crate::stream::Stream;

@ -1,11 +1,11 @@
use std::cmp::Ordering; use core::cmp::Ordering;
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use super::fuse::Fuse; use super::fuse::Fuse;
use crate::prelude::*; use crate::stream::stream::StreamExt;
use crate::stream::Stream; use crate::stream::Stream;
use crate::task::{Context, Poll}; use crate::task::{Context, Poll};

@ -1,7 +1,7 @@
use pin_project_lite::pin_project; use pin_project_lite::pin_project;
use std::default::Default; use core::default::Default;
use std::future::Future; use core::future::Future;
use std::pin::Pin; use core::pin::Pin;
use crate::stream::Stream; use crate::stream::Stream;
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

Loading…
Cancel
Save