From 0156dc879bc67e313ef123978a8edfb24ae36fae Mon Sep 17 00:00:00 2001 From: Sun Date: Tue, 20 Aug 2019 13:24:13 +0800 Subject: [PATCH 001/194] move surf to dev-dependencies (#84) --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 70ae254..bddd8b3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,8 +36,8 @@ mio-uds = "0.6.7" num_cpus = "1.10.0" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" -surf = "1.0.1" [dev-dependencies] femme = "1.1.0" tempdir = "0.3.7" +surf = "1.0.1" From 04cafeab2c8d6796a3b29a67d5c3186ff6a4afc3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 20 Aug 2019 11:18:11 +0300 Subject: [PATCH 002/194] fix counting lines example --- src/io/buf_read.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/buf_read.rs b/src/io/buf_read.rs index c0882fd..675d01c 100644 --- a/src/io/buf_read.rs +++ b/src/io/buf_read.rs @@ -149,7 +149,7 @@ pub trait BufRead { /// let mut lines = BufReader::new(file).lines(); /// let mut count = 0; /// - /// for line in lines.next().await { + /// while let Some(line) = lines.next().await { /// line?; /// count += 1; /// } From 63ad786768410c219a69d52d76c3a718aa1bb4b5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 20 Aug 2019 17:16:22 -0700 Subject: [PATCH 003/194] remove async_await feature gate Signed-off-by: Yoshua Wuyts --- README.md | 4 ---- benches/task_local.rs | 2 +- docs/src/tutorial/accept_loop.md | 2 -- docs/src/tutorial/all_together.md | 2 -- docs/src/tutorial/handling_disconnection.md | 2 -- docs/src/tutorial/implementing_a_client.md | 2 -- examples/hello-world.rs | 2 -- examples/line-count.rs | 2 -- examples/list-dir.rs | 2 -- examples/logging.rs | 2 -- examples/print-file.rs | 2 -- examples/stdin-echo.rs | 2 -- examples/stdin-timeout.rs | 2 -- examples/surf-web.rs | 2 -- examples/task-local.rs | 2 -- examples/task-name.rs | 2 -- examples/tcp-client.rs | 2 -- examples/tcp-echo.rs | 2 -- examples/udp-client.rs | 2 -- examples/udp-echo.rs | 2 -- src/fs/canonicalize.rs | 1 - src/fs/copy.rs | 1 - src/fs/create_dir.rs | 1 - src/fs/create_dir_all.rs | 1 - src/fs/dir_builder.rs | 1 - src/fs/dir_entry.rs | 4 ---- src/fs/file.rs | 9 --------- src/fs/hard_link.rs | 1 - src/fs/metadata.rs | 1 - src/fs/mod.rs | 1 - src/fs/open_options.rs | 10 ---------- src/fs/read.rs | 1 - src/fs/read_dir.rs | 1 - src/fs/read_link.rs | 1 - src/fs/read_to_string.rs | 1 - src/fs/remove_dir.rs | 1 - src/fs/remove_dir_all.rs | 1 - src/fs/remove_file.rs | 1 - src/fs/rename.rs | 1 - src/fs/set_permissions.rs | 1 - src/fs/symlink_metadata.rs | 1 - src/fs/write.rs | 1 - src/future/pending.rs | 1 - src/future/ready.rs | 1 - src/io/buf_read.rs | 3 --- src/io/buf_reader.rs | 7 ------- src/io/copy.rs | 1 - src/io/empty.rs | 1 - src/io/mod.rs | 1 - src/io/read.rs | 4 ---- src/io/seek.rs | 1 - src/io/sink.rs | 1 - src/io/stderr.rs | 1 - src/io/stdin.rs | 2 -- src/io/stdout.rs | 1 - src/io/timeout.rs | 1 - src/io/write.rs | 3 --- src/lib.rs | 2 -- src/net/mod.rs | 1 - src/net/tcp/listener.rs | 5 ----- src/net/tcp/stream.rs | 10 ---------- src/net/udp/mod.rs | 10 ---------- src/os/unix/fs.rs | 1 - src/os/unix/net/datagram.rs | 12 ------------ src/os/unix/net/listener.rs | 5 ----- src/os/unix/net/stream.rs | 6 ------ src/stream/empty.rs | 1 - src/stream/mod.rs | 1 - src/stream/once.rs | 1 - src/stream/repeat.rs | 1 - src/stream/stream.rs | 3 --- src/sync/mod.rs | 1 - src/sync/mutex.rs | 4 ---- src/sync/rwlock.rs | 7 ------- src/task/local.rs | 3 --- src/task/mod.rs | 1 - src/task/pool.rs | 3 --- src/task/sleep.rs | 1 - src/task/task.rs | 2 -- tests/block_on.rs | 2 -- tests/mutex.rs | 2 -- tests/rwlock.rs | 2 -- tests/task_local.rs | 2 -- tests/tcp.rs | 2 -- tests/udp.rs | 2 -- tests/uds.rs | 1 - 86 files changed, 1 insertion(+), 206 deletions(-) diff --git a/README.md b/README.md index d4393ff..b3f4189 100644 --- a/README.md +++ b/README.md @@ -38,8 +38,6 @@ $ cargo add async-std ## Hello world ```rust -#![feature(async_await)] - use async_std::task; fn main() { @@ -52,8 +50,6 @@ fn main() { ## Low-Friction Sockets with Built-In Timeouts ```rust -#![feature(async_await)] - use std::time::Duration; use async_std::{ diff --git a/benches/task_local.rs b/benches/task_local.rs index 2800365..3b6c859 100644 --- a/benches/task_local.rs +++ b/benches/task_local.rs @@ -1,4 +1,4 @@ -#![feature(async_await, test)] +#![feature(test)] extern crate test; diff --git a/docs/src/tutorial/accept_loop.md b/docs/src/tutorial/accept_loop.md index 54b4187..8a91321 100644 --- a/docs/src/tutorial/accept_loop.md +++ b/docs/src/tutorial/accept_loop.md @@ -6,8 +6,6 @@ Let's implement the scaffold of the server: a loop that binds a TCP socket to an First of all, let's add required import boilerplate: ```rust -#![feature(async_await)] - use std::net::ToSocketAddrs; // 1 use async_std::{ diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 91101bd..64fba71 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -4,8 +4,6 @@ At this point, we only need to start the broker to get a fully-functioning (in the happy case!) chat: ```rust -#![feature(async_await)] - use std::{ net::ToSocketAddrs, sync::Arc, diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 865bf95..4cda69f 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -95,8 +95,6 @@ This also allows us to establish a useful invariant that the message channel str The final code looks like this: ```rust -#![feature(async_await)] - use std::{ net::ToSocketAddrs, sync::Arc, diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 509bd9a..b3c2d1c 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -15,8 +15,6 @@ Programming this with threads is cumbersome, especially when implementing clean With async, we can just use the `select!` macro. ```rust -#![feature(async_await)] - use std::net::ToSocketAddrs; use futures::select; diff --git a/examples/hello-world.rs b/examples/hello-world.rs index a3f064c..8d66333 100644 --- a/examples/hello-world.rs +++ b/examples/hello-world.rs @@ -1,7 +1,5 @@ //! Spawns a task that says hello. -#![feature(async_await)] - use async_std::task; async fn say_hi() { diff --git a/examples/line-count.rs b/examples/line-count.rs index 367789a..847352d 100644 --- a/examples/line-count.rs +++ b/examples/line-count.rs @@ -1,7 +1,5 @@ //! Counts the number of lines in a file given as an argument. -#![feature(async_await)] - use std::env::args; use async_std::fs::File; diff --git a/examples/list-dir.rs b/examples/list-dir.rs index 10c16d1..814004a 100644 --- a/examples/list-dir.rs +++ b/examples/list-dir.rs @@ -1,7 +1,5 @@ //! Lists files in a directory given as an argument. -#![feature(async_await)] - use std::env::args; use async_std::fs; diff --git a/examples/logging.rs b/examples/logging.rs index 8ada5c7..bd55aaa 100644 --- a/examples/logging.rs +++ b/examples/logging.rs @@ -1,7 +1,5 @@ //! Prints the runtime's execution log on the standard output. -#![feature(async_await)] - use async_std::task; fn main() { diff --git a/examples/print-file.rs b/examples/print-file.rs index 9c88635..d74bdd8 100644 --- a/examples/print-file.rs +++ b/examples/print-file.rs @@ -1,7 +1,5 @@ //! Prints a file given as an argument to stdout. -#![feature(async_await)] - use std::env::args; use async_std::fs::File; diff --git a/examples/stdin-echo.rs b/examples/stdin-echo.rs index 6940654..9514219 100644 --- a/examples/stdin-echo.rs +++ b/examples/stdin-echo.rs @@ -1,7 +1,5 @@ //! Echoes lines read on stdin to stdout. -#![feature(async_await)] - use async_std::io; use async_std::prelude::*; use async_std::task; diff --git a/examples/stdin-timeout.rs b/examples/stdin-timeout.rs index 7cc05bf..f13c387 100644 --- a/examples/stdin-timeout.rs +++ b/examples/stdin-timeout.rs @@ -1,7 +1,5 @@ //! Reads a line from stdin, or exits with an error if nothing is read in 5 seconds. -#![feature(async_await)] - use std::time::Duration; use async_std::io; diff --git a/examples/surf-web.rs b/examples/surf-web.rs index 7581edb..fd19c29 100644 --- a/examples/surf-web.rs +++ b/examples/surf-web.rs @@ -1,7 +1,5 @@ //! Sends an HTTP request to the Rust website. -#![feature(async_await)] - use async_std::task; fn main() -> Result<(), surf::Exception> { diff --git a/examples/task-local.rs b/examples/task-local.rs index 50e1473..320b383 100644 --- a/examples/task-local.rs +++ b/examples/task-local.rs @@ -1,7 +1,5 @@ //! Creates a task-local value. -#![feature(async_await)] - use std::cell::Cell; use async_std::prelude::*; diff --git a/examples/task-name.rs b/examples/task-name.rs index 39ad080..f0bfdff 100644 --- a/examples/task-name.rs +++ b/examples/task-name.rs @@ -1,7 +1,5 @@ //! Spawns a named task that prints its name. -#![feature(async_await)] - use async_std::task; async fn print_name() { diff --git a/examples/tcp-client.rs b/examples/tcp-client.rs index b6c0ae7..02250ee 100644 --- a/examples/tcp-client.rs +++ b/examples/tcp-client.rs @@ -12,8 +12,6 @@ //! $ cargo run --example tcp-client //! ``` -#![feature(async_await)] - use async_std::io; use async_std::net::TcpStream; use async_std::prelude::*; diff --git a/examples/tcp-echo.rs b/examples/tcp-echo.rs index 0816439..7c50be0 100644 --- a/examples/tcp-echo.rs +++ b/examples/tcp-echo.rs @@ -6,8 +6,6 @@ //! $ nc localhost 8080 //! ``` -#![feature(async_await)] - use async_std::io; use async_std::net::{TcpListener, TcpStream}; use async_std::prelude::*; diff --git a/examples/udp-client.rs b/examples/udp-client.rs index c1a8c92..180db0b 100644 --- a/examples/udp-client.rs +++ b/examples/udp-client.rs @@ -12,8 +12,6 @@ //! $ cargo run --example udp-client //! ``` -#![feature(async_await)] - use async_std::io; use async_std::net::UdpSocket; use async_std::task; diff --git a/examples/udp-echo.rs b/examples/udp-echo.rs index 0d17c00..f436d01 100644 --- a/examples/udp-echo.rs +++ b/examples/udp-echo.rs @@ -6,8 +6,6 @@ //! $ nc -u localhost 8080 //! ``` -#![feature(async_await)] - use async_std::io; use async_std::net::UdpSocket; use async_std::task; diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 9d5cf95..572a657 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -23,7 +23,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/copy.rs b/src/fs/copy.rs index bb3316e..56fd84b 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -27,7 +27,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index b823714..9532eaf 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -21,7 +21,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 3bafd39..413f5f6 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -20,7 +20,6 @@ use crate::io; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index b8b6ff9..21e2999 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -73,7 +73,6 @@ impl DirBuilder { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::DirBuilder; diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index b2312c0..4e07a3f 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -74,7 +74,6 @@ impl DirEntry { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; @@ -100,7 +99,6 @@ impl DirEntry { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; @@ -154,7 +152,6 @@ impl DirEntry { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; @@ -206,7 +203,6 @@ impl DirEntry { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/file.rs b/src/fs/file.rs index c61a249..b297181 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -33,7 +33,6 @@ use crate::task::{blocking, Context, Poll}; /// Create a new file and write some bytes to it: /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -48,7 +47,6 @@ use crate::task::{blocking, Context, Poll}; /// Read the contents of a file into a `Vec`: /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -124,7 +122,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -171,7 +168,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -218,7 +214,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -274,7 +269,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -334,7 +328,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -381,7 +374,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -433,7 +425,6 @@ impl File { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index ba106ff..8427e8f 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -22,7 +22,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 67ab620..032fe24 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -22,7 +22,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 28b55b8..dd3525e 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -9,7 +9,6 @@ //! Create a new file and write some bytes to it: //! //! ```no_run -//! # #![feature(async_await)] //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # //! use async_std::fs::File; diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 72ce24c..54a6f76 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -32,7 +32,6 @@ use crate::task::blocking; /// Opening a file for reading: /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -48,7 +47,6 @@ use crate::task::blocking; /// Opening a file for both reading and writing, creating it if it doesn't exist: /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -73,7 +71,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -96,7 +93,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -123,7 +119,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -169,7 +164,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -196,7 +190,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -226,7 +219,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -263,7 +255,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; @@ -316,7 +307,6 @@ impl OpenOptions { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::OpenOptions; diff --git a/src/fs/read.rs b/src/fs/read.rs index ba79d50..105ae79 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -24,7 +24,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index 7bdd532..dffab3a 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -30,7 +30,6 @@ use crate::task::{blocking, Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 0612b28..9ab87e5 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index b6bd858..c5ce36b 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index 6377774..275e799 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 46a253b..0be81df 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 4e691a2..09bd07e 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/rename.rs b/src/fs/rename.rs index 1940d82..05f755a 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -21,7 +21,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 9634221..05e5bda 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index 1c34bb9..78c95be 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -20,7 +20,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/fs/write.rs b/src/fs/write.rs index 83a1938..ceaf0fc 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -22,7 +22,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs; diff --git a/src/future/pending.rs b/src/future/pending.rs index 57a40ea..41284f5 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -2,7 +2,6 @@ /// /// # Examples /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use std::time::Duration; diff --git a/src/future/ready.rs b/src/future/ready.rs index 6438c60..04f37b8 100644 --- a/src/future/ready.rs +++ b/src/future/ready.rs @@ -7,7 +7,6 @@ /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::future; diff --git a/src/io/buf_read.rs b/src/io/buf_read.rs index c0882fd..b13aea7 100644 --- a/src/io/buf_read.rs +++ b/src/io/buf_read.rs @@ -46,7 +46,6 @@ pub trait BufRead { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -97,7 +96,6 @@ pub trait BufRead { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -138,7 +136,6 @@ pub trait BufRead { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index bf9adce..2ad10cc 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -31,7 +31,6 @@ const DEFAULT_CAPACITY: usize = 8 * 1024; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -60,7 +59,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -79,7 +77,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -117,7 +114,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -139,7 +135,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -161,7 +156,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -183,7 +177,6 @@ impl BufReader { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; diff --git a/src/io/copy.rs b/src/io/copy.rs index 829bf0c..961c826 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -28,7 +28,6 @@ use crate::io; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; diff --git a/src/io/empty.rs b/src/io/empty.rs index 4e5236a..a832677 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -11,7 +11,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ```rust -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; diff --git a/src/io/mod.rs b/src/io/mod.rs index 13b91d6..fd41587 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -9,7 +9,6 @@ //! Read a line from the standard input: //! //! ```no_run -//! # #![feature(async_await)] //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # //! use async_std::io; diff --git a/src/io/read.rs b/src/io/read.rs index c76217b..cf3732f 100644 --- a/src/io/read.rs +++ b/src/io/read.rs @@ -52,7 +52,6 @@ pub trait Read { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -102,7 +101,6 @@ pub trait Read { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -140,7 +138,6 @@ pub trait Read { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -193,7 +190,6 @@ pub trait Read { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; diff --git a/src/io/seek.rs b/src/io/seek.rs index eefb96f..9250faa 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -43,7 +43,6 @@ pub trait Seek { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; diff --git a/src/io/sink.rs b/src/io/sink.rs index f4f5fdd..ec38431 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -11,7 +11,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ```rust -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 4c81abb..73b64ce 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -17,7 +17,6 @@ use crate::task::{blocking, Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; diff --git a/src/io/stdin.rs b/src/io/stdin.rs index a3b9368..bd4c111 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -18,7 +18,6 @@ use crate::task::{blocking, Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; @@ -92,7 +91,6 @@ impl Stdin { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; diff --git a/src/io/stdout.rs b/src/io/stdout.rs index abf42ef..5045d11 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -17,7 +17,6 @@ use crate::task::{blocking, Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 5197981..36112cb 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -13,7 +13,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use std::time::Duration; diff --git a/src/io/write.rs b/src/io/write.rs index c05e151..0fba81b 100644 --- a/src/io/write.rs +++ b/src/io/write.rs @@ -47,7 +47,6 @@ pub trait Write { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -68,7 +67,6 @@ pub trait Write { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; @@ -114,7 +112,6 @@ pub trait Write { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::fs::File; diff --git a/src/lib.rs b/src/lib.rs index a9a0685..e5615b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,7 +14,6 @@ //! Spawn a task and block the current thread on its result: //! //! ``` -//! # #![feature(async_await)] //! use async_std::task; //! //! fn main() { @@ -27,7 +26,6 @@ //! See [here](https://github.com/async-rs/async-std/tree/master/examples) //! for more examples. -#![feature(async_await)] #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] #![doc(test(attr(deny(rust_2018_idioms, warnings))))] diff --git a/src/net/mod.rs b/src/net/mod.rs index 6ec9439..db4bd3c 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -13,7 +13,6 @@ //! A simple UDP echo server: //! //! ```no_run -//! # #![feature(async_await)] //! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { //! # //! use async_std::net::UdpSocket; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index e85d90b..60a9689 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -30,7 +30,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::io; @@ -70,7 +69,6 @@ impl TcpListener { /// Create a TCP listener bound to 127.0.0.1:0: /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpListener; @@ -119,7 +117,6 @@ impl TcpListener { /// ## Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpListener; @@ -172,7 +169,6 @@ impl TcpListener { /// ## Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpListener; @@ -200,7 +196,6 @@ impl TcpListener { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpListener; diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 585d1ae..6687a1b 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -34,7 +34,6 @@ use crate::task::{Context, Poll}; /// ## Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -70,7 +69,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -155,7 +153,6 @@ impl TcpStream { /// ## Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -174,7 +171,6 @@ impl TcpStream { /// ## Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -197,7 +193,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -221,7 +216,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -248,7 +242,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -286,7 +279,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -313,7 +305,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::TcpStream; @@ -339,7 +330,6 @@ impl TcpStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use std::net::Shutdown; diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 04f1c55..3e9e749 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -29,7 +29,6 @@ use crate::task::Poll; /// ## Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -65,7 +64,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -114,7 +112,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -135,7 +132,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -188,7 +184,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -230,7 +225,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -265,7 +259,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -308,7 +301,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::net::UdpSocket; @@ -442,7 +434,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use std::net::Ipv4Addr; @@ -472,7 +463,6 @@ impl UdpSocket { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use std::net::{Ipv6Addr, SocketAddr}; diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index cb6b346..16d7cab 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -18,7 +18,6 @@ use crate::task::blocking; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::fs::symlink; diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 8ad24c5..62debc4 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -29,7 +29,6 @@ use crate::task::{blocking, Poll}; /// ## Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -63,7 +62,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -83,7 +81,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -104,7 +101,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -132,7 +128,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -153,7 +148,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -176,7 +170,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -198,7 +191,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -232,7 +224,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -266,7 +257,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -299,7 +289,6 @@ impl UnixDatagram { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; @@ -336,7 +325,6 @@ impl UnixDatagram { /// ## Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixDatagram; diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index ca55e90..74b0a28 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -33,7 +33,6 @@ use crate::task::{blocking, Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixListener; @@ -62,7 +61,6 @@ impl UnixListener { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixListener; @@ -88,7 +86,6 @@ impl UnixListener { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixListener; @@ -136,7 +133,6 @@ impl UnixListener { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixListener; @@ -161,7 +157,6 @@ impl UnixListener { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixListener; diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index c7bdedf..74afdee 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -26,7 +26,6 @@ use crate::task::{blocking, Context, Poll}; /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixStream; @@ -53,7 +52,6 @@ impl UnixStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixStream; @@ -115,7 +113,6 @@ impl UnixStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixStream; @@ -142,7 +139,6 @@ impl UnixStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixStream; @@ -161,7 +157,6 @@ impl UnixStream { /// # Examples /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixStream; @@ -183,7 +178,6 @@ impl UnixStream { /// [`Shutdown`]: https://doc.rust-lang.org/std/net/enum.Shutdown.html /// /// ```no_run - /// # #![feature(async_await)] /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # /// use async_std::os::unix::net::UnixStream; diff --git a/src/stream/empty.rs b/src/stream/empty.rs index c03146e..4445e43 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -8,7 +8,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index a760a9b..8dcc6d5 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -7,7 +7,6 @@ //! # Examples //! //! ``` -//! # #![feature(async_await)] //! # fn main() { async_std::task::block_on(async { //! # //! use async_std::prelude::*; diff --git a/src/stream/once.rs b/src/stream/once.rs index 7c1e45b..160d6cf 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -7,7 +7,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index 4b4b442..d42b727 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -7,7 +7,6 @@ use crate::task::{Context, Poll}; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; diff --git a/src/stream/stream.rs b/src/stream/stream.rs index e4e5012..6623207 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -7,7 +7,6 @@ //! # Examples //! //! ``` -//! # #![feature(async_await)] //! # fn main() { async_std::task::block_on(async { //! # //! use async_std::prelude::*; @@ -69,7 +68,6 @@ pub trait Stream { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; @@ -91,7 +89,6 @@ pub trait Stream { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 1a4b068..42dcb60 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -9,7 +9,6 @@ //! Spawn a task that updates an integer protected by a mutex: //! //! ``` -//! # #![feature(async_await)] //! # fn main() { async_std::task::block_on(async { //! # //! use std::sync::Arc; diff --git a/src/sync/mutex.rs b/src/sync/mutex.rs index d0f110b..8ae51ad 100644 --- a/src/sync/mutex.rs +++ b/src/sync/mutex.rs @@ -24,7 +24,6 @@ const BLOCKED: usize = 1 << 1; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use std::sync::Arc; @@ -83,7 +82,6 @@ impl Mutex { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use std::sync::Arc; @@ -198,7 +196,6 @@ impl Mutex { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use std::sync::Arc; @@ -252,7 +249,6 @@ impl Mutex { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::Mutex; diff --git a/src/sync/rwlock.rs b/src/sync/rwlock.rs index 647a3bc..36f475b 100644 --- a/src/sync/rwlock.rs +++ b/src/sync/rwlock.rs @@ -33,7 +33,6 @@ const READ_COUNT_MASK: usize = !(ONE_READ - 1); /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; @@ -90,7 +89,6 @@ impl RwLock { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; @@ -213,7 +211,6 @@ impl RwLock { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; @@ -256,7 +253,6 @@ impl RwLock { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; @@ -378,7 +374,6 @@ impl RwLock { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; @@ -419,7 +414,6 @@ impl RwLock { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// use async_std::sync::RwLock; /// /// let lock = RwLock::new(10); @@ -437,7 +431,6 @@ impl RwLock { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::sync::RwLock; diff --git a/src/task/local.rs b/src/task/local.rs index eb34801..8897696 100644 --- a/src/task/local.rs +++ b/src/task/local.rs @@ -20,7 +20,6 @@ use super::pool; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # /// use std::cell::Cell; /// @@ -92,7 +91,6 @@ impl LocalKey { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # /// use std::cell::Cell; /// @@ -132,7 +130,6 @@ impl LocalKey { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # /// use std::cell::Cell; /// diff --git a/src/task/mod.rs b/src/task/mod.rs index 4f572f3..42b7e08 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -10,7 +10,6 @@ //! Spawn a task and await its result: //! //! ``` -//! # #![feature(async_await)] //! # fn main() { async_std::task::block_on(async { //! # //! use async_std::task; diff --git a/src/task/pool.rs b/src/task/pool.rs index 331b03c..ab09ffb 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -29,7 +29,6 @@ use crate::io; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::task; @@ -51,7 +50,6 @@ pub fn current() -> Task { /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::task; @@ -83,7 +81,6 @@ where /// # Examples /// /// ```no_run -/// # #![feature(async_await)] /// use async_std::task; /// /// fn main() { diff --git a/src/task/sleep.rs b/src/task/sleep.rs index fa8804e..9db09ff 100644 --- a/src/task/sleep.rs +++ b/src/task/sleep.rs @@ -14,7 +14,6 @@ use crate::io; /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use std::time::Duration; diff --git a/src/task/task.rs b/src/task/task.rs index 03947ae..c0d52db 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -65,7 +65,6 @@ impl JoinHandle { /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::task; @@ -98,7 +97,6 @@ impl Future for JoinHandle { /// # Examples /// /// ``` -/// # #![feature(async_await)] /// # /// use async_std::task; /// diff --git a/tests/block_on.rs b/tests/block_on.rs index 7c6a316..c422d06 100644 --- a/tests/block_on.rs +++ b/tests/block_on.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use async_std::task; #[test] diff --git a/tests/mutex.rs b/tests/mutex.rs index 8871f59..fd1c07b 100644 --- a/tests/mutex.rs +++ b/tests/mutex.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use std::sync::Arc; use async_std::prelude::*; diff --git a/tests/rwlock.rs b/tests/rwlock.rs index 2d1e0e1..ff25e86 100644 --- a/tests/rwlock.rs +++ b/tests/rwlock.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use std::cell::Cell; use std::num::Wrapping; use std::pin::Pin; diff --git a/tests/task_local.rs b/tests/task_local.rs index e910b07..813185c 100644 --- a/tests/task_local.rs +++ b/tests/task_local.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use std::sync::atomic::{AtomicBool, Ordering}; use async_std::task; diff --git a/tests/tcp.rs b/tests/tcp.rs index a2f47ce..c8281d7 100644 --- a/tests/tcp.rs +++ b/tests/tcp.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use async_std::io; use async_std::net::{TcpListener, TcpStream}; use async_std::prelude::*; diff --git a/tests/udp.rs b/tests/udp.rs index 24d8e39..319dc74 100644 --- a/tests/udp.rs +++ b/tests/udp.rs @@ -1,5 +1,3 @@ -#![feature(async_await)] - use async_std::io; use async_std::net::UdpSocket; use async_std::task; diff --git a/tests/uds.rs b/tests/uds.rs index f062bf9..9dbda87 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -1,5 +1,4 @@ #![cfg(unix)] -#![feature(async_await)] use async_std::io; use async_std::os::unix::net::UnixDatagram; From 2644089e496c876f36278f2feda949287ceb13c6 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 21 Aug 2019 11:24:24 -0700 Subject: [PATCH 004/194] Changelog for 0.99.4 --- CHANGELOG.md | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 744ced8..0b26e8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ -# Version 0.99.3 +# Changelog + +All notable changes to async-std will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://book.async.rs/overview/stability-guarantees.html). + +## [Unreleased] + +# [0.99.4] - 2019-08-21 + +## Changes + +- Many small changes in the book, mostly typos +- Documentation fixes correcting examples +- Now works with recent nightly with stabilised async/await (> 2019-08-21) + +# [0.99.3] - 2019-08-16 - Initial beta release + +[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.99.3...HEAD +[0.99.4]: https://github.com/async-rs/async-std/compare/v0.99.3...v0.99.4 +[0.99.3]: https://github.com/async-rs/async-std/tree/v0.99.3 From b95dd13d3bf760ba23861e561c172958404749f7 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 21 Aug 2019 12:57:00 +0300 Subject: [PATCH 005/194] add tests for io::timeout --- tests/io_timeout.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/io_timeout.rs diff --git a/tests/io_timeout.rs b/tests/io_timeout.rs new file mode 100644 index 0000000..85a17ab --- /dev/null +++ b/tests/io_timeout.rs @@ -0,0 +1,40 @@ +use std::time::Duration; + +use async_std::io; +use async_std::task; + +#[test] +#[should_panic(expected = "timed out")] +fn io_timeout_timedout() { + task::block_on(async { + io::timeout(Duration::from_secs(1), async { + let stdin = io::stdin(); + let mut line = String::new(); + let _n = stdin.read_line(&mut line).await?; + Ok(()) + }) + .await + .unwrap(); // We should panic with a timeout error + }); +} + +#[test] +#[should_panic(expected = "My custom error")] +fn io_timeout_future_err() { + task::block_on(async { + io::timeout(Duration::from_secs(1), async { + Err::<(), io::Error>(io::Error::new(io::ErrorKind::Other, "My custom error")) + }) + .await + .unwrap(); // We should panic with our own error + }); +} + +#[test] +fn io_timeout_future_ok() { + task::block_on(async { + io::timeout(Duration::from_secs(1), async { Ok(()) }) + .await + .unwrap(); // We shouldn't panic at all + }); +} From 393b72e4fca76fe533733a5be47bb4bdd5fa9563 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Wed, 21 Aug 2019 20:04:31 -0700 Subject: [PATCH 006/194] Release version 0.99.4 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index bddd8b3..07fddb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.3" +version = "0.99.4" authors = [ "Stjepan Glavina ", "The async-std Project Developers", From c9d8b197cad0565693d2a552eb278e4126e6eb18 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Thu, 22 Aug 2019 07:53:32 -0700 Subject: [PATCH 007/194] Add dependencies explanation to book --- docs/src/tutorial/specification.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/src/tutorial/specification.md b/docs/src/tutorial/specification.md index 884a065..ce2abcb 100644 --- a/docs/src/tutorial/specification.md +++ b/docs/src/tutorial/specification.md @@ -45,3 +45,11 @@ $ rustup override add nightly $ rustc --version rustc 1.38.0-nightly (c4715198b 2019-08-05) ``` + +Add the following lines to `Cargo.toml`: + +```toml +[dependencies] +futures-preview = { version = "0.3.0-alpha.18", features = [ "async-await", "nightly" ] } +async-std = "0.99" +``` From c61f4c8b81537d925bb572b159cfc70ec08739d1 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 22 Aug 2019 23:17:52 +0200 Subject: [PATCH 008/194] Update receiving_messages.md --- docs/src/tutorial/receiving_messages.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 0926667..286f091 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -8,6 +8,7 @@ We need to: 3. parse the rest of the lines as a `login: message` ```rust +use async_std::io::BufReader; use async_std::net::TcpStream; async fn server(addr: impl ToSocketAddrs) -> Result<()> { From be616f35bfe02ffff81421652a452266a47b65d1 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 22 Aug 2019 23:56:13 +0200 Subject: [PATCH 009/194] Update sending_messages.md --- docs/src/tutorial/sending_messages.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index da62ad5..0722bc0 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -14,6 +14,7 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the ```rust use futures::channel::mpsc; // 1 use futures::SinkExt; +use std::sync::Arc; type Sender = mpsc::UnboundedSender; // 2 type Receiver = mpsc::UnboundedReceiver; From 794c12fe7e9ba8c52ccc9f3e11206646b94cca07 Mon Sep 17 00:00:00 2001 From: Rui Loura Date: Fri, 23 Aug 2019 06:37:54 -0700 Subject: [PATCH 010/194] Fix some typos in tutorial (#98) * fix typo in tutorial * add async_std::io::BufReader to tutorial code * writers in clean_shutdown.md return unit type --- docs/src/tutorial/accept_loop.md | 2 +- docs/src/tutorial/clean_shutdown.md | 2 +- docs/src/tutorial/receiving_messages.md | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorial/accept_loop.md b/docs/src/tutorial/accept_loop.md index 8a91321..c46019d 100644 --- a/docs/src/tutorial/accept_loop.md +++ b/docs/src/tutorial/accept_loop.md @@ -70,5 +70,5 @@ fn main() -> Result<()> { The crucial thing to realise that is in Rust, unlike other languages, calling an async function does **not** run any code. Async functions only construct futures, which are inert state machines. To start stepping through the future state-machine in an async function, you should use `.await`. -In a non-async function, a way to execute a future is to handle it to the executor. +In a non-async function, a way to execute a future is to hand it to the executor. In this case, we use `task::block_on` to execute a future on the current thread and block until it's done. diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 714a926..0f54dac 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -69,7 +69,7 @@ async fn broker(mut events: Receiver) -> Result<()> { } drop(peers); // 3 for writer in writers { // 4 - writer.await?; + writer.await; } Ok(()) } diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 286f091..3dc1903 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -10,6 +10,7 @@ We need to: ```rust use async_std::io::BufReader; use async_std::net::TcpStream; +use async_std::io::BufReader; async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; From 6d6328a9c8c28ec612ca8d1ba1ff9c30bdaa6857 Mon Sep 17 00:00:00 2001 From: Zach Kemp Date: Fri, 23 Aug 2019 17:16:17 -0700 Subject: [PATCH 011/194] use `broker_handle` to avoid overloading the `broker` fn (#100) --- docs/src/tutorial/clean_shutdown.md | 4 ++-- docs/src/tutorial/handling_disconnection.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 0f54dac..4a0c3b7 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -25,7 +25,7 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let (broker_sender, broker_receiver) = mpsc::unbounded(); - let broker = task::spawn(broker(broker_receiver)); + let broker_handle = task::spawn(broker(broker_receiver)); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let stream = stream?; @@ -33,7 +33,7 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { spawn_and_log_error(client(broker_sender.clone(), stream)); } drop(broker_sender); // 1 - broker.await?; // 5 + broker_handle.await?; // 5 Ok(()) } ``` diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 4cda69f..5217a82 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -130,7 +130,7 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let (broker_sender, broker_receiver) = mpsc::unbounded(); - let broker = task::spawn(broker(broker_receiver)); + let broker_handle = task::spawn(broker(broker_receiver)); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let stream = stream?; @@ -138,7 +138,7 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { spawn_and_log_error(client(broker_sender.clone(), stream)); } drop(broker_sender); - broker.await; + broker_handle.await; Ok(()) } From 34802cae7eb93d39bbbef54ae0c443c01cb8edf8 Mon Sep 17 00:00:00 2001 From: Oleksii Kachaiev Date: Fri, 23 Aug 2019 17:17:12 -0700 Subject: [PATCH 012/194] [docs] Make sure that "All Together" code compiles (#104) Added missing `spawn_and_log_error` function declaration --- docs/src/tutorial/all_together.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 64fba71..74c6987 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -125,6 +125,18 @@ async fn broker(mut events: Receiver) -> Result<()> { } Ok(()) } + +fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> +where + F: Future> + Send + 'static, +{ + task::spawn(async move { + if let Err(e) = fut.await { + eprintln!("{}", e) + } + }) +} + ``` 1. Inside the `server`, we create the broker's channel and `task`. From 05a9bd1abd2840917b10efce21516956f8b6e986 Mon Sep 17 00:00:00 2001 From: Darin Morrison Date: Sun, 18 Aug 2019 10:11:39 -0700 Subject: [PATCH 013/194] Enable "async-await" for futures-preview (select!) [ci skip] --- Cargo.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 07fddb4..6aabfec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,6 @@ docs = [] async-task = "1.0.0" cfg-if = "0.1.9" crossbeam-channel = "0.3.9" -futures-preview = "0.3.0-alpha.17" futures-timer = "0.3.0" lazy_static = "1.3.0" log = { version = "0.4.8", features = ["kv_unstable"] } @@ -37,6 +36,10 @@ num_cpus = "1.10.0" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" +[dependencies.futures-preview] +version = "0.3.0-alpha.17" +features = ["async-await", "nightly"] + [dev-dependencies] femme = "1.1.0" tempdir = "0.3.7" From 58e783b194a7e59d4131c89498840f6929bbfd7d Mon Sep 17 00:00:00 2001 From: Darin Morrison Date: Sun, 18 Aug 2019 13:49:52 -0700 Subject: [PATCH 014/194] Add empty data.csv for book tests [ci skip] --- docs/src/concepts/data.csv | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/src/concepts/data.csv diff --git a/docs/src/concepts/data.csv b/docs/src/concepts/data.csv new file mode 100644 index 0000000..e69de29 From 6302805b549e1c7da44d30c9121ab3ac00826dcb Mon Sep 17 00:00:00 2001 From: Darin Morrison Date: Sat, 17 Aug 2019 08:48:39 -0700 Subject: [PATCH 015/194] Fix book tests [ci skip] --- docs/src/concepts/futures.md | 33 ++-- docs/src/concepts/tasks.md | 47 +++-- docs/src/patterns/small-patterns.md | 12 +- docs/src/security/policy.md | 2 +- docs/src/tutorial/accept_loop.md | 60 ++++-- docs/src/tutorial/all_together.md | 40 ++-- docs/src/tutorial/clean_shutdown.md | 177 +++++++++++++++++- .../connecting_readers_and_writers.md | 43 ++++- docs/src/tutorial/handling_disconnection.md | 88 +++++---- docs/src/tutorial/implementing_a_client.md | 33 ++-- docs/src/tutorial/receiving_messages.md | 67 ++++++- docs/src/tutorial/sending_messages.md | 11 +- 12 files changed, 479 insertions(+), 134 deletions(-) diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md index d481023..02a37ba 100644 --- a/docs/src/concepts/futures.md +++ b/docs/src/concepts/futures.md @@ -50,12 +50,14 @@ Remember the talk about "deferred computation" in the intro? That's all it is. I Let's have a look at a simple function, specifically the return value: -```rust +```rust,edition2018 +# use std::{fs::File, io::{self, Read}}; +# fn read_file(path: &str) -> Result { - let mut file = File.open(path)?; + let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; - contents + Ok(contents) } ``` @@ -64,12 +66,14 @@ Note that this return value talks about the past. The past has a drawback: all d But we wanted to abstract over *computation* and let someone else choose how to run it. That's fundamentally incompatible with looking at the results of previous computation all the time. So, let's find a type that *describes* a computation without running it. Let's look at the function again: -```rust +```rust,edition2018 +# use std::{fs::File, io::{self, Read}}; +# fn read_file(path: &str) -> Result { - let mut file = File.open(path)?; + let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; - contents + Ok(contents) } ``` @@ -79,10 +83,11 @@ This is the moment where we could reach for [threads](https://en.wikipedia.org/w What we are searching for is something that represents ongoing work towards a result in the future. Whenever we say "something" in Rust, we almost always mean a trait. Let's start with an incomplete definition of the `Future` trait: -```rust +```rust,edition2018 +# use std::{pin::Pin, task::{Context, Poll}}; +# trait Future { type Output; - fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll; } ``` @@ -105,14 +110,16 @@ Note that calling `poll` again after case 1 happened may result in confusing beh While the `Future` trait has existed in Rust for a while, it was inconvenient to build and describe them. For this, Rust now has a special syntax: `async`. The example from above, implemented with `async-std`, would look like this: -```rust -use async_std::fs::File; - +```rust,edition2018 +# extern crate async_std; +# use async_std::{fs::File, io::Read}; +# use std::io; +# async fn read_file(path: &str) -> Result { - let mut file = File.open(path).await?; + let mut file = File::open(path).await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; - contents + Ok(contents) } ``` diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index d8c71c9..5c62811 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -4,15 +4,16 @@ Now that we know what Futures are, we want to run them! In `async-std`, the [`tasks`][tasks] module is responsible for this. The simplest way is using the `block_on` function: -```rust -use async_std::fs::File; -use async_std::task; - +```rust,edition2018 +# extern crate async_std; +# use async_std::{fs::File, io::Read, task}; +# use std::io; +# async fn read_file(path: &str) -> Result { let mut file = File::open(path).await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; - contents + Ok(contents) } fn main() { @@ -31,24 +32,35 @@ fn main() { This asks the runtime baked into `async_std` to execute the code that reads a file. Let's go one by one, though, inside to outside. -```rust +```rust,edition2018 +# extern crate async_std; +# use async_std::{fs::File, io::Read, task}; +# use std::io; +# +# async fn read_file(path: &str) -> Result { +# let mut file = File::open(path).await?; +# let mut contents = String::new(); +# file.read_to_string(&mut contents).await?; +# Ok(contents) +# } +# async { let result = read_file("data.csv").await; match result { Ok(s) => println!("{}", s), Err(e) => println!("Error reading file: {:?}", e) } -} +}; ``` This is an `async` *block*. Async blocks are necessary to call `async` functions, and will instruct the compiler to include all the relevant instructions to do so. In Rust, all blocks return a value and `async` blocks happen to return a value of the kind `Future`. But let's get to the interesting part: -```rust - -task::spawn(async { }) - +```rust,edition2018 +# extern crate async_std; +# use async_std::task; +task::spawn(async { }); ``` `spawn` takes a `Future` and starts running it on a `Task`. It returns a `JoinHandle`. Futures in Rust are sometimes called *cold* Futures. You need something that starts running them. To run a Future, there may be some additional bookkeeping required, e.g. whether it's running or finished, where it is being placed in memory and what the current state is. This bookkeeping part is abstracted away in a `Task`. @@ -72,7 +84,9 @@ Tasks in `async_std` are one of the core abstractions. Much like Rust's `thread` `Task`s are assumed to run _concurrently_, potentially by sharing a thread of execution. This means that operations blocking an _operating system thread_, such as `std::thread::sleep` or io function from Rust's `std` library will _stop execution of all tasks sharing this thread_. Other libraries (such as database drivers) have similar behaviour. Note that _blocking the current thread_ is not in and by itself bad behaviour, just something that does not mix well with the concurrent execution model of `async-std`. Essentially, never do this: -```rust +```rust,edition2018 +# extern crate async_std; +# use async_std::task; fn main() { task::block_on(async { // this is std::fs, which blocks @@ -91,7 +105,9 @@ In case of `panic`, behaviour differs depending on whether there's a reasonable In practice, that means that `block_on` propagates panics to the blocking component: -```rust +```rust,edition2018,should_panic +# extern crate async_std; +# use async_std::task; fn main() { task::block_on(async { panic!("test"); @@ -106,7 +122,10 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. While panicing a spawned task will abort: -```rust +```rust,edition2018,should_panic +# extern crate async_std; +# use async_std::task; +# use std::time::Duration; task::spawn(async { panic!("test"); }); diff --git a/docs/src/patterns/small-patterns.md b/docs/src/patterns/small-patterns.md index 2250d19..1bc1d90 100644 --- a/docs/src/patterns/small-patterns.md +++ b/docs/src/patterns/small-patterns.md @@ -6,11 +6,11 @@ A collection of small, useful patterns. `async-std` doesn't provide a `split()` method on `io` handles. Instead, splitting a stream into a read and write half can be done like this: -```rust -use async_std::io; - -async fn echo(stream: io::TcpStream) { +```rust,edition2018 +# extern crate async_std; +use async_std::{io, net::TcpStream}; +async fn echo(stream: TcpStream) { let (reader, writer) = &mut (&stream, &stream); - io::copy(reader, writer).await?; + io::copy(reader, writer).await; } -``` \ No newline at end of file +``` diff --git a/docs/src/security/policy.md b/docs/src/security/policy.md index 990d811..06a08b4 100644 --- a/docs/src/security/policy.md +++ b/docs/src/security/policy.md @@ -32,7 +32,7 @@ This policy is adapted from the [Rust project](https://www.rust-lang.org/policie ## PGP Key -``` +```text -----BEGIN PGP PUBLIC KEY BLOCK----- mQENBF1Wu/ABCADJaGt4HwSlqKB9BGHWYKZj/6mTMbmc29vsEOcCSQKo6myCf9zc diff --git a/docs/src/tutorial/accept_loop.md b/docs/src/tutorial/accept_loop.md index c46019d..d40d348 100644 --- a/docs/src/tutorial/accept_loop.md +++ b/docs/src/tutorial/accept_loop.md @@ -2,15 +2,14 @@ Let's implement the scaffold of the server: a loop that binds a TCP socket to an address and starts accepting connections. - First of all, let's add required import boilerplate: -```rust +```rust,edition2018 +# extern crate async_std; use std::net::ToSocketAddrs; // 1 - use async_std::{ prelude::*, // 2 - task, // 3 + task, // 3 net::TcpListener, // 4 }; @@ -18,7 +17,7 @@ type Result = std::result::Result ``` 1. `async_std` uses `std` types where appropriate. - We'll need `ToSocketAddrs` to specify address to listen on. + We'll need `ToSocketAddrs` to specify address to listen on. 2. `prelude` re-exports some traits required to work with futures and streams. 3. The `task` module roughly corresponds to the `std::thread` module, but tasks are much lighter weight. A single thread can run many tasks. @@ -27,10 +26,18 @@ type Result = std::result::Result To propagate the errors, we will use a boxed error trait object. Do you know that there's `From<&'_ str> for Box` implementation in stdlib, which allows you to use strings with `?` operator? - Now we can write the server's accept loop: -```rust +```rust,edition2018 +# extern crate async_std; +# use async_std::{ +# net::TcpListener, +# prelude::Stream, +# }; +# use std::net::ToSocketAddrs; +# +# type Result = std::result::Result>; +# async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1 let listener = TcpListener::bind(addr).await?; // 2 let mut incoming = listener.incoming(); @@ -48,20 +55,39 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1 Mirroring API of `std` is an explicit design goal of `async_std`. 3. Here, we would like to iterate incoming sockets, just how one would do in `std`: - ```rust - let listener: std::net::TcpListener = unimplemented!(); - for stream in listener.incoming() { +```rust,edition2018,should_panic +let listener: std::net::TcpListener = unimplemented!(); +for stream in listener.incoming() { +} +``` - } - ``` - - Unfortunately this doesn't quite work with `async` yet, because there's no support for `async` for-loops in the language yet. - For this reason we have to implement the loop manually, by using `while let Some(item) = iter.next().await` pattern. +Unfortunately this doesn't quite work with `async` yet, because there's no support for `async` for-loops in the language yet. +For this reason we have to implement the loop manually, by using `while let Some(item) = iter.next().await` pattern. Finally, let's add main: -```rust -fn main() -> Result<()> { +```rust,edition2018 +# extern crate async_std; +# use async_std::{ +# net::TcpListener, +# prelude::Stream, +# task, +# }; +# use std::net::ToSocketAddrs; +# +# type Result = std::result::Result>; +# +# async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1 +# let listener = TcpListener::bind(addr).await?; // 2 +# let mut incoming = listener.incoming(); +# while let Some(stream) = incoming.next().await { // 3 +# // TODO +# } +# Ok(()) +# } +# +// main +fn run() -> Result<()> { let fut = server("127.0.0.1:8080"); task::block_on(fut) } diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 74c6987..178f537 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -1,36 +1,46 @@ - ## All Together At this point, we only need to start the broker to get a fully-functioning (in the happy case!) chat: -```rust -use std::{ - net::ToSocketAddrs, - sync::Arc, - collections::hash_map::{HashMap, Entry}, +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +use async_std::{ + io::{self, BufReader}, + net::{TcpListener, TcpStream}, + prelude::*, + task, }; - use futures::{ channel::mpsc, SinkExt, }; - -use async_std::{ - io::BufReader, - prelude::*, - task, - net::{TcpListener, TcpStream}, +use std::{ + collections::hash_map::{HashMap, Entry}, + net::ToSocketAddrs, + sync::Arc, }; type Result = std::result::Result>; type Sender = mpsc::UnboundedSender; type Receiver = mpsc::UnboundedReceiver; - -fn main() -> Result<()> { +// main +fn run() -> Result<()> { task::block_on(server("127.0.0.1:8080")) } +fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> +where + F: Future> + Send + 'static, +{ + task::spawn(async move { + if let Err(e) = fut.await { + eprintln!("{}", e) + } + }) +} + async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 4a0c3b7..1c2fc76 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -20,7 +20,122 @@ In `a-chat`, we already have an unidirectional flow of messages: `reader -> brok However, we never wait for broker and writers, which might cause some messages to get dropped. Let's add waiting to the server: -```rust +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +# use async_std::{ +# io::{self, BufReader}, +# net::{TcpListener, TcpStream}, +# prelude::*, +# task, +# }; +# use futures::{ +# channel::mpsc, +# SinkExt, +# }; +# use std::{ +# collections::hash_map::{HashMap, Entry}, +# net::ToSocketAddrs, +# sync::Arc, +# }; +# +# type Result = std::result::Result>; +# type Sender = mpsc::UnboundedSender; +# type Receiver = mpsc::UnboundedReceiver; +# +# fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> +# where +# F: Future> + Send + 'static, +# { +# task::spawn(async move { +# if let Err(e) = fut.await { +# eprintln!("{}", e) +# } +# }) +# } +# +# +# async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { +# let stream = Arc::new(stream); // 2 +# let reader = BufReader::new(&*stream); +# let mut lines = reader.lines(); +# +# let name = match lines.next().await { +# None => Err("peer disconnected immediately")?, +# Some(line) => line?, +# }; +# broker.send(Event::NewPeer { name: name.clone(), stream: Arc::clone(&stream) }).await // 3 +# .unwrap(); +# +# while let Some(line) = lines.next().await { +# let line = line?; +# let (dest, msg) = match line.find(':') { +# None => continue, +# Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), +# }; +# let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); +# let msg: String = msg.trim().to_string(); +# +# broker.send(Event::Message { // 4 +# from: name.clone(), +# to: dest, +# msg, +# }).await.unwrap(); +# } +# Ok(()) +# } +# +# async fn client_writer( +# mut messages: Receiver, +# stream: Arc, +# ) -> Result<()> { +# let mut stream = &*stream; +# while let Some(msg) = messages.next().await { +# stream.write_all(msg.as_bytes()).await?; +# } +# Ok(()) +# } +# +# #[derive(Debug)] +# enum Event { +# NewPeer { +# name: String, +# stream: Arc, +# }, +# Message { +# from: String, +# to: Vec, +# msg: String, +# }, +# } +# +# async fn broker(mut events: Receiver) -> Result<()> { +# let mut peers: HashMap> = HashMap::new(); +# +# while let Some(event) = events.next().await { +# match event { +# Event::Message { from, to, msg } => { +# for addr in to { +# if let Some(peer) = peers.get_mut(&addr) { +# peer.send(format!("from {}: {}\n", from, msg)).await? +# } +# } +# } +# Event::NewPeer { name, stream} => { +# match peers.entry(name) { +# Entry::Occupied(..) => (), +# Entry::Vacant(entry) => { +# let (client_sender, client_receiver) = mpsc::unbounded(); +# entry.insert(client_sender); // 4 +# spawn_and_log_error(client_writer(client_receiver, stream)); // 5 +# } +# } +# } +# } +# } +# Ok(()) +# } +# async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; @@ -40,11 +155,67 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { And to the broker: -```rust +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +# use async_std::{ +# io::{self, BufReader}, +# net::{TcpListener, TcpStream}, +# prelude::*, +# task, +# }; +# use futures::{ +# channel::mpsc, +# SinkExt, +# }; +# use std::{ +# collections::hash_map::{HashMap, Entry}, +# net::ToSocketAddrs, +# sync::Arc, +# }; +# +# type Result = std::result::Result>; +# type Sender = mpsc::UnboundedSender; +# type Receiver = mpsc::UnboundedReceiver; +# +# async fn client_writer( +# mut messages: Receiver, +# stream: Arc, +# ) -> Result<()> { +# let mut stream = &*stream; +# while let Some(msg) = messages.next().await { +# stream.write_all(msg.as_bytes()).await?; +# } +# Ok(()) +# } +# +# fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> +# where +# F: Future> + Send + 'static, +# { +# task::spawn(async move { +# if let Err(e) = fut.await { +# eprintln!("{}", e) +# } +# }) +# } +# +# #[derive(Debug)] +# enum Event { +# NewPeer { +# name: String, +# stream: Arc, +# }, +# Message { +# from: String, +# to: Vec, +# msg: String, +# }, +# } +# async fn broker(mut events: Receiver) -> Result<()> { let mut writers = Vec::new(); let mut peers: HashMap> = HashMap::new(); - while let Some(event) = events.next().await { // 2 match event { Event::Message { from, to, msg } => { diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index 531656b..7aba3a1 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -10,7 +10,48 @@ We can create a dedicated broker tasks which owns the `peers` map and communicat By hiding `peers` inside such an "actor" task, we remove the need for mutxes and also make serialization point explicit. The order of events "Bob sends message to Alice" and "Alice joins" is determined by the order of the corresponding events in the broker's event queue. -```rust +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +# use async_std::{ +# io::{Write}, +# net::TcpStream, +# prelude::{Future, Stream}, +# task, +# }; +# use futures::channel::mpsc; +# use futures::SinkExt; +# use std::{ +# collections::hash_map::{Entry, HashMap}, +# sync::Arc, +# }; +# +# type Result = std::result::Result>; +# type Sender = mpsc::UnboundedSender; +# type Receiver = mpsc::UnboundedReceiver; +# +# async fn client_writer( +# mut messages: Receiver, +# stream: Arc, +# ) -> Result<()> { +# let mut stream = &*stream; +# while let Some(msg) = messages.next().await { +# stream.write_all(msg.as_bytes()).await?; +# } +# Ok(()) +# } +# +# fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> +# where +# F: Future> + Send + 'static, +# { +# task::spawn(async move { +# if let Err(e) = fut.await { +# eprintln!("{}", e) +# } +# }) +# } +# #[derive(Debug)] enum Event { // 1 NewPeer { diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 5217a82..1cc07b2 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -1,11 +1,11 @@ ## Handling Disconnections -Currently, we only ever *add* new peers to the map. +Currently, we only ever _add_ new peers to the map. This is clearly wrong: if a peer closes connection to the chat, we should not try to send any more messages to it. One subtlety with handling disconnection is that we can detect it either in the reader's task, or in the writer's task. The most obvious solution here is to just remove the peer from the `peers` map in both cases, but this would be wrong. -If *both* read and write fail, we'll remove the peer twice, but it can be the case that the peer reconnected between the two failures! +If _both_ read and write fail, we'll remove the peer twice, but it can be the case that the peer reconnected between the two failures! To fix this, we will only remove the peer when the write side finishes. If the read side finishes we will notify the write side that it should stop as well. That is, we need to add an ability to signal shutdown for the writer task. @@ -17,7 +17,17 @@ This way, we statically guarantee that we issue shutdown exactly once, even if w First, let's add a shutdown channel to the `client`: -```rust +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +# use async_std::net::TcpStream; +# use futures::{channel::mpsc, SinkExt}; +# use std::sync::Arc; +# +# type Result = std::result::Result>; +# type Sender = mpsc::UnboundedSender; +# type Receiver = mpsc::UnboundedReceiver; +# #[derive(Debug)] enum Void {} // 1 @@ -35,17 +45,17 @@ enum Event { }, } -async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { +async fn client(mut broker: Sender, stream: Arc) -> Result<()> { // ... - +# let name: String = unimplemented!(); let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::(); // 3 broker.send(Event::NewPeer { name: name.clone(), stream: Arc::clone(&stream), shutdown: shutdown_receiver, }).await.unwrap(); - // ... +# unimplemented!() } ``` @@ -56,23 +66,35 @@ async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { In the `client_writer`, we now need to choose between shutdown and message channels. We use the `select` macro for this purpose: -```rust -use futures::select; -use futures::FutureExt; +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +# use async_std::{io::Write, net::TcpStream}; +use futures::{channel::mpsc, select, FutureExt, StreamExt}; +# use std::sync::Arc; + +# type Receiver = mpsc::UnboundedReceiver; +# type Result = std::result::Result>; +# type Sender = mpsc::UnboundedSender; + +# #[derive(Debug)] +# enum Void {} // 1 async fn client_writer( messages: &mut Receiver, stream: Arc, - mut shutdown: Receiver, // 1 + shutdown: Receiver, // 1 ) -> Result<()> { let mut stream = &*stream; + let mut messages = messages.fuse(); + let mut shutdown = shutdown.fuse(); loop { // 2 select! { - msg = messages.next().fuse() => match msg { + msg = messages.next() => match msg { Some(msg) => stream.write_all(msg.as_bytes()).await?, None => break, }, - void = shutdown.next().fuse() => match void { + void = shutdown.next() => match void { Some(void) => match void {}, // 3 None => break, } @@ -94,25 +116,19 @@ This also allows us to establish a useful invariant that the message channel str The final code looks like this: -```rust +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +use async_std::{ + io::{BufReader, BufRead, Write}, + net::{TcpListener, TcpStream}, + task, +}; +use futures::{channel::mpsc, future::Future, select, FutureExt, SinkExt, StreamExt}; use std::{ + collections::hash_map::{Entry, HashMap}, net::ToSocketAddrs, sync::Arc, - collections::hash_map::{HashMap, Entry}, -}; - -use futures::{ - channel::mpsc, - SinkExt, - FutureExt, - select, -}; - -use async_std::{ - io::BufReader, - prelude::*, - task, - net::{TcpListener, TcpStream}, }; type Result = std::result::Result>; @@ -122,13 +138,13 @@ type Receiver = mpsc::UnboundedReceiver; #[derive(Debug)] enum Void {} -fn main() -> Result<()> { +// main +fn run() -> Result<()> { task::block_on(server("127.0.0.1:8080")) } async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; - let (broker_sender, broker_receiver) = mpsc::unbounded(); let broker_handle = task::spawn(broker(broker_receiver)); let mut incoming = listener.incoming(); @@ -180,16 +196,18 @@ async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { async fn client_writer( messages: &mut Receiver, stream: Arc, - mut shutdown: Receiver, + shutdown: Receiver, ) -> Result<()> { let mut stream = &*stream; + let mut messages = messages.fuse(); + let mut shutdown = shutdown.fuse(); loop { select! { - msg = messages.next().fuse() => match msg { + msg = messages.next() => match msg { Some(msg) => stream.write_all(msg.as_bytes()).await?, None => break, }, - void = shutdown.next().fuse() => match void { + void = shutdown.next() => match void { Some(void) => match void {}, None => break, } @@ -212,11 +230,11 @@ enum Event { }, } -async fn broker(mut events: Receiver) { +async fn broker(events: Receiver) { let (disconnect_sender, mut disconnect_receiver) = // 1 mpsc::unbounded::<(String, Receiver)>(); let mut peers: HashMap> = HashMap::new(); - + let mut events = events.fuse(); loop { let event = select! { event = events.next() => match event { diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index b3c2d1c..35cccd8 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -14,44 +14,39 @@ Specifically, the client should *simultaneously* read from stdin and from the so Programming this with threads is cumbersome, especially when implementing clean shutdown. With async, we can just use the `select!` macro. -```rust -use std::net::ToSocketAddrs; - -use futures::select; -use futures::FutureExt; - +```rust,edition2018 +# extern crate async_std; +# extern crate futures; use async_std::{ - prelude::*, + io::{stdin, BufRead, BufReader, Write}, net::TcpStream, task, - io::{stdin, BufReader}, }; +use futures::{select, FutureExt, StreamExt}; +use std::net::ToSocketAddrs; type Result = std::result::Result>; - -fn main() -> Result<()> { - task::block_on(try_main("127.0.0.1:8080")) +// main +fn run() -> Result<()> { + task::block_on(try_run("127.0.0.1:8080")) } -async fn try_main(addr: impl ToSocketAddrs) -> Result<()> { +async fn try_run(addr: impl ToSocketAddrs) -> Result<()> { let stream = TcpStream::connect(addr).await?; let (reader, mut writer) = (&stream, &stream); // 1 - let reader = BufReader::new(reader); - let mut lines_from_server = futures::StreamExt::fuse(reader.lines()); // 2 - - let stdin = BufReader::new(stdin()); - let mut lines_from_stdin = futures::StreamExt::fuse(stdin.lines()); // 2 + let mut lines_from_server = BufReader::new(reader).lines().fuse(); // 2 + let mut lines_from_stdin = BufReader::new(stdin()).lines().fuse(); // 2 loop { select! { // 3 - line = lines_from_server.next().fuse() => match line { + line = lines_from_server.next() => match line { Some(line) => { let line = line?; println!("{}", line); }, None => break, }, - line = lines_from_stdin.next().fuse() => match line { + line = lines_from_stdin.next() => match line { Some(line) => { let line = line?; writer.write_all(line.as_bytes()).await?; diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 3dc1903..9cef56d 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -7,11 +7,18 @@ We need to: 2. interpret the first line as a login 3. parse the rest of the lines as a `login: message` -```rust -use async_std::io::BufReader; -use async_std::net::TcpStream; -use async_std::io::BufReader; - +```rust,edition2018 +# extern crate async_std; +# use async_std::{ +# io::{BufRead, BufReader}, +# net::{TcpListener, TcpStream}, +# prelude::Stream, +# task, +# }; +# use std::net::ToSocketAddrs; +# +# type Result = std::result::Result>; +# async fn server(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let mut incoming = listener.incoming(); @@ -65,9 +72,45 @@ One serious problem in the above solution is that, while we correctly propagate That is, `task::spawn` does not return an error immediately (it can't, it needs to run the future to completion first), only after it is joined. We can "fix" it by waiting for the task to be joined, like this: -```rust +```rust,edition2018 +# #![feature(async_closure)] +# extern crate async_std; +# use async_std::{ +# io::{BufRead, BufReader}, +# net::{TcpListener, TcpStream}, +# prelude::Stream, +# task, +# }; +# use std::net::ToSocketAddrs; +# +# type Result = std::result::Result>; +# +# async fn client(stream: TcpStream) -> Result<()> { +# let reader = BufReader::new(&stream); // 2 +# let mut lines = reader.lines(); +# +# let name = match lines.next().await { // 3 +# None => Err("peer disconnected immediately")?, +# Some(line) => line?, +# }; +# println!("name = {}", name); +# +# while let Some(line) = lines.next().await { // 4 +# let line = line?; +# let (dest, msg) = match line.find(':') { // 5 +# None => continue, +# Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), +# }; +# let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); +# let msg: String = msg.trim().to_string(); +# } +# Ok(()) +# } +# +# async move |stream| { let handle = task::spawn(client(stream)); -handle.await? +handle.await +# }; ``` The `.await` waits until the client finishes, and `?` propagates the result. @@ -80,10 +123,16 @@ That is, a flaky internet connection of one peer brings down the whole chat room A correct way to handle client errors in this case is log them, and continue serving other clients. So let's use a helper function for this: -```rust +```rust,edition2018 +# extern crate async_std; +# use async_std::{ +# io, +# prelude::Future, +# task, +# }; fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> where - F: Future> + Send + 'static, + F: Future> + Send + 'static, { task::spawn(async move { if let Err(e) = fut.await { diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index 0722bc0..3184c26 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -11,11 +11,20 @@ So let's create a `client_writer` task which receives messages over a channel an This task would be the point of serialization of messages. if Alice and Charley send two messages to Bob at the same time, Bob will see the messages in the same order as they arrive in the channel. -```rust +```rust,edition2018 +# extern crate async_std; +# extern crate futures; +# use async_std::{ +# io::Write, +# net::TcpStream, +# prelude::Stream, +# }; +# use std::sync::Arc; use futures::channel::mpsc; // 1 use futures::SinkExt; use std::sync::Arc; +# type Result = std::result::Result>; type Sender = mpsc::UnboundedSender; // 2 type Receiver = mpsc::UnboundedReceiver; From 06952b4c971f547d89f6f9a02465223abb5fae24 Mon Sep 17 00:00:00 2001 From: Darin Morrison Date: Sun, 18 Aug 2019 16:35:54 -0700 Subject: [PATCH 016/194] Test and build the book on travis --- .travis.yml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index bc25dc6..4c8dd88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,15 +3,12 @@ language: rust env: - RUSTFLAGS="-D warnings" -before_script: - - rustup component add rustfmt - matrix: fast_finish: true include: - rust: nightly os: linux - env: BUILD_DOCS=1 + env: BUILD_DOCS=1 BUILD_BOOK=1 - rust: nightly os: osx osx_image: xcode9.2 @@ -19,8 +16,15 @@ matrix: - rust: nightly-x86_64-pc-windows-msvc os: windows +before_script: + - rustup component add rustfmt + - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.3" mdbook) + - cargo install-update -a + script: - - cargo check --all --benches --bins --examples --tests - - cargo test --all + - if ![[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi + - if [[ -n "$BUILD_BOOK" ]]; then cargo test --all --benches --bins --examples --tests; fi - cargo fmt --all -- --check - - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi + - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi + - if [[ -n "$BUILD_BOOK" ]]; then mdbook build docs && mdbook test -L ./target/debug/deps docs; fi From 9370cd329885fc0f6c559629f16f964d892d3c9b Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 26 Aug 2019 11:08:51 -0700 Subject: [PATCH 017/194] Fix README example --- README.md | 4 ++-- examples/socket-timeouts.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 examples/socket-timeouts.rs diff --git a/README.md b/README.md index b3f4189..3ba7c5f 100644 --- a/README.md +++ b/README.md @@ -66,9 +66,9 @@ async fn get() -> io::Result> { let mut buf = vec![]; io::timeout(Duration::from_secs(5), async { - stream.read_to_end(&mut buf).await? + stream.read_to_end(&mut buf).await?; Ok(buf) - }) + }).await } fn main() { diff --git a/examples/socket-timeouts.rs b/examples/socket-timeouts.rs new file mode 100644 index 0000000..b5cb9be --- /dev/null +++ b/examples/socket-timeouts.rs @@ -0,0 +1,29 @@ +use std::time::Duration; + +use async_std::{ + prelude::*, + task, + io, + net::TcpStream, +}; + +async fn get() -> io::Result> { + let mut stream = TcpStream::connect("example.com:80").await?; + stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?; + + let mut buf = vec![]; + + io::timeout(Duration::from_secs(5), async { + stream.read_to_end(&mut buf).await?; + Ok(buf) + }).await +} + +fn main() { + task::block_on(async { + let raw_response = get().await.expect("request"); + let response = String::from_utf8(raw_response) + .expect("utf8 conversion"); + println!("received: {}", response); + }); +} From dfd520c7787d4d432ed172d457c7bd88be7d99b0 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 26 Aug 2019 11:38:11 -0700 Subject: [PATCH 018/194] rustfmt all examples --- examples/socket-timeouts.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/examples/socket-timeouts.rs b/examples/socket-timeouts.rs index b5cb9be..b2f770e 100644 --- a/examples/socket-timeouts.rs +++ b/examples/socket-timeouts.rs @@ -1,29 +1,26 @@ use std::time::Duration; -use async_std::{ - prelude::*, - task, - io, - net::TcpStream, -}; +use async_std::{io, net::TcpStream, prelude::*, task}; async fn get() -> io::Result> { let mut stream = TcpStream::connect("example.com:80").await?; - stream.write_all(b"GET /index.html HTTP/1.0\r\n\r\n").await?; + stream + .write_all(b"GET /index.html HTTP/1.0\r\n\r\n") + .await?; let mut buf = vec![]; io::timeout(Duration::from_secs(5), async { stream.read_to_end(&mut buf).await?; Ok(buf) - }).await + }) + .await } fn main() { task::block_on(async { let raw_response = get().await.expect("request"); - let response = String::from_utf8(raw_response) - .expect("utf8 conversion"); + let response = String::from_utf8(raw_response).expect("utf8 conversion"); println!("received: {}", response); }); } From 8451789da5211c8075ffef2b47461e03e508878d Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Mon, 26 Aug 2019 12:47:15 -0700 Subject: [PATCH 019/194] Expose fs::create_dir_all --- src/fs/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index dd3525e..8d98330 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -32,6 +32,7 @@ pub use std::fs::{FileType, Metadata, Permissions}; pub use canonicalize::canonicalize; pub use copy::copy; pub use create_dir::create_dir; +pub use create_dir_all::create_dir_all; pub use hard_link::hard_link; pub use metadata::metadata; pub use read::read; @@ -49,6 +50,7 @@ pub use write::write; mod canonicalize; mod copy; mod create_dir; +mod create_dir_all; mod dir_builder; mod dir_entry; mod file; From c21e38109872da161379ff6aac71fb591a744753 Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Mon, 26 Aug 2019 12:50:06 -0700 Subject: [PATCH 020/194] Remove unused import from fs::create_dir_all --- src/fs/create_dir_all.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index 413f5f6..e78896e 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,5 +1,5 @@ use std::fs; -use std::path::{Path, PathBuf}; +use std::path::Path; use crate::task::blocking; use crate::io; From 2fc6610d6deac28381be049a8884c51c86a378dc Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Mon, 26 Aug 2019 12:54:55 -0700 Subject: [PATCH 021/194] Update CHANGELOG.md --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b26e8b..2fbe971 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +- Expose `fs::create_dir_all` + # [0.99.4] - 2019-08-21 ## Changes From d47f7d3e9214d080b2e9b5fcfd90eea40a1533b5 Mon Sep 17 00:00:00 2001 From: Dylan Frankland Date: Mon, 26 Aug 2019 12:59:30 -0700 Subject: [PATCH 022/194] rustfmt fs::create_dir_all --- src/fs/create_dir_all.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index e78896e..b63c362 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,8 +1,8 @@ use std::fs; use std::path::Path; -use crate::task::blocking; use crate::io; +use crate::task::blocking; /// Creates a new, empty directory and all of its parents if they are missing. /// From 101979fcc3aebc1c139073d640efd62c951a020e Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 26 Aug 2019 14:24:20 -0700 Subject: [PATCH 023/194] Fix some final errors --- Cargo.toml | 2 +- docs/src/tutorial/all_together.md | 12 ------------ docs/src/tutorial/sending_messages.md | 1 - 3 files changed, 1 insertion(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6aabfec..eba38cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,7 +37,7 @@ pin-utils = "0.1.0-alpha.4" slab = "0.4.2" [dependencies.futures-preview] -version = "0.3.0-alpha.17" +version = "0.3.0-alpha.18" features = ["async-await", "nightly"] [dev-dependencies] diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 178f537..4e81cb2 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -135,18 +135,6 @@ async fn broker(mut events: Receiver) -> Result<()> { } Ok(()) } - -fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> -where - F: Future> + Send + 'static, -{ - task::spawn(async move { - if let Err(e) = fut.await { - eprintln!("{}", e) - } - }) -} - ``` 1. Inside the `server`, we create the broker's channel and `task`. diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index 3184c26..465d674 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -19,7 +19,6 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the # net::TcpStream, # prelude::Stream, # }; -# use std::sync::Arc; use futures::channel::mpsc; // 1 use futures::SinkExt; use std::sync::Arc; From bfaa9c510cfb18df3c6010c42d30d7ec4f7abcd0 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 26 Aug 2019 14:33:11 -0700 Subject: [PATCH 024/194] Import HashMap visibly in the tutorial Fixes #101 --- docs/src/tutorial/connecting_readers_and_writers.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index 7aba3a1..31631f8 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -21,10 +21,7 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined # }; # use futures::channel::mpsc; # use futures::SinkExt; -# use std::{ -# collections::hash_map::{Entry, HashMap}, -# sync::Arc, -# }; +# use std::sync::Arc; # # type Result = std::result::Result>; # type Sender = mpsc::UnboundedSender; @@ -52,6 +49,8 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined # }) # } # +use std::collections::hash_map::{Entry, HashMap}; + #[derive(Debug)] enum Event { // 1 NewPeer { From b768a7bab7688a0aa605ddb295fe61b42a16914f Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 26 Aug 2019 14:35:57 -0700 Subject: [PATCH 025/194] Don't trim msg twice Fixes #102 --- docs/src/tutorial/all_together.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 4e81cb2..352d692 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -74,7 +74,7 @@ async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { Some(idx) => (&line[..idx], line[idx + 1 ..].trim()), }; let dest: Vec = dest.split(',').map(|name| name.trim().to_string()).collect(); - let msg: String = msg.trim().to_string(); + let msg: String = msg.to_string(); broker.send(Event::Message { // 4 from: name.clone(), From 366546b9cec777f4dc9e52659b3f7c81d55de192 Mon Sep 17 00:00:00 2001 From: Florian Gilcher Date: Mon, 26 Aug 2019 14:39:25 -0700 Subject: [PATCH 026/194] Visibly import in tasks example Fixes #97 --- docs/src/concepts/tasks.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index 5c62811..0711c12 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -6,9 +6,8 @@ In `async-std`, the [`tasks`][tasks] module is responsible for this. The simples ```rust,edition2018 # extern crate async_std; -# use async_std::{fs::File, io::Read, task}; -# use std::io; -# +use async_std::{io, task, fs::File, io::Read}; + async fn read_file(path: &str) -> Result { let mut file = File::open(path).await?; let mut contents = String::new(); From 8dff8951a6cd4e1a323dc4155054edb908dd8a73 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 27 Aug 2019 12:47:15 +0300 Subject: [PATCH 027/194] Reduce io::TimeoutFuture to futures_timer::TryFutureExt (#113) --- src/io/timeout.rs | 41 ++--------------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 36112cb..6d77737 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -1,12 +1,9 @@ -use std::pin::Pin; use std::time::Duration; -use futures_timer::Delay; -use pin_utils::unsafe_pinned; +use futures_timer::TryFutureExt; use crate::future::Future; use crate::io; -use crate::task::{Context, Poll}; /// Awaits an I/O future or times out after a duration of time. /// @@ -33,39 +30,5 @@ pub async fn timeout(dur: Duration, f: F) -> io::Result where F: Future>, { - let f = TimeoutFuture { - future: f, - delay: Delay::new(dur), - }; - f.await -} - -struct TimeoutFuture { - future: F, - delay: Delay, -} - -impl TimeoutFuture { - unsafe_pinned!(future: F); - unsafe_pinned!(delay: Delay); -} - -impl Future for TimeoutFuture -where - F: Future>, -{ - type Output = F::Output; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.as_mut().future().poll(cx) { - Poll::Ready(v) => Poll::Ready(v), - Poll::Pending => match self.delay().poll(cx) { - Poll::Ready(_) => Poll::Ready(Err(io::Error::new( - io::ErrorKind::TimedOut, - "I/O operation has timed out", - ))), - Poll::Pending => Poll::Pending, - }, - } - } + f.timeout(dur).await } From de6262046099ae784b789df2ca95860b5c1eca83 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 15:23:34 +0200 Subject: [PATCH 028/194] Implement installation using trust --- .travis.yml | 2 +- ci/install-mdbook.sh | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100755 ci/install-mdbook.sh diff --git a/.travis.yml b/.travis.yml index 4c8dd88..4e68a8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,7 +19,7 @@ matrix: before_script: - rustup component add rustfmt - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) - - (test -x $HOME/.cargo/bin/mdbook || cargo install --vers "^0.3" mdbook) + - (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh) - cargo install-update -a script: diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh new file mode 100755 index 0000000..112fc02 --- /dev/null +++ b/ci/install-mdbook.sh @@ -0,0 +1,19 @@ +set -euxo pipefail + +# Based on the Rust-Embedded WG's book CI +# https://github.com/rust-embedded/book/blob/master/ci/install.sh + +main() { + # Note - this will only accept releases tagged with v0.3.x + local tag=$(git ls-remote --tags --refs --exit-code \ + https://github.com/rust-lang-nursery/mdbook \ + | cut -d/ -f3 \ + | grep -E '^v0.3.[0-9]+$' \ + | sort --version-sort \ + | tail -n1) + + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- --git rust-lang-nursery/mdbook --tag $tag +} + +main From eba85c3ede72326f1343db3e2ac40a66761131ae Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 15:33:46 +0200 Subject: [PATCH 029/194] Fix regex, also install cargo-update with trust --- .travis.yml | 2 +- ci/install-cargo-update.sh | 19 +++++++++++++++++++ ci/install-mdbook.sh | 2 +- 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100755 ci/install-cargo-update.sh diff --git a/.travis.yml b/.travis.yml index 4e68a8b..01a364e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: before_script: - rustup component add rustfmt - - (test -x $HOME/.cargo/bin/cargo-install-update || cargo install cargo-update) + - (test -x $HOME/.cargo/bin/cargo-install-update || ./ci/install-cargo-update.sh) - (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh) - cargo install-update -a diff --git a/ci/install-cargo-update.sh b/ci/install-cargo-update.sh new file mode 100755 index 0000000..0755adc --- /dev/null +++ b/ci/install-cargo-update.sh @@ -0,0 +1,19 @@ +set -euxo pipefail + +# Based on the Rust-Embedded WG's book CI +# https://github.com/rust-embedded/book/blob/master/ci/install.sh + +main() { + # Note - this will accept any tagged release + local tag=$(git ls-remote --tags --refs --exit-code \ + https://github.com/nabijaczleweli/cargo-update \ + | cut -d/ -f3 \ + | grep -E '^v[0-9\.]+$' \ + | sort --version-sort \ + | tail -n1) + + curl -LSfs https://japaric.github.io/trust/install.sh | \ + sh -s -- --git nabijaczleweli/cargo-update --tag $tag +} + +main diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh index 112fc02..01c87f8 100755 --- a/ci/install-mdbook.sh +++ b/ci/install-mdbook.sh @@ -8,7 +8,7 @@ main() { local tag=$(git ls-remote --tags --refs --exit-code \ https://github.com/rust-lang-nursery/mdbook \ | cut -d/ -f3 \ - | grep -E '^v0.3.[0-9]+$' \ + | grep -E '^v0\.3\.[0-9]+$' \ | sort --version-sort \ | tail -n1) From f5823df70c13394ef9fbf923ad8fdbfdba00c55c Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 15:52:55 +0200 Subject: [PATCH 030/194] Include modified trust file --- ci/install-cargo-update.sh | 3 +- ci/install-tbz2.sh | 158 +++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 2 deletions(-) create mode 100755 ci/install-tbz2.sh diff --git a/ci/install-cargo-update.sh b/ci/install-cargo-update.sh index 0755adc..12382e6 100755 --- a/ci/install-cargo-update.sh +++ b/ci/install-cargo-update.sh @@ -12,8 +12,7 @@ main() { | sort --version-sort \ | tail -n1) - curl -LSfs https://japaric.github.io/trust/install.sh | \ - sh -s -- --git nabijaczleweli/cargo-update --tag $tag + $(dirname $(realpath $0))/install-tbz2.sh --git nabijaczleweli/cargo-update --tag $tag --crate cargo-install-update } main diff --git a/ci/install-tbz2.sh b/ci/install-tbz2.sh new file mode 100755 index 0000000..94a6529 --- /dev/null +++ b/ci/install-tbz2.sh @@ -0,0 +1,158 @@ +#!/bin/sh + +# NOTE: This is based on https://japaric.github.io/trust/install.sh, +# but has been modified to not expect a target tag, and to instead +# expect bzip2 tar files (.tbz2) instead of gzip tar files (.tar.gz) + +set -e + +help() { + cat <<'EOF' +Install a binary release of a Rust crate hosted on GitHub + +Usage: + install.sh [options] + +Options: + -h, --help Display this message + --git SLUG Get the crate from "https://github/$SLUG" + -f, --force Force overwriting an existing binary + --crate NAME Name of the crate to install (default ) + --tag TAG Tag (version) of the crate to install (default ) + --target TARGET Install the release compiled for $TARGET (default <`rustc` host>) + --to LOCATION Where to install the binary (default ~/.cargo/bin) +EOF +} + +say() { + echo "install.sh: $1" +} + +say_err() { + say "$1" >&2 +} + +err() { + if [ ! -z $td ]; then + rm -rf $td + fi + + say_err "ERROR $1" + exit 1 +} + +need() { + if ! command -v $1 > /dev/null 2>&1; then + err "need $1 (command not found)" + fi +} + +force=false +while test $# -gt 0; do + case $1 in + --crate) + crate=$2 + shift + ;; + --force | -f) + force=true + ;; + --git) + git=$2 + shift + ;; + --help | -h) + help + exit 0 + ;; + --tag) + tag=$2 + shift + ;; + --target) + target=$2 + shift + ;; + --to) + dest=$2 + shift + ;; + *) + ;; + esac + shift +done + +# Dependencies +need basename +need curl +need install +need mkdir +need mktemp +need tar + +# Optional dependencies +if [ -z $crate ] || [ -z $tag ] || [ -z $target ]; then + need cut +fi + +if [ -z $tag ]; then + need rev +fi + +if [ -z $target ]; then + need grep + need rustc +fi + +if [ -z $git ]; then + err 'must specify a git repository using `--git`. Example: `install.sh --git japaric/cross`' +fi + +url="https://github.com/$git" +say_err "GitHub repository: $url" + +if [ -z $crate ]; then + crate=$(echo $git | cut -d'/' -f2) +fi + +say_err "Crate: $crate" + +url="$url/releases" + +if [ -z $tag ]; then + tag=$(curl -s "$url/latest" | cut -d'"' -f2 | rev | cut -d'/' -f1 | rev) + say_err "Tag: latest ($tag)" +else + say_err "Tag: $tag" +fi + +if [ -z $target ]; then + target=$(rustc -Vv | grep host | cut -d' ' -f2) +fi + +say_err "Target: $target" + +if [ -z $dest ]; then + dest="$HOME/.cargo/bin" +fi + +say_err "Installing to: $dest" + +url="$url/download/$tag/$crate-$tag.tbz2" + +td=$(mktemp -d || mktemp -d -t tmp) +curl -sL $url | tar -C $td -xj + +for f in $(ls $td); do + test -x $td/$f || continue + + if [ -e "$dest/$f" ] && [ $force = false ]; then + err "$f already exists in $dest" + else + mkdir -p $dest + install -m 755 $td/$f $dest + fi +done + +rm -rf $td From e0212e5229ddc787077a9359777793fb23aff42a Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 15:59:28 +0200 Subject: [PATCH 031/194] Don't install cargo-update at all --- .travis.yml | 2 - ci/install-cargo-update.sh | 18 ----- ci/install-tbz2.sh | 158 ------------------------------------- 3 files changed, 178 deletions(-) delete mode 100755 ci/install-cargo-update.sh delete mode 100755 ci/install-tbz2.sh diff --git a/.travis.yml b/.travis.yml index 01a364e..5f9aac5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,9 +18,7 @@ matrix: before_script: - rustup component add rustfmt - - (test -x $HOME/.cargo/bin/cargo-install-update || ./ci/install-cargo-update.sh) - (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh) - - cargo install-update -a script: - if ![[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi diff --git a/ci/install-cargo-update.sh b/ci/install-cargo-update.sh deleted file mode 100755 index 12382e6..0000000 --- a/ci/install-cargo-update.sh +++ /dev/null @@ -1,18 +0,0 @@ -set -euxo pipefail - -# Based on the Rust-Embedded WG's book CI -# https://github.com/rust-embedded/book/blob/master/ci/install.sh - -main() { - # Note - this will accept any tagged release - local tag=$(git ls-remote --tags --refs --exit-code \ - https://github.com/nabijaczleweli/cargo-update \ - | cut -d/ -f3 \ - | grep -E '^v[0-9\.]+$' \ - | sort --version-sort \ - | tail -n1) - - $(dirname $(realpath $0))/install-tbz2.sh --git nabijaczleweli/cargo-update --tag $tag --crate cargo-install-update -} - -main diff --git a/ci/install-tbz2.sh b/ci/install-tbz2.sh deleted file mode 100755 index 94a6529..0000000 --- a/ci/install-tbz2.sh +++ /dev/null @@ -1,158 +0,0 @@ -#!/bin/sh - -# NOTE: This is based on https://japaric.github.io/trust/install.sh, -# but has been modified to not expect a target tag, and to instead -# expect bzip2 tar files (.tbz2) instead of gzip tar files (.tar.gz) - -set -e - -help() { - cat <<'EOF' -Install a binary release of a Rust crate hosted on GitHub - -Usage: - install.sh [options] - -Options: - -h, --help Display this message - --git SLUG Get the crate from "https://github/$SLUG" - -f, --force Force overwriting an existing binary - --crate NAME Name of the crate to install (default ) - --tag TAG Tag (version) of the crate to install (default ) - --target TARGET Install the release compiled for $TARGET (default <`rustc` host>) - --to LOCATION Where to install the binary (default ~/.cargo/bin) -EOF -} - -say() { - echo "install.sh: $1" -} - -say_err() { - say "$1" >&2 -} - -err() { - if [ ! -z $td ]; then - rm -rf $td - fi - - say_err "ERROR $1" - exit 1 -} - -need() { - if ! command -v $1 > /dev/null 2>&1; then - err "need $1 (command not found)" - fi -} - -force=false -while test $# -gt 0; do - case $1 in - --crate) - crate=$2 - shift - ;; - --force | -f) - force=true - ;; - --git) - git=$2 - shift - ;; - --help | -h) - help - exit 0 - ;; - --tag) - tag=$2 - shift - ;; - --target) - target=$2 - shift - ;; - --to) - dest=$2 - shift - ;; - *) - ;; - esac - shift -done - -# Dependencies -need basename -need curl -need install -need mkdir -need mktemp -need tar - -# Optional dependencies -if [ -z $crate ] || [ -z $tag ] || [ -z $target ]; then - need cut -fi - -if [ -z $tag ]; then - need rev -fi - -if [ -z $target ]; then - need grep - need rustc -fi - -if [ -z $git ]; then - err 'must specify a git repository using `--git`. Example: `install.sh --git japaric/cross`' -fi - -url="https://github.com/$git" -say_err "GitHub repository: $url" - -if [ -z $crate ]; then - crate=$(echo $git | cut -d'/' -f2) -fi - -say_err "Crate: $crate" - -url="$url/releases" - -if [ -z $tag ]; then - tag=$(curl -s "$url/latest" | cut -d'"' -f2 | rev | cut -d'/' -f1 | rev) - say_err "Tag: latest ($tag)" -else - say_err "Tag: $tag" -fi - -if [ -z $target ]; then - target=$(rustc -Vv | grep host | cut -d' ' -f2) -fi - -say_err "Target: $target" - -if [ -z $dest ]; then - dest="$HOME/.cargo/bin" -fi - -say_err "Installing to: $dest" - -url="$url/download/$tag/$crate-$tag.tbz2" - -td=$(mktemp -d || mktemp -d -t tmp) -curl -sL $url | tar -C $td -xj - -for f in $(ls $td); do - test -x $td/$f || continue - - if [ -e "$dest/$f" ] && [ $force = false ]; then - err "$f already exists in $dest" - else - mkdir -p $dest - install -m 755 $td/$f $dest - fi -done - -rm -rf $td From b6c81868463bc58e1ac4287b587507c5861afa03 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 16:06:51 +0200 Subject: [PATCH 032/194] Don't use version sort (bsd sort doesn't seem to have it) --- ci/install-mdbook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh index 01c87f8..3417c9c 100755 --- a/ci/install-mdbook.sh +++ b/ci/install-mdbook.sh @@ -9,7 +9,7 @@ main() { https://github.com/rust-lang-nursery/mdbook \ | cut -d/ -f3 \ | grep -E '^v0\.3\.[0-9]+$' \ - | sort --version-sort \ + | sort \ | tail -n1) curl -LSfs https://japaric.github.io/trust/install.sh | \ From b39c72068164ca823cf572dcb60499688cb844e3 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 16:41:31 +0200 Subject: [PATCH 033/194] Only install mdbook if building the book --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5f9aac5..024d372 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ matrix: before_script: - rustup component add rustfmt - - (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh) + - if [[ -n "$BUILD_BOOK" ]]; then (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh); fi script: - if ![[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi From 192c9683d958e3022cf21d2a07466136752af360 Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 27 Aug 2019 16:45:32 +0200 Subject: [PATCH 034/194] Correct boolean inversion and overrided env vars --- .travis.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.travis.yml b/.travis.yml index 024d372..125dc16 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,28 +1,26 @@ language: rust -env: - - RUSTFLAGS="-D warnings" - matrix: fast_finish: true include: - rust: nightly os: linux - env: BUILD_DOCS=1 BUILD_BOOK=1 + env: RUSTFLAGS="-D warnings" BUILD_DOCS=1 BUILD_BOOK=1 - rust: nightly os: osx osx_image: xcode9.2 - env: BUILD_DOCS=1 + env: RUSTFLAGS="-D warnings" BUILD_DOCS=1 - rust: nightly-x86_64-pc-windows-msvc os: windows + env: RUSTFLAGS="-D warnings" before_script: - rustup component add rustfmt - - if [[ -n "$BUILD_BOOK" ]]; then (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh); fi + - if [[ -n "$BUILD_BOOK" ]]; then (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh); fi script: - - if ![[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi - - if [[ -n "$BUILD_BOOK" ]]; then cargo test --all --benches --bins --examples --tests; fi + - if ! [[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi + - if [[ -n "$BUILD_BOOK" ]]; then cargo test --all --benches --bins --examples --tests; fi - cargo fmt --all -- --check - - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi - - if [[ -n "$BUILD_BOOK" ]]; then mdbook build docs && mdbook test -L ./target/debug/deps docs; fi + - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi + - if [[ -n "$BUILD_BOOK" ]]; then mdbook build docs && mdbook test -L ./target/debug/deps docs; fi From c6e4c659c4ed21ed2f2d03094fcaca0c03c3395a Mon Sep 17 00:00:00 2001 From: James Munns Date: Wed, 28 Aug 2019 17:16:02 +0200 Subject: [PATCH 035/194] Restore Version Sort (#121) Since we only build the book on Linux for now, restore the `--version-sort` flag for gnu sort. This makes me feel better that when sorting numbering oddities (e.g. multiple digits) will be handled correctly. This was removed when I was trying to get this script to work on Windows and OSX, which is no longer relevant. --- ci/install-mdbook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/install-mdbook.sh b/ci/install-mdbook.sh index 3417c9c..01c87f8 100755 --- a/ci/install-mdbook.sh +++ b/ci/install-mdbook.sh @@ -9,7 +9,7 @@ main() { https://github.com/rust-lang-nursery/mdbook \ | cut -d/ -f3 \ | grep -E '^v0\.3\.[0-9]+$' \ - | sort \ + | sort --version-sort \ | tail -n1) curl -LSfs https://japaric.github.io/trust/install.sh | \ From 374f0c9eb88637cbc8b70bced57df607c2857ea3 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Wed, 28 Aug 2019 23:09:15 +0300 Subject: [PATCH 036/194] Refactor TcpStream::connect into resolving loop and TcpStream::connect_to (#119) --- src/net/tcp/stream.rs | 107 ++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 52 deletions(-) diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 6687a1b..d823317 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -78,61 +78,10 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub async fn connect(addrs: A) -> io::Result { - enum State { - Waiting(TcpStream), - Error(io::Error), - Done, - } - let mut last_err = None; for addr in addrs.to_socket_addrs()? { - let mut state = { - match mio::net::TcpStream::connect(&addr) { - Ok(mio_stream) => { - #[cfg(unix)] - let stream = TcpStream { - raw_fd: mio_stream.as_raw_fd(), - io_handle: IoHandle::new(mio_stream), - }; - - #[cfg(windows)] - let stream = TcpStream { - // raw_socket: mio_stream.as_raw_socket(), - io_handle: IoHandle::new(mio_stream), - }; - - State::Waiting(stream) - } - Err(err) => State::Error(err), - } - }; - - let res = future::poll_fn(|cx| { - match mem::replace(&mut state, State::Done) { - State::Waiting(stream) => { - // Once we've connected, wait for the stream to be writable as that's when - // the actual connection has been initiated. Once we're writable we check - // for `take_socket_error` to see if the connect actually hit an error or - // not. - // - // If all that succeeded then we ship everything on up. - if let Poll::Pending = stream.io_handle.poll_writable(cx)? { - state = State::Waiting(stream); - return Poll::Pending; - } - - if let Some(err) = stream.io_handle.get_ref().take_error()? { - return Poll::Ready(Err(err)); - } - - Poll::Ready(Ok(stream)) - } - State::Error(err) => Poll::Ready(Err(err)), - State::Done => panic!("`TcpStream::connect()` future polled after completion"), - } - }) - .await; + let res = Self::connect_to(addr).await; match res { Ok(stream) => return Ok(stream), @@ -148,6 +97,60 @@ impl TcpStream { })) } + /// Creates a new TCP stream connected to the specified address. + async fn connect_to(addr: SocketAddr) -> io::Result { + let stream = mio::net::TcpStream::connect(&addr).map(|mio_stream| { + #[cfg(unix)] + let stream = TcpStream { + raw_fd: mio_stream.as_raw_fd(), + io_handle: IoHandle::new(mio_stream), + }; + + #[cfg(windows)] + let stream = TcpStream { + // raw_socket: mio_stream.as_raw_socket(), + io_handle: IoHandle::new(mio_stream), + }; + + stream + }); + + enum State { + Waiting(TcpStream), + Error(io::Error), + Done, + } + let mut state = match stream { + Ok(stream) => State::Waiting(stream), + Err(err) => State::Error(err), + }; + future::poll_fn(|cx| { + match mem::replace(&mut state, State::Done) { + State::Waiting(stream) => { + // Once we've connected, wait for the stream to be writable as that's when + // the actual connection has been initiated. Once we're writable we check + // for `take_socket_error` to see if the connect actually hit an error or + // not. + // + // If all that succeeded then we ship everything on up. + if let Poll::Pending = stream.io_handle.poll_writable(cx)? { + state = State::Waiting(stream); + return Poll::Pending; + } + + if let Some(err) = stream.io_handle.get_ref().take_error()? { + return Poll::Ready(Err(err)); + } + + Poll::Ready(Ok(stream)) + } + State::Error(err) => Poll::Ready(Err(err)), + State::Done => panic!("`TcpStream::connect_to()` future polled after completion"), + } + }) + .await + } + /// Returns the local address that this stream is connected to. /// /// ## Examples From 3b801655326676fcc5fbf6b474205943f502bc2c Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Fri, 30 Aug 2019 17:42:35 +0200 Subject: [PATCH 037/194] add stream::all method --- src/stream/stream.rs | 108 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/stream/stream.rs b/src/stream/stream.rs index 6623207..7cb9f36 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -27,6 +27,7 @@ use cfg_if::cfg_if; use crate::future::Future; use crate::task::{Context, Poll}; +use std::marker::PhantomData; cfg_if! { if #[cfg(feature = "docs")] { @@ -111,6 +112,71 @@ pub trait Stream { remaining: n, } } + + /// Tests if every element of the stream matches a predicate. + /// + /// `all()` takes a closure that returns `true` or `false`. It applies + /// this closure to each element of the stream, and if they all return + /// `true`, then so does `all()`. If any of them return `false`, it + /// returns `false`. + /// + /// `all()` is short-circuiting; in other words, it will stop processing + /// as soon as it finds a `false`, given that no matter what else happens, + /// the result will also be `false`. + /// + /// An empty stream returns `true`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat(9).take(3); + /// + /// while let Some(v) = s.next().await { + /// assert_eq!(v, 9); + /// } + /// # + /// # }) } + /// ``` + /// ``` + /// let a = [1, 2, 3]; + /// + /// assert!(a.iter().all(|&x| x > 0)); + /// + /// assert!(!a.iter().all(|&x| x > 2)); + /// ``` + /// + /// Stopping at the first `false`: + /// + /// ``` + /// let a = [1, 2, 3]; + /// + /// let mut iter = a.iter(); + /// + /// assert!(!iter.all(|&x| x != 2)); + /// + /// // we can still use `iter`, as there are more elements. + /// assert_eq!(iter.next(), Some(&3)); + /// ``` + #[inline] + fn all(&mut self, f: F) -> AllFuture<'_, Self, F, Self::Item> + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + AllFuture { + stream: self, + result: true, + __item: PhantomData, + f, + } + } } impl Stream for T { @@ -168,3 +234,45 @@ impl futures::Stream for Take { } } } + +pub struct AllFuture<'a, S, F, T> +where + S: ?Sized, + F: FnMut(T) -> bool, +{ + stream: &'a mut S, + f: F, + result: bool, + __item: PhantomData, +} + +impl<'a, S, F, T> AllFuture<'a, S, F, T> +where + S: ?Sized, + F: FnMut(T) -> bool, +{ + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(result: bool); + pin_utils::unsafe_unpinned!(f: F); +} + +impl Future for AllFuture<'_, S, F, S::Item> +where + S: futures::Stream + Unpin + ?Sized, + F: FnMut(S::Item) -> bool, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures::Stream; + let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(v) => { + // me: *screams* + *self.as_mut().result() = (self.as_mut().f())(v); + Poll::Pending + } + None => Poll::Ready(self.result), + } + } +} From fe45ba5628075a82741e761795e5358d7da005b9 Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Fri, 30 Aug 2019 18:35:51 +0200 Subject: [PATCH 038/194] update docs and examples --- src/stream/stream.rs | 51 +++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/stream/stream.rs b/src/stream/stream.rs index 7cb9f36..5982e92 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -136,34 +136,31 @@ pub trait Stream { /// use async_std::prelude::*; /// use async_std::stream; /// - /// let mut s = stream::repeat(9).take(3); + /// let mut s = stream::repeat::(42).take(3); + /// assert!(s.all(|x| x == 42).await); /// - /// while let Some(v) = s.next().await { - /// assert_eq!(v, 9); - /// } /// # /// # }) } /// ``` + /// + /// Empty stream: + /// /// ``` - /// let a = [1, 2, 3]; + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; /// - /// assert!(a.iter().all(|&x| x > 0)); + /// let mut s = stream::empty::(); + /// assert!(s.all(|_| false).await); /// - /// assert!(!a.iter().all(|&x| x > 2)); + /// # + /// # }) } /// ``` - /// /// Stopping at the first `false`: /// - /// ``` - /// let a = [1, 2, 3]; - /// - /// let mut iter = a.iter(); - /// - /// assert!(!iter.all(|&x| x != 2)); - /// - /// // we can still use `iter`, as there are more elements. - /// assert_eq!(iter.next(), Some(&3)); - /// ``` + /// TODO: add example here + /// TODO: add more examples #[inline] fn all(&mut self, f: F) -> AllFuture<'_, Self, F, Self::Item> where @@ -235,9 +232,9 @@ impl futures::Stream for Take { } } +#[derive(Debug)] pub struct AllFuture<'a, S, F, T> where - S: ?Sized, F: FnMut(T) -> bool, { stream: &'a mut S, @@ -248,7 +245,6 @@ where impl<'a, S, F, T> AllFuture<'a, S, F, T> where - S: ?Sized, F: FnMut(T) -> bool, { pin_utils::unsafe_pinned!(stream: &'a mut S); @@ -258,8 +254,9 @@ where impl Future for AllFuture<'_, S, F, S::Item> where - S: futures::Stream + Unpin + ?Sized, + S: futures::Stream + Unpin + Sized, F: FnMut(S::Item) -> bool, + S::Item: std::fmt::Debug, { type Output = bool; @@ -268,9 +265,15 @@ where let next = futures::ready!(self.as_mut().stream().poll_next(cx)); match next { Some(v) => { - // me: *screams* - *self.as_mut().result() = (self.as_mut().f())(v); - Poll::Pending + let result = (self.as_mut().f())(v); + *self.as_mut().result() = result; + if result { + // don't forget to wake this task again to pull the next item from stream + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(false) + } } None => Poll::Ready(self.result), } From 243a48c14ed10b3d1ee42b2843abd2ae9677bce9 Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Fri, 30 Aug 2019 18:37:58 +0200 Subject: [PATCH 039/194] remove debug --- src/stream/stream.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/stream.rs b/src/stream/stream.rs index 5982e92..8710cb2 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -256,7 +256,6 @@ impl Future for AllFuture<'_, S, F, S::Item> where S: futures::Stream + Unpin + Sized, F: FnMut(S::Item) -> bool, - S::Item: std::fmt::Debug, { type Output = bool; From 38a86766d3e3394e2f22cbba03cc7af448c67614 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Fri, 30 Aug 2019 20:28:49 +0200 Subject: [PATCH 040/194] Add future::timeout() (#20) * Add future::timeout() * Update src/future/timeout.rs Co-Authored-By: Yoshua Wuyts * Update src/future/timeout.rs Co-Authored-By: Yoshua Wuyts * Put futues::timeout behind unstable feature --- .travis.yml | 4 +-- Cargo.toml | 1 + src/future/mod.rs | 9 +++++ src/future/timeout.rs | 80 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 src/future/timeout.rs diff --git a/.travis.yml b/.travis.yml index 125dc16..9d78f7f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,8 +19,8 @@ before_script: - if [[ -n "$BUILD_BOOK" ]]; then (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh); fi script: - - if ! [[ -n "$BUILD_BOOK" ]]; then cargo check --all --benches --bins --examples --tests && cargo test --all; fi - - if [[ -n "$BUILD_BOOK" ]]; then cargo test --all --benches --bins --examples --tests; fi + - if ! [[ -n "$BUILD_BOOK" ]]; then cargo check --features unstable --all --benches --bins --examples --tests && cargo test --features unstable --all; fi + - if [[ -n "$BUILD_BOOK" ]]; then cargo test --features unstable --all --benches --bins --examples --tests; fi - cargo fmt --all -- --check - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi - if [[ -n "$BUILD_BOOK" ]]; then mdbook build docs && mdbook test -L ./target/debug/deps docs; fi diff --git a/Cargo.toml b/Cargo.toml index eba38cc..230de64 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ rustdoc-args = ["--cfg", "feature=\"docs\""] [features] docs = [] +unstable = [] [dependencies] async-task = "1.0.0" diff --git a/src/future/mod.rs b/src/future/mod.rs index 29e2b04..5d510a4 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -3,8 +3,17 @@ #[doc(inline)] pub use std::future::Future; +use cfg_if::cfg_if; + pub use pending::pending; pub use ready::ready; mod pending; mod ready; + +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod timeout; + pub use timeout::{timeout, TimeoutError}; + } +} diff --git a/src/future/timeout.rs b/src/future/timeout.rs new file mode 100644 index 0000000..cf146ae --- /dev/null +++ b/src/future/timeout.rs @@ -0,0 +1,80 @@ +use std::error::Error; +use std::fmt; +use std::pin::Pin; +use std::time::Duration; + +use futures_timer::Delay; + +use crate::future::Future; +use crate::task::{Context, Poll}; + +/// Awaits a future or times out after a duration of time. +/// +/// # Examples +/// +/// ``` +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use std::time::Duration; +/// +/// use async_std::future; +/// +/// let never = future::pending::<()>(); +/// let dur = Duration::from_millis(5); +/// assert!(future::timeout(dur, never).await.is_err()); +/// # +/// # Ok(()) }) } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub async fn timeout(dur: Duration, f: F) -> Result +where + F: Future, +{ + let f = TimeoutFuture { + future: f, + delay: Delay::new(dur), + }; + f.await +} + +/// A future that times out after a duration of time. +#[doc(hidden)] +#[allow(missing_debug_implementations)] +struct TimeoutFuture { + future: F, + delay: Delay, +} + +impl TimeoutFuture { + pin_utils::unsafe_pinned!(future: F); + pin_utils::unsafe_pinned!(delay: Delay); +} + +impl Future for TimeoutFuture { + type Output = Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.as_mut().future().poll(cx) { + Poll::Ready(v) => Poll::Ready(Ok(v)), + Poll::Pending => match self.delay().poll(cx) { + Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _priv: () })), + Poll::Pending => Poll::Pending, + }, + } + } +} + +/// An error returned when a future times out. +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct TimeoutError { + _priv: (), +} + +impl Error for TimeoutError {} + +impl fmt::Display for TimeoutError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + "future has timed out".fmt(f) + } +} From e8860454e7e9d242dee25a3c62cfff0504ae692d Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Fri, 30 Aug 2019 20:30:48 +0200 Subject: [PATCH 041/194] remove extra newline Co-Authored-By: Yoshua Wuyts --- src/stream/stream.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stream/stream.rs b/src/stream/stream.rs index 8710cb2..a4f98f1 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -153,7 +153,6 @@ pub trait Stream { /// /// let mut s = stream::empty::(); /// assert!(s.all(|_| false).await); - /// /// # /// # }) } /// ``` From e517c60fb1e1d7470a5d63490b73f3be0885997a Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Fri, 30 Aug 2019 20:32:03 +0200 Subject: [PATCH 042/194] remove comments --- src/stream/stream.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/stream/stream.rs b/src/stream/stream.rs index a4f98f1..e2dc856 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -156,10 +156,6 @@ pub trait Stream { /// # /// # }) } /// ``` - /// Stopping at the first `false`: - /// - /// TODO: add example here - /// TODO: add more examples #[inline] fn all(&mut self, f: F) -> AllFuture<'_, Self, F, Self::Item> where From a4d2cd1c827ac7025d577da268848b8b39dafcf9 Mon Sep 17 00:00:00 2001 From: Jason Davies Date: Sat, 31 Aug 2019 10:36:34 +0100 Subject: [PATCH 043/194] Fix typo. (#134) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ba7c5f..eb0160f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ are used to, but in an async version and ready for Rust's `async`/`await` syntax ## Quickstart -Add the following lines to you `Cargo.toml`: +Add the following lines to your `Cargo.toml`: ```toml [dependencies] From 532c73cf77a762e34e6fbafb9be9e7ff0bd66c78 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sat, 31 Aug 2019 23:15:26 +0900 Subject: [PATCH 044/194] Fix typo in stability-guarantees.md (#136) --- docs/src/overview/stability-guarantees.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/overview/stability-guarantees.md b/docs/src/overview/stability-guarantees.md index a843440..84bb68d 100644 --- a/docs/src/overview/stability-guarantees.md +++ b/docs/src/overview/stability-guarantees.md @@ -5,7 +5,7 @@ In short: we are versioning our software as `MAJOR.MINOR.PATCH`. We increase the: * MAJOR version when there are incompatible API changes, -* MINOR version when we introducece functionality in a backwards-compatible manner +* MINOR version when we introduce functionality in a backwards-compatible manner * PATCH version when we make backwards-compatible bug fixes We will provide migration documentation between major versions. From bff10fe83b62d112d10e7748d78468f5b31ec972 Mon Sep 17 00:00:00 2001 From: Shady Khalifa Date: Sun, 1 Sep 2019 19:58:16 +0200 Subject: [PATCH 045/194] Stream::any implementation (#135) * add stream::any method * use `ret` macro and small improvements * fix docs return type in `ret` macro --- src/stream/stream.rs | 117 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 113 insertions(+), 4 deletions(-) diff --git a/src/stream/stream.rs b/src/stream/stream.rs index e2dc856..7a127b0 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -36,10 +36,15 @@ cfg_if! { macro_rules! ret { ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); + ($a:lifetime, $f:tt, $o:ty, $t1:ty) => (ImplFuture<$a, $o>); + ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => (ImplFuture<$a, $o>); + } } else { macro_rules! ret { ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); + ($a:lifetime, $f:tt, $o:ty, $t1:ty) => ($f<$a, Self, $t1>); + ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => ($f<$a, Self, $t1, $t2>); } } } @@ -81,7 +86,7 @@ pub trait Stream { /// # /// # }) } /// ``` - fn next<'a>(&'a mut self) -> ret!('a, NextFuture, Option) + fn next(&mut self) -> ret!('_, NextFuture, Option) where Self: Unpin; @@ -157,14 +162,71 @@ pub trait Stream { /// # }) } /// ``` #[inline] - fn all(&mut self, f: F) -> AllFuture<'_, Self, F, Self::Item> + fn all(&mut self, f: F) -> ret!('_, AllFuture, bool, F, Self::Item) where Self: Sized, F: FnMut(Self::Item) -> bool, { AllFuture { stream: self, - result: true, + result: true, // the default if the empty stream + __item: PhantomData, + f, + } + } + + /// Tests if any element of the stream matches a predicate. + /// + /// `any()` takes a closure that returns `true` or `false`. It applies + /// this closure to each element of the stream, and if any of them return + /// `true`, then so does `any()`. If they all return `false`, it + /// returns `false`. + /// + /// `any()` is short-circuiting; in other words, it will stop processing + /// as soon as it finds a `true`, given that no matter what else happens, + /// the result will also be `true`. + /// + /// An empty stream returns `false`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat::(42).take(3); + /// assert!(s.any(|x| x == 42).await); + /// + /// # + /// # }) } + /// ``` + /// + /// Empty stream: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::empty::(); + /// assert!(!s.any(|_| false).await); + /// # + /// # }) } + /// ``` + #[inline] + fn any(&mut self, f: F) -> ret!('_, AnyFuture, bool, F, Self::Item) + where + Self: Sized, + F: FnMut(Self::Item) -> bool, + { + AnyFuture { + stream: self, + result: false, // the default if the empty stream __item: PhantomData, f, } @@ -174,7 +236,7 @@ pub trait Stream { impl Stream for T { type Item = ::Item; - fn next<'a>(&'a mut self) -> ret!('a, NextFuture, Option) + fn next(&mut self) -> ret!('_, NextFuture, Option) where Self: Unpin, { @@ -273,3 +335,50 @@ where } } } + +#[derive(Debug)] +pub struct AnyFuture<'a, S, F, T> +where + F: FnMut(T) -> bool, +{ + stream: &'a mut S, + f: F, + result: bool, + __item: PhantomData, +} + +impl<'a, S, F, T> AnyFuture<'a, S, F, T> +where + F: FnMut(T) -> bool, +{ + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(result: bool); + pin_utils::unsafe_unpinned!(f: F); +} + +impl Future for AnyFuture<'_, S, F, S::Item> +where + S: futures::Stream + Unpin + Sized, + F: FnMut(S::Item) -> bool, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures::Stream; + let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(v) => { + let result = (self.as_mut().f())(v); + *self.as_mut().result() = result; + if result { + Poll::Ready(true) + } else { + // don't forget to wake this task again to pull the next item from stream + cx.waker().wake_by_ref(); + Poll::Pending + } + } + None => Poll::Ready(self.result), + } + } +} From dde4b89369cc901c7e242a44265099f27bce1698 Mon Sep 17 00:00:00 2001 From: Roman Proskuryakov Date: Tue, 3 Sep 2019 10:09:05 +0300 Subject: [PATCH 046/194] Make Travis cfg pretty and modular (#118) --- .travis.yml | 40 +++++++++++++++++++++++++++++----------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9d78f7f..260a753 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,26 +1,44 @@ language: rust +env: RUSTFLAGS="-D warnings" + matrix: fast_finish: true include: - rust: nightly os: linux - env: RUSTFLAGS="-D warnings" BUILD_DOCS=1 BUILD_BOOK=1 + - rust: nightly os: osx osx_image: xcode9.2 - env: RUSTFLAGS="-D warnings" BUILD_DOCS=1 + - rust: nightly-x86_64-pc-windows-msvc os: windows - env: RUSTFLAGS="-D warnings" -before_script: - - rustup component add rustfmt - - if [[ -n "$BUILD_BOOK" ]]; then (test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh); fi + - name: fmt + rust: nightly + os: linux + before_script: + - rustup component add rustfmt + script: + - cargo fmt --all -- --check + + - name: docs + rust: nightly + os: linux + script: + - cargo doc --features docs + + - name: book + rust: nightly + os: linux + before_script: + - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh + - cargo build # to find 'extern crate async_std' by `mdbook test` + script: + - mdbook build docs + - mdbook test -L ./target/debug/deps docs script: - - if ! [[ -n "$BUILD_BOOK" ]]; then cargo check --features unstable --all --benches --bins --examples --tests && cargo test --features unstable --all; fi - - if [[ -n "$BUILD_BOOK" ]]; then cargo test --features unstable --all --benches --bins --examples --tests; fi - - cargo fmt --all -- --check - - if [[ -n "$BUILD_DOCS" ]]; then cargo doc --features docs; fi - - if [[ -n "$BUILD_BOOK" ]]; then mdbook build docs && mdbook test -L ./target/debug/deps docs; fi + - cargo check --features unstable --all --benches --bins --examples --tests + - cargo test --features unstable --all From 1f7f318c362d1083ddc4ecc0b5e77b146dc5e1e8 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 4 Sep 2019 19:32:47 +0200 Subject: [PATCH 047/194] Add bors.toml --- bors.toml | 1 + 1 file changed, 1 insertion(+) create mode 100644 bors.toml diff --git a/bors.toml b/bors.toml new file mode 100644 index 0000000..359f894 --- /dev/null +++ b/bors.toml @@ -0,0 +1 @@ +status = ["continuous-integration/travis-ci/push"] From 238a3c882bcb86da8c4041834ce25688ce270c7f Mon Sep 17 00:00:00 2001 From: DCjanus Date: Thu, 5 Sep 2019 02:09:49 +0800 Subject: [PATCH 048/194] Implement an async version of ToSocketAddrs (#74) * Implement an async version of ToSocketAddrs * fix documentation issue * genius hack: pretending to be `impl Future` * replace `std::net::ToSocketAddrs` with `async-std::net::ToSocketAddrs` * Move unit tests into the tests directory * Stylistic changes * Remove re-exports in async_std::net * fix broken link * some mirror changes * remove unnecessary format * migrate: `std::net::ToSocketAddrs` -> `async_std::net::ToSocketAddrs` * fix typo(tutorial) * remove unnecessary type bound * lifetime for future --- docs/src/tutorial/accept_loop.md | 25 ++- docs/src/tutorial/all_together.md | 3 +- docs/src/tutorial/clean_shutdown.md | 6 +- docs/src/tutorial/handling_disconnection.md | 3 +- docs/src/tutorial/implementing_a_client.md | 3 +- docs/src/tutorial/receiving_messages.md | 6 +- src/net/addr.rs | 162 ++++++++++++++++++++ src/net/mod.rs | 2 + src/net/tcp/listener.rs | 11 +- src/net/tcp/stream.rs | 11 +- src/net/udp/mod.rs | 16 +- tests/addr.rs | 84 ++++++++++ 12 files changed, 286 insertions(+), 46 deletions(-) create mode 100644 src/net/addr.rs create mode 100644 tests/addr.rs diff --git a/docs/src/tutorial/accept_loop.md b/docs/src/tutorial/accept_loop.md index d40d348..96c15ba 100644 --- a/docs/src/tutorial/accept_loop.md +++ b/docs/src/tutorial/accept_loop.md @@ -6,23 +6,20 @@ First of all, let's add required import boilerplate: ```rust,edition2018 # extern crate async_std; -use std::net::ToSocketAddrs; // 1 use async_std::{ - prelude::*, // 2 - task, // 3 - net::TcpListener, // 4 + prelude::*, // 1 + task, // 2 + net::{TcpListener, ToSocketAddrs}, // 3 }; -type Result = std::result::Result>; // 5 +type Result = std::result::Result>; // 4 ``` -1. `async_std` uses `std` types where appropriate. - We'll need `ToSocketAddrs` to specify address to listen on. -2. `prelude` re-exports some traits required to work with futures and streams. -3. The `task` module roughly corresponds to the `std::thread` module, but tasks are much lighter weight. +1. `prelude` re-exports some traits required to work with futures and streams. +2. The `task` module roughly corresponds to the `std::thread` module, but tasks are much lighter weight. A single thread can run many tasks. -4. For the socket type, we use `TcpListener` from `async_std`, which is just like `std::net::TcpListener`, but is non-blocking and uses `async` API. -5. We will skip implementing comprehensive error handling in this example. +3. For the socket type, we use `TcpListener` from `async_std`, which is just like `std::net::TcpListener`, but is non-blocking and uses `async` API. +4. We will skip implementing comprehensive error handling in this example. To propagate the errors, we will use a boxed error trait object. Do you know that there's `From<&'_ str> for Box` implementation in stdlib, which allows you to use strings with `?` operator? @@ -31,10 +28,9 @@ Now we can write the server's accept loop: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# net::TcpListener, +# net::{TcpListener, ToSocketAddrs}, # prelude::Stream, # }; -# use std::net::ToSocketAddrs; # # type Result = std::result::Result>; # @@ -69,11 +65,10 @@ Finally, let's add main: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# net::TcpListener, +# net::{TcpListener, ToSocketAddrs}, # prelude::Stream, # task, # }; -# use std::net::ToSocketAddrs; # # type Result = std::result::Result>; # diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 352d692..415f3b8 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -7,7 +7,7 @@ At this point, we only need to start the broker to get a fully-functioning (in t # extern crate futures; use async_std::{ io::{self, BufReader}, - net::{TcpListener, TcpStream}, + net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; @@ -17,7 +17,6 @@ use futures::{ }; use std::{ collections::hash_map::{HashMap, Entry}, - net::ToSocketAddrs, sync::Arc, }; diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 1c2fc76..6bf7056 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -25,7 +25,7 @@ Let's add waiting to the server: # extern crate futures; # use async_std::{ # io::{self, BufReader}, -# net::{TcpListener, TcpStream}, +# net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; @@ -35,7 +35,6 @@ Let's add waiting to the server: # }; # use std::{ # collections::hash_map::{HashMap, Entry}, -# net::ToSocketAddrs, # sync::Arc, # }; # @@ -160,7 +159,7 @@ And to the broker: # extern crate futures; # use async_std::{ # io::{self, BufReader}, -# net::{TcpListener, TcpStream}, +# net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; @@ -170,7 +169,6 @@ And to the broker: # }; # use std::{ # collections::hash_map::{HashMap, Entry}, -# net::ToSocketAddrs, # sync::Arc, # }; # diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 1cc07b2..30827ba 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -121,13 +121,12 @@ The final code looks like this: # extern crate futures; use async_std::{ io::{BufReader, BufRead, Write}, - net::{TcpListener, TcpStream}, + net::{TcpListener, TcpStream, ToSocketAddrs}, task, }; use futures::{channel::mpsc, future::Future, select, FutureExt, SinkExt, StreamExt}; use std::{ collections::hash_map::{Entry, HashMap}, - net::ToSocketAddrs, sync::Arc, }; diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 35cccd8..97e7319 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -19,11 +19,10 @@ With async, we can just use the `select!` macro. # extern crate futures; use async_std::{ io::{stdin, BufRead, BufReader, Write}, - net::TcpStream, + net::{TcpStream, ToSocketAddrs}, task, }; use futures::{select, FutureExt, StreamExt}; -use std::net::ToSocketAddrs; type Result = std::result::Result>; diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 9cef56d..667cf1c 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -11,11 +11,10 @@ We need to: # extern crate async_std; # use async_std::{ # io::{BufRead, BufReader}, -# net::{TcpListener, TcpStream}, +# net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::Stream, # task, # }; -# use std::net::ToSocketAddrs; # # type Result = std::result::Result>; # @@ -77,11 +76,10 @@ We can "fix" it by waiting for the task to be joined, like this: # extern crate async_std; # use async_std::{ # io::{BufRead, BufReader}, -# net::{TcpListener, TcpStream}, +# net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::Stream, # task, # }; -# use std::net::ToSocketAddrs; # # type Result = std::result::Result>; # diff --git a/src/net/addr.rs b/src/net/addr.rs new file mode 100644 index 0000000..39dba52 --- /dev/null +++ b/src/net/addr.rs @@ -0,0 +1,162 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; +use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; +use std::pin::Pin; + +use cfg_if::cfg_if; +use futures::future::{ready, Ready}; + +use crate::future::Future; +use crate::io; +use crate::task::blocking; +use crate::task::{Context, Poll}; +use std::marker::PhantomData; + +cfg_if! { + if #[cfg(feature = "docs")] { + #[doc(hidden)] + pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); + + macro_rules! ret { + ($a:lifetime, $f:tt, $i:ty) => (ImplFuture<$a, io::Result<$i>>); + } + } else { + macro_rules! ret { + ($a:lifetime, $f:tt, $i:ty) => ($f<$a, $i>); + } + } +} + +/// A trait for objects which can be converted or resolved to one or more [`SocketAddr`] values. +/// +/// This trait is an async version of [`std::net::ToSocketAddrs`]. +/// +/// [`std::net::ToSocketAddrs`]: https://doc.rust-lang.org/std/net/trait.ToSocketAddrs.html +/// [`SocketAddr`]: https://doc.rust-lang.org/std/net/enum.SocketAddr.html +pub trait ToSocketAddrs { + /// Returned iterator over socket addresses which this type may correspond to. + type Iter: Iterator; + + /// Converts this object to an iterator of resolved `SocketAddr`s. + /// + /// The returned iterator may not actually yield any values depending on the outcome of any + /// resolution performed. + /// + /// Note that this function may block a backend thread while resolution is performed. + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter); +} + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub enum ToSocketAddrsFuture<'a, I: Iterator> { + Phantom(PhantomData<&'a ()>), + Join(blocking::JoinHandle>), + Ready(Ready>), +} + +impl> Future for ToSocketAddrsFuture<'_, I> { + type Output = io::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + match self.get_mut() { + ToSocketAddrsFuture::Join(f) => Pin::new(&mut *f).poll(cx), + ToSocketAddrsFuture::Ready(f) => Pin::new(&mut *f).poll(cx), + _ => unreachable!(), + } + } +} + +impl ToSocketAddrs for SocketAddr { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for SocketAddrV4 { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for SocketAddrV6 { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for (IpAddr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for (Ipv4Addr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for (Ipv6Addr, u16) { + type Iter = std::option::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for (&str, u16) { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + let host = self.0.to_string(); + let port = self.1; + let join = blocking::spawn(async move { + std::net::ToSocketAddrs::to_socket_addrs(&(host.as_str(), port)) + }); + ToSocketAddrsFuture::Join(join) + } +} + +impl ToSocketAddrs for str { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + let socket_addrs = self.to_string(); + let join = + blocking::spawn(async move { std::net::ToSocketAddrs::to_socket_addrs(&socket_addrs) }); + ToSocketAddrsFuture::Join(join) + } +} + +impl<'a> ToSocketAddrs for &'a [SocketAddr] { + type Iter = std::iter::Cloned>; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + } +} + +impl ToSocketAddrs for &T { + type Iter = T::Iter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + (**self).to_socket_addrs() + } +} + +impl ToSocketAddrs for String { + type Iter = std::vec::IntoIter; + + fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { + ToSocketAddrs::to_socket_addrs(self.as_str()) + } +} diff --git a/src/net/mod.rs b/src/net/mod.rs index db4bd3c..259dc1d 100644 --- a/src/net/mod.rs +++ b/src/net/mod.rs @@ -28,9 +28,11 @@ //! # }) } //! ``` +pub use addr::ToSocketAddrs; pub use tcp::{Incoming, TcpListener, TcpStream}; pub use udp::UdpSocket; +mod addr; pub(crate) mod driver; mod tcp; mod udp; diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 60a9689..ac18387 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -1,4 +1,4 @@ -use std::net::{self, SocketAddr, ToSocketAddrs}; +use std::net::SocketAddr; use std::pin::Pin; use cfg_if::cfg_if; @@ -8,6 +8,7 @@ use super::TcpStream; use crate::future::Future; use crate::io; use crate::net::driver::IoHandle; +use crate::net::ToSocketAddrs; use crate::task::{Context, Poll}; /// A TCP socket server, listening for connections. @@ -82,7 +83,7 @@ impl TcpListener { pub async fn bind(addrs: A) -> io::Result { let mut last_err = None; - for addr in addrs.to_socket_addrs()? { + for addr in addrs.to_socket_addrs().await? { match mio::net::TcpListener::bind(&addr) { Ok(mio_listener) => { #[cfg(unix)] @@ -236,9 +237,9 @@ impl<'a> futures::Stream for Incoming<'a> { } } -impl From for TcpListener { +impl From for TcpListener { /// Converts a `std::net::TcpListener` into its asynchronous equivalent. - fn from(listener: net::TcpListener) -> TcpListener { + fn from(listener: std::net::TcpListener) -> TcpListener { let mio_listener = mio::net::TcpListener::from_std(listener).unwrap(); #[cfg(unix)] @@ -279,7 +280,7 @@ cfg_if! { impl FromRawFd for TcpListener { unsafe fn from_raw_fd(fd: RawFd) -> TcpListener { - net::TcpListener::from_raw_fd(fd).into() + std::net::TcpListener::from_raw_fd(fd).into() } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index d823317..5ea181f 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -1,6 +1,6 @@ use std::io::{IoSlice, IoSliceMut}; use std::mem; -use std::net::{self, SocketAddr, ToSocketAddrs}; +use std::net::SocketAddr; use std::pin::Pin; use cfg_if::cfg_if; @@ -9,6 +9,7 @@ use futures::io::{AsyncRead, AsyncWrite}; use crate::io; use crate::net::driver::IoHandle; +use crate::net::ToSocketAddrs; use crate::task::{Context, Poll}; /// A TCP stream between a local and a remote socket. @@ -80,7 +81,7 @@ impl TcpStream { pub async fn connect(addrs: A) -> io::Result { let mut last_err = None; - for addr in addrs.to_socket_addrs()? { + for addr in addrs.to_socket_addrs().await? { let res = Self::connect_to(addr).await; match res { @@ -437,9 +438,9 @@ impl AsyncWrite for &TcpStream { } } -impl From for TcpStream { +impl From for TcpStream { /// Converts a `std::net::TcpStream` into its asynchronous equivalent. - fn from(stream: net::TcpStream) -> TcpStream { + fn from(stream: std::net::TcpStream) -> TcpStream { let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap(); #[cfg(unix)] @@ -480,7 +481,7 @@ cfg_if! { impl FromRawFd for TcpStream { unsafe fn from_raw_fd(fd: RawFd) -> TcpStream { - net::TcpStream::from_raw_fd(fd).into() + std::net::TcpStream::from_raw_fd(fd).into() } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 3e9e749..19119a5 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -1,10 +1,12 @@ use std::io; -use std::net::{self, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; +use std::net::SocketAddr; use cfg_if::cfg_if; use futures::future; +use std::net::{Ipv4Addr, Ipv6Addr}; use crate::net::driver::IoHandle; +use crate::net::ToSocketAddrs; use crate::task::Poll; /// A UDP socket. @@ -75,7 +77,7 @@ impl UdpSocket { pub async fn bind(addr: A) -> io::Result { let mut last_err = None; - for addr in addr.to_socket_addrs()? { + for addr in addr.to_socket_addrs().await? { match mio::net::UdpSocket::bind(&addr) { Ok(mio_socket) => { #[cfg(unix)] @@ -152,7 +154,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn send_to(&self, buf: &[u8], addrs: A) -> io::Result { - let addr = match addrs.to_socket_addrs()?.next() { + let addr = match addrs.to_socket_addrs().await?.next() { Some(addr) => addr, None => { return Err(io::Error::new( @@ -237,7 +239,7 @@ impl UdpSocket { pub async fn connect(&self, addrs: A) -> io::Result<()> { let mut last_err = None; - for addr in addrs.to_socket_addrs()? { + for addr in addrs.to_socket_addrs().await? { match self.io_handle.get_ref().connect(addr) { Ok(()) => return Ok(()), Err(err) => last_err = Some(err), @@ -506,9 +508,9 @@ impl UdpSocket { } } -impl From for UdpSocket { +impl From for UdpSocket { /// Converts a `std::net::UdpSocket` into its asynchronous equivalent. - fn from(socket: net::UdpSocket) -> UdpSocket { + fn from(socket: std::net::UdpSocket) -> UdpSocket { let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap(); #[cfg(unix)] @@ -549,7 +551,7 @@ cfg_if! { impl FromRawFd for UdpSocket { unsafe fn from_raw_fd(fd: RawFd) -> UdpSocket { - net::UdpSocket::from_raw_fd(fd).into() + std::net::UdpSocket::from_raw_fd(fd).into() } } diff --git a/tests/addr.rs b/tests/addr.rs new file mode 100644 index 0000000..aada557 --- /dev/null +++ b/tests/addr.rs @@ -0,0 +1,84 @@ +use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6}; + +use async_std::net::ToSocketAddrs; +use async_std::task; + +fn blocking_resolve(a: A) -> Result, String> +where + A: ToSocketAddrs, + A::Iter: Send, +{ + let socket_addrs = task::block_on(a.to_socket_addrs()); + match socket_addrs { + Ok(a) => Ok(a.collect()), + Err(e) => Err(e.to_string()), + } +} + +#[test] +fn to_socket_addr_ipaddr_u16() { + let a = Ipv4Addr::new(77, 88, 21, 11); + let p = 12345; + let e = SocketAddr::V4(SocketAddrV4::new(a, p)); + assert_eq!(Ok(vec![e]), blocking_resolve((a, p))); +} + +#[test] +fn to_socket_addr_str_u16() { + let a = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 24352)); + assert_eq!(Ok(vec![a]), blocking_resolve(("77.88.21.11", 24352))); + + let a = SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), + 53, + 0, + 0, + )); + assert_eq!(Ok(vec![a]), blocking_resolve(("2a02:6b8:0:1::1", 53))); + + let a = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 23924)); + #[cfg(not(target_env = "sgx"))] + assert!(blocking_resolve(("localhost", 23924)).unwrap().contains(&a)); + #[cfg(target_env = "sgx")] + let _ = a; +} + +#[test] +fn to_socket_addr_str() { + let a = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 24352)); + assert_eq!(Ok(vec![a]), blocking_resolve("77.88.21.11:24352")); + + let a = SocketAddr::V6(SocketAddrV6::new( + Ipv6Addr::new(0x2a02, 0x6b8, 0, 1, 0, 0, 0, 1), + 53, + 0, + 0, + )); + assert_eq!(Ok(vec![a]), blocking_resolve("[2a02:6b8:0:1::1]:53")); + + let a = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 23924)); + #[cfg(not(target_env = "sgx"))] + assert!(blocking_resolve("localhost:23924").unwrap().contains(&a)); + #[cfg(target_env = "sgx")] + let _ = a; +} + +#[test] +fn to_socket_addr_string() { + let a = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(77, 88, 21, 11), 24352)); + let s: &str = "77.88.21.11:24352"; + assert_eq!(Ok(vec![a]), blocking_resolve(s)); + + let s: &String = &"77.88.21.11:24352".to_string(); + assert_eq!(Ok(vec![a]), blocking_resolve(s)); + + let s: String = "77.88.21.11:24352".to_string(); + assert_eq!(Ok(vec![a]), blocking_resolve(s)); +} + +// FIXME: figure out why this fails on openbsd and fix it +#[test] +#[cfg(not(any(windows, target_os = "openbsd")))] +fn to_socket_addr_str_bad() { + assert!(blocking_resolve("1200::AB00:1234::2552:7777:1313:34300").is_err()); +} From 5b96fa9daa7e9dac616727d2c0e698c7ffda1522 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Wed, 4 Sep 2019 19:39:04 +0300 Subject: [PATCH 049/194] move a-chat tutorial's code to this repo --- docs/src/tutorial/index.md | 3 +- examples/a-chat/client.rs | 45 +++++++++ examples/a-chat/main.rs | 13 +++ examples/a-chat/server.rs | 185 +++++++++++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 examples/a-chat/client.rs create mode 100644 examples/a-chat/main.rs create mode 100644 examples/a-chat/server.rs diff --git a/docs/src/tutorial/index.md b/docs/src/tutorial/index.md index 0136c1f..99ddf8e 100644 --- a/docs/src/tutorial/index.md +++ b/docs/src/tutorial/index.md @@ -8,5 +8,4 @@ How do you distribute the messages? In this tutorial, we will show you how to write one in `async-std`. -You can also find the tutorial in [our repository](https://github.com/async-rs/a-chat). - +You can also find the tutorial in [our repository](https://github.com/async-rs/async-std/blob/master/examples/a-chat). diff --git a/examples/a-chat/client.rs b/examples/a-chat/client.rs new file mode 100644 index 0000000..48634ba --- /dev/null +++ b/examples/a-chat/client.rs @@ -0,0 +1,45 @@ +use futures::select; +use futures::FutureExt; + +use async_std::{ + io::{stdin, BufReader}, + net::{TcpStream, ToSocketAddrs}, + prelude::*, + task, +}; + +type Result = std::result::Result>; + +pub(crate) fn main() -> Result<()> { + task::block_on(try_main("127.0.0.1:8080")) +} + +async fn try_main(addr: impl ToSocketAddrs) -> Result<()> { + let stream = TcpStream::connect(addr).await?; + let (reader, mut writer) = (&stream, &stream); + let reader = BufReader::new(reader); + let mut lines_from_server = futures::StreamExt::fuse(reader.lines()); + + let stdin = BufReader::new(stdin()); + let mut lines_from_stdin = futures::StreamExt::fuse(stdin.lines()); + loop { + select! { + line = lines_from_server.next().fuse() => match line { + Some(line) => { + let line = line?; + println!("{}", line); + }, + None => break, + }, + line = lines_from_stdin.next().fuse() => match line { + Some(line) => { + let line = line?; + writer.write_all(line.as_bytes()).await?; + writer.write_all(b"\n").await?; + } + None => break, + } + } + } + Ok(()) +} diff --git a/examples/a-chat/main.rs b/examples/a-chat/main.rs new file mode 100644 index 0000000..ced7cac --- /dev/null +++ b/examples/a-chat/main.rs @@ -0,0 +1,13 @@ +mod client; +mod server; + +type Result = std::result::Result>; + +fn main() -> Result<()> { + let mut args = std::env::args(); + match (args.nth(1).as_ref().map(String::as_str), args.next()) { + (Some("client"), None) => client::main(), + (Some("server"), None) => server::main(), + _ => Err("Usage: a-chat [client|server]")?, + } +} diff --git a/examples/a-chat/server.rs b/examples/a-chat/server.rs new file mode 100644 index 0000000..911d160 --- /dev/null +++ b/examples/a-chat/server.rs @@ -0,0 +1,185 @@ +use std::{ + collections::hash_map::{Entry, HashMap}, + sync::Arc, +}; + +use futures::{channel::mpsc, select, FutureExt, SinkExt}; + +use async_std::{ + io::BufReader, + net::{TcpListener, TcpStream, ToSocketAddrs}, + prelude::*, + task, +}; + +type Result = std::result::Result>; +type Sender = mpsc::UnboundedSender; +type Receiver = mpsc::UnboundedReceiver; + +#[derive(Debug)] +enum Void {} + +pub(crate) fn main() -> Result<()> { + task::block_on(accept_loop("127.0.0.1:8080")) +} + +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { + let listener = TcpListener::bind(addr).await?; + + let (broker_sender, broker_receiver) = mpsc::unbounded(); + let broker = task::spawn(broker_loop(broker_receiver)); + let mut incoming = listener.incoming(); + while let Some(stream) = incoming.next().await { + let stream = stream?; + println!("Accepting from: {}", stream.peer_addr()?); + spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); + } + drop(broker_sender); + broker.await; + Ok(()) +} + +async fn connection_loop(mut broker: Sender, stream: TcpStream) -> Result<()> { + let stream = Arc::new(stream); + let reader = BufReader::new(&*stream); + let mut lines = reader.lines(); + + let name = match lines.next().await { + None => Err("peer disconnected immediately")?, + Some(line) => line?, + }; + let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::(); + broker + .send(Event::NewPeer { + name: name.clone(), + stream: Arc::clone(&stream), + shutdown: shutdown_receiver, + }) + .await + .unwrap(); + + while let Some(line) = lines.next().await { + let line = line?; + let (dest, msg) = match line.find(':') { + None => continue, + Some(idx) => (&line[..idx], line[idx + 1..].trim()), + }; + let dest: Vec = dest + .split(',') + .map(|name| name.trim().to_string()) + .collect(); + let msg: String = msg.trim().to_string(); + + broker + .send(Event::Message { + from: name.clone(), + to: dest, + msg, + }) + .await + .unwrap(); + } + + Ok(()) +} + +async fn connection_writer_loop( + messages: &mut Receiver, + stream: Arc, + mut shutdown: Receiver, +) -> Result<()> { + let mut stream = &*stream; + loop { + select! { + msg = messages.next().fuse() => match msg { + Some(msg) => stream.write_all(msg.as_bytes()).await?, + None => break, + }, + void = shutdown.next().fuse() => match void { + Some(void) => match void {}, + None => break, + } + } + } + Ok(()) +} + +#[derive(Debug)] +enum Event { + NewPeer { + name: String, + stream: Arc, + shutdown: Receiver, + }, + Message { + from: String, + to: Vec, + msg: String, + }, +} + +async fn broker_loop(mut events: Receiver) { + let (disconnect_sender, mut disconnect_receiver) = + mpsc::unbounded::<(String, Receiver)>(); + let mut peers: HashMap> = HashMap::new(); + + loop { + let event = select! { + event = events.next().fuse() => match event { + None => break, + Some(event) => event, + }, + disconnect = disconnect_receiver.next().fuse() => { + let (name, _pending_messages) = disconnect.unwrap(); + assert!(peers.remove(&name).is_some()); + continue; + }, + }; + match event { + Event::Message { from, to, msg } => { + for addr in to { + if let Some(peer) = peers.get_mut(&addr) { + peer.send(format!("from {}: {}\n", from, msg)) + .await + .unwrap() + } + } + } + Event::NewPeer { + name, + stream, + shutdown, + } => match peers.entry(name.clone()) { + Entry::Occupied(..) => (), + Entry::Vacant(entry) => { + let (client_sender, mut client_receiver) = mpsc::unbounded(); + entry.insert(client_sender); + let mut disconnect_sender = disconnect_sender.clone(); + spawn_and_log_error(async move { + let res = + connection_writer_loop(&mut client_receiver, stream, shutdown).await; + disconnect_sender + .send((name, client_receiver)) + .await + .unwrap(); + res + }); + } + }, + } + } + drop(peers); + drop(disconnect_sender); + while let Some((_name, _pending_messages)) = disconnect_receiver.next().await {} +} + +fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> +where + F: Future> + Send + 'static, +{ + task::spawn(async move { + if let Err(e) = fut.await { + eprintln!("{}", e) + } + }) +} From bac74c2d7ff62ef0267b70cdbb4793fcd0b74b5f Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 5 Sep 2019 01:22:41 +0200 Subject: [PATCH 050/194] Reduce dependency on futures crate (#140) * Add future::poll_fn * Replace all uses of poll_fn with the new one * Remove some uses of futures * Simplify ReadDir and DirEntry * Remove some use of futures from File * Use futures subcrates * Fix imports in docs * Remove futures-util dependency * Remove futures-executor-preview * Refactor * Require more features in the futures-preview crate --- Cargo.toml | 13 +- .../connecting_readers_and_writers.md | 2 +- docs/src/tutorial/sending_messages.md | 2 +- src/fs/dir_entry.rs | 105 ++------------ src/fs/file.rs | 75 +++++----- src/fs/read_dir.rs | 61 +++----- src/future/mod.rs | 2 + src/future/pending.rs | 28 +++- src/future/poll_fn.rs | 49 +++++++ src/io/buf_read.rs | 12 +- src/io/buf_reader.rs | 43 +++--- src/io/copy.rs | 61 +++++++- src/io/empty.rs | 2 +- src/io/read.rs | 8 +- src/io/seek.rs | 2 +- src/io/sink.rs | 2 +- src/io/stderr.rs | 6 +- src/io/stdin.rs | 9 +- src/io/stdout.rs | 6 +- src/io/write.rs | 4 +- src/net/addr.rs | 28 ++-- src/net/driver/mod.rs | 14 +- src/net/tcp/listener.rs | 9 +- src/net/tcp/stream.rs | 6 +- src/net/udp/mod.rs | 10 +- src/os/unix/net/datagram.rs | 10 +- src/os/unix/net/listener.rs | 11 +- src/os/unix/net/stream.rs | 6 +- src/stream/empty.rs | 2 +- src/stream/once.rs | 2 +- src/stream/repeat.rs | 2 +- src/stream/stream.rs | 24 ++-- src/task/block_on.rs | 135 ++++++++++++++++++ src/task/mod.rs | 4 +- src/task/pool.rs | 62 +------- 35 files changed, 454 insertions(+), 363 deletions(-) create mode 100644 src/future/poll_fn.rs create mode 100644 src/task/block_on.rs diff --git a/Cargo.toml b/Cargo.toml index 230de64..24badb1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,9 @@ unstable = [] async-task = "1.0.0" cfg-if = "0.1.9" crossbeam-channel = "0.3.9" +futures-channel-preview = "0.3.0-alpha.18" +futures-core-preview = "0.3.0-alpha.18" +futures-io-preview = "0.3.0-alpha.18" futures-timer = "0.3.0" lazy_static = "1.3.0" log = { version = "0.4.8", features = ["kv_unstable"] } @@ -37,11 +40,11 @@ num_cpus = "1.10.0" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" -[dependencies.futures-preview] -version = "0.3.0-alpha.18" -features = ["async-await", "nightly"] - [dev-dependencies] femme = "1.1.0" -tempdir = "0.3.7" surf = "1.0.1" +tempdir = "0.3.7" + +[dev-dependencies.futures-preview] +version = "0.3.0-alpha.18" +features = ["std", "nightly", "async-await"] diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index 31631f8..f3ccf2d 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -20,7 +20,7 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined # task, # }; # use futures::channel::mpsc; -# use futures::SinkExt; +# use futures::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index 465d674..80784c2 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -20,7 +20,7 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the # prelude::Stream, # }; use futures::channel::mpsc; // 1 -use futures::SinkExt; +use futures::sink::SinkExt; use std::sync::Arc; # type Result = std::result::Result>; diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index 4e07a3f..c7928eb 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -1,15 +1,12 @@ use std::ffi::OsString; use std::fs; use std::path::PathBuf; -use std::pin::Pin; -use std::sync::Mutex; +use std::sync::Arc; use cfg_if::cfg_if; -use futures::future::{self, FutureExt, TryFutureExt}; -use crate::future::Future; use crate::io; -use crate::task::{blocking, Poll}; +use crate::task::blocking; /// An entry inside a directory. /// @@ -21,26 +18,11 @@ use crate::task::{blocking, Poll}; /// [`std::fs::DirEntry`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html #[derive(Debug)] pub struct DirEntry { - /// The state of the entry. - state: Mutex, - - /// The full path to the entry. - path: PathBuf, + /// The inner synchronous `DirEntry`. + inner: Arc, #[cfg(unix)] ino: u64, - - /// The bare name of the entry without the leading path. - file_name: OsString, -} - -/// The state of an asynchronous `DirEntry`. -/// -/// The `DirEntry` can be either idle or busy performing an asynchronous operation. -#[derive(Debug)] -enum State { - Idle(Option), - Busy(blocking::JoinHandle), } impl DirEntry { @@ -48,17 +30,13 @@ impl DirEntry { pub(crate) fn new(inner: fs::DirEntry) -> DirEntry { #[cfg(unix)] let dir_entry = DirEntry { - path: inner.path(), - file_name: inner.file_name(), ino: inner.ino(), - state: Mutex::new(State::Idle(Some(inner))), + inner: Arc::new(inner), }; #[cfg(windows)] let dir_entry = DirEntry { - path: inner.path(), - file_name: inner.file_name(), - state: Mutex::new(State::Idle(Some(inner))), + inner: Arc::new(inner), }; dir_entry @@ -89,7 +67,7 @@ impl DirEntry { /// # Ok(()) }) } /// ``` pub fn path(&self) -> PathBuf { - self.path.clone() + self.inner.path() } /// Returns the metadata for this entry. @@ -114,35 +92,8 @@ impl DirEntry { /// # Ok(()) }) } /// ``` pub async fn metadata(&self) -> io::Result { - future::poll_fn(|cx| { - let state = &mut *self.state.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.metadata(); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), - } - } - }) - .map(|opt| opt.ok_or_else(|| io_error("invalid state"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + let inner = self.inner.clone(); + blocking::spawn(async move { inner.metadata() }).await } /// Returns the file type for this entry. @@ -167,35 +118,8 @@ impl DirEntry { /// # Ok(()) }) } /// ``` pub async fn file_type(&self) -> io::Result { - future::poll_fn(|cx| { - let state = &mut *self.state.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file_type(); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), - } - } - }) - .map(|opt| opt.ok_or_else(|| io_error("invalid state"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + let inner = self.inner.clone(); + blocking::spawn(async move { inner.file_type() }).await } /// Returns the bare name of this entry without the leading path. @@ -218,15 +142,10 @@ impl DirEntry { /// # Ok(()) }) } /// ``` pub fn file_name(&self) -> OsString { - self.file_name.clone() + self.inner.file_name() } } -/// Creates a custom `io::Error` with an arbitrary error type. -fn io_error(err: impl Into>) -> io::Error { - io::Error::new(io::ErrorKind::Other, err) -} - cfg_if! { if #[cfg(feature = "docs")] { use crate::os::unix::fs::DirEntryExt; diff --git a/src/fs/file.rs b/src/fs/file.rs index b297181..09e2ad6 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -7,10 +7,9 @@ use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures::future::{self, FutureExt, TryFutureExt}; -use futures::io::{AsyncRead, AsyncSeek, AsyncWrite, Initializer}; +use futures_io::{AsyncRead, AsyncSeek, AsyncWrite, Initializer}; -use crate::future::Future; +use crate::future::{self, Future}; use crate::io; use crate::task::{blocking, Context, Poll}; @@ -234,7 +233,7 @@ impl File { State::Idle(opt) => match opt.take() { None => return Poll::Ready(None), Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); + let (s, r) = futures_channel::oneshot::channel(); // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { @@ -247,14 +246,14 @@ impl File { } }, // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } }) - .map(|opt| opt.ok_or_else(|| io_error("file closed"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + .await + .ok_or_else(|| io_error("file closed"))? + .await + .map_err(|_| io_error("blocking task failed"))? } /// Similar to [`sync_all`], except that it may not synchronize file metadata. @@ -289,7 +288,7 @@ impl File { State::Idle(opt) => match opt.take() { None => return Poll::Ready(None), Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); + let (s, r) = futures_channel::oneshot::channel(); // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { @@ -302,14 +301,14 @@ impl File { } }, // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } }) - .map(|opt| opt.ok_or_else(|| io_error("file closed"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + .await + .ok_or_else(|| io_error("file closed"))? + .await + .map_err(|_| io_error("blocking task failed"))? } /// Truncates or extends the underlying file. @@ -346,7 +345,7 @@ impl File { State::Idle(opt) => match opt.take() { None => return Poll::Ready(None), Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); + let (s, r) = futures_channel::oneshot::channel(); // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { @@ -359,14 +358,14 @@ impl File { } }, // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } }) - .map(|opt| opt.ok_or_else(|| io_error("file closed"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + .await + .ok_or_else(|| io_error("file closed"))? + .await + .map_err(|_| io_error("blocking task failed"))? } /// Queries metadata about the file. @@ -392,7 +391,7 @@ impl File { State::Idle(opt) => match opt.take() { None => return Poll::Ready(None), Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); + let (s, r) = futures_channel::oneshot::channel(); // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { @@ -405,14 +404,14 @@ impl File { } }, // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } }) - .map(|opt| opt.ok_or_else(|| io_error("file closed"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + .await + .ok_or_else(|| io_error("file closed"))? + .await + .map_err(|_| io_error("blocking task failed"))? } /// Changes the permissions on the underlying file. @@ -448,7 +447,7 @@ impl File { State::Idle(opt) => match opt.take() { None => return Poll::Ready(None), Some(inner) => { - let (s, r) = futures::channel::oneshot::channel(); + let (s, r) = futures_channel::oneshot::channel(); let perm = perm.take().unwrap(); // Start the operation asynchronously. @@ -462,14 +461,14 @@ impl File { } }, // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } }) - .map(|opt| opt.ok_or_else(|| io_error("file closed"))) - .await? - .map_err(|_| io_error("blocking task failed")) - .await? + .await + .ok_or_else(|| io_error("file closed"))? + .await + .map_err(|_| io_error("blocking task failed"))? } } @@ -543,7 +542,7 @@ impl AsyncRead for &File { })); } // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } @@ -619,7 +618,7 @@ impl AsyncWrite for &File { } } // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } @@ -652,7 +651,7 @@ impl AsyncWrite for &File { } } // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } @@ -677,7 +676,7 @@ impl AsyncWrite for &File { })); } // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } @@ -723,7 +722,7 @@ impl AsyncSeek for &File { } } // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index dffab3a..b377161 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,7 +1,6 @@ use std::fs; use std::path::Path; use std::pin::Pin; -use std::sync::Mutex; use super::DirEntry; use crate::future::Future; @@ -64,71 +63,45 @@ pub async fn read_dir>(path: P) -> io::Result { /// [`DirEntry`]: struct.DirEntry.html /// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html #[derive(Debug)] -pub struct ReadDir(Mutex); +pub struct ReadDir(State); /// The state of an asynchronous `ReadDir`. /// /// The `ReadDir` can be either idle or busy performing an asynchronous operation. #[derive(Debug)] enum State { - Idle(Option), - Busy(blocking::JoinHandle), -} - -/// Inner representation of an asynchronous `DirEntry`. -#[derive(Debug)] -struct Inner { - /// The blocking handle. - read_dir: fs::ReadDir, - - /// The next item in the stream. - item: Option>, + Idle(Option), + Busy(blocking::JoinHandle<(fs::ReadDir, Option>)>), } impl ReadDir { /// Creates an asynchronous `ReadDir` from a synchronous handle. pub(crate) fn new(inner: fs::ReadDir) -> ReadDir { - ReadDir(Mutex::new(State::Idle(Some(Inner { - read_dir: inner, - item: None, - })))) + ReadDir(State::Idle(Some(inner))) } } -impl futures::Stream for ReadDir { +impl futures_core::stream::Stream for ReadDir { type Item = io::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.0.lock().unwrap(); - loop { - match state { + match &mut self.0 { State::Idle(opt) => { - let inner = match opt.as_mut() { - None => return Poll::Ready(None), - Some(inner) => inner, - }; + let mut inner = opt.take().unwrap(); - // Check if the operation has completed. - if let Some(res) = inner.item.take() { - return Poll::Ready(Some(res)); - } else { - let mut inner = opt.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - match inner.read_dir.next() { - None => State::Idle(None), - Some(res) => { - inner.item = Some(res.map(DirEntry::new)); - State::Idle(Some(inner)) - } - } - })); - } + // Start the operation asynchronously. + self.0 = State::Busy(blocking::spawn(async move { + let next = inner.next(); + (inner, next) + })); } // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => { + let (inner, opt) = futures_core::ready!(Pin::new(task).poll(cx)); + self.0 = State::Idle(Some(inner)); + return Poll::Ready(opt.map(|res| res.map(DirEntry::new))); + } } } } diff --git a/src/future/mod.rs b/src/future/mod.rs index 5d510a4..7d88b90 100644 --- a/src/future/mod.rs +++ b/src/future/mod.rs @@ -6,9 +6,11 @@ pub use std::future::Future; use cfg_if::cfg_if; pub use pending::pending; +pub use poll_fn::poll_fn; pub use ready::ready; mod pending; +mod poll_fn; mod ready; cfg_if! { diff --git a/src/future/pending.rs b/src/future/pending.rs index 41284f5..aaee706 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -1,16 +1,23 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::future::Future; +use crate::task::{Context, Poll}; + /// Never resolves to a value. /// /// # Examples +/// /// ``` /// # fn main() { async_std::task::block_on(async { /// # /// use std::time::Duration; /// -/// use async_std::future::pending; +/// use async_std::future; /// use async_std::io; /// /// let dur = Duration::from_secs(1); -/// let fut = pending(); +/// let fut = future::pending(); /// /// let res: io::Result<()> = io::timeout(dur, fut).await; /// assert!(res.is_err()); @@ -18,5 +25,20 @@ /// # }) } /// ``` pub async fn pending() -> T { - futures::future::pending::().await + let fut = Pending { + _marker: PhantomData, + }; + fut.await +} + +struct Pending { + _marker: PhantomData, +} + +impl Future for Pending { + type Output = T; + + fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { + Poll::Pending + } } diff --git a/src/future/poll_fn.rs b/src/future/poll_fn.rs new file mode 100644 index 0000000..116e71c --- /dev/null +++ b/src/future/poll_fn.rs @@ -0,0 +1,49 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::task::{Context, Poll}; + +/// Creates a new future wrapping around a function returning [`Poll`]. +/// +/// Polling the returned future delegates to the wrapped function. +/// +/// # Examples +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::future; +/// use async_std::task::{Context, Poll}; +/// +/// fn poll_greeting(_: &mut Context<'_>) -> Poll { +/// Poll::Ready("hello world".to_string()) +/// } +/// +/// assert_eq!(future::poll_fn(poll_greeting).await, "hello world"); +/// # +/// # }) } +/// ``` +pub async fn poll_fn(f: F) -> T +where + F: FnMut(&mut Context<'_>) -> Poll, +{ + let fut = PollFn { f }; + fut.await +} + +struct PollFn { + f: F, +} + +impl Unpin for PollFn {} + +impl Future for PollFn +where + F: FnMut(&mut Context<'_>) -> Poll, +{ + type Output = T; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + (&mut self.f)(cx) + } +} diff --git a/src/io/buf_read.rs b/src/io/buf_read.rs index 6b7b9db..b05bf89 100644 --- a/src/io/buf_read.rs +++ b/src/io/buf_read.rs @@ -3,7 +3,7 @@ use std::pin::Pin; use std::str; use cfg_if::cfg_if; -use futures::io::AsyncBufRead; +use futures_io::AsyncBufRead; use crate::future::Future; use crate::io; @@ -212,7 +212,7 @@ impl Future for ReadLineFuture<'_, T> { } = &mut *self; let reader = Pin::new(reader); - let ret = futures::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + let ret = futures_core::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); if str::from_utf8(&bytes).is_err() { Poll::Ready(ret.and_then(|_| { Err(io::Error::new( @@ -247,7 +247,7 @@ pub struct Lines { read: usize, } -impl futures::Stream for Lines { +impl futures_core::stream::Stream for Lines { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -258,7 +258,7 @@ impl futures::Stream for Lines { read, } = unsafe { self.get_unchecked_mut() }; let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures::ready!(read_line_internal(reader, cx, buf, bytes, read))?; + let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; if n == 0 && buf.is_empty() { return Poll::Ready(None); } @@ -279,7 +279,7 @@ pub fn read_line_internal( bytes: &mut Vec, read: &mut usize, ) -> Poll> { - let ret = futures::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + let ret = futures_core::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); if str::from_utf8(&bytes).is_err() { Poll::Ready(ret.and_then(|_| { Err(io::Error::new( @@ -305,7 +305,7 @@ pub fn read_until_internal( ) -> Poll> { loop { let (done, used) = { - let available = futures::ready!(reader.as_mut().poll_fill_buf(cx))?; + let available = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; if let Some(i) = memchr::memchr(byte, available) { buf.extend_from_slice(&available[..=i]); (true, i + 1) diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 2ad10cc..f38307a 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -2,7 +2,7 @@ use std::io::{IoSliceMut, Read as _}; use std::pin::Pin; use std::{cmp, fmt}; -use futures::io::{AsyncBufRead, AsyncRead, AsyncSeek, Initializer}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, Initializer}; use crate::io::{self, SeekFrom}; use crate::task::{Context, Poll}; @@ -51,7 +51,7 @@ pub struct BufReader { cap: usize, } -impl BufReader { +impl BufReader { /// Creates a buffered reader with default buffer capacity. /// /// The default capacity is currently 8 KB, but may change in the future. @@ -87,17 +87,11 @@ impl BufReader { /// # Ok(()) }) } /// ``` pub fn with_capacity(capacity: usize, inner: R) -> BufReader { - unsafe { - let mut buffer = Vec::with_capacity(capacity); - buffer.set_len(capacity); - inner.initializer().initialize(&mut buffer); - - BufReader { - inner, - buf: buffer.into_boxed_slice(), - pos: 0, - cap: 0, - } + BufReader { + inner, + buf: vec![0; capacity].into_boxed_slice(), + pos: 0, + cap: 0, } } } @@ -209,11 +203,11 @@ impl AsyncRead for BufReader { // (larger than our internal buffer), bypass our internal buffer // entirely. if self.pos == self.cap && buf.len() >= self.buf.len() { - let res = futures::ready!(self.as_mut().inner().poll_read(cx, buf)); + let res = futures_core::ready!(self.as_mut().inner().poll_read(cx, buf)); self.discard_buffer(); return Poll::Ready(res); } - let mut rem = futures::ready!(self.as_mut().poll_fill_buf(cx))?; + let mut rem = futures_core::ready!(self.as_mut().poll_fill_buf(cx))?; let nread = rem.read(buf)?; self.consume(nread); Poll::Ready(Ok(nread)) @@ -226,11 +220,11 @@ impl AsyncRead for BufReader { ) -> Poll> { let total_len = bufs.iter().map(|b| b.len()).sum::(); if self.pos == self.cap && total_len >= self.buf.len() { - let res = futures::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); + let res = futures_core::ready!(self.as_mut().inner().poll_read_vectored(cx, bufs)); self.discard_buffer(); return Poll::Ready(res); } - let mut rem = futures::ready!(self.as_mut().poll_fill_buf(cx))?; + let mut rem = futures_core::ready!(self.as_mut().poll_fill_buf(cx))?; let nread = rem.read_vectored(bufs)?; self.consume(nread); Poll::Ready(Ok(nread)) @@ -261,7 +255,7 @@ impl AsyncBufRead for BufReader { // to tell the compiler that the pos..cap slice is always valid. if *pos >= *cap { debug_assert!(*pos == *cap); - *cap = futures::ready!(inner.as_mut().poll_read(cx, buf))?; + *cap = futures_core::ready!(inner.as_mut().poll_read(cx, buf))?; *pos = 0; } Poll::Ready(Ok(&buf[*pos..*cap])) @@ -272,7 +266,7 @@ impl AsyncBufRead for BufReader { } } -impl fmt::Debug for BufReader { +impl fmt::Debug for BufReader { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("BufReader") .field("reader", &self.inner) @@ -316,25 +310,26 @@ impl AsyncSeek for BufReader { // support seeking by i64::min_value() so we need to handle underflow when subtracting // remainder. if let Some(offset) = n.checked_sub(remainder) { - result = futures::ready!( + result = futures_core::ready!( self.as_mut() .inner() .poll_seek(cx, SeekFrom::Current(offset)) )?; } else { // seek backwards by our remainder, and then by the offset - futures::ready!( + futures_core::ready!( self.as_mut() .inner() .poll_seek(cx, SeekFrom::Current(-remainder)) )?; self.as_mut().discard_buffer(); - result = - futures::ready!(self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)))?; + result = futures_core::ready!( + self.as_mut().inner().poll_seek(cx, SeekFrom::Current(n)) + )?; } } else { // Seeking with Start/End doesn't care about our buffer length. - result = futures::ready!(self.as_mut().inner().poll_seek(cx, pos))?; + result = futures_core::ready!(self.as_mut().inner().poll_seek(cx, pos))?; } self.discard_buffer(); Poll::Ready(Ok(result)) diff --git a/src/io/copy.rs b/src/io/copy.rs index 961c826..ccc6bc8 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,6 +1,10 @@ -use futures::io::{AsyncRead, AsyncReadExt, AsyncWrite}; +use std::pin::Pin; -use crate::io; +use futures_io::{AsyncBufRead, AsyncRead, AsyncWrite}; + +use crate::future::Future; +use crate::io::{self, BufReader}; +use crate::task::{Context, Poll}; /// Copies the entire contents of a reader into a writer. /// @@ -44,6 +48,55 @@ where R: AsyncRead + Unpin + ?Sized, W: AsyncWrite + Unpin + ?Sized, { - let bytes_read = reader.copy_into(writer).await?; - Ok(bytes_read) + pub struct CopyFuture<'a, R, W: ?Sized> { + reader: R, + writer: &'a mut W, + amt: u64, + } + + impl CopyFuture<'_, R, W> { + fn project(self: Pin<&mut Self>) -> (Pin<&mut R>, Pin<&mut W>, &mut u64) { + unsafe { + let this = self.get_unchecked_mut(); + ( + Pin::new_unchecked(&mut this.reader), + Pin::new(&mut *this.writer), + &mut this.amt, + ) + } + } + } + + impl Future for CopyFuture<'_, R, W> + where + R: AsyncBufRead, + W: AsyncWrite + Unpin + ?Sized, + { + type Output = io::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let (mut reader, mut writer, amt) = self.project(); + loop { + let buffer = futures_core::ready!(reader.as_mut().poll_fill_buf(cx))?; + if buffer.is_empty() { + futures_core::ready!(writer.as_mut().poll_flush(cx))?; + return Poll::Ready(Ok(*amt)); + } + + let i = futures_core::ready!(writer.as_mut().poll_write(cx, buffer))?; + if i == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + *amt += i as u64; + reader.as_mut().consume(i); + } + } + } + + let future = CopyFuture { + reader: BufReader::new(reader), + writer, + amt: 0, + }; + future.await } diff --git a/src/io/empty.rs b/src/io/empty.rs index a832677..35ed732 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -1,7 +1,7 @@ use std::fmt; use std::pin::Pin; -use futures::io::{AsyncBufRead, AsyncRead, Initializer}; +use futures_io::{AsyncBufRead, AsyncRead, Initializer}; use crate::io; use crate::task::{Context, Poll}; diff --git a/src/io/read.rs b/src/io/read.rs index cf3732f..4b6bb1f 100644 --- a/src/io/read.rs +++ b/src/io/read.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use std::str; use cfg_if::cfg_if; -use futures::io::AsyncRead; +use futures_io::AsyncRead; use crate::future::Future; use crate::io; @@ -290,7 +290,7 @@ impl Future for ReadToStringFuture<'_, T> { } = &mut *self; let reader = Pin::new(reader); - let ret = futures::ready!(read_to_end_internal(reader, cx, bytes, *start_len)); + let ret = futures_core::ready!(read_to_end_internal(reader, cx, bytes, *start_len)); if str::from_utf8(&bytes).is_err() { Poll::Ready(ret.and_then(|_| { Err(io::Error::new( @@ -321,7 +321,7 @@ impl Future for ReadExactFuture<'_, T> { let Self { reader, buf } = &mut *self; while !buf.is_empty() { - let n = futures::ready!(Pin::new(&mut *reader).poll_read(cx, buf))?; + let n = futures_core::ready!(Pin::new(&mut *reader).poll_read(cx, buf))?; let (_, rest) = mem::replace(buf, &mut []).split_at_mut(n); *buf = rest; @@ -377,7 +377,7 @@ pub fn read_to_end_internal( } } - match futures::ready!(rd.as_mut().poll_read(cx, &mut g.buf[g.len..])) { + match futures_core::ready!(rd.as_mut().poll_read(cx, &mut g.buf[g.len..])) { Ok(0) => { ret = Poll::Ready(Ok(g.len - start_len)); break; diff --git a/src/io/seek.rs b/src/io/seek.rs index 9250faa..61a5d9c 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use cfg_if::cfg_if; -use futures::io::AsyncSeek; +use futures_io::AsyncSeek; use crate::future::Future; use crate::io::{self, SeekFrom}; diff --git a/src/io/sink.rs b/src/io/sink.rs index ec38431..fba5633 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -1,7 +1,7 @@ use std::fmt; use std::pin::Pin; -use futures::io::AsyncWrite; +use futures_io::AsyncWrite; use crate::io; use crate::task::{Context, Poll}; diff --git a/src/io/stderr.rs b/src/io/stderr.rs index 73b64ce..bb2318f 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -3,7 +3,7 @@ use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures::io::AsyncWrite; +use futures_io::AsyncWrite; use crate::future::Future; use crate::task::{blocking, Context, Poll}; @@ -125,7 +125,7 @@ impl AsyncWrite for Stderr { } } // Poll the asynchronous operation the stderr is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } @@ -153,7 +153,7 @@ impl AsyncWrite for Stderr { } } // Poll the asynchronous operation the stderr is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index bd4c111..9fe432d 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -3,10 +3,9 @@ use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures::future; -use futures::io::{AsyncRead, Initializer}; +use futures_io::{AsyncRead, Initializer}; -use crate::future::Future; +use crate::future::{self, Future}; use crate::task::{blocking, Context, Poll}; /// Constructs a new handle to the standard input of the current process. @@ -130,7 +129,7 @@ impl Stdin { } } // Poll the asynchronous operation the stdin is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } }) @@ -182,7 +181,7 @@ impl AsyncRead for Stdin { } } // Poll the asynchronous operation the stdin is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index 5045d11..f62f3df 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -3,7 +3,7 @@ use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures::io::AsyncWrite; +use futures_io::AsyncWrite; use crate::future::Future; use crate::task::{blocking, Context, Poll}; @@ -125,7 +125,7 @@ impl AsyncWrite for Stdout { } } // Poll the asynchronous operation the stdout is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } @@ -153,7 +153,7 @@ impl AsyncWrite for Stdout { } } // Poll the asynchronous operation the stdout is currently blocked on. - State::Busy(task) => *state = futures::ready!(Pin::new(task).poll(cx)), + State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), } } } diff --git a/src/io/write.rs b/src/io/write.rs index 0fba81b..d332eec 100644 --- a/src/io/write.rs +++ b/src/io/write.rs @@ -3,7 +3,7 @@ use std::mem; use std::pin::Pin; use cfg_if::cfg_if; -use futures::io::AsyncWrite; +use futures_io::AsyncWrite; use crate::future::Future; use crate::io; @@ -201,7 +201,7 @@ impl Future for WriteAllFuture<'_, T> { let Self { writer, buf } = &mut *self; while !buf.is_empty() { - let n = futures::ready!(Pin::new(&mut **writer).poll_write(cx, buf))?; + let n = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buf))?; let (_, rest) = mem::replace(buf, &[]).split_at(n); *buf = rest; diff --git a/src/net/addr.rs b/src/net/addr.rs index 39dba52..71f43a5 100644 --- a/src/net/addr.rs +++ b/src/net/addr.rs @@ -1,15 +1,14 @@ +use std::marker::PhantomData; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; use std::net::{SocketAddr, SocketAddrV4, SocketAddrV6}; use std::pin::Pin; use cfg_if::cfg_if; -use futures::future::{ready, Ready}; use crate::future::Future; use crate::io; use crate::task::blocking; use crate::task::{Context, Poll}; -use std::marker::PhantomData; cfg_if! { if #[cfg(feature = "docs")] { @@ -47,19 +46,22 @@ pub trait ToSocketAddrs { #[doc(hidden)] #[allow(missing_debug_implementations)] -pub enum ToSocketAddrsFuture<'a, I: Iterator> { +pub enum ToSocketAddrsFuture<'a, I> { Phantom(PhantomData<&'a ()>), Join(blocking::JoinHandle>), - Ready(Ready>), + Ready(Option>), } impl> Future for ToSocketAddrsFuture<'_, I> { type Output = io::Result; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - match self.get_mut() { + match unsafe { self.get_unchecked_mut() } { ToSocketAddrsFuture::Join(f) => Pin::new(&mut *f).poll(cx), - ToSocketAddrsFuture::Ready(f) => Pin::new(&mut *f).poll(cx), + ToSocketAddrsFuture::Ready(res) => { + let res = res.take().expect("polled a completed future"); + Poll::Ready(res) + } _ => unreachable!(), } } @@ -69,7 +71,7 @@ impl ToSocketAddrs for SocketAddr { type Iter = std::option::IntoIter; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } @@ -77,7 +79,7 @@ impl ToSocketAddrs for SocketAddrV4 { type Iter = std::option::IntoIter; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } @@ -85,7 +87,7 @@ impl ToSocketAddrs for SocketAddrV6 { type Iter = std::option::IntoIter; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } @@ -93,7 +95,7 @@ impl ToSocketAddrs for (IpAddr, u16) { type Iter = std::option::IntoIter; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } @@ -101,7 +103,7 @@ impl ToSocketAddrs for (Ipv4Addr, u16) { type Iter = std::option::IntoIter; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } @@ -109,7 +111,7 @@ impl ToSocketAddrs for (Ipv6Addr, u16) { type Iter = std::option::IntoIter; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } @@ -141,7 +143,7 @@ impl<'a> ToSocketAddrs for &'a [SocketAddr] { type Iter = std::iter::Cloned>; fn to_socket_addrs(&self) -> ret!('_, ToSocketAddrsFuture, Self::Iter) { - ToSocketAddrsFuture::Ready(ready(std::net::ToSocketAddrs::to_socket_addrs(self))) + ToSocketAddrsFuture::Ready(Some(std::net::ToSocketAddrs::to_socket_addrs(self))) } } diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 4002254..04ab4bb 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -4,7 +4,7 @@ use std::pin::Pin; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use futures::io::{AsyncRead, AsyncWrite}; +use futures_io::{AsyncRead, AsyncWrite}; use lazy_static::lazy_static; use mio::{self, Evented}; use slab::Slab; @@ -303,7 +303,7 @@ impl AsyncRead for IoHandle { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - futures::ready!(Pin::new(&mut *self).poll_readable(cx)?); + futures_core::ready!(Pin::new(&mut *self).poll_readable(cx)?); match self.source.read(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { @@ -324,7 +324,7 @@ where cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - futures::ready!(Pin::new(&mut *self).poll_readable(cx)?); + futures_core::ready!(Pin::new(&mut *self).poll_readable(cx)?); match (&self.source).read(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { @@ -342,7 +342,7 @@ impl AsyncWrite for IoHandle { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - futures::ready!(self.poll_writable(cx)?); + futures_core::ready!(self.poll_writable(cx)?); match self.source.write(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { @@ -354,7 +354,7 @@ impl AsyncWrite for IoHandle { } fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures::ready!(self.poll_writable(cx)?); + futures_core::ready!(self.poll_writable(cx)?); match self.source.flush() { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { @@ -379,7 +379,7 @@ where cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - futures::ready!(self.poll_writable(cx)?); + futures_core::ready!(self.poll_writable(cx)?); match (&self.source).write(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { @@ -391,7 +391,7 @@ where } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures::ready!(self.poll_writable(cx)?); + futures_core::ready!(self.poll_writable(cx)?); match (&self.source).flush() { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index ac18387..67a4865 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -2,10 +2,9 @@ use std::net::SocketAddr; use std::pin::Pin; use cfg_if::cfg_if; -use futures::future; use super::TcpStream; -use crate::future::Future; +use crate::future::{self, Future}; use crate::io; use crate::net::driver::IoHandle; use crate::net::ToSocketAddrs; @@ -129,7 +128,7 @@ impl TcpListener { /// ``` pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_readable(cx)?); + futures_core::ready!(self.io_handle.poll_readable(cx)?); match self.io_handle.get_ref().accept_std() { Ok((io, addr)) => { @@ -225,14 +224,14 @@ impl TcpListener { #[derive(Debug)] pub struct Incoming<'a>(&'a TcpListener); -impl<'a> futures::Stream for Incoming<'a> { +impl<'a> futures_core::stream::Stream for Incoming<'a> { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let future = self.0.accept(); pin_utils::pin_mut!(future); - let (socket, _) = futures::ready!(future.poll(cx))?; + let (socket, _) = futures_core::ready!(future.poll(cx))?; Poll::Ready(Some(Ok(socket))) } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 5ea181f..b63b7f2 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -4,9 +4,9 @@ use std::net::SocketAddr; use std::pin::Pin; use cfg_if::cfg_if; -use futures::future; -use futures::io::{AsyncRead, AsyncWrite}; +use futures_io::{AsyncRead, AsyncWrite}; +use crate::future; use crate::io; use crate::net::driver::IoHandle; use crate::net::ToSocketAddrs; @@ -259,7 +259,7 @@ impl TcpStream { /// ``` pub async fn peek(&self, buf: &mut [u8]) -> io::Result { let res = future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_readable(cx)?); + futures_core::ready!(self.io_handle.poll_readable(cx)?); match self.io_handle.get_ref().peek(buf) { Ok(len) => Poll::Ready(Ok(len)), diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 19119a5..2302427 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -2,9 +2,9 @@ use std::io; use std::net::SocketAddr; use cfg_if::cfg_if; -use futures::future; use std::net::{Ipv4Addr, Ipv6Addr}; +use crate::future; use crate::net::driver::IoHandle; use crate::net::ToSocketAddrs; use crate::task::Poll; @@ -165,7 +165,7 @@ impl UdpSocket { }; future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_writable(cx)?); + futures_core::ready!(self.io_handle.poll_writable(cx)?); match self.io_handle.get_ref().send_to(buf, &addr) { Ok(n) => Poll::Ready(Ok(n)), @@ -200,7 +200,7 @@ impl UdpSocket { /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_readable(cx)?); + futures_core::ready!(self.io_handle.poll_readable(cx)?); match self.io_handle.get_ref().recv_from(buf) { Ok(n) => Poll::Ready(Ok(n)), @@ -282,7 +282,7 @@ impl UdpSocket { /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_writable(cx)?); + futures_core::ready!(self.io_handle.poll_writable(cx)?); match self.io_handle.get_ref().send(buf) { Ok(n) => Poll::Ready(Ok(n)), @@ -317,7 +317,7 @@ impl UdpSocket { /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_readable(cx)?); + futures_core::ready!(self.io_handle.poll_readable(cx)?); match self.io_handle.get_ref().recv(buf) { Ok(n) => Poll::Ready(Ok(n)), diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 62debc4..fb5b5f1 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -4,10 +4,10 @@ use std::fmt; use std::net::Shutdown; use std::path::Path; -use futures::future; use mio_uds; use super::SocketAddr; +use crate::future; use crate::io; use crate::net::driver::IoHandle; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; @@ -203,7 +203,7 @@ impl UnixDatagram { /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_readable(cx)?); + futures_core::ready!(self.io_handle.poll_readable(cx)?); match self.io_handle.get_ref().recv_from(buf) { Ok(n) => Poll::Ready(Ok(n)), @@ -236,7 +236,7 @@ impl UnixDatagram { /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_writable(cx)?); + futures_core::ready!(self.io_handle.poll_writable(cx)?); match self.io_handle.get_ref().recv(buf) { Ok(n) => Poll::Ready(Ok(n)), @@ -268,7 +268,7 @@ impl UnixDatagram { /// ``` pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_writable(cx)?); + futures_core::ready!(self.io_handle.poll_writable(cx)?); match self.io_handle.get_ref().send_to(buf, path.as_ref()) { Ok(n) => Poll::Ready(Ok(n)), @@ -301,7 +301,7 @@ impl UnixDatagram { /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_writable(cx)?); + futures_core::ready!(self.io_handle.poll_writable(cx)?); match self.io_handle.get_ref().send(buf) { Ok(n) => Poll::Ready(Ok(n)), diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 74b0a28..61df251 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -4,12 +4,11 @@ use std::fmt; use std::path::Path; use std::pin::Pin; -use futures::future; use mio_uds; use super::SocketAddr; use super::UnixStream; -use crate::future::Future; +use crate::future::{self, Future}; use crate::io; use crate::net::driver::IoHandle; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; @@ -97,7 +96,7 @@ impl UnixListener { /// ``` pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { future::poll_fn(|cx| { - futures::ready!(self.io_handle.poll_readable(cx)?); + futures_core::ready!(self.io_handle.poll_readable(cx)?); match self.io_handle.get_ref().accept_std() { Ok(Some((io, addr))) => { @@ -198,14 +197,14 @@ impl fmt::Debug for UnixListener { #[derive(Debug)] pub struct Incoming<'a>(&'a UnixListener); -impl futures::Stream for Incoming<'_> { +impl futures_core::stream::Stream for Incoming<'_> { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let future = self.0.accept(); - futures::pin_mut!(future); + pin_utils::pin_mut!(future); - let (socket, _) = futures::ready!(future.poll(cx))?; + let (socket, _) = futures_core::ready!(future.poll(cx))?; Poll::Ready(Some(Ok(socket))) } } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 74afdee..389720e 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -6,11 +6,11 @@ use std::net::Shutdown; use std::path::Path; use std::pin::Pin; -use futures::future; -use futures::io::{AsyncRead, AsyncWrite}; +use futures_io::{AsyncRead, AsyncWrite}; use mio_uds; use super::SocketAddr; +use crate::future; use crate::io; use crate::net::driver::IoHandle; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; @@ -81,7 +81,7 @@ impl UnixStream { future::poll_fn(|cx| { match &mut state { State::Waiting(stream) => { - futures::ready!(stream.io_handle.poll_writable(cx)?); + futures_core::ready!(stream.io_handle.poll_writable(cx)?); if let Some(err) = stream.io_handle.get_ref().take_error()? { return Poll::Ready(Err(err)); diff --git a/src/stream/empty.rs b/src/stream/empty.rs index 4445e43..f4cf552 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -35,7 +35,7 @@ pub struct Empty { _marker: PhantomData, } -impl futures::Stream for Empty { +impl futures_core::stream::Stream for Empty { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { diff --git a/src/stream/once.rs b/src/stream/once.rs index 160d6cf..09811d8 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -33,7 +33,7 @@ pub struct Once { value: Option, } -impl futures::Stream for Once { +impl futures_core::stream::Stream for Once { type Item = T; fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index d42b727..c2ee97a 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -36,7 +36,7 @@ pub struct Repeat { item: T, } -impl futures::Stream for Repeat { +impl futures_core::stream::Stream for Repeat { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { diff --git a/src/stream/stream.rs b/src/stream/stream.rs index 7a127b0..698eb32 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -233,8 +233,8 @@ pub trait Stream { } } -impl Stream for T { - type Item = ::Item; +impl Stream for T { + type Item = ::Item; fn next(&mut self) -> ret!('_, NextFuture, Option) where @@ -250,7 +250,7 @@ pub struct NextFuture<'a, T: Unpin + ?Sized> { stream: &'a mut T, } -impl Future for NextFuture<'_, T> { +impl Future for NextFuture<'_, T> { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -267,19 +267,19 @@ pub struct Take { impl Unpin for Take {} -impl Take { +impl Take { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(remaining: usize); } -impl futures::Stream for Take { +impl futures_core::stream::Stream for Take { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.remaining == 0 { Poll::Ready(None) } else { - let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); match next { Some(_) => *self.as_mut().remaining() -= 1, None => *self.as_mut().remaining() = 0, @@ -311,14 +311,14 @@ where impl Future for AllFuture<'_, S, F, S::Item> where - S: futures::Stream + Unpin + Sized, + S: futures_core::stream::Stream + Unpin + Sized, F: FnMut(S::Item) -> bool, { type Output = bool; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures::Stream; - let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + use futures_core::stream::Stream; + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); match next { Some(v) => { let result = (self.as_mut().f())(v); @@ -358,14 +358,14 @@ where impl Future for AnyFuture<'_, S, F, S::Item> where - S: futures::Stream + Unpin + Sized, + S: futures_core::stream::Stream + Unpin + Sized, F: FnMut(S::Item) -> bool, { type Output = bool; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures::Stream; - let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + use futures_core::stream::Stream; + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); match next { Some(v) => { let result = (self.as_mut().f())(v); diff --git a/src/task/block_on.rs b/src/task/block_on.rs new file mode 100644 index 0000000..92c4b51 --- /dev/null +++ b/src/task/block_on.rs @@ -0,0 +1,135 @@ +use std::cell::UnsafeCell; +use std::mem::{self, ManuallyDrop}; +use std::panic::{self, AssertUnwindSafe, UnwindSafe}; +use std::pin::Pin; +use std::sync::Arc; +use std::task::{RawWaker, RawWakerVTable}; +use std::thread::{self, Thread}; + +use super::pool; +use super::Builder; +use crate::future::Future; +use crate::task::{Context, Poll, Waker}; + +/// Spawns a task and blocks the current thread on its result. +/// +/// Calling this function is similar to [spawning] a thread and immediately [joining] it, except an +/// asynchronous task will be spawned. +/// +/// [spawning]: https://doc.rust-lang.org/std/thread/fn.spawn.html +/// [joining]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join +/// +/// # Examples +/// +/// ```no_run +/// use async_std::task; +/// +/// fn main() { +/// task::block_on(async { +/// println!("Hello, world!"); +/// }) +/// } +/// ``` +pub fn block_on(future: F) -> T +where + F: Future + Send, + T: Send, +{ + unsafe { + // A place on the stack where the result will be stored. + let out = &mut UnsafeCell::new(None); + + // Wrap the future into one that stores the result into `out`. + let future = { + let out = out.get(); + + async move { + let future = CatchUnwindFuture { + future: AssertUnwindSafe(future), + }; + *out = Some(future.await); + } + }; + + // Pin the future onto the stack. + pin_utils::pin_mut!(future); + + // Transmute the future into one that is static and sendable. + let future = mem::transmute::< + Pin<&mut dyn Future>, + Pin<&'static mut (dyn Future + Send)>, + >(future); + + // Spawn the future and wait for it to complete. + block(pool::spawn_with_builder(Builder::new(), future, "block_on")); + + // Take out the result. + match (*out.get()).take().unwrap() { + Ok(v) => v, + Err(err) => panic::resume_unwind(err), + } + } +} + +struct CatchUnwindFuture { + future: F, +} + +impl CatchUnwindFuture { + pin_utils::unsafe_pinned!(future: F); +} + +impl Future for CatchUnwindFuture { + type Output = thread::Result; + + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + panic::catch_unwind(AssertUnwindSafe(|| self.future().poll(cx)))?.map(Ok) + } +} + +fn block(f: F) -> F::Output { + thread_local! { + static ARC_THREAD: Arc = Arc::new(thread::current()); + } + + pin_utils::pin_mut!(f); + + ARC_THREAD.with(|arc_thread: &Arc| { + let ptr = (&**arc_thread as *const Thread) as *const (); + let vt = vtable(); + + let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, vt))) }; + let cx = &mut Context::from_waker(&waker); + + loop { + if let Poll::Ready(t) = f.as_mut().poll(cx) { + return t; + } + thread::park(); + } + }) +} + +fn vtable() -> &'static RawWakerVTable { + unsafe fn clone_raw(ptr: *const ()) -> RawWaker { + let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread)); + mem::forget(arc.clone()); + RawWaker::new(ptr, vtable()) + } + + unsafe fn wake_raw(ptr: *const ()) { + let arc = Arc::from_raw(ptr as *const Thread); + arc.unpark(); + } + + unsafe fn wake_by_ref_raw(ptr: *const ()) { + let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Thread)); + arc.unpark(); + } + + unsafe fn drop_raw(ptr: *const ()) { + drop(Arc::from_raw(ptr as *const Thread)) + } + + &RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) +} diff --git a/src/task/mod.rs b/src/task/mod.rs index 42b7e08..eef7284 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -24,11 +24,13 @@ #[doc(inline)] pub use std::task::{Context, Poll, Waker}; +pub use block_on::block_on; pub use local::{AccessError, LocalKey}; -pub use pool::{block_on, current, spawn, Builder}; +pub use pool::{current, spawn, Builder}; pub use sleep::sleep; pub use task::{JoinHandle, Task, TaskId}; +mod block_on; mod local; mod pool; mod sleep; diff --git a/src/task/pool.rs b/src/task/pool.rs index ab09ffb..3640909 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -1,13 +1,10 @@ -use std::cell::{Cell, UnsafeCell}; +use std::cell::Cell; use std::fmt::Arguments; use std::mem; -use std::panic::{self, AssertUnwindSafe}; -use std::pin::Pin; use std::ptr; use std::thread; use crossbeam_channel::{unbounded, Sender}; -use futures::future::FutureExt; use lazy_static::lazy_static; use super::task; @@ -70,63 +67,6 @@ where spawn_with_builder(Builder::new(), future, "spawn") } -/// Spawns a task and blocks the current thread on its result. -/// -/// Calling this function is similar to [spawning] a thread and immediately [joining] it, except an -/// asynchronous task will be spawned. -/// -/// [spawning]: https://doc.rust-lang.org/std/thread/fn.spawn.html -/// [joining]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join -/// -/// # Examples -/// -/// ```no_run -/// use async_std::task; -/// -/// fn main() { -/// task::block_on(async { -/// println!("Hello, world!"); -/// }) -/// } -/// ``` -pub fn block_on(future: F) -> T -where - F: Future + Send, - T: Send, -{ - unsafe { - // A place on the stack where the result will be stored. - let out = &mut UnsafeCell::new(None); - - // Wrap the future into one that stores the result into `out`. - let future = { - let out = out.get(); - async move { - let v = AssertUnwindSafe(future).catch_unwind().await; - *out = Some(v); - } - }; - - // Pin the future onto the stack. - futures::pin_mut!(future); - - // Transmute the future into one that is static and sendable. - let future = mem::transmute::< - Pin<&mut dyn Future>, - Pin<&'static mut (dyn Future + Send)>, - >(future); - - // Spawn the future and wait for it to complete. - futures::executor::block_on(spawn_with_builder(Builder::new(), future, "block_on")); - - // Take out the result. - match (*out.get()).take().unwrap() { - Ok(v) => v, - Err(err) => panic::resume_unwind(err), - } - } -} - /// Task builder that configures the settings of a new task. #[derive(Debug)] pub struct Builder { From 2ca9c46b4b93d3707794dd3d14699c8332e68379 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 5 Sep 2019 23:56:31 +0100 Subject: [PATCH 051/194] Add tests for UnixDatagram from_raw_fd/into_raw_fd --- tests/uds.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/uds.rs b/tests/uds.rs index 9dbda87..e160ad7 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -22,3 +22,21 @@ fn send_recv() -> io::Result<()> { Ok(()) }) } + +#[test] +fn into_raw_fd() -> io::Result<()> { + use async_std::os::unix::io::{FromRawFd, IntoRawFd}; + task::block_on(async { + let (socket1, socket2) = UnixDatagram::pair().unwrap(); + socket1.send(JULIUS_CAESAR).await?; + + let mut buf = vec![0; 1024]; + + let socket2 = unsafe { UnixDatagram::from_raw_fd(socket2.into_raw_fd()) }; + let n = socket2.recv(&mut buf).await?; + assert_eq!(&buf[..n], JULIUS_CAESAR); + + Ok(()) + }) + +} From 876059cfe0235ffb4c786fd31dce07b9ee9dcd2f Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Thu, 5 Sep 2019 23:57:24 +0100 Subject: [PATCH 052/194] Make sure ownership is transferred in into_raw_fd Previously all of the into_raw_fd implementations only returns a copy of the inner RawFd, while still holding the ownership of the file descriptor when returning for into_raw_fd. Since `self` is dropped at the end of into_raw_fd, the returned file descriptor will actually be closed, render the function unuseable. The patch makes sure that into_raw_fd actually takes the ownership of the file descriptor all the way from the inner IoHandle. To achieve this, I have to use an Option in IoHandle to store the I/O source. It's not pretty, but I cannot come up with a better way. --- src/net/driver/mod.rs | 37 +++++++++++++++++++++++++------------ src/net/tcp/listener.rs | 10 ++-------- src/net/tcp/stream.rs | 9 ++------- src/net/udp/mod.rs | 9 ++------- src/os/unix/net/datagram.rs | 8 ++------ src/os/unix/net/listener.rs | 9 ++------- src/os/unix/net/stream.rs | 10 ++-------- tests/uds.rs | 1 - 8 files changed, 37 insertions(+), 56 deletions(-) diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 04ab4bb..4d09637 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -183,7 +183,7 @@ pub struct IoHandle { entry: Arc, /// The I/O event source. - source: T, + source: Option, } impl IoHandle { @@ -196,13 +196,13 @@ impl IoHandle { entry: REACTOR .register(&source) .expect("cannot register an I/O event source"), - source, + source: Some(source), } } /// Returns a reference to the inner I/O event source. pub fn get_ref(&self) -> &T { - &self.source + self.source.as_ref().unwrap() } /// Polls the I/O handle for reading. @@ -278,13 +278,26 @@ impl IoHandle { Ok(()) } + + /// Deregister and return the I/O source + /// + /// This method is to support IntoRawFd in struct that uses IoHandle + 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 Drop for IoHandle { fn drop(&mut self) { - REACTOR - .deregister(&self.source, &self.entry) - .expect("cannot deregister I/O event source"); + if let Some(ref source) = self.source { + REACTOR + .deregister(source, &self.entry) + .expect("cannot deregister I/O event source"); + } } } @@ -305,7 +318,7 @@ impl AsyncRead for IoHandle { ) -> Poll> { futures_core::ready!(Pin::new(&mut *self).poll_readable(cx)?); - match self.source.read(buf) { + match self.source.as_mut().unwrap().read(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { self.clear_readable(cx)?; Poll::Pending @@ -326,7 +339,7 @@ where ) -> Poll> { futures_core::ready!(Pin::new(&mut *self).poll_readable(cx)?); - match (&self.source).read(buf) { + match self.source.as_ref().unwrap().read(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { self.clear_readable(cx)?; Poll::Pending @@ -344,7 +357,7 @@ impl AsyncWrite for IoHandle { ) -> Poll> { futures_core::ready!(self.poll_writable(cx)?); - match self.source.write(buf) { + match self.source.as_mut().unwrap().write(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { self.clear_writable(cx)?; Poll::Pending @@ -356,7 +369,7 @@ impl AsyncWrite for IoHandle { fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { futures_core::ready!(self.poll_writable(cx)?); - match self.source.flush() { + match self.source.as_mut().unwrap().flush() { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { self.clear_writable(cx)?; Poll::Pending @@ -381,7 +394,7 @@ where ) -> Poll> { futures_core::ready!(self.poll_writable(cx)?); - match (&self.source).write(buf) { + match self.get_ref().write(buf) { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { self.clear_writable(cx)?; Poll::Pending @@ -393,7 +406,7 @@ where fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { futures_core::ready!(self.poll_writable(cx)?); - match (&self.source).flush() { + match self.get_ref().flush() { Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { self.clear_writable(cx)?; Poll::Pending diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 67a4865..7f1ebcd 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -50,9 +50,6 @@ use crate::task::{Context, Poll}; #[derive(Debug)] pub struct TcpListener { io_handle: IoHandle, - - #[cfg(unix)] - raw_fd: std::os::unix::io::RawFd, // #[cfg(windows)] // raw_socket: std::os::windows::io::RawSocket, } @@ -87,7 +84,6 @@ impl TcpListener { Ok(mio_listener) => { #[cfg(unix)] let listener = TcpListener { - raw_fd: mio_listener.as_raw_fd(), io_handle: IoHandle::new(mio_listener), }; @@ -136,7 +132,6 @@ impl TcpListener { #[cfg(unix)] let stream = TcpStream { - raw_fd: mio_stream.as_raw_fd(), io_handle: IoHandle::new(mio_stream), }; @@ -243,7 +238,6 @@ impl From for TcpListener { #[cfg(unix)] let listener = TcpListener { - raw_fd: mio_listener.as_raw_fd(), io_handle: IoHandle::new(mio_listener), }; @@ -273,7 +267,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for TcpListener { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.io_handle.get_ref().as_raw_fd() } } @@ -285,7 +279,7 @@ cfg_if! { impl IntoRawFd for TcpListener { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.io_handle.into_inner().into_raw_fd() } } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index b63b7f2..fd8de9c 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -51,9 +51,6 @@ use crate::task::{Context, Poll}; #[derive(Debug)] pub struct TcpStream { pub(super) io_handle: IoHandle, - - #[cfg(unix)] - pub(super) raw_fd: std::os::unix::io::RawFd, // #[cfg(windows)] // pub(super) raw_socket: std::os::windows::io::RawSocket, } @@ -103,7 +100,6 @@ impl TcpStream { let stream = mio::net::TcpStream::connect(&addr).map(|mio_stream| { #[cfg(unix)] let stream = TcpStream { - raw_fd: mio_stream.as_raw_fd(), io_handle: IoHandle::new(mio_stream), }; @@ -445,7 +441,6 @@ impl From for TcpStream { #[cfg(unix)] let stream = TcpStream { - raw_fd: mio_stream.as_raw_fd(), io_handle: IoHandle::new(mio_stream), }; @@ -475,7 +470,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for TcpStream { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.io_handle.get_ref().as_raw_fd() } } @@ -487,7 +482,7 @@ cfg_if! { impl IntoRawFd for TcpStream { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.io_handle.into_inner().into_raw_fd() } } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 2302427..9240484 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -48,9 +48,6 @@ use crate::task::Poll; #[derive(Debug)] pub struct UdpSocket { io_handle: IoHandle, - - #[cfg(unix)] - raw_fd: std::os::unix::io::RawFd, // #[cfg(windows)] // raw_socket: std::os::windows::io::RawSocket, } @@ -82,7 +79,6 @@ impl UdpSocket { Ok(mio_socket) => { #[cfg(unix)] let socket = UdpSocket { - raw_fd: mio_socket.as_raw_fd(), io_handle: IoHandle::new(mio_socket), }; @@ -515,7 +511,6 @@ impl From for UdpSocket { #[cfg(unix)] let socket = UdpSocket { - raw_fd: mio_socket.as_raw_fd(), io_handle: IoHandle::new(mio_socket), }; @@ -545,7 +540,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for UdpSocket { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.io_handle.get_ref().as_raw_fd() } } @@ -557,7 +552,7 @@ cfg_if! { impl IntoRawFd for UdpSocket { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.io_handle.into_inner().into_raw_fd() } } } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index fb5b5f1..6b6a7b4 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -44,15 +44,12 @@ use crate::task::{blocking, Poll}; pub struct UnixDatagram { #[cfg(not(feature = "docs"))] io_handle: IoHandle, - - raw_fd: RawFd, } impl UnixDatagram { #[cfg(not(feature = "docs"))] fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram { UnixDatagram { - raw_fd: socket.as_raw_fd(), io_handle: IoHandle::new(socket), } } @@ -362,7 +359,6 @@ impl From for UnixDatagram { fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram { let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap(); UnixDatagram { - raw_fd: mio_datagram.as_raw_fd(), io_handle: IoHandle::new(mio_datagram), } } @@ -370,7 +366,7 @@ impl From for UnixDatagram { impl AsRawFd for UnixDatagram { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.io_handle.get_ref().as_raw_fd() } } @@ -383,6 +379,6 @@ impl FromRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.io_handle.into_inner().into_raw_fd() } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 61df251..5710769 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -50,8 +50,6 @@ use crate::task::{blocking, Context, Poll}; pub struct UnixListener { #[cfg(not(feature = "docs"))] io_handle: IoHandle, - - raw_fd: RawFd, } impl UnixListener { @@ -73,7 +71,6 @@ impl UnixListener { let listener = blocking::spawn(async move { mio_uds::UnixListener::bind(path) }).await?; Ok(UnixListener { - raw_fd: listener.as_raw_fd(), io_handle: IoHandle::new(listener), }) } @@ -102,7 +99,6 @@ impl UnixListener { Ok(Some((io, addr))) => { let mio_stream = mio_uds::UnixStream::from_stream(io)?; let stream = UnixStream { - raw_fd: mio_stream.as_raw_fd(), io_handle: IoHandle::new(mio_stream), }; Poll::Ready(Ok((stream, addr))) @@ -214,7 +210,6 @@ impl From for UnixListener { fn from(listener: std::os::unix::net::UnixListener) -> UnixListener { let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap(); UnixListener { - raw_fd: mio_listener.as_raw_fd(), io_handle: IoHandle::new(mio_listener), } } @@ -222,7 +217,7 @@ impl From for UnixListener { impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.io_handle.get_ref().as_raw_fd() } } @@ -235,6 +230,6 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.io_handle.into_inner().into_raw_fd() } } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 389720e..05a3139 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -42,8 +42,6 @@ use crate::task::{blocking, Context, Poll}; pub struct UnixStream { #[cfg(not(feature = "docs"))] pub(super) io_handle: IoHandle, - - pub(super) raw_fd: RawFd, } impl UnixStream { @@ -71,7 +69,6 @@ impl UnixStream { let mut state = { match blocking::spawn(async move { mio_uds::UnixStream::connect(path) }).await { Ok(mio_stream) => State::Waiting(UnixStream { - raw_fd: mio_stream.as_raw_fd(), io_handle: IoHandle::new(mio_stream), }), Err(err) => State::Error(err), @@ -124,11 +121,9 @@ impl UnixStream { pub fn pair() -> io::Result<(UnixStream, UnixStream)> { let (a, b) = mio_uds::UnixStream::pair()?; let a = UnixStream { - raw_fd: a.as_raw_fd(), io_handle: IoHandle::new(a), }; let b = UnixStream { - raw_fd: b.as_raw_fd(), io_handle: IoHandle::new(b), }; Ok((a, b)) @@ -271,7 +266,6 @@ impl From for UnixStream { fn from(stream: std::os::unix::net::UnixStream) -> UnixStream { let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap(); UnixStream { - raw_fd: mio_stream.as_raw_fd(), io_handle: IoHandle::new(mio_stream), } } @@ -279,7 +273,7 @@ impl From for UnixStream { impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.io_handle.get_ref().as_raw_fd() } } @@ -292,6 +286,6 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.io_handle.into_inner().into_raw_fd() } } diff --git a/tests/uds.rs b/tests/uds.rs index e160ad7..e64af3c 100644 --- a/tests/uds.rs +++ b/tests/uds.rs @@ -38,5 +38,4 @@ fn into_raw_fd() -> io::Result<()> { Ok(()) }) - } From 7e3599a6a508683de31aef32d8d12240ec30560b Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Fri, 6 Sep 2019 13:08:51 +0300 Subject: [PATCH 053/194] add stream::min_by method (#146) * add stream::min_by method * Update src/stream/stream.rs Co-Authored-By: Yoshua Wuyts --- src/stream/min_by.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 1 + src/stream/stream.rs | 35 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+) create mode 100644 src/stream/min_by.rs diff --git a/src/stream/min_by.rs b/src/stream/min_by.rs new file mode 100644 index 0000000..b21de77 --- /dev/null +++ b/src/stream/min_by.rs @@ -0,0 +1,54 @@ +use std::cmp::Ordering; +use std::pin::Pin; + +use super::stream::Stream; +use crate::future::Future; +use crate::task::{Context, Poll}; + +/// A future that yields the minimum item in a stream by a given comparison function. +#[derive(Clone, Debug)] +pub struct MinBy { + stream: S, + compare: F, + min: Option, +} + +impl Unpin for MinBy {} + +impl MinBy { + pub(super) fn new(stream: S, compare: F) -> Self { + MinBy { + stream, + compare, + min: None, + } + } +} + +impl Future for MinBy +where + S: futures_core::stream::Stream + Unpin, + S::Item: Copy, + F: FnMut(&S::Item, &S::Item) -> Ordering, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); + + match next { + Some(new) => { + cx.waker().wake_by_ref(); + match self.as_mut().min.take() { + None => self.as_mut().min = Some(new), + Some(old) => match (&mut self.as_mut().compare)(&new, &old) { + Ordering::Less => self.as_mut().min = Some(new), + _ => self.as_mut().min = Some(old), + }, + } + Poll::Pending + } + None => Poll::Ready(self.min), + } + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8dcc6d5..5eed9c2 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -27,6 +27,7 @@ pub use repeat::{repeat, Repeat}; pub use stream::{Stream, Take}; mod empty; +mod min_by; mod once; mod repeat; mod stream; diff --git a/src/stream/stream.rs b/src/stream/stream.rs index 698eb32..95b4a61 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream.rs @@ -21,10 +21,12 @@ //! # }) } //! ``` +use std::cmp::Ordering; use std::pin::Pin; use cfg_if::cfg_if; +use super::min_by::MinBy; use crate::future::Future; use crate::task::{Context, Poll}; use std::marker::PhantomData; @@ -118,6 +120,39 @@ pub trait Stream { } } + /// Returns the element that gives the minimum value with respect to the + /// specified comparison function. If several elements are equally minimum, + /// the first element is returned. If the stream is empty, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let min = Stream::min_by(s.clone(), |x, y| x.cmp(y)).await; + /// assert_eq!(min, Some(1)); + /// + /// let min = Stream::min_by(s, |x, y| y.cmp(x)).await; + /// assert_eq!(min, Some(3)); + /// + /// let min = Stream::min_by(VecDeque::::new(), |x, y| x.cmp(y)).await; + /// assert_eq!(min, None); + /// # + /// # }) } + /// ``` + fn min_by(self, compare: F) -> MinBy + where + Self: Sized + Unpin, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + MinBy::new(self, compare) + } + /// Tests if every element of the stream matches a predicate. /// /// `all()` takes a closure that returns `true` or `false`. It applies From a2c2413bc587e5e63d7884e4b6bc931f2b306ddb Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Fri, 6 Sep 2019 17:45:24 +0300 Subject: [PATCH 054/194] fixes docs for io::buf_read::read_until --- src/io/buf_read.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/buf_read.rs b/src/io/buf_read.rs index b05bf89..74242ba 100644 --- a/src/io/buf_read.rs +++ b/src/io/buf_read.rs @@ -54,7 +54,7 @@ pub trait BufRead { /// /// let mut file = BufReader::new(File::open("a.txt").await?); /// - /// let mut buf = vec![0; 1024]; + /// let mut buf = Vec::with_capacity(1024); /// let n = file.read_until(b'\n', &mut buf).await?; /// # /// # Ok(()) }) } From 5d73776c6917a7db4363d5775120b55cf286d0f5 Mon Sep 17 00:00:00 2001 From: Atul Bhosale Date: Fri, 6 Sep 2019 23:33:26 +0530 Subject: [PATCH 055/194] Use the latest toolchain with rustfmt available if rustfmt is unavailable on the latest nightly (#155) --- .travis.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 260a753..9664ed9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,14 @@ matrix: - name: fmt rust: nightly os: linux - before_script: - - rustup component add rustfmt + before_script: | + if ! rustup component add rustfmt; then + target=`curl https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/rustfmt`; + echo "'rustfmt' is unavailable on the toolchain 'nightly', use the toolchain 'nightly-$target' instead"; + rustup toolchain install nightly-$target; + rustup default nightly-$target; + rustup component add rustfmt; + fi script: - cargo fmt --all -- --check From 91a66c2d94dca7691eb07b51948a2d590212c276 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Fri, 6 Sep 2019 21:58:53 +0300 Subject: [PATCH 056/194] append doc example for io::buf_read::read_until --- src/io/buf_read.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/io/buf_read.rs b/src/io/buf_read.rs index 74242ba..7e6b6fb 100644 --- a/src/io/buf_read.rs +++ b/src/io/buf_read.rs @@ -59,6 +59,29 @@ pub trait BufRead { /// # /// # Ok(()) }) } /// ``` + /// + /// Multiple successful calls to `read_until` append all bytes up to and including to `buf`: + /// ``` + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let from: &[u8] = b"append\nexample\n"; + /// let mut reader = BufReader::new(from); + /// let mut buf = vec![]; + /// + /// let mut size = reader.read_until(b'\n', &mut buf).await?; + /// assert_eq!(size, 7); + /// assert_eq!(buf, b"append\n"); + /// + /// size += reader.read_until(b'\n', &mut buf).await?; + /// assert_eq!(size, from.len()); + /// + /// assert_eq!(buf, from); + /// # + /// # Ok(()) }) } + /// ``` fn read_until<'a>( &'a mut self, byte: u8, From 98d9284e643680da94183c0608fa72d27bfdc2de Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 7 Sep 2019 23:11:20 +0200 Subject: [PATCH 057/194] disable mdbook to allow tests to pass again (#159) Signed-off-by: Yoshua Wuyts --- .travis.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9664ed9..ff3a53d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,15 +35,16 @@ matrix: script: - cargo doc --features docs - - name: book - rust: nightly - os: linux - before_script: - - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh - - cargo build # to find 'extern crate async_std' by `mdbook test` - script: - - mdbook build docs - - mdbook test -L ./target/debug/deps docs + # TODO(yoshuawuyts): re-enable mdbook + # - name: book + # rust: nightly + # os: linux + # before_script: + # - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh + # - cargo build # to find 'extern crate async_std' by `mdbook test` + # script: + # - mdbook build docs + # - mdbook test -L ./target/debug/deps docs script: - cargo check --features unstable --all --benches --bins --examples --tests From a90100962d6fc3e959bd099d132e72cf8553abc0 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 6 Sep 2019 12:56:30 +0200 Subject: [PATCH 058/194] split io::read into multiple files Signed-off-by: Yoshua Wuyts --- src/io/{read.rs => read/mod.rs} | 195 ++------------------------------ src/io/read/read.rs | 23 ++++ src/io/read/read_exact.rs | 35 ++++++ src/io/read/read_to_end.rs | 87 ++++++++++++++ src/io/read/read_to_string.rs | 48 ++++++++ src/io/read/read_vectored.rs | 25 ++++ 6 files changed, 230 insertions(+), 183 deletions(-) rename src/io/{read.rs => read/mod.rs} (57%) create mode 100644 src/io/read/read.rs create mode 100644 src/io/read/read_exact.rs create mode 100644 src/io/read/read_to_end.rs create mode 100644 src/io/read/read_to_string.rs create mode 100644 src/io/read/read_vectored.rs diff --git a/src/io/read.rs b/src/io/read/mod.rs similarity index 57% rename from src/io/read.rs rename to src/io/read/mod.rs index 4b6bb1f..3cd2350 100644 --- a/src/io/read.rs +++ b/src/io/read/mod.rs @@ -1,15 +1,21 @@ +mod read; +mod read_vectored; +mod read_to_end; +mod read_exact; +mod read_to_string; + +use read_to_string::ReadToStringFuture; +use read_to_end::{ReadToEndFuture, read_to_end_internal}; +use read::ReadFuture; +use read_vectored::ReadVectoredFuture; +use read_exact::ReadExactFuture; + use std::io::IoSliceMut; use std::mem; -use std::pin::Pin; -use std::str; use cfg_if::cfg_if; use futures_io::AsyncRead; -use crate::future::Future; -use crate::io; -use crate::task::{Context, Poll}; - cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] @@ -215,180 +221,3 @@ impl Read for T { ReadFuture { reader: self, buf } } } - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - buf: &'a mut [u8], -} - -impl Future for ReadFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { reader, buf } = &mut *self; - Pin::new(reader).poll_read(cx, buf) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadVectoredFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - bufs: &'a mut [IoSliceMut<'a>], -} - -impl Future for ReadVectoredFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { reader, bufs } = &mut *self; - Pin::new(reader).poll_read_vectored(cx, bufs) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadToEndFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - buf: &'a mut Vec, - start_len: usize, -} - -impl Future for ReadToEndFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { - reader, - buf, - start_len, - } = &mut *self; - read_to_end_internal(Pin::new(reader), cx, buf, *start_len) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadToStringFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - buf: &'a mut String, - bytes: Vec, - start_len: usize, -} - -impl Future for ReadToStringFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { - reader, - buf, - bytes, - start_len, - } = &mut *self; - let reader = Pin::new(reader); - - let ret = futures_core::ready!(read_to_end_internal(reader, cx, bytes, *start_len)); - if str::from_utf8(&bytes).is_err() { - Poll::Ready(ret.and_then(|_| { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8", - )) - })) - } else { - debug_assert!(buf.is_empty()); - // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. - mem::swap(unsafe { buf.as_mut_vec() }, bytes); - Poll::Ready(ret) - } - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadExactFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - buf: &'a mut [u8], -} - -impl Future for ReadExactFuture<'_, T> { - type Output = io::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { reader, buf } = &mut *self; - - while !buf.is_empty() { - let n = futures_core::ready!(Pin::new(&mut *reader).poll_read(cx, buf))?; - let (_, rest) = mem::replace(buf, &mut []).split_at_mut(n); - *buf = rest; - - if n == 0 { - return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())); - } - } - - Poll::Ready(Ok(())) - } -} - -// This uses an adaptive system to extend the vector when it fills. We want to -// avoid paying to allocate and zero a huge chunk of memory if the reader only -// has 4 bytes while still making large reads if the reader does have a ton -// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every -// time is 4,500 times (!) slower than this if the reader has a very small -// amount of data to return. -// -// Because we're extending the buffer with uninitialized data for trusted -// readers, we need to make sure to truncate that if any of this panics. -pub fn read_to_end_internal( - mut rd: Pin<&mut R>, - cx: &mut Context<'_>, - buf: &mut Vec, - start_len: usize, -) -> Poll> { - struct Guard<'a> { - buf: &'a mut Vec, - len: usize, - } - - impl Drop for Guard<'_> { - fn drop(&mut self) { - unsafe { - self.buf.set_len(self.len); - } - } - } - - let mut g = Guard { - len: buf.len(), - buf, - }; - let ret; - loop { - if g.len == g.buf.len() { - unsafe { - g.buf.reserve(32); - let capacity = g.buf.capacity(); - g.buf.set_len(capacity); - rd.initializer().initialize(&mut g.buf[g.len..]); - } - } - - match futures_core::ready!(rd.as_mut().poll_read(cx, &mut g.buf[g.len..])) { - Ok(0) => { - ret = Poll::Ready(Ok(g.len - start_len)); - break; - } - Ok(n) => g.len += n, - Err(e) => { - ret = Poll::Ready(Err(e)); - break; - } - } - } - - ret -} diff --git a/src/io/read/read.rs b/src/io/read/read.rs new file mode 100644 index 0000000..5f729e8 --- /dev/null +++ b/src/io/read/read.rs @@ -0,0 +1,23 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::pin::Pin; +use std::io; + +use futures_io::AsyncRead; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) buf: &'a mut [u8], +} + +impl Future for ReadFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { reader, buf } = &mut *self; + Pin::new(reader).poll_read(cx, buf) + } +} diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs new file mode 100644 index 0000000..1e960d2 --- /dev/null +++ b/src/io/read/read_exact.rs @@ -0,0 +1,35 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io; +use std::pin::Pin; +use std::mem; + +use futures_io::AsyncRead; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadExactFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) buf: &'a mut [u8], +} + +impl Future for ReadExactFuture<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { reader, buf } = &mut *self; + + while !buf.is_empty() { + let n = futures_core::ready!(Pin::new(&mut *reader).poll_read(cx, buf))?; + let (_, rest) = mem::replace(buf, &mut []).split_at_mut(n); + *buf = rest; + + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())); + } + } + + Poll::Ready(Ok(())) + } +} diff --git a/src/io/read/read_to_end.rs b/src/io/read/read_to_end.rs new file mode 100644 index 0000000..d51f599 --- /dev/null +++ b/src/io/read/read_to_end.rs @@ -0,0 +1,87 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io; +use std::pin::Pin; + +use futures_io::AsyncRead; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadToEndFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) buf: &'a mut Vec, + pub(crate) start_len: usize, +} + +impl Future for ReadToEndFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { + reader, + buf, + start_len, + } = &mut *self; + read_to_end_internal(Pin::new(reader), cx, buf, *start_len) + } +} + +// This uses an adaptive system to extend the vector when it fills. We want to +// avoid paying to allocate and zero a huge chunk of memory if the reader only +// has 4 bytes while still making large reads if the reader does have a ton +// of data to return. Simply tacking on an extra DEFAULT_BUF_SIZE space every +// time is 4,500 times (!) slower than this if the reader has a very small +// amount of data to return. +// +// Because we're extending the buffer with uninitialized data for trusted +// readers, we need to make sure to truncate that if any of this panics. +pub fn read_to_end_internal( + mut rd: Pin<&mut R>, + cx: &mut Context<'_>, + buf: &mut Vec, + start_len: usize, +) -> Poll> { + struct Guard<'a> { + buf: &'a mut Vec, + len: usize, + } + + impl Drop for Guard<'_> { + fn drop(&mut self) { + unsafe { + self.buf.set_len(self.len); + } + } + } + + let mut g = Guard { + len: buf.len(), + buf, + }; + let ret; + loop { + if g.len == g.buf.len() { + unsafe { + g.buf.reserve(32); + let capacity = g.buf.capacity(); + g.buf.set_len(capacity); + rd.initializer().initialize(&mut g.buf[g.len..]); + } + } + + match futures_core::ready!(rd.as_mut().poll_read(cx, &mut g.buf[g.len..])) { + Ok(0) => { + ret = Poll::Ready(Ok(g.len - start_len)); + break; + } + Ok(n) => g.len += n, + Err(e) => { + ret = Poll::Ready(Err(e)); + break; + } + } + } + + ret +} diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs new file mode 100644 index 0000000..10daba5 --- /dev/null +++ b/src/io/read/read_to_string.rs @@ -0,0 +1,48 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; +use super::read_to_end_internal; + +use std::io; +use std::pin::Pin; +use std::str; +use std::mem; + +use futures_io::AsyncRead; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadToStringFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) buf: &'a mut String, + pub(crate) bytes: Vec, + pub(crate) start_len: usize, +} + +impl Future for ReadToStringFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { + reader, + buf, + bytes, + start_len, + } = &mut *self; + let reader = Pin::new(reader); + + let ret = futures_core::ready!(read_to_end_internal(reader, cx, bytes, *start_len)); + if str::from_utf8(&bytes).is_err() { + Poll::Ready(ret.and_then(|_| { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "stream did not contain valid UTF-8", + )) + })) + } else { + debug_assert!(buf.is_empty()); + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. + mem::swap(unsafe { buf.as_mut_vec() }, bytes); + Poll::Ready(ret) + } + } +} diff --git a/src/io/read/read_vectored.rs b/src/io/read/read_vectored.rs new file mode 100644 index 0000000..b65b79f --- /dev/null +++ b/src/io/read/read_vectored.rs @@ -0,0 +1,25 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io::IoSliceMut; +use std::pin::Pin; + +use futures_io::AsyncRead; + +use crate::io; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadVectoredFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) bufs: &'a mut [IoSliceMut<'a>], +} + +impl Future for ReadVectoredFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { reader, bufs } = &mut *self; + Pin::new(reader).poll_read_vectored(cx, bufs) + } +} From 4a2194f37c3489360f8206642b4a045c4755cb33 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 6 Sep 2019 13:04:20 +0200 Subject: [PATCH 059/194] split io::write into multiple files Signed-off-by: Yoshua Wuyts --- src/io/write/flush.rs | 21 ++++++++ src/io/{write.rs => write/mod.rs} | 89 ++++--------------------------- src/io/write/write.rs | 23 ++++++++ src/io/write/write_all.rs | 35 ++++++++++++ src/io/write/write_vectored.rs | 24 +++++++++ 5 files changed, 113 insertions(+), 79 deletions(-) create mode 100644 src/io/write/flush.rs rename src/io/{write.rs => write/mod.rs} (66%) create mode 100644 src/io/write/write.rs create mode 100644 src/io/write/write_all.rs create mode 100644 src/io/write/write_vectored.rs diff --git a/src/io/write/flush.rs b/src/io/write/flush.rs new file mode 100644 index 0000000..21ada15 --- /dev/null +++ b/src/io/write/flush.rs @@ -0,0 +1,21 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io; +use std::pin::Pin; + +use futures_io::AsyncWrite; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct FlushFuture<'a, T: Unpin + ?Sized> { + pub(crate) writer: &'a mut T, +} + +impl Future for FlushFuture<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut *self.writer).poll_flush(cx) + } +} diff --git a/src/io/write.rs b/src/io/write/mod.rs similarity index 66% rename from src/io/write.rs rename to src/io/write/mod.rs index d332eec..41a00c1 100644 --- a/src/io/write.rs +++ b/src/io/write/mod.rs @@ -1,14 +1,18 @@ +mod flush; +mod write_all; +mod write; +mod write_vectored; + +use flush::FlushFuture; +use write_all::WriteAllFuture; +use write::WriteFuture; +use write_vectored::WriteVectoredFuture; + use std::io::IoSlice; -use std::mem; -use std::pin::Pin; use cfg_if::cfg_if; use futures_io::AsyncWrite; -use crate::future::Future; -use crate::io; -use crate::task::{Context, Poll}; - cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] @@ -140,76 +144,3 @@ impl Write for T { FlushFuture { writer: self } } } - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct WriteFuture<'a, T: Unpin + ?Sized> { - writer: &'a mut T, - buf: &'a [u8], -} - -impl Future for WriteFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let buf = self.buf; - Pin::new(&mut *self.writer).poll_write(cx, buf) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FlushFuture<'a, T: Unpin + ?Sized> { - writer: &'a mut T, -} - -impl Future for FlushFuture<'_, T> { - type Output = io::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new(&mut *self.writer).poll_flush(cx) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct WriteVectoredFuture<'a, T: Unpin + ?Sized> { - writer: &'a mut T, - bufs: &'a [IoSlice<'a>], -} - -impl Future for WriteVectoredFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let bufs = self.bufs; - Pin::new(&mut *self.writer).poll_write_vectored(cx, bufs) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct WriteAllFuture<'a, T: Unpin + ?Sized> { - writer: &'a mut T, - buf: &'a [u8], -} - -impl Future for WriteAllFuture<'_, T> { - type Output = io::Result<()>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { writer, buf } = &mut *self; - - while !buf.is_empty() { - let n = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buf))?; - let (_, rest) = mem::replace(buf, &[]).split_at(n); - *buf = rest; - - if n == 0 { - return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); - } - } - - Poll::Ready(Ok(())) - } -} diff --git a/src/io/write/write.rs b/src/io/write/write.rs new file mode 100644 index 0000000..361e6af --- /dev/null +++ b/src/io/write/write.rs @@ -0,0 +1,23 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io; +use std::pin::Pin; + +use futures_io::AsyncWrite; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct WriteFuture<'a, T: Unpin + ?Sized> { + pub(crate) writer: &'a mut T, + pub(crate) buf: &'a [u8], +} + +impl Future for WriteFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let buf = self.buf; + Pin::new(&mut *self.writer).poll_write(cx, buf) + } +} diff --git a/src/io/write/write_all.rs b/src/io/write/write_all.rs new file mode 100644 index 0000000..a5c3090 --- /dev/null +++ b/src/io/write/write_all.rs @@ -0,0 +1,35 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io; +use std::pin::Pin; +use std::mem; + +use futures_io::AsyncWrite; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct WriteAllFuture<'a, T: Unpin + ?Sized> { + pub(crate) writer: &'a mut T, + pub(crate) buf: &'a [u8], +} + +impl Future for WriteAllFuture<'_, T> { + type Output = io::Result<()>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { writer, buf } = &mut *self; + + while !buf.is_empty() { + let n = futures_core::ready!(Pin::new(&mut **writer).poll_write(cx, buf))?; + let (_, rest) = mem::replace(buf, &[]).split_at(n); + *buf = rest; + + if n == 0 { + return Poll::Ready(Err(io::ErrorKind::WriteZero.into())); + } + } + + Poll::Ready(Ok(())) + } +} diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs new file mode 100644 index 0000000..fadf2fc --- /dev/null +++ b/src/io/write/write_vectored.rs @@ -0,0 +1,24 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::io; +use std::pin::Pin; +use std::io::IoSlice; + +use futures_io::AsyncWrite; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct WriteVectoredFuture<'a, T: Unpin + ?Sized> { + pub(crate) writer: &'a mut T, + pub(crate) bufs: &'a [IoSlice<'a>], +} + +impl Future for WriteVectoredFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let bufs = self.bufs; + Pin::new(&mut *self.writer).poll_write_vectored(cx, bufs) + } +} From e1137345d4deee79a2177b12e42a584a439ae24b Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 6 Sep 2019 13:08:09 +0200 Subject: [PATCH 060/194] cargo fmt Signed-off-by: Yoshua Wuyts --- src/io/read/mod.rs | 10 +++++----- src/io/read/read.rs | 2 +- src/io/read/read_exact.rs | 2 +- src/io/read/read_to_string.rs | 4 ++-- src/io/write/mod.rs | 4 ++-- src/io/write/write_all.rs | 2 +- src/io/write/write_vectored.rs | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 3cd2350..cf858b2 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -1,14 +1,14 @@ mod read; -mod read_vectored; -mod read_to_end; mod read_exact; +mod read_to_end; mod read_to_string; +mod read_vectored; -use read_to_string::ReadToStringFuture; -use read_to_end::{ReadToEndFuture, read_to_end_internal}; use read::ReadFuture; -use read_vectored::ReadVectoredFuture; use read_exact::ReadExactFuture; +use read_to_end::{read_to_end_internal, ReadToEndFuture}; +use read_to_string::ReadToStringFuture; +use read_vectored::ReadVectoredFuture; use std::io::IoSliceMut; use std::mem; diff --git a/src/io/read/read.rs b/src/io/read/read.rs index 5f729e8..0f88353 100644 --- a/src/io/read/read.rs +++ b/src/io/read/read.rs @@ -1,8 +1,8 @@ use crate::future::Future; use crate::task::{Context, Poll}; -use std::pin::Pin; use std::io; +use std::pin::Pin; use futures_io::AsyncRead; diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs index 1e960d2..ffc3600 100644 --- a/src/io/read/read_exact.rs +++ b/src/io/read/read_exact.rs @@ -2,8 +2,8 @@ use crate::future::Future; use crate::task::{Context, Poll}; use std::io; -use std::pin::Pin; use std::mem; +use std::pin::Pin; use futures_io::AsyncRead; diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs index 10daba5..9a3bced 100644 --- a/src/io/read/read_to_string.rs +++ b/src/io/read/read_to_string.rs @@ -1,11 +1,11 @@ +use super::read_to_end_internal; use crate::future::Future; use crate::task::{Context, Poll}; -use super::read_to_end_internal; use std::io; +use std::mem; use std::pin::Pin; use std::str; -use std::mem; use futures_io::AsyncRead; diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 41a00c1..98fd834 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -1,11 +1,11 @@ mod flush; -mod write_all; mod write; +mod write_all; mod write_vectored; use flush::FlushFuture; -use write_all::WriteAllFuture; use write::WriteFuture; +use write_all::WriteAllFuture; use write_vectored::WriteVectoredFuture; use std::io::IoSlice; diff --git a/src/io/write/write_all.rs b/src/io/write/write_all.rs index a5c3090..d43854a 100644 --- a/src/io/write/write_all.rs +++ b/src/io/write/write_all.rs @@ -2,8 +2,8 @@ use crate::future::Future; use crate::task::{Context, Poll}; use std::io; -use std::pin::Pin; use std::mem; +use std::pin::Pin; use futures_io::AsyncWrite; diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs index fadf2fc..0f3e49a 100644 --- a/src/io/write/write_vectored.rs +++ b/src/io/write/write_vectored.rs @@ -2,8 +2,8 @@ use crate::future::Future; use crate::task::{Context, Poll}; use std::io; -use std::pin::Pin; use std::io::IoSlice; +use std::pin::Pin; use futures_io::AsyncWrite; From 910801e2d6e9c23e88e26a5a2350a2055148441d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 6 Sep 2019 14:29:27 +0200 Subject: [PATCH 061/194] fix doc compile Signed-off-by: Yoshua Wuyts --- src/io/read/mod.rs | 4 ++-- src/io/write/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index cf858b2..819f26e 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -10,7 +10,7 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture}; use read_to_string::ReadToStringFuture; use read_vectored::ReadVectoredFuture; -use std::io::IoSliceMut; +use std::io; use std::mem; use cfg_if::cfg_if; @@ -86,7 +86,7 @@ pub trait Read { /// [`read`]: #tymethod.read fn read_vectored<'a>( &'a mut self, - bufs: &'a mut [IoSliceMut<'a>], + bufs: &'a mut [io::IoSliceMut<'a>], ) -> ret!('a, ReadVectoredFuture, io::Result) where Self: Unpin, diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 98fd834..64cae42 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -8,7 +8,7 @@ use write::WriteFuture; use write_all::WriteAllFuture; use write_vectored::WriteVectoredFuture; -use std::io::IoSlice; +use std::io; use cfg_if::cfg_if; use futures_io::AsyncWrite; @@ -99,7 +99,7 @@ pub trait Write { /// [`write`]: #tymethod.write fn write_vectored<'a>( &'a mut self, - bufs: &'a [IoSlice<'a>], + bufs: &'a [io::IoSlice<'a>], ) -> ret!('a, WriteVectoredFuture, io::Result) where Self: Unpin, From 17c95a39d7e35d5ceb14b62660a91ef0fbced333 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 5 Sep 2019 01:23:27 +0200 Subject: [PATCH 062/194] More robust file implementation Signed-off-by: Yoshua Wuyts --- Cargo.toml | 3 +- examples/print-file.rs | 2 +- src/fs/file.rs | 906 +++++++++++++++++++++-------------------- src/net/driver/mod.rs | 12 +- 4 files changed, 466 insertions(+), 457 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 24badb1..c737d04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,10 +27,9 @@ unstable = [] async-task = "1.0.0" cfg-if = "0.1.9" crossbeam-channel = "0.3.9" -futures-channel-preview = "0.3.0-alpha.18" futures-core-preview = "0.3.0-alpha.18" futures-io-preview = "0.3.0-alpha.18" -futures-timer = "0.3.0" +futures-timer = "0.4.0" lazy_static = "1.3.0" log = { version = "0.4.8", features = ["kv_unstable"] } memchr = "2.2.1" diff --git a/examples/print-file.rs b/examples/print-file.rs index d74bdd8..e2cdde7 100644 --- a/examples/print-file.rs +++ b/examples/print-file.rs @@ -7,7 +7,7 @@ use async_std::io; use async_std::prelude::*; use async_std::task; -const LEN: usize = 4 * 1024 * 1024; // 4 Mb +const LEN: usize = 16 * 1024; // 16 Kb fn main() -> io::Result<()> { let path = args().nth(1).expect("missing path argument"); diff --git a/src/fs/file.rs b/src/fs/file.rs index 09e2ad6..48ee2e1 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1,17 +1,21 @@ -//! Types for working with files. +//! Async file implementation. +use std::cell::UnsafeCell; +use std::cmp; use std::fs; use std::io::{Read as _, Seek, SeekFrom, Write as _}; +use std::ops::{Deref, DerefMut}; use std::path::Path; use std::pin::Pin; -use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; use cfg_if::cfg_if; use futures_io::{AsyncRead, AsyncSeek, AsyncWrite, Initializer}; -use crate::future::{self, Future}; -use crate::io; -use crate::task::{blocking, Context, Poll}; +use crate::future; +use crate::io::{self, Write}; +use crate::task::{self, blocking, Context, Poll, Waker}; /// A reference to a file on the filesystem. /// @@ -59,51 +63,8 @@ use crate::task::{blocking, Context, Poll}; /// ``` #[derive(Debug)] pub struct File { - mutex: Mutex, - - #[cfg(unix)] - raw_fd: std::os::unix::io::RawFd, - - #[cfg(windows)] - raw_handle: UnsafeShared, -} - -/// The state of an asynchronous file. -/// -/// The file can be either idle or busy performing an asynchronous operation. -#[derive(Debug)] -enum State { - /// The file is idle. - /// - /// If the inner representation is `None`, that means the file is closed. - Idle(Option), - - /// The file is blocked on an asynchronous operation. - /// - /// Awaiting this operation will result in the new state of the file. - Busy(blocking::JoinHandle), -} - -/// Inner representation of an asynchronous file. -#[derive(Debug)] -struct Inner { - /// The blocking file handle. - file: fs::File, - - /// The read/write buffer. - buf: Vec, - - /// The result of the last asynchronous operation on the file. - last_op: Option, -} - -/// Possible results of an asynchronous operation on a file. -#[derive(Debug)] -enum Operation { - Read(io::Result), - Write(io::Result), - Seek(io::Result), - Flush(io::Result<()>), + file: Arc, + lock: Lock, } impl File { @@ -132,28 +93,7 @@ impl File { pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = blocking::spawn(async move { fs::File::open(&path) }).await?; - - #[cfg(unix)] - let file = File { - raw_fd: file.as_raw_fd(), - mutex: Mutex::new(State::Idle(Some(Inner { - file, - buf: Vec::new(), - last_op: None, - }))), - }; - - #[cfg(windows)] - let file = File { - raw_handle: UnsafeShared(file.as_raw_handle()), - mutex: Mutex::new(State::Idle(Some(Inner { - file, - buf: Vec::new(), - last_op: None, - }))), - }; - - Ok(file) + Ok(file.into()) } /// Opens a file in write-only mode. @@ -178,28 +118,7 @@ impl File { pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); let file = blocking::spawn(async move { fs::File::create(&path) }).await?; - - #[cfg(unix)] - let file = File { - raw_fd: file.as_raw_fd(), - mutex: Mutex::new(State::Idle(Some(Inner { - file, - buf: Vec::new(), - last_op: None, - }))), - }; - - #[cfg(windows)] - let file = File { - raw_handle: UnsafeShared(file.as_raw_handle()), - mutex: Mutex::new(State::Idle(Some(Inner { - file, - buf: Vec::new(), - last_op: None, - }))), - }; - - Ok(file) + Ok(file.into()) } /// Attempts to synchronize all OS-internal metadata to disk. @@ -225,35 +144,14 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn sync_all(&self) -> io::Result<()> { - future::poll_fn(|cx| { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures_channel::oneshot::channel(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.sync_all(); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + // Drain the write cache before calling `sync_all()`. + let state = future::poll_fn(|cx| { + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_drain(cx) }) - .await - .ok_or_else(|| io_error("file closed"))? - .await - .map_err(|_| io_error("blocking task failed"))? + .await?; + + blocking::spawn(async move { state.file.sync_all() }).await } /// Similar to [`sync_all`], except that it may not synchronize file metadata. @@ -280,35 +178,14 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn sync_data(&self) -> io::Result<()> { - future::poll_fn(|cx| { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures_channel::oneshot::channel(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.sync_data(); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + // Flush the write cache before calling `sync_data()`. + let state = future::poll_fn(|cx| { + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_flush(cx) }) - .await - .ok_or_else(|| io_error("file closed"))? - .await - .map_err(|_| io_error("blocking task failed"))? + .await?; + + blocking::spawn(async move { state.file.sync_data() }).await } /// Truncates or extends the underlying file. @@ -337,35 +214,15 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn set_len(&self, size: u64) -> io::Result<()> { - future::poll_fn(|cx| { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures_channel::oneshot::channel(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.set_len(size); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + // Invalidate the read/write cache before calling `set_len()`. + let state = future::poll_fn(|cx| { + let state = futures_core::ready!(self.lock.poll_lock(cx)); + let state = futures_core::ready!(state.poll_unread(cx))?; + state.poll_drain(cx) }) - .await - .ok_or_else(|| io_error("file closed"))? - .await - .map_err(|_| io_error("blocking task failed"))? + .await?; + + blocking::spawn(async move { state.file.set_len(size) }).await } /// Queries metadata about the file. @@ -383,35 +240,8 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn metadata(&self) -> io::Result { - future::poll_fn(|cx| { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures_channel::oneshot::channel(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.metadata(); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - }) - .await - .ok_or_else(|| io_error("file closed"))? - .await - .map_err(|_| io_error("blocking task failed"))? + let file = self.file.clone(); + blocking::spawn(async move { file.metadata() }).await } /// Changes the permissions on the underlying file. @@ -437,38 +267,18 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn set_permissions(&self, perm: fs::Permissions) -> io::Result<()> { - let mut perm = Some(perm); + let file = self.file.clone(); + blocking::spawn(async move { file.set_permissions(perm) }).await + } +} - future::poll_fn(|cx| { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => match opt.take() { - None => return Poll::Ready(None), - Some(inner) => { - let (s, r) = futures_channel::oneshot::channel(); - let perm = perm.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.set_permissions(perm); - let _ = s.send(res); - State::Idle(Some(inner)) - })); - - return Poll::Ready(Some(r)); - } - }, - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } - }) - .await - .ok_or_else(|| io_error("file closed"))? - .await - .map_err(|_| io_error("blocking task failed"))? +impl Drop for File { + fn drop(&mut self) { + // We need to flush the file on drop. Unfortunately, that is not possible to do in a + // non-blocking fashion, but our only other option here is data that is residing in the + // 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. + let _ = task::block_on(self.flush()); } } @@ -489,62 +299,12 @@ impl AsyncRead for File { impl AsyncRead for &File { fn poll_read( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - // Grab a reference to the inner representation of the file or return an error - // if the file is closed. - let inner = opt.as_mut().ok_or_else(|| io_error("file closed"))?; - let mut offset = 0; - - // Check if the operation has completed. - if let Some(Operation::Read(res)) = inner.last_op.take() { - let n = res?; - - if n <= buf.len() { - // Copy the read data into the buffer and return. - buf[..n].copy_from_slice(&inner.buf[..n]); - return Poll::Ready(Ok(n)); - } - - // If more data was read than fits into the buffer, let's retry the read - // operation, but first move the cursor where it was before the previous - // read. - offset = n; - } - - let mut inner = opt.take().unwrap(); - - // Set the length of the inner buffer to the length of the provided buffer. - if inner.buf.len() < buf.len() { - inner.buf.reserve(buf.len() - inner.buf.len()); - } - unsafe { - inner.buf.set_len(buf.len()); - } - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - if offset > 0 { - let pos = SeekFrom::Current(-(offset as i64)); - let _ = Seek::seek(&mut inner.file, pos); - } - - let res = inner.file.read(&mut inner.buf); - inner.last_op = Some(Operation::Read(res)); - State::Idle(Some(inner)) - })); - } - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_read(cx, buf) } #[inline] @@ -573,112 +333,22 @@ impl AsyncWrite for File { impl AsyncWrite for &File { fn poll_write( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - // Grab a reference to the inner representation of the file or return an error - // if the file is closed. - let inner = opt.as_mut().ok_or_else(|| io_error("file closed"))?; - - // Check if the operation has completed. - if let Some(Operation::Write(res)) = inner.last_op.take() { - let n = res?; - - // If more data was written than is available in the buffer, let's retry - // the write operation. - if n <= buf.len() { - return Poll::Ready(Ok(n)); - } - } else { - let mut inner = opt.take().unwrap(); - - // Set the length of the inner buffer to the length of the provided buffer. - if inner.buf.len() < buf.len() { - inner.buf.reserve(buf.len() - inner.buf.len()); - } - unsafe { - inner.buf.set_len(buf.len()); - } - - // Copy the data to write into the inner buffer. - inner.buf[..buf.len()].copy_from_slice(buf); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.write(&mut inner.buf); - inner.last_op = Some(Operation::Write(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_write(cx, buf) } - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - // Grab a reference to the inner representation of the file or return if the - // file is closed. - let inner = match opt.as_mut() { - None => return Poll::Ready(Ok(())), - Some(s) => s, - }; - - // Check if the operation has completed. - if let Some(Operation::Flush(res)) = inner.last_op.take() { - return Poll::Ready(res); - } else { - let mut inner = opt.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.flush(); - inner.last_op = Some(Operation::Flush(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_flush(cx).map(|res| res.map(drop)) } - fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - // Grab a reference to the inner representation of the file or return if the - // file is closed. - let inner = match opt.take() { - None => return Poll::Ready(Ok(())), - Some(s) => s, - }; - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - drop(inner); - State::Idle(None) - })); - } - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_close(cx) } } @@ -694,69 +364,30 @@ impl AsyncSeek for File { impl AsyncSeek for &File { fn poll_seek( - mut self: Pin<&mut Self>, + self: Pin<&mut Self>, cx: &mut Context<'_>, pos: SeekFrom, ) -> Poll> { - let state = &mut *self.mutex.lock().unwrap(); - - loop { - match state { - State::Idle(opt) => { - // Grab a reference to the inner representation of the file or return an error - // if the file is closed. - let inner = opt.as_mut().ok_or_else(|| io_error("file closed"))?; - - // Check if the operation has completed. - if let Some(Operation::Seek(res)) = inner.last_op.take() { - return Poll::Ready(res); - } else { - let mut inner = opt.take().unwrap(); - - // Start the operation asynchronously. - *state = State::Busy(blocking::spawn(async move { - let res = inner.file.seek(pos); - inner.last_op = Some(Operation::Seek(res)); - State::Idle(Some(inner)) - })); - } - } - // Poll the asynchronous operation the file is currently blocked on. - State::Busy(task) => *state = futures_core::ready!(Pin::new(task).poll(cx)), - } - } + let state = futures_core::ready!(self.lock.poll_lock(cx)); + state.poll_seek(cx, pos) } } -/// Creates a custom `io::Error` with an arbitrary error type. -fn io_error(err: impl Into>) -> io::Error { - io::Error::new(io::ErrorKind::Other, err) -} - impl From for File { /// Converts a `std::fs::File` into its asynchronous equivalent. fn from(file: fs::File) -> File { - #[cfg(unix)] - let file = File { - raw_fd: file.as_raw_fd(), - mutex: Mutex::new(State::Idle(Some(Inner { + let file = Arc::new(file); + File { + file: file.clone(), + lock: Lock::new(State { file, - buf: Vec::new(), - last_op: None, - }))), - }; - - #[cfg(windows)] - let file = File { - raw_handle: UnsafeShared(file.as_raw_handle()), - mutex: Mutex::new(State::Idle(Some(Inner { - file, - buf: Vec::new(), - last_op: None, - }))), - }; - - file + mode: Mode::Idle, + cache: Vec::new(), + is_flushed: false, + last_read_err: None, + last_write_err: None, + }), + } } } @@ -776,7 +407,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for File { fn as_raw_fd(&self) -> RawFd { - self.raw_fd + self.file.as_raw_fd() } } @@ -788,7 +419,7 @@ cfg_if! { impl IntoRawFd for File { fn into_raw_fd(self) -> RawFd { - self.raw_fd + self.file.as_raw_fd() } } } @@ -799,7 +430,7 @@ cfg_if! { if #[cfg(any(windows, feature = "docs"))] { impl AsRawHandle for File { fn as_raw_handle(&self) -> RawHandle { - self.raw_handle.0 + self.file.as_raw_handle() } } @@ -811,14 +442,385 @@ cfg_if! { impl IntoRawHandle for File { fn into_raw_handle(self) -> RawHandle { - self.raw_handle.0 + self.file.as_raw_handle() + } + } + } +} + +/// An async mutex with non-borrowing lock guards. +#[derive(Debug)] +struct Lock(Arc>); + +unsafe impl Send for Lock {} +unsafe impl Sync for Lock {} + +#[derive(Debug)] +/// The state of the lock. +struct LockState { + /// Set to `true` when locked. + locked: AtomicBool, + + /// The inner value. + value: UnsafeCell, + + /// A list of tasks interested in locking. + wakers: Mutex>, +} + +impl Lock { + /// Creates a new lock with the given value. + fn new(value: T) -> Lock { + Lock(Arc::new(LockState { + locked: AtomicBool::new(false), + value: UnsafeCell::new(value), + wakers: Mutex::new(Vec::new()), + })) + } + + /// Attempts to acquire the lock. + fn poll_lock(&self, cx: &mut Context<'_>) -> Poll> { + // Try acquiring the lock. + if self.0.locked.swap(true, Ordering::Acquire) { + // Lock the list of wakers. + let mut list = self.0.wakers.lock().unwrap(); + + // Try acquiring the lock again. + if self.0.locked.swap(true, Ordering::Acquire) { + // If failed again, add the current task to the list and return. + if list.iter().all(|w| !w.will_wake(cx.waker())) { + list.push(cx.waker().clone()); + } + return Poll::Pending; } } - #[derive(Debug)] - struct UnsafeShared(T); - - unsafe impl Send for UnsafeShared {} - unsafe impl Sync for UnsafeShared {} + // The lock was successfully aquired. + Poll::Ready(LockGuard(self.0.clone())) + } +} + +/// A lock guard. +/// +/// When dropped, ownership of the inner value is returned back to the lock. +#[derive(Debug)] +struct LockGuard(Arc>); + +unsafe impl Send for LockGuard {} +unsafe impl Sync for LockGuard {} + +impl LockGuard { + /// Registers a task interested in locking. + /// + /// When this lock guard gets dropped, all registered tasks will be woken up. + fn register(&self, cx: &Context<'_>) { + let mut list = self.0.wakers.lock().unwrap(); + + if list.iter().all(|w| !w.will_wake(cx.waker())) { + list.push(cx.waker().clone()); + } + } +} + +impl Drop for LockGuard { + fn drop(&mut self) { + self.0.locked.store(false, Ordering::Release); + + for w in self.0.wakers.lock().unwrap().drain(..) { + w.wake(); + } + } +} + +impl Deref for LockGuard { + type Target = T; + + fn deref(&self) -> &T { + unsafe { &*self.0.value.get() } + } +} + +impl DerefMut for LockGuard { + fn deref_mut(&mut self) -> &mut T { + unsafe { &mut *self.0.value.get() } + } +} + +/// The current mode. +/// +/// The file can either be in idle mode, in reading mode, or writing mode. +#[derive(Debug)] +enum Mode { + /// The cache is empty. + Idle, + + /// The cache contains data read from the inner file. + /// + /// This `usize` represents how many bytes from the beginning of cache have been consumed. + Reading(usize), + + /// The cache contains data that needs to be written to the inner file. + Writing, +} + +/// The current state of a file. +/// +/// The `File` struct puts this state behind a lock. +/// +/// Filesystem operations that get spawned as blocking tasks will take ownership of the state and +/// return it back once the operation completes. +#[derive(Debug)] +struct State { + /// The inner file. + file: Arc, + + /// The current mode (idle, reading, or writing). + mode: Mode, + + /// The read/write cache. + /// + /// If in reading mode, the cache contains a chunk of data that has been read from the file. + /// If in writing mode, the cache contains data that will eventually be written into the file. + cache: Vec, + + /// `true` if the file is flushed. + /// + /// When a file is flushed, the write cache and the inner file's buffer are empty. + is_flushed: bool, + + /// The last read error that came from an async operation. + last_read_err: Option, + + /// The last write error that came from an async operation. + last_write_err: Option, +} + +impl LockGuard { + /// Seeks to a new position in the file. + fn poll_seek(mut self, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { + // If this operation doesn't move the cursor, then poll the current position inside the + // file. This call will hopefully not block. + if pos == SeekFrom::Current(0) { + return Poll::Ready((&*self.file).seek(pos)); + } + + // Invalidate the read/write cache before calling `seek()`. + self = futures_core::ready!(self.poll_unread(cx))?; + self = futures_core::ready!(self.poll_drain(cx))?; + + // Seek to the new position. This call is hopefully not blocking because it should just + // change the internal offset into the file and not touch the actual file. + Poll::Ready((&*self.file).seek(pos)) + } + + /// Reads some bytes from the file into a buffer. + fn poll_read(mut self, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + // If an async operation has left a read error, return it now. + if let Some(err) = self.last_read_err.take() { + return Poll::Ready(Err(err)); + } + + match self.mode { + Mode::Idle => {} + Mode::Reading(start) => { + // How many bytes in the cache are available for reading. + let available = self.cache.len() - start; + + // If there is cached unconsumed data or if the cache is empty, we can read from + // it. Empty cache in reading mode indicates that the last operation didn't read + // any bytes, i.e. it reached the end of the file. + if available > 0 || self.cache.is_empty() { + // Copy data from the cache into the buffer. + let n = cmp::min(available, buf.len()); + buf[..n].copy_from_slice(&self.cache[start..n]); + + // Move the read cursor forward. + self.mode = Mode::Reading(start + n); + + return Poll::Ready(Ok(n)); + } + } + Mode::Writing => { + // If we're in writing mode, drain the write cache. + self = futures_core::ready!(self.poll_drain(cx))?; + } + } + + // Make the cache as long as `buf`. + if self.cache.len() < buf.len() { + let diff = buf.len() - self.cache.len(); + self.cache.reserve(diff); + } + unsafe { + self.cache.set_len(buf.len()); + } + + // Register current task's interest in the file lock. + self.register(cx); + + // Start a read operation asynchronously. + blocking::spawn(async move { + // Read some data from the file into the cache. + let res = { + let State { file, cache, .. } = &mut *self; + (&**file).read(cache) + }; + + match res { + Ok(n) => { + // Update cache length and switch to reading mode, starting from index 0. + unsafe { + self.cache.set_len(n); + } + self.mode = Mode::Reading(0); + } + Err(err) => { + // Save the error and switch to idle mode. + self.cache.clear(); + self.mode = Mode::Idle; + self.last_read_err = Some(err); + } + } + }); + + Poll::Pending + } + + /// Invalidates the read cache. + /// + /// This method will also move the file cursor backwards by the number of unconsumed bytes in + /// the read cache. + fn poll_unread(mut self, _: &mut Context<'_>) -> Poll> { + match self.mode { + Mode::Idle | Mode::Writing => Poll::Ready(Ok(self)), + Mode::Reading(start) => { + // Number of unconsumed bytes in the read cache. + let n = self.cache.len() - start; + + if n > 0 { + // Seek `n` bytes backwards. This call is hopefully not blocking because it + // should just change the internal offset into the file and not touch the + // actual file. + (&*self.file).seek(SeekFrom::Current(-(n as i64)))?; + } + + // Switch to idle mode. + self.cache.clear(); + self.mode = Mode::Idle; + + Poll::Ready(Ok(self)) + } + } + } + + /// Writes some data from a buffer into the file. + fn poll_write(mut self, cx: &mut Context<'_>, buf: &[u8]) -> Poll> { + // If an async operation has left a write error, return it now. + if let Some(err) = self.last_write_err.take() { + return Poll::Ready(Err(err)); + } + + // If we're in reading mode, invalidate the read buffer. + self = futures_core::ready!(self.poll_unread(cx))?; + + // Make the cache have as much capacity as `buf`. + if self.cache.capacity() < buf.len() { + let diff = buf.len() - self.cache.capacity(); + self.cache.reserve(diff); + } + + // How many bytes can be written into the cache before filling up. + let available = self.cache.capacity() - self.cache.len(); + + // If there is available space in the cache or if the buffer is empty, we can write data + // into the cache. + if available > 0 || buf.is_empty() { + let n = cmp::min(available, buf.len()); + let start = self.cache.len(); + + // Copy data from the buffer into the cache. + unsafe { + self.cache.set_len(start + n); + } + self.cache[start..start + n].copy_from_slice(&buf[..n]); + + // Mark the file as not flushed and switch to writing mode. + self.is_flushed = false; + self.mode = Mode::Writing; + Poll::Ready(Ok(n)) + } else { + // Drain the write cache because it's full. + futures_core::ready!(self.poll_drain(cx))?; + Poll::Pending + } + } + + /// Drains the write cache. + fn poll_drain(mut self, cx: &mut Context<'_>) -> Poll> { + // If an async operation has left a write error, return it now. + if let Some(err) = self.last_write_err.take() { + return Poll::Ready(Err(err)); + } + + match self.mode { + Mode::Idle | Mode::Reading(..) => Poll::Ready(Ok(self)), + Mode::Writing => { + // Register current task's interest in the file lock. + self.register(cx); + + // Start a write operation asynchronously. + blocking::spawn(async move { + match (&*self.file).write_all(&self.cache) { + Ok(_) => { + // Switch to idle mode. + self.cache.clear(); + self.mode = Mode::Idle; + } + Err(err) => { + // Save the error. + self.last_write_err = Some(err); + } + }; + }); + + Poll::Pending + } + } + } + + /// Flushes the write cache into the file. + fn poll_flush(mut self, cx: &mut Context<'_>) -> Poll> { + // If the file is already in flushed state, do nothing. + if self.is_flushed { + return Poll::Ready(Ok(self)); + } + + // If there is data in the write cache, drain in. + self = futures_core::ready!(self.poll_drain(cx))?; + + // Register current task's interest in the file lock. + self.register(cx); + + // Start a flush operation asynchronously. + blocking::spawn(async move { + match (&*self.file).flush() { + Ok(()) => { + // Mark the file as flushed. + self.is_flushed = true; + } + Err(err) => { + // Save the error. + self.last_write_err = Some(err); + } + } + }); + + Poll::Pending + } + + // This function does nothing because we're not sure about `AsyncWrite::poll_close()`'s + // semantics nor whether it will stay in the `AsyncWrite` trait. + fn poll_close(self, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } } diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 04ab4bb..7cf809a 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -213,7 +213,11 @@ impl IoHandle { let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst)); if (readiness & mask).is_empty() { - self.entry.readers.lock().unwrap().push(cx.waker().clone()); + let mut list = self.entry.readers.lock().unwrap(); + if list.iter().all(|w| !w.will_wake(cx.waker())) { + list.push(cx.waker().clone()); + } + readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst)); } @@ -250,7 +254,11 @@ impl IoHandle { let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst)); if (readiness & mask).is_empty() { - self.entry.writers.lock().unwrap().push(cx.waker().clone()); + let mut list = self.entry.writers.lock().unwrap(); + if list.iter().all(|w| !w.will_wake(cx.waker())) { + list.push(cx.waker().clone()); + } + readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst)); } From b1d85ab460360e45865101f8389b44d0e4936fc9 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Sep 2019 01:55:39 +0200 Subject: [PATCH 063/194] add io::prelude Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 2 ++ src/io/prelude.rs | 11 +++++++++++ 2 files changed, 13 insertions(+) create mode 100644 src/io/prelude.rs diff --git a/src/io/mod.rs b/src/io/mod.rs index fd41587..5152b03 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -20,6 +20,8 @@ //! # Ok(()) }) } //! ``` +pub mod prelude; + #[doc(inline)] pub use std::io::{Error, ErrorKind, Result, SeekFrom}; diff --git a/src/io/prelude.rs b/src/io/prelude.rs new file mode 100644 index 0000000..e7303a9 --- /dev/null +++ b/src/io/prelude.rs @@ -0,0 +1,11 @@ +//! The I/O Prelude +//! +//! The purpose of this module is to alleviate imports of many common I/O traits +//! by adding a glob import to the top of I/O heavy modules: +//! +//! ``` +//! # #![allow(unused_imports)] +//! use async_std::io::prelude::*; +//! ``` + +pub use super::{BufRead, Read, Seek, Write}; From ec1f33fe622098e4cdbc8bf6ea494aaa41dcb234 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Sep 2019 02:03:09 +0200 Subject: [PATCH 064/194] inline better Signed-off-by: Yoshua Wuyts --- src/io/prelude.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/io/prelude.rs b/src/io/prelude.rs index e7303a9..65438e1 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -1,4 +1,4 @@ -//! The I/O Prelude +//! The async I/O Prelude //! //! The purpose of this module is to alleviate imports of many common I/O traits //! by adding a glob import to the top of I/O heavy modules: @@ -8,4 +8,11 @@ //! use async_std::io::prelude::*; //! ``` -pub use super::{BufRead, Read, Seek, Write}; +#[doc(no_inline)] +pub use super::BufRead as _; +#[doc(no_inline)] +pub use super::Read as _; +#[doc(no_inline)] +pub use super::Seek as _; +#[doc(no_inline)] +pub use super::Write as _; From 6ed0e857fd0f020d5e25070b444c579855113242 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 8 Sep 2019 10:43:47 +0200 Subject: [PATCH 065/194] Fix some typos, expand comments --- src/fs/file.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 48ee2e1..ff71b49 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -275,7 +275,7 @@ impl File { impl Drop for File { fn drop(&mut self) { // We need to flush the file on drop. Unfortunately, that is not possible to do in a - // non-blocking fashion, but our only other option here is data that is residing 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 // file destructors so we don't expect this to be a common problem in practice. let _ = task::block_on(self.flush()); @@ -456,7 +456,7 @@ unsafe impl Send for Lock {} unsafe impl Sync for Lock {} #[derive(Debug)] -/// The state of the lock. +/// The state of a lock. struct LockState { /// Set to `true` when locked. locked: AtomicBool, @@ -495,7 +495,7 @@ impl Lock { } } - // The lock was successfully aquired. + // The lock was successfully acquired. Poll::Ready(LockGuard(self.0.clone())) } } @@ -546,9 +546,9 @@ impl DerefMut for LockGuard { } } -/// The current mode. +/// Modes a file can be in. /// -/// The file can either be in idle mode, in reading mode, or writing mode. +/// The file can either be in idle mode, reading mode, or writing mode. #[derive(Debug)] enum Mode { /// The cache is empty. @@ -688,8 +688,8 @@ impl LockGuard { /// Invalidates the read cache. /// - /// This method will also move the file cursor backwards by the number of unconsumed bytes in - /// the read cache. + /// This method will also move the internal file's cursor backwards by the number of unconsumed + /// bytes in the read cache. fn poll_unread(mut self, _: &mut Context<'_>) -> Poll> { match self.mode { Mode::Idle | Mode::Writing => Poll::Ready(Ok(self)), @@ -790,12 +790,12 @@ impl LockGuard { /// Flushes the write cache into the file. fn poll_flush(mut self, cx: &mut Context<'_>) -> Poll> { - // If the file is already in flushed state, do nothing. + // If the file is already in flushed state, return. if self.is_flushed { return Poll::Ready(Ok(self)); } - // If there is data in the write cache, drain in. + // If there is data in the write cache, drain it. self = futures_core::ready!(self.poll_drain(cx))?; // Register current task's interest in the file lock. @@ -818,7 +818,7 @@ impl LockGuard { Poll::Pending } - // This function does nothing because we're not sure about `AsyncWrite::poll_close()`'s + // This function does nothing because we're not sure about `AsyncWrite::poll_close()`'s exact // semantics nor whether it will stay in the `AsyncWrite` trait. fn poll_close(self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) From 8c00cc53ce2dc070d21ba896984c005652b4d3d6 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 8 Sep 2019 12:14:07 +0200 Subject: [PATCH 066/194] Flush more often to prevent flushes during seek --- src/fs/file.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index ff71b49..3b20bbb 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -144,10 +144,10 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn sync_all(&self) -> io::Result<()> { - // Drain the write cache before calling `sync_all()`. + // Flush the write cache before calling `sync_all()`. let state = future::poll_fn(|cx| { let state = futures_core::ready!(self.lock.poll_lock(cx)); - state.poll_drain(cx) + state.poll_flush(cx) }) .await?; @@ -214,11 +214,11 @@ impl File { /// # Ok(()) }) } /// ``` pub async fn set_len(&self, size: u64) -> io::Result<()> { - // Invalidate the read/write cache before calling `set_len()`. + // Invalidate the read cache and flush the write cache before calling `set_len()`. let state = future::poll_fn(|cx| { let state = futures_core::ready!(self.lock.poll_lock(cx)); let state = futures_core::ready!(state.poll_unread(cx))?; - state.poll_drain(cx) + state.poll_flush(cx) }) .await?; @@ -604,9 +604,9 @@ impl LockGuard { return Poll::Ready((&*self.file).seek(pos)); } - // Invalidate the read/write cache before calling `seek()`. + // Invalidate the read cache and flush the write cache before calling `seek()`. self = futures_core::ready!(self.poll_unread(cx))?; - self = futures_core::ready!(self.poll_drain(cx))?; + self = futures_core::ready!(self.poll_flush(cx))?; // Seek to the new position. This call is hopefully not blocking because it should just // change the internal offset into the file and not touch the actual file. @@ -641,8 +641,8 @@ impl LockGuard { } } Mode::Writing => { - // If we're in writing mode, drain the write cache. - self = futures_core::ready!(self.poll_drain(cx))?; + // If we're in writing mode, flush the write cache. + self = futures_core::ready!(self.poll_flush(cx))?; } } From 55550c6fc9ebcb7f5c6e6e8b5fe765bbbad4393e Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 8 Sep 2019 12:54:22 +0200 Subject: [PATCH 067/194] Split BufRead into multiple files --- src/io/buf_read/lines.rs | 75 +++++++++++++ src/io/{buf_read.rs => buf_read/mod.rs} | 138 ++---------------------- src/io/buf_read/read_line.rs | 49 +++++++++ src/io/buf_read/read_until.rs | 31 ++++++ 4 files changed, 163 insertions(+), 130 deletions(-) create mode 100644 src/io/buf_read/lines.rs rename src/io/{buf_read.rs => buf_read/mod.rs} (63%) create mode 100644 src/io/buf_read/read_line.rs create mode 100644 src/io/buf_read/read_until.rs diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs new file mode 100644 index 0000000..17ec447 --- /dev/null +++ b/src/io/buf_read/lines.rs @@ -0,0 +1,75 @@ +use std::mem; +use std::pin::Pin; +use std::str; + +use futures_io::AsyncBufRead; + +use super::read_until_internal; +use crate::io; +use crate::task::{Context, Poll}; + +/// A stream of lines in a byte stream. +/// +/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. +/// +/// This type is an async version of [`std::io::Lines`]. +/// +/// [`lines`]: trait.BufRead.html#method.lines +/// [`BufRead`]: trait.BufRead.html +/// [`std::io::Lines`]: https://doc.rust-lang.org/nightly/std/io/struct.Lines.html +#[derive(Debug)] +pub struct Lines { + pub(crate) reader: R, + pub(crate) buf: String, + pub(crate) bytes: Vec, + pub(crate) read: usize, +} + +impl futures_core::stream::Stream for Lines { + type Item = io::Result; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let Self { + reader, + buf, + bytes, + read, + } = unsafe { self.get_unchecked_mut() }; + let reader = unsafe { Pin::new_unchecked(reader) }; + let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; + if n == 0 && buf.is_empty() { + return Poll::Ready(None); + } + if buf.ends_with('\n') { + buf.pop(); + if buf.ends_with('\r') { + buf.pop(); + } + } + Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) + } +} + +pub fn read_line_internal( + reader: Pin<&mut R>, + cx: &mut Context<'_>, + buf: &mut String, + bytes: &mut Vec, + read: &mut usize, +) -> Poll> { + let ret = futures_core::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + if str::from_utf8(&bytes).is_err() { + Poll::Ready(ret.and_then(|_| { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "stream did not contain valid UTF-8", + )) + })) + } else { + debug_assert!(buf.is_empty()); + debug_assert_eq!(*read, 0); + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. + mem::swap(unsafe { buf.as_mut_vec() }, bytes); + Poll::Ready(ret) + } +} diff --git a/src/io/buf_read.rs b/src/io/buf_read/mod.rs similarity index 63% rename from src/io/buf_read.rs rename to src/io/buf_read/mod.rs index 7e6b6fb..e320375 100644 --- a/src/io/buf_read.rs +++ b/src/io/buf_read/mod.rs @@ -1,11 +1,17 @@ +mod lines; +mod read_line; +mod read_until; + +pub use lines::Lines; +use read_line::ReadLineFuture; +use read_until::ReadUntilFuture; + use std::mem; use std::pin::Pin; -use std::str; use cfg_if::cfg_if; use futures_io::AsyncBufRead; -use crate::future::Future; use crate::io; use crate::task::{Context, Poll}; @@ -191,134 +197,6 @@ pub trait BufRead { impl BufRead for T {} -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadUntilFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - byte: u8, - buf: &'a mut Vec, - read: usize, -} - -impl Future for ReadUntilFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { - reader, - byte, - buf, - read, - } = &mut *self; - read_until_internal(Pin::new(reader), cx, *byte, buf, read) - } -} - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct ReadLineFuture<'a, T: Unpin + ?Sized> { - reader: &'a mut T, - buf: &'a mut String, - bytes: Vec, - read: usize, -} - -impl Future for ReadLineFuture<'_, T> { - type Output = io::Result; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let Self { - reader, - buf, - bytes, - read, - } = &mut *self; - let reader = Pin::new(reader); - - let ret = futures_core::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); - if str::from_utf8(&bytes).is_err() { - Poll::Ready(ret.and_then(|_| { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8", - )) - })) - } else { - debug_assert!(buf.is_empty()); - debug_assert_eq!(*read, 0); - // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. - mem::swap(unsafe { buf.as_mut_vec() }, bytes); - Poll::Ready(ret) - } - } -} - -/// A stream of lines in a byte stream. -/// -/// This stream is created by the [`lines`] method on types that implement [`BufRead`]. -/// -/// This type is an async version of [`std::io::Lines`]. -/// -/// [`lines`]: trait.BufRead.html#method.lines -/// [`BufRead`]: trait.BufRead.html -/// [`std::io::Lines`]: https://doc.rust-lang.org/nightly/std/io/struct.Lines.html -#[derive(Debug)] -pub struct Lines { - reader: R, - buf: String, - bytes: Vec, - read: usize, -} - -impl futures_core::stream::Stream for Lines { - type Item = io::Result; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { - reader, - buf, - bytes, - read, - } = unsafe { self.get_unchecked_mut() }; - let reader = unsafe { Pin::new_unchecked(reader) }; - let n = futures_core::ready!(read_line_internal(reader, cx, buf, bytes, read))?; - if n == 0 && buf.is_empty() { - return Poll::Ready(None); - } - if buf.ends_with('\n') { - buf.pop(); - if buf.ends_with('\r') { - buf.pop(); - } - } - Poll::Ready(Some(Ok(mem::replace(buf, String::new())))) - } -} - -pub fn read_line_internal( - reader: Pin<&mut R>, - cx: &mut Context<'_>, - buf: &mut String, - bytes: &mut Vec, - read: &mut usize, -) -> Poll> { - let ret = futures_core::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); - if str::from_utf8(&bytes).is_err() { - Poll::Ready(ret.and_then(|_| { - Err(io::Error::new( - io::ErrorKind::InvalidData, - "stream did not contain valid UTF-8", - )) - })) - } else { - debug_assert!(buf.is_empty()); - debug_assert_eq!(*read, 0); - // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. - mem::swap(unsafe { buf.as_mut_vec() }, bytes); - Poll::Ready(ret) - } -} - pub fn read_until_internal( mut reader: Pin<&mut R>, cx: &mut Context<'_>, diff --git a/src/io/buf_read/read_line.rs b/src/io/buf_read/read_line.rs new file mode 100644 index 0000000..7424637 --- /dev/null +++ b/src/io/buf_read/read_line.rs @@ -0,0 +1,49 @@ +use std::mem; +use std::pin::Pin; +use std::str; + +use futures_io::AsyncBufRead; + +use super::read_until_internal; +use crate::future::Future; +use crate::io; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadLineFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) buf: &'a mut String, + pub(crate) bytes: Vec, + pub(crate) read: usize, +} + +impl Future for ReadLineFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { + reader, + buf, + bytes, + read, + } = &mut *self; + let reader = Pin::new(reader); + + let ret = futures_core::ready!(read_until_internal(reader, cx, b'\n', bytes, read)); + if str::from_utf8(&bytes).is_err() { + Poll::Ready(ret.and_then(|_| { + Err(io::Error::new( + io::ErrorKind::InvalidData, + "stream did not contain valid UTF-8", + )) + })) + } else { + debug_assert!(buf.is_empty()); + debug_assert_eq!(*read, 0); + // Safety: `bytes` is a valid UTF-8 because `str::from_utf8` returned `Ok`. + mem::swap(unsafe { buf.as_mut_vec() }, bytes); + Poll::Ready(ret) + } + } +} diff --git a/src/io/buf_read/read_until.rs b/src/io/buf_read/read_until.rs new file mode 100644 index 0000000..c57a820 --- /dev/null +++ b/src/io/buf_read/read_until.rs @@ -0,0 +1,31 @@ +use std::pin::Pin; + +use futures_io::AsyncBufRead; + +use super::read_until_internal; +use crate::future::Future; +use crate::io; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct ReadUntilFuture<'a, T: Unpin + ?Sized> { + pub(crate) reader: &'a mut T, + pub(crate) byte: u8, + pub(crate) buf: &'a mut Vec, + pub(crate) read: usize, +} + +impl Future for ReadUntilFuture<'_, T> { + type Output = io::Result; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let Self { + reader, + byte, + buf, + read, + } = &mut *self; + read_until_internal(Pin::new(reader), cx, *byte, buf, read) + } +} From be71ac9d762bfb3a6ff0f8510ec3e278b1760417 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Sep 2019 12:56:25 +0200 Subject: [PATCH 068/194] update deps (#149) Signed-off-by: Yoshua Wuyts --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c737d04..69aa174 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,18 +30,18 @@ crossbeam-channel = "0.3.9" futures-core-preview = "0.3.0-alpha.18" futures-io-preview = "0.3.0-alpha.18" futures-timer = "0.4.0" -lazy_static = "1.3.0" +lazy_static = "1.4.0" log = { version = "0.4.8", features = ["kv_unstable"] } memchr = "2.2.1" mio = "0.6.19" mio-uds = "0.6.7" -num_cpus = "1.10.0" +num_cpus = "1.10.1" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" [dev-dependencies] -femme = "1.1.0" -surf = "1.0.1" +femme = "1.2.0" +surf = "1.0.2" tempdir = "0.3.7" [dev-dependencies.futures-preview] From ba43a05d01d997860e12616a0e36587a11fb4aee Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Sep 2019 12:56:51 +0200 Subject: [PATCH 069/194] split stream into multiple files (#150) * split stream into multiple files Signed-off-by: Yoshua Wuyts * cargo fmt Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 1 - src/stream/stream/all.rs | 52 ++++++++ src/stream/stream/any.rs | 52 ++++++++ src/stream/{ => stream}/min_by.rs | 12 +- src/stream/{stream.rs => stream/mod.rs} | 163 +++--------------------- src/stream/stream/next.rs | 17 +++ src/stream/stream/take.rs | 34 +++++ 7 files changed, 177 insertions(+), 154 deletions(-) create mode 100644 src/stream/stream/all.rs create mode 100644 src/stream/stream/any.rs rename src/stream/{ => stream}/min_by.rs (84%) rename src/stream/{stream.rs => stream/mod.rs} (65%) create mode 100644 src/stream/stream/next.rs create mode 100644 src/stream/stream/take.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 5eed9c2..8dcc6d5 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -27,7 +27,6 @@ pub use repeat::{repeat, Repeat}; pub use stream::{Stream, Take}; mod empty; -mod min_by; mod once; mod repeat; mod stream; diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs new file mode 100644 index 0000000..6271024 --- /dev/null +++ b/src/stream/stream/all.rs @@ -0,0 +1,52 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::marker::PhantomData; +use std::pin::Pin; + +#[derive(Debug)] +pub struct AllFuture<'a, S, F, T> +where + F: FnMut(T) -> bool, +{ + pub(crate) stream: &'a mut S, + pub(crate) f: F, + pub(crate) result: bool, + pub(crate) __item: PhantomData, +} + +impl<'a, S, F, T> AllFuture<'a, S, F, T> +where + F: FnMut(T) -> bool, +{ + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(result: bool); + pin_utils::unsafe_unpinned!(f: F); +} + +impl Future for AllFuture<'_, S, F, S::Item> +where + S: futures_core::stream::Stream + Unpin + Sized, + F: FnMut(S::Item) -> bool, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures_core::stream::Stream; + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(v) => { + let result = (self.as_mut().f())(v); + *self.as_mut().result() = result; + if result { + // don't forget to wake this task again to pull the next item from stream + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(false) + } + } + None => Poll::Ready(self.result), + } + } +} diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs new file mode 100644 index 0000000..f1f551a --- /dev/null +++ b/src/stream/stream/any.rs @@ -0,0 +1,52 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; + +use std::marker::PhantomData; +use std::pin::Pin; + +#[derive(Debug)] +pub struct AnyFuture<'a, S, F, T> +where + F: FnMut(T) -> bool, +{ + pub(crate) stream: &'a mut S, + pub(crate) f: F, + pub(crate) result: bool, + pub(crate) __item: PhantomData, +} + +impl<'a, S, F, T> AnyFuture<'a, S, F, T> +where + F: FnMut(T) -> bool, +{ + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(result: bool); + pin_utils::unsafe_unpinned!(f: F); +} + +impl Future for AnyFuture<'_, S, F, S::Item> +where + S: futures_core::stream::Stream + Unpin + Sized, + F: FnMut(S::Item) -> bool, +{ + type Output = bool; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures_core::stream::Stream; + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(v) => { + let result = (self.as_mut().f())(v); + *self.as_mut().result() = result; + if result { + Poll::Ready(true) + } else { + // don't forget to wake this task again to pull the next item from stream + cx.waker().wake_by_ref(); + Poll::Pending + } + } + None => Poll::Ready(self.result), + } + } +} diff --git a/src/stream/min_by.rs b/src/stream/stream/min_by.rs similarity index 84% rename from src/stream/min_by.rs rename to src/stream/stream/min_by.rs index b21de77..b65d88d 100644 --- a/src/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -1,23 +1,23 @@ use std::cmp::Ordering; use std::pin::Pin; -use super::stream::Stream; use crate::future::Future; +use crate::stream::Stream; use crate::task::{Context, Poll}; /// A future that yields the minimum item in a stream by a given comparison function. #[derive(Clone, Debug)] -pub struct MinBy { +pub struct MinByFuture { stream: S, compare: F, min: Option, } -impl Unpin for MinBy {} +impl Unpin for MinByFuture {} -impl MinBy { +impl MinByFuture { pub(super) fn new(stream: S, compare: F) -> Self { - MinBy { + MinByFuture { stream, compare, min: None, @@ -25,7 +25,7 @@ impl MinBy { } } -impl Future for MinBy +impl Future for MinByFuture where S: futures_core::stream::Stream + Unpin, S::Item: Copy, diff --git a/src/stream/stream.rs b/src/stream/stream/mod.rs similarity index 65% rename from src/stream/stream.rs rename to src/stream/stream/mod.rs index 95b4a61..91b111e 100644 --- a/src/stream/stream.rs +++ b/src/stream/stream/mod.rs @@ -21,16 +21,24 @@ //! # }) } //! ``` +mod all; +mod any; +mod min_by; +mod next; +mod take; + +pub use take::Take; + +use all::AllFuture; +use any::AnyFuture; +use min_by::MinByFuture; +use next::NextFuture; + use std::cmp::Ordering; -use std::pin::Pin; +use std::marker::PhantomData; use cfg_if::cfg_if; -use super::min_by::MinBy; -use crate::future::Future; -use crate::task::{Context, Poll}; -use std::marker::PhantomData; - cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] @@ -145,12 +153,12 @@ pub trait Stream { /// # /// # }) } /// ``` - fn min_by(self, compare: F) -> MinBy + fn min_by(self, compare: F) -> MinByFuture where Self: Sized + Unpin, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { - MinBy::new(self, compare) + MinByFuture::new(self, compare) } /// Tests if every element of the stream matches a predicate. @@ -278,142 +286,3 @@ impl Stream for T { NextFuture { stream: self } } } - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct NextFuture<'a, T: Unpin + ?Sized> { - stream: &'a mut T, -} - -impl Future for NextFuture<'_, T> { - type Output = Option; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - Pin::new(&mut *self.stream).poll_next(cx) - } -} - -/// A stream that yields the first `n` items of another stream. -#[derive(Clone, Debug)] -pub struct Take { - stream: S, - remaining: usize, -} - -impl Unpin for Take {} - -impl Take { - pin_utils::unsafe_pinned!(stream: S); - pin_utils::unsafe_unpinned!(remaining: usize); -} - -impl futures_core::stream::Stream for Take { - type Item = S::Item; - - fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - if self.remaining == 0 { - Poll::Ready(None) - } else { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(_) => *self.as_mut().remaining() -= 1, - None => *self.as_mut().remaining() = 0, - } - Poll::Ready(next) - } - } -} - -#[derive(Debug)] -pub struct AllFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ - stream: &'a mut S, - f: F, - result: bool, - __item: PhantomData, -} - -impl<'a, S, F, T> AllFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(result: bool); - pin_utils::unsafe_unpinned!(f: F); -} - -impl Future for AllFuture<'_, S, F, S::Item> -where - S: futures_core::stream::Stream + Unpin + Sized, - F: FnMut(S::Item) -> bool, -{ - type Output = bool; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(v) => { - let result = (self.as_mut().f())(v); - *self.as_mut().result() = result; - if result { - // don't forget to wake this task again to pull the next item from stream - cx.waker().wake_by_ref(); - Poll::Pending - } else { - Poll::Ready(false) - } - } - None => Poll::Ready(self.result), - } - } -} - -#[derive(Debug)] -pub struct AnyFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ - stream: &'a mut S, - f: F, - result: bool, - __item: PhantomData, -} - -impl<'a, S, F, T> AnyFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(result: bool); - pin_utils::unsafe_unpinned!(f: F); -} - -impl Future for AnyFuture<'_, S, F, S::Item> -where - S: futures_core::stream::Stream + Unpin + Sized, - F: FnMut(S::Item) -> bool, -{ - type Output = bool; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(v) => { - let result = (self.as_mut().f())(v); - *self.as_mut().result() = result; - if result { - Poll::Ready(true) - } else { - // don't forget to wake this task again to pull the next item from stream - cx.waker().wake_by_ref(); - Poll::Pending - } - } - None => Poll::Ready(self.result), - } - } -} diff --git a/src/stream/stream/next.rs b/src/stream/stream/next.rs new file mode 100644 index 0000000..b64750d --- /dev/null +++ b/src/stream/stream/next.rs @@ -0,0 +1,17 @@ +use crate::future::Future; +use crate::task::{Context, Poll}; +use std::pin::Pin; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct NextFuture<'a, T: Unpin + ?Sized> { + pub(crate) stream: &'a mut T, +} + +impl Future for NextFuture<'_, T> { + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + Pin::new(&mut *self.stream).poll_next(cx) + } +} diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs new file mode 100644 index 0000000..0499a6a --- /dev/null +++ b/src/stream/stream/take.rs @@ -0,0 +1,34 @@ +use crate::task::{Context, Poll}; + +use std::pin::Pin; + +/// A stream that yields the first `n` items of another stream. +#[derive(Clone, Debug)] +pub struct Take { + pub(crate) stream: S, + pub(crate) remaining: usize, +} + +impl Unpin for Take {} + +impl Take { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(remaining: usize); +} + +impl futures_core::stream::Stream for Take { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.remaining == 0 { + Poll::Ready(None) + } else { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(_) => *self.as_mut().remaining() -= 1, + None => *self.as_mut().remaining() = 0, + } + Poll::Ready(next) + } + } +} From 55bdea464921206e36c88712e823c3fa0924ee41 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 8 Sep 2019 18:09:33 +0300 Subject: [PATCH 070/194] adds stream::filter_map combinator --- src/stream/stream/filter_map.rs | 48 +++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 41 ++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 src/stream/stream/filter_map.rs diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs new file mode 100644 index 0000000..626a8ec --- /dev/null +++ b/src/stream/stream/filter_map.rs @@ -0,0 +1,48 @@ +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// A stream that both filters and maps. +#[derive(Clone, Debug)] +pub struct FilterMap { + stream: S, + f: F, + __from: PhantomData, + __to: PhantomData, +} + +impl FilterMap { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(f: F); + + pub(crate) fn new(stream: S, f: F) -> Self { + FilterMap { + stream, + f, + __from: PhantomData, + __to: PhantomData, + } + } +} + +impl futures_core::stream::Stream for FilterMap +where + S: futures_core::stream::Stream, + F: FnMut(S::Item) -> Option, +{ + type Item = B; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(v) => match (self.as_mut().f())(v) { + Some(b) => Poll::Ready(Some(b)), + None => { + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 91b111e..72bee64 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -23,6 +23,7 @@ mod all; mod any; +mod filter_map; mod min_by; mod next; mod take; @@ -31,6 +32,7 @@ pub use take::Take; use all::AllFuture; use any::AnyFuture; +use filter_map::FilterMap; use min_by::MinByFuture; use next::NextFuture; @@ -128,6 +130,45 @@ pub trait Stream { } } + /// Both filters and maps a stream. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque<&str> = vec!["1", "lol", "3", "NaN", "5"].into_iter().collect(); + /// + /// let mut parsed = s.filter_map(|a| a.parse::().ok()); + /// + /// let one = parsed.next().await; + /// assert_eq!(one, Some(1)); + /// + /// let three = parsed.next().await; + /// assert_eq!(three, Some(3)); + /// + /// let five = parsed.next().await; + /// assert_eq!(five, Some(5)); + /// + /// let end = parsed.next().await; + /// assert_eq!(end, None); + /// + /// # + /// # }) } + fn filter_map(self, f: F) -> FilterMap + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + FilterMap::new(self, f) + } + /// Returns the element that gives the minimum value with respect to the /// specified comparison function. If several elements are equally minimum, /// the first element is returned. If the stream is empty, `None` is returned. From 9bf06ce52bc29a1949a297b876c790448543347d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sun, 8 Sep 2019 17:41:23 +0200 Subject: [PATCH 071/194] fix io::copy link (#164) Signed-off-by: Yoshua Wuyts --- src/io/copy.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/copy.rs b/src/io/copy.rs index ccc6bc8..d046b4b 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -18,7 +18,7 @@ use crate::task::{Context, Poll}; /// If you’re wanting to copy the contents of one file to another and you’re /// working with filesystem paths, see the [`fs::copy`] function. /// -/// This function is an async version of [`std::fs::write`]. +/// This function is an async version of [`std::io::copy`]. /// /// [`std::io::copy`]: https://doc.rust-lang.org/std/io/fn.copy.html /// [`fs::copy`]: ../fs/fn.copy.html From 41f345d319c3d63429b05e816a91ffd35732294a Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 8 Sep 2019 19:18:37 +0200 Subject: [PATCH 072/194] Fix a bug in conversion of File into raw handle --- src/fs/file.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 3b20bbb..4febb4c 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -419,7 +419,11 @@ cfg_if! { impl IntoRawFd for File { fn into_raw_fd(self) -> RawFd { - self.file.as_raw_fd() + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect("cannot acquire ownership of file handle after drop") + .into_raw_fd() } } } @@ -442,7 +446,11 @@ cfg_if! { impl IntoRawHandle for File { fn into_raw_handle(self) -> RawHandle { - self.file.as_raw_handle() + let file = self.file.clone(); + drop(self); + Arc::try_unwrap(file) + .expect("cannot acquire ownership of file's handle after drop") + .into_raw_handle() } } } From 45cd3b0894b4ef814a5f70b251d0f2d4962555b2 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 8 Sep 2019 21:42:35 +0300 Subject: [PATCH 073/194] adds stream::nth combinator --- src/stream/stream/mod.rs | 60 ++++++++++++++++++++++++++++++++++++++++ src/stream/stream/nth.rs | 41 +++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 src/stream/stream/nth.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 91b111e..1d6c3fe 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -25,6 +25,7 @@ mod all; mod any; mod min_by; mod next; +mod nth; mod take; pub use take::Take; @@ -33,6 +34,7 @@ use all::AllFuture; use any::AnyFuture; use min_by::MinByFuture; use next::NextFuture; +use nth::NthFuture; use std::cmp::Ordering; use std::marker::PhantomData; @@ -161,6 +163,64 @@ pub trait Stream { MinByFuture::new(self, compare) } + /// Returns the nth element of the stream. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let second = s.nth(1).await; + /// assert_eq!(second, Some(2)); + /// # + /// # }) } + /// ``` + /// Calling `nth()` multiple times: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let second = s.nth(0).await; + /// assert_eq!(second, Some(1)); + /// + /// let second = s.nth(0).await; + /// assert_eq!(second, Some(2)); + /// # + /// # }) } + /// ``` + /// Returning `None` if the stream finished before returning `n` elements: + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let fourth = s.nth(4).await; + /// assert_eq!(fourth, None); + /// # + /// # }) } + /// ``` + fn nth(&mut self, n: usize) -> ret!('_, NthFuture, Option) + where + Self: Sized, + { + NthFuture::new(self, n) + } + /// Tests if every element of the stream matches a predicate. /// /// `all()` takes a closure that returns `true` or `false`. It applies diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs new file mode 100644 index 0000000..346fa1a --- /dev/null +++ b/src/stream/stream/nth.rs @@ -0,0 +1,41 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; + +#[derive(Debug)] +pub struct NthFuture<'a, S> { + stream: &'a mut S, + n: usize, +} + +impl<'a, S> NthFuture<'a, S> { + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(n: usize); + + pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { + NthFuture { stream, n } + } +} + +impl<'a, S> futures_core::future::Future for NthFuture<'a, S> +where + S: futures_core::stream::Stream + Unpin + Sized, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures_core::stream::Stream; + + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + match next { + Some(v) => match self.n { + 0 => Poll::Ready(Some(v)), + _ => { + *self.as_mut().n() -= 1; + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} From 8e2bf244563a671bbbf6c66922f7f0340aea3092 Mon Sep 17 00:00:00 2001 From: yshui Date: Sun, 8 Sep 2019 23:16:34 +0100 Subject: [PATCH 074/194] Apply suggestions from code review Co-Authored-By: Stjepan Glavina --- src/net/driver/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 4d09637..5c4e38a 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -279,9 +279,9 @@ impl IoHandle { Ok(()) } - /// Deregister and return the I/O source + /// Deregisters and returns the inner I/O source. /// - /// This method is to support IntoRawFd in struct that uses IoHandle + /// This method is typically used to convert `IoHandle`s to raw file descriptors/handles. pub fn into_inner(mut self) -> T { let source = self.source.take().unwrap(); REACTOR From 2c02037673e93514b55d0a8ec89ae1b2d439dc75 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 9 Sep 2019 09:18:56 +0200 Subject: [PATCH 075/194] Fix a typo --- src/fs/file.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index 4febb4c..3ee4979 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -449,7 +449,7 @@ cfg_if! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of file's handle after drop") + .expect("cannot acquire ownership of file handle after drop") .into_raw_handle() } } From 714e1739482ea0772b8ece944bcb5c37f703685d Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 9 Sep 2019 09:26:00 +0200 Subject: [PATCH 076/194] Cache cargo artifacts --- .travis.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ff3a53d..569aceb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,16 @@ language: rust -env: RUSTFLAGS="-D warnings" +env: + - RUSTFLAGS="-D warnings" + +# Cache the whole `~/.cargo` directory to keep `~/cargo/.crates.toml`. +cache: + directories: + - /home/travis/.cargo + +# Don't cache the cargo registry because it's too big. +before_cache: + - rm -rf /home/travis/.cargo/registry matrix: fast_finish: true From 43b7523c6971005362068a75cfe9161ec77df037 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Mon, 9 Sep 2019 12:42:52 +0300 Subject: [PATCH 077/194] remove Debug derive from NthFuture --- src/stream/stream/nth.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index 346fa1a..169ded5 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use std::task::{Context, Poll}; -#[derive(Debug)] +#[allow(missing_debug_implementations)] pub struct NthFuture<'a, S> { stream: &'a mut S, n: usize, From 6db71e597bdb9506c39dac358879e39ae7cf996c Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 10 Sep 2019 03:50:03 +0200 Subject: [PATCH 078/194] Add link to silence doc warning --- src/io/write/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 64cae42..04cff74 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -127,6 +127,8 @@ pub trait Write { /// # /// # Ok(()) }) } /// ``` + /// + /// [`write`]: #tymethod.write fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> ret!('a, WriteAllFuture, io::Result<()>) where Self: Unpin, From 45bd0ef13df4d55cc13d6dd146c64e60c520cf10 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 10 Sep 2019 09:59:00 +0300 Subject: [PATCH 079/194] adds stream::find_map combinator --- src/stream/stream/find_map.rs | 50 +++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 27 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 src/stream/stream/find_map.rs diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs new file mode 100644 index 0000000..dcc29d8 --- /dev/null +++ b/src/stream/stream/find_map.rs @@ -0,0 +1,50 @@ +use std::marker::PhantomData; +use std::pin::Pin; +use std::task::{Context, Poll}; + +#[allow(missing_debug_implementations)] +pub struct FindMapFuture<'a, S, F, T, B> { + stream: &'a mut S, + f: F, + __b: PhantomData, + __t: PhantomData, +} + +impl<'a, S, B, F, T> FindMapFuture<'a, S, F, T, B> { + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(f: F); + + pub(super) fn new(stream: &'a mut S, f: F) -> Self { + FindMapFuture { + stream, + f, + __b: PhantomData, + __t: PhantomData, + } + } +} + +impl<'a, S, B, F> futures_core::future::Future for FindMapFuture<'a, S, F, S::Item, B> +where + S: futures_core::stream::Stream + Unpin + Sized, + F: FnMut(S::Item) -> Option, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures_core::stream::Stream; + + let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match item { + Some(v) => match (self.as_mut().f())(v) { + Some(v) => Poll::Ready(Some(v)), + None => { + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 91b111e..c203d6c 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -23,6 +23,7 @@ mod all; mod any; +mod find_map; mod min_by; mod next; mod take; @@ -31,6 +32,7 @@ pub use take::Take; use all::AllFuture; use any::AnyFuture; +use find_map::FindMapFuture; use min_by::MinByFuture; use next::NextFuture; @@ -48,6 +50,7 @@ cfg_if! { ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); ($a:lifetime, $f:tt, $o:ty, $t1:ty) => (ImplFuture<$a, $o>); ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => (ImplFuture<$a, $o>); + ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty, $t3:ty) => (ImplFuture<$a, $o>); } } else { @@ -55,6 +58,7 @@ cfg_if! { ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); ($a:lifetime, $f:tt, $o:ty, $t1:ty) => ($f<$a, Self, $t1>); ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => ($f<$a, Self, $t1, $t2>); + ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty, $t3:ty) => ($f<$a, Self, $t1, $t2, $t3>); } } } @@ -218,6 +222,29 @@ pub trait Stream { } } + /// Applies function to the elements of stream and returns the first non-none result. + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let mut s: VecDeque<&str> = vec!["lol", "NaN", "2", "5"].into_iter().collect(); + /// let first_number = s.find_map(|s| s.parse().ok()).await; + /// + /// assert_eq!(first_number, Some(2)); + /// # + /// # }) } + /// ``` + fn find_map(&mut self, f: F) -> ret!('_, FindMapFuture, Option, F, Self::Item, B) + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + FindMapFuture::new(self, f) + } + /// Tests if any element of the stream matches a predicate. /// /// `any()` takes a closure that returns `true` or `false`. It applies From a8090be3eb4d9a343d5cf30b76395c76300b170d Mon Sep 17 00:00:00 2001 From: James Munns Date: Tue, 10 Sep 2019 12:54:06 +0200 Subject: [PATCH 080/194] Fix book to use futures_channel and futures_util, re-enable testing (#172) * Fix book to use futures_channel and futures_util, re-enable testing * Make dev dependencies for the book explicit --- .travis.yml | 19 +++++++++---------- Cargo.toml | 4 ++++ docs/src/tutorial/all_together.md | 9 ++++----- docs/src/tutorial/clean_shutdown.md | 18 ++++++++---------- .../connecting_readers_and_writers.md | 7 ++++--- docs/src/tutorial/handling_disconnection.md | 19 +++++++++++++------ docs/src/tutorial/implementing_a_client.md | 4 ++-- docs/src/tutorial/sending_messages.md | 7 ++++--- 8 files changed, 48 insertions(+), 39 deletions(-) diff --git a/.travis.yml b/.travis.yml index ff3a53d..9664ed9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,16 +35,15 @@ matrix: script: - cargo doc --features docs - # TODO(yoshuawuyts): re-enable mdbook - # - name: book - # rust: nightly - # os: linux - # before_script: - # - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh - # - cargo build # to find 'extern crate async_std' by `mdbook test` - # script: - # - mdbook build docs - # - mdbook test -L ./target/debug/deps docs + - name: book + rust: nightly + os: linux + before_script: + - test -x $HOME/.cargo/bin/mdbook || ./ci/install-mdbook.sh + - cargo build # to find 'extern crate async_std' by `mdbook test` + script: + - mdbook build docs + - mdbook test -L ./target/debug/deps docs script: - cargo check --features unstable --all --benches --bins --examples --tests diff --git a/Cargo.toml b/Cargo.toml index 69aa174..ca0f3fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,6 +44,10 @@ femme = "1.2.0" surf = "1.0.2" tempdir = "0.3.7" +# These are used by the book for examples +futures-channel-preview = "0.3.0-alpha.18" +futures-util-preview = "0.3.0-alpha.18" + [dev-dependencies.futures-preview] version = "0.3.0-alpha.18" features = ["std", "nightly", "async-await"] diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 415f3b8..a638e02 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -4,17 +4,16 @@ At this point, we only need to start the broker to get a fully-functioning (in t ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; use async_std::{ io::{self, BufReader}, net::{TcpListener, TcpStream, ToSocketAddrs}, prelude::*, task, }; -use futures::{ - channel::mpsc, - SinkExt, -}; +use futures_channel::mpsc; +use futures_util::SinkExt; use std::{ collections::hash_map::{HashMap, Entry}, sync::Arc, diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 6bf7056..f61adf2 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -22,17 +22,16 @@ Let's add waiting to the server: ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; # use async_std::{ # io::{self, BufReader}, # net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; -# use futures::{ -# channel::mpsc, -# SinkExt, -# }; +# use futures_channel::mpsc; +# use futures_util::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, @@ -156,17 +155,16 @@ And to the broker: ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; # use async_std::{ # io::{self, BufReader}, # net::{TcpListener, TcpStream, ToSocketAddrs}, # prelude::*, # task, # }; -# use futures::{ -# channel::mpsc, -# SinkExt, -# }; +# use futures_channel::mpsc; +# use futures_util::SinkExt; # use std::{ # collections::hash_map::{HashMap, Entry}, # sync::Arc, diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index f3ccf2d..7399cec 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -12,15 +12,16 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; # use async_std::{ # io::{Write}, # net::TcpStream, # prelude::{Future, Stream}, # task, # }; -# use futures::channel::mpsc; -# use futures::sink::SinkExt; +# use futures_channel::mpsc; +# use futures_util::sink::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 30827ba..351f253 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -19,9 +19,11 @@ First, let's add a shutdown channel to the `client`: ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; # use async_std::net::TcpStream; -# use futures::{channel::mpsc, SinkExt}; +# use futures_channel::mpsc; +# use futures_util::SinkExt; # use std::sync::Arc; # # type Result = std::result::Result>; @@ -68,9 +70,11 @@ We use the `select` macro for this purpose: ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; # use async_std::{io::Write, net::TcpStream}; -use futures::{channel::mpsc, select, FutureExt, StreamExt}; +use futures_channel::mpsc; +use futures_util::{select, FutureExt, StreamExt}; # use std::sync::Arc; # type Receiver = mpsc::UnboundedReceiver; @@ -118,15 +122,18 @@ The final code looks like this: ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; use async_std::{ io::{BufReader, BufRead, Write}, net::{TcpListener, TcpStream, ToSocketAddrs}, task, }; -use futures::{channel::mpsc, future::Future, select, FutureExt, SinkExt, StreamExt}; +use futures_channel::mpsc; +use futures_util::{select, FutureExt, SinkExt, StreamExt}; use std::{ collections::hash_map::{Entry, HashMap}, + future::Future, sync::Arc, }; diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 97e7319..a3ba93a 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -16,13 +16,13 @@ With async, we can just use the `select!` macro. ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_util; use async_std::{ io::{stdin, BufRead, BufReader, Write}, net::{TcpStream, ToSocketAddrs}, task, }; -use futures::{select, FutureExt, StreamExt}; +use futures_util::{select, FutureExt, StreamExt}; type Result = std::result::Result>; diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index 80784c2..6fec8c9 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -13,14 +13,15 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the ```rust,edition2018 # extern crate async_std; -# extern crate futures; +# extern crate futures_channel; +# extern crate futures_util; # use async_std::{ # io::Write, # net::TcpStream, # prelude::Stream, # }; -use futures::channel::mpsc; // 1 -use futures::sink::SinkExt; +use futures_channel::mpsc; // 1 +use futures_util::sink::SinkExt; use std::sync::Arc; # type Result = std::result::Result>; From 272f74c1da8d5e58d4c7d39634931656fa0162fd Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 10 Sep 2019 14:53:30 +0300 Subject: [PATCH 081/194] fixes to stream::min_by (#162) * fixes to stream::min_by * no reason to split these impls * remove Debug derive from MinByFuture --- src/stream/stream/min_by.rs | 30 +++++++++++++++--------------- src/stream/stream/mod.rs | 4 ++-- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index b65d88d..f63d52b 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -2,20 +2,20 @@ use std::cmp::Ordering; use std::pin::Pin; use crate::future::Future; -use crate::stream::Stream; use crate::task::{Context, Poll}; -/// A future that yields the minimum item in a stream by a given comparison function. -#[derive(Clone, Debug)] -pub struct MinByFuture { +#[allow(missing_debug_implementations)] +pub struct MinByFuture { stream: S, compare: F, - min: Option, + min: Option, } -impl Unpin for MinByFuture {} +impl MinByFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(compare: F); + pin_utils::unsafe_unpinned!(min: Option); -impl MinByFuture { pub(super) fn new(stream: S, compare: F) -> Self { MinByFuture { stream, @@ -25,25 +25,25 @@ impl MinByFuture { } } -impl Future for MinByFuture +impl Future for MinByFuture where - S: futures_core::stream::Stream + Unpin, + S: futures_core::stream::Stream + Unpin + Sized, S::Item: Copy, F: FnMut(&S::Item, &S::Item) -> Ordering, { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(Pin::new(&mut self.stream).poll_next(cx)); + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); match next { Some(new) => { cx.waker().wake_by_ref(); - match self.as_mut().min.take() { - None => self.as_mut().min = Some(new), - Some(old) => match (&mut self.as_mut().compare)(&new, &old) { - Ordering::Less => self.as_mut().min = Some(new), - _ => self.as_mut().min = Some(old), + match self.as_mut().min().take() { + None => *self.as_mut().min() = Some(new), + Some(old) => match (&mut self.as_mut().compare())(&new, &old) { + Ordering::Less => *self.as_mut().min() = Some(new), + _ => *self.as_mut().min() = Some(old), }, } Poll::Pending diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 91b111e..4e1e416 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -153,9 +153,9 @@ pub trait Stream { /// # /// # }) } /// ``` - fn min_by(self, compare: F) -> MinByFuture + fn min_by(self, compare: F) -> MinByFuture where - Self: Sized + Unpin, + Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, { MinByFuture::new(self, compare) From 9b381e427f90fe211f0b18adf6200ac5161678d3 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 10 Sep 2019 15:01:25 +0300 Subject: [PATCH 082/194] Apply suggestions from code review Co-Authored-By: Yoshua Wuyts --- src/stream/stream/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 72bee64..89881c0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -137,7 +137,6 @@ pub trait Stream { /// Basic usage: /// /// ``` - /// /// # fn main() { async_std::task::block_on(async { /// # /// use std::collections::VecDeque; @@ -158,7 +157,6 @@ pub trait Stream { /// /// let end = parsed.next().await; /// assert_eq!(end, None); - /// /// # /// # }) } fn filter_map(self, f: F) -> FilterMap From ed944d051a9331b76357a7618ccf04c6e525b5a3 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 10 Sep 2019 20:39:45 +0300 Subject: [PATCH 083/194] adds stream::enumerate combinator --- src/stream/stream/enumerate.rs | 38 ++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 32 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 src/stream/stream/enumerate.rs diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs new file mode 100644 index 0000000..de29578 --- /dev/null +++ b/src/stream/stream/enumerate.rs @@ -0,0 +1,38 @@ +use crate::task::{Context, Poll}; +use std::pin::Pin; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct Enumerate { + stream: S, + i: usize, +} + +impl Enumerate { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(i: usize); + + pub(super) fn new(stream: S) -> Self { + Enumerate { stream, i: 0 } + } +} + +impl futures_core::stream::Stream for Enumerate +where + S: futures_core::stream::Stream, +{ + type Item = (usize, S::Item); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => { + let ret = (self.i, v); + *self.as_mut().i() += 1; + Poll::Ready(Some(ret)) + } + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index eddafe2..4ca42c6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -23,6 +23,7 @@ mod all; mod any; +mod enumerate; mod filter_map; mod find_map; mod min_by; @@ -34,6 +35,7 @@ pub use take::Take; use all::AllFuture; use any::AnyFuture; +use enumerate::Enumerate; use filter_map::FilterMap; use find_map::FindMapFuture; use min_by::MinByFuture; @@ -136,6 +138,36 @@ pub trait Stream { } } + /// Creates a stream that gives the current element's count as well as the next value. + /// + /// # Overflow behaviour. + /// + /// This combinator does no guarding against overflows. + /// + /// # Examples + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat(9).take(4).enumerate(); + /// let mut c: usize = 0; + /// + /// while let Some((i, v)) = s.next().await { + /// assert_eq!(c, i); + /// assert_eq!(v, 9); + /// c += 1; + /// } + /// # + /// # }) } + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + Enumerate::new(self) + } + /// Both filters and maps a stream. /// /// # Examples From 97a5f9b50c95e49639bf999227229094afeb37b0 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 10 Sep 2019 23:38:11 +0300 Subject: [PATCH 084/194] adds stream::find combinator --- src/stream/stream/find.rs | 49 +++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 src/stream/stream/find.rs diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs new file mode 100644 index 0000000..dfab089 --- /dev/null +++ b/src/stream/stream/find.rs @@ -0,0 +1,49 @@ +use crate::task::{Context, Poll}; +use std::marker::PhantomData; +use std::pin::Pin; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct FindFuture<'a, S, P, T> { + stream: &'a mut S, + p: P, + __t: PhantomData, +} + +impl<'a, S, P, T> FindFuture<'a, S, P, T> { + pin_utils::unsafe_pinned!(stream: &'a mut S); + pin_utils::unsafe_unpinned!(p: P); + + pub(super) fn new(stream: &'a mut S, p: P) -> Self { + FindFuture { + stream, + p, + __t: PhantomData, + } + } +} + +impl<'a, S, P> futures_core::future::Future for FindFuture<'a, S, P, S::Item> +where + S: futures_core::stream::Stream + Unpin + Sized, + P: FnMut(&S::Item) -> bool, +{ + type Output = Option; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + use futures_core::stream::Stream; + + let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match item { + Some(v) => match (self.as_mut().p())(&v) { + true => Poll::Ready(Some(v)), + false => { + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index eddafe2..462aeae 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod filter_map; +mod find; mod find_map; mod min_by; mod next; @@ -35,6 +36,7 @@ pub use take::Take; use all::AllFuture; use any::AnyFuture; use filter_map::FilterMap; +use find::FindFuture; use find_map::FindMapFuture; use min_by::MinByFuture; use next::NextFuture; @@ -321,6 +323,50 @@ pub trait Stream { } } + /// Searches for an element in a stream that satisfies a predicate. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let res = s.find(|x| *x == 2).await; + /// assert_eq!(res, Some(2)); + /// # + /// # }) } + /// ``` + /// + /// Resuming after a first find: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let res = s.find(|x| *x == 2).await; + /// assert_eq!(res, Some(2)); + /// + /// let next = s.next().await; + /// assert_eq!(next, Some(3)); + /// # + /// # }) } + /// ``` + fn find

(self, predicate: P) -> Filter + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + Filter::new(self, predicate) + } /// Both filters and maps a stream. /// From 570329b17648727a2800b3ecb1bf21dfd13bb320 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 14:40:25 +0300 Subject: [PATCH 170/194] adds stream::skip combinator --- src/stream/stream/mod.rs | 27 ++++++++++++++++++++++++++ src/stream/stream/skip.rs | 41 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/stream/stream/skip.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ddab21..5f4010c 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -33,6 +33,7 @@ mod min_by; mod next; mod nth; mod scan; +mod skip; mod take; mod zip; @@ -51,6 +52,7 @@ use fold::FoldFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; +use skip::Skip; use std::cmp::Ordering; use std::marker::PhantomData; @@ -661,6 +663,31 @@ pub trait Stream { Scan::new(self, initial_state, f) } + /// Creates a combinator that skips the first `n` elements. + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let mut skipped = s.skip(2); + /// + /// assert_eq!(skipped.next().await, Some(3)); + /// assert_eq!(skipped.next().await, None); + /// # + /// # }) } + /// ``` + fn skip(self, n: usize) -> Skip + where + Self: Sized, + { + Skip::new(self, n) + } + /// 'Zips up' two streams into a single stream of pairs. /// /// `zip()` returns a new stream that will iterate over two other streams, returning a tuple diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs new file mode 100644 index 0000000..09f5cab --- /dev/null +++ b/src/stream/stream/skip.rs @@ -0,0 +1,41 @@ +use std::pin::Pin; +use std::task::{Context, Poll}; + +use crate::stream::Stream; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct Skip { + stream: S, + n: usize, +} + +impl Skip { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(n: usize); + + pub(crate) fn new(stream: S, n: usize) -> Self { + Skip { stream, n } + } +} + +impl Stream for Skip +where + S: Stream, +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => match self.n { + 0 => return Poll::Ready(Some(v)), + _ => *self.as_mut().n() -= 1, + }, + None => return Poll::Ready(None), + } + } + } +} From f9f97c43c44f180957b9a9bc619c0dea0d0a3de3 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 15:10:00 +0300 Subject: [PATCH 171/194] adds stream::skip_while combinator --- src/stream/stream/mod.rs | 34 +++++++++++++++++++++ src/stream/stream/skip_while.rs | 54 +++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 src/stream/stream/skip_while.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ddab21..5444312 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -33,6 +33,7 @@ mod min_by; mod next; mod nth; mod scan; +mod skip_while; mod take; mod zip; @@ -51,6 +52,7 @@ use fold::FoldFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; +use skip_while::SkipWhile; use std::cmp::Ordering; use std::marker::PhantomData; @@ -661,6 +663,38 @@ pub trait Stream { Scan::new(self, initial_state, f) } + /// Combinator that `skip`s elements based on a predicate. + /// + /// Takes a closure argument. It will call this closure on every element in + /// the stream and ignore elements until it returns `false`. + /// + /// After `false` is returned, `SkipWhile`'s job is over and all further + /// elements in the strem are yeilded. + /// + /// ## Examples + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let a: VecDeque<_> = vec![-1i32, 0, 1].into_iter().collect(); + /// let mut s = a.skip_while(|x| x.is_negative()); + /// + /// assert_eq!(s.next().await, Some(0)); + /// assert_eq!(s.next().await, Some(1)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn skip_while

(self, predicate: P) -> SkipWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + SkipWhile::new(self, predicate) + } + /// 'Zips up' two streams into a single stream of pairs. /// /// `zip()` returns a new stream that will iterate over two other streams, returning a tuple diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs new file mode 100644 index 0000000..59f564a --- /dev/null +++ b/src/stream/stream/skip_while.rs @@ -0,0 +1,54 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct SkipWhile { + stream: S, + predicate: Option

, + __t: PhantomData, +} + +impl SkipWhile { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(predicate: Option

); + + pub(crate) fn new(stream: S, predicate: P) -> Self { + SkipWhile { + stream, + predicate: Some(predicate), + __t: PhantomData, + } + } +} + +impl Stream for SkipWhile +where + S: Stream, + P: FnMut(&S::Item) -> bool, +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => match self.as_mut().predicate() { + Some(p) => match p(&v) { + true => (), + false => { + *self.as_mut().predicate() = None; + return Poll::Ready(Some(v)); + } + }, + None => return Poll::Ready(Some(v)), + }, + None => return Poll::Ready(None), + } + } + } +} From edfa2358a42872af35024dd83e78ea80e8fe6a4a Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 21 Sep 2019 10:39:56 +0200 Subject: [PATCH 172/194] Re-export IO traits from futures --- .travis.yml | 1 + src/fs/file.rs | 26 +- src/io/buf_read/fill_buf.rs | 32 --- src/io/buf_read/lines.rs | 8 +- src/io/buf_read/mod.rs | 412 +++++++++++++++++++------------- src/io/buf_read/read_line.rs | 6 +- src/io/buf_read/read_until.rs | 6 +- src/io/buf_reader.rs | 15 +- src/io/copy.rs | 12 +- src/io/cursor.rs | 43 ++-- src/io/empty.rs | 13 +- src/io/mod.rs | 12 +- src/io/prelude.rs | 17 +- src/io/read/mod.rs | 424 +++++++++++++++++++++------------ src/io/read/read.rs | 10 +- src/io/read/read_exact.rs | 10 +- src/io/read/read_to_end.rs | 12 +- src/io/read/read_to_string.rs | 12 +- src/io/read/read_vectored.rs | 12 +- src/io/repeat.rs | 11 +- src/io/seek.rs | 148 ++++++++---- src/io/sink.rs | 6 +- src/io/stderr.rs | 17 +- src/io/stdin.rs | 20 +- src/io/stdout.rs | 17 +- src/io/write/flush.rs | 10 +- src/io/write/mod.rs | 361 +++++++++++++++++++--------- src/io/write/write.rs | 10 +- src/io/write/write_all.rs | 10 +- src/io/write/write_vectored.rs | 11 +- src/lib.rs | 8 +- src/net/tcp/listener.rs | 3 +- src/net/tcp/stream.rs | 11 +- src/os/unix/net/stream.rs | 11 +- src/prelude.rs | 11 + src/result/from_stream.rs | 11 +- src/stream/from_stream.rs | 6 +- src/stream/into_stream.rs | 4 +- src/stream/mod.rs | 12 +- src/stream/stream/mod.rs | 47 ++-- src/stream/stream/scan.rs | 4 +- src/stream/stream/zip.rs | 4 +- src/vec/from_stream.rs | 11 +- 43 files changed, 1075 insertions(+), 772 deletions(-) delete mode 100644 src/io/buf_read/fill_buf.rs diff --git a/.travis.yml b/.travis.yml index d2862fc..e482b50 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,5 +63,6 @@ matrix: - mdbook test -L ./target/debug/deps docs script: + - cargo check --all --benches --bins --examples --tests - cargo check --features unstable --all --benches --bins --examples --tests - cargo test --all --doc --features unstable diff --git a/src/fs/file.rs b/src/fs/file.rs index 33022bc..b2a897e 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -9,11 +9,11 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use cfg_if::cfg_if; -use futures_io::{AsyncRead, AsyncSeek, AsyncWrite, Initializer}; use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::io::{self, SeekFrom, Write}; +use crate::io::{self, Seek, SeekFrom, Read, Write}; +use crate::prelude::*; use crate::task::{self, blocking, Context, Poll, Waker}; /// An open file on the filesystem. @@ -302,7 +302,7 @@ impl fmt::Debug for File { } } -impl AsyncRead for File { +impl Read for File { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -310,14 +310,9 @@ impl AsyncRead for File { ) -> Poll> { Pin::new(&mut &*self).poll_read(cx, buf) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } -impl AsyncRead for &File { +impl Read for &File { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -326,14 +321,9 @@ impl AsyncRead for &File { let state = futures_core::ready!(self.lock.poll_lock(cx)); state.poll_read(cx, buf) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } -impl AsyncWrite for File { +impl Write for File { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -351,7 +341,7 @@ impl AsyncWrite for File { } } -impl AsyncWrite for &File { +impl Write for &File { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -372,7 +362,7 @@ impl AsyncWrite for &File { } } -impl AsyncSeek for File { +impl Seek for File { fn poll_seek( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -382,7 +372,7 @@ impl AsyncSeek for File { } } -impl AsyncSeek for &File { +impl Seek for &File { fn poll_seek( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/src/io/buf_read/fill_buf.rs b/src/io/buf_read/fill_buf.rs deleted file mode 100644 index 0ce58cf..0000000 --- a/src/io/buf_read/fill_buf.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::pin::Pin; - -use futures_io::AsyncBufRead; - -use crate::future::Future; -use crate::io; -use crate::task::{Context, Poll}; - -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct FillBufFuture<'a, R: ?Sized> { - reader: &'a mut R, -} - -impl<'a, R: ?Sized> FillBufFuture<'a, R> { - pub(crate) fn new(reader: &'a mut R) -> Self { - Self { reader } - } -} - -impl<'a, R: AsyncBufRead + Unpin + ?Sized> Future for FillBufFuture<'a, R> { - type Output = io::Result<&'a [u8]>; - - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - let Self { reader } = &mut *self; - let result = Pin::new(reader).poll_fill_buf(cx); - // This is safe because: - // 1. The buffer is valid for the lifetime of the reader. - // 2. Output is unrelated to the wrapper (Self). - result.map_ok(|buf| unsafe { std::mem::transmute::<&'_ [u8], &'a [u8]>(buf) }) - } -} diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index 0536086..a761eb4 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -2,10 +2,8 @@ use std::mem; use std::pin::Pin; use std::str; -use futures_io::AsyncBufRead; - use super::read_until_internal; -use crate::io; +use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; /// A stream of lines in a byte stream. @@ -25,7 +23,7 @@ pub struct Lines { pub(crate) read: usize, } -impl futures_core::stream::Stream for Lines { +impl futures_core::stream::Stream for Lines { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -50,7 +48,7 @@ impl futures_core::stream::Stream for Lines { } } -pub fn read_line_internal( +pub fn read_line_internal( reader: Pin<&mut R>, cx: &mut Context<'_>, buf: &mut String, diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index b65d177..fa8e9eb 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -1,9 +1,7 @@ -mod fill_buf; mod lines; mod read_line; mod read_until; -use fill_buf::FillBufFuture; pub use lines::Lines; use read_line::ReadLineFuture; use read_until::ReadUntilFuture; @@ -12,115 +10,259 @@ use std::mem; use std::pin::Pin; use cfg_if::cfg_if; -use futures_io::AsyncBufRead; use crate::io; use crate::task::{Context, Poll}; cfg_if! { if #[cfg(feature = "docs")] { + use std::ops::{Deref, DerefMut}; + #[doc(hidden)] pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); + /// Allows reading from a buffered byte stream. + /// + /// This trait is a re-export of [`futures::io::AsyncBufRead`] and is an async version of + /// [`std::io::BufRead`]. + /// + /// The [provided methods] do not really exist in the trait itself, but they become + /// available when the prelude is imported: + /// + /// ``` + /// # #[allow(unused_imports)] + /// use async_std::prelude::*; + /// ``` + /// + /// [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html + /// [`futures::io::AsyncBufRead`]: + /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html + /// [provided methods]: #provided-methods + pub trait BufRead { + /// Returns the contents of the internal buffer, filling it with more data from the + /// inner reader if it is empty. + /// + /// This function is a lower-level call. It needs to be paired with the [`consume`] + /// method to function properly. When calling this method, none of the contents will be + /// "read" in the sense that later calling `read` may return the same contents. As + /// such, [`consume`] must be called with the number of bytes that are consumed from + /// this buffer to ensure that the bytes are never returned twice. + /// + /// [`consume`]: #tymethod.consume + /// + /// An empty buffer returned indicates that the stream has reached EOF. + // TODO: write a proper doctest with `consume` + fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + /// Tells this buffer that `amt` bytes have been consumed from the buffer, so they + /// should no longer be returned in calls to `read`. + fn consume(self: Pin<&mut Self>, amt: usize); + + /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. + /// + /// This function will read bytes from the underlying stream until the delimiter or EOF + /// is found. Once found, all bytes up to, and including, the delimiter (if found) will + /// be appended to `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let mut file = BufReader::new(File::open("a.txt").await?); + /// + /// let mut buf = Vec::with_capacity(1024); + /// let n = file.read_until(b'\n', &mut buf).await?; + /// # + /// # Ok(()) }) } + /// ``` + /// + /// Multiple successful calls to `read_until` append all bytes up to and including to + /// `buf`: + /// ``` + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let from: &[u8] = b"append\nexample\n"; + /// let mut reader = BufReader::new(from); + /// let mut buf = vec![]; + /// + /// let mut size = reader.read_until(b'\n', &mut buf).await?; + /// assert_eq!(size, 7); + /// assert_eq!(buf, b"append\n"); + /// + /// size += reader.read_until(b'\n', &mut buf).await?; + /// assert_eq!(size, from.len()); + /// + /// assert_eq!(buf, from); + /// # + /// # Ok(()) }) } + /// ``` + fn read_until<'a>( + &'a mut self, + byte: u8, + buf: &'a mut Vec, + ) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is + /// reached. + /// + /// This function will read bytes from the underlying stream until the newline + /// delimiter (the 0xA byte) or EOF is found. Once found, all bytes up to, and + /// including, the delimiter (if found) will be appended to `buf`. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// If this function returns `Ok(0)`, the stream has reached EOF. + /// + /// # Errors + /// + /// This function has the same error semantics as [`read_until`] and will also return + /// an error if the read bytes are not valid UTF-8. If an I/O error is encountered then + /// `buf` may contain some bytes already read in the event that all data read so far + /// was valid UTF-8. + /// + /// [`read_until`]: #method.read_until + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let mut file = BufReader::new(File::open("a.txt").await?); + /// + /// let mut buf = String::new(); + /// file.read_line(&mut buf).await?; + /// # + /// # Ok(()) }) } + /// ``` + fn read_line<'a>( + &'a mut self, + buf: &'a mut String, + ) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Returns a stream over the lines of this byte stream. + /// + /// The stream returned from this function will yield instances of + /// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte + /// (the 0xA byte) or CRLF (0xD, 0xA bytes) at the end. + /// + /// [`io::Result`]: type.Result.html + /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::io::BufReader; + /// use async_std::prelude::*; + /// + /// let file = File::open("a.txt").await?; + /// let mut lines = BufReader::new(file).lines(); + /// let mut count = 0; + /// + /// while let Some(line) = lines.next().await { + /// line?; + /// count += 1; + /// } + /// # + /// # Ok(()) }) } + /// ``` + fn lines(self) -> Lines + where + Self: Unpin + Sized, + { + unreachable!() + } + } + + impl BufRead for Box { + fn poll_fill_buf( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + unreachable!() + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + unreachable!() + } + } + + impl BufRead for &mut T { + fn poll_fill_buf( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + unreachable!() + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + unreachable!() + } + } + + impl

BufRead for Pin

+ where + P: DerefMut + Unpin, +

::Target: BufRead, + { + fn poll_fill_buf( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + unreachable!() + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + unreachable!() + } + } + + impl BufRead for &[u8] { + fn poll_fill_buf( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + ) -> Poll> { + unreachable!() + } + + fn consume(self: Pin<&mut Self>, amt: usize) { + unreachable!() + } } } else { - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); - } + pub use futures_io::AsyncBufRead as BufRead; } } -/// Allows reading from a buffered byte stream. -/// -/// This trait is an async version of [`std::io::BufRead`]. -/// -/// While it is currently not possible to implement this trait directly, it gets implemented -/// automatically for all types that implement [`futures::io::AsyncBufRead`]. -/// -/// [`std::io::BufRead`]: https://doc.rust-lang.org/std/io/trait.BufRead.html -/// [`futures::io::AsyncBufRead`]: -/// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html -pub trait BufRead { - /// Tells this buffer that `amt` bytes have been consumed from the buffer, so they should no - /// longer be returned in calls to `read`. - fn consume(&mut self, amt: usize) - where - Self: Unpin; - - /// Returns the contents of the internal buffer, filling it with more data from the inner - /// reader if it is empty. - /// - /// This function is a lower-level call. It needs to be paired with the [`consume`] method to - /// function properly. When calling this method, none of the contents will be "read" in the - /// sense that later calling `read` may return the same contents. As such, [`consume`] must be - /// called with the number of bytes that are consumed from this buffer to ensure that the bytes - /// are never returned twice. - /// - /// [`consume`]: #tymethod.consume - /// - /// An empty buffer returned indicates that the stream has reached EOF. - // TODO: write a proper doctest with `consume` - fn fill_buf<'a>(&'a mut self) -> ret!('a, FillBufFuture, io::Result<&'a [u8]>) - where - Self: Unpin, - { - FillBufFuture::new(self) - } - - /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. - /// - /// This function will read bytes from the underlying stream until the delimiter or EOF is - /// found. Once found, all bytes up to, and including, the delimiter (if found) will be - /// appended to `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::io::BufReader; - /// use async_std::prelude::*; - /// - /// let mut file = BufReader::new(File::open("a.txt").await?); - /// - /// let mut buf = Vec::with_capacity(1024); - /// let n = file.read_until(b'\n', &mut buf).await?; - /// # - /// # Ok(()) }) } - /// ``` - /// - /// Multiple successful calls to `read_until` append all bytes up to and including to `buf`: - /// ``` - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::io::BufReader; - /// use async_std::prelude::*; - /// - /// let from: &[u8] = b"append\nexample\n"; - /// let mut reader = BufReader::new(from); - /// let mut buf = vec![]; - /// - /// let mut size = reader.read_until(b'\n', &mut buf).await?; - /// assert_eq!(size, 7); - /// assert_eq!(buf, b"append\n"); - /// - /// size += reader.read_until(b'\n', &mut buf).await?; - /// assert_eq!(size, from.len()); - /// - /// assert_eq!(buf, from); - /// # - /// # Ok(()) }) } - /// ``` - fn read_until<'a>( - &'a mut self, - byte: u8, - buf: &'a mut Vec, - ) -> ret!('a, ReadUntilFuture, io::Result) +#[doc(hidden)] +pub trait BufReadExt: futures_io::AsyncBufRead { + fn read_until<'a>(&'a mut self, byte: u8, buf: &'a mut Vec) -> ReadUntilFuture<'a, Self> where Self: Unpin, { @@ -132,44 +274,7 @@ pub trait BufRead { } } - /// Reads all bytes and appends them into `buf` until a newline (the 0xA byte) is reached. - /// - /// This function will read bytes from the underlying stream until the newline delimiter (the - /// 0xA byte) or EOF is found. Once found, all bytes up to, and including, the delimiter (if - /// found) will be appended to `buf`. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// If this function returns `Ok(0)`, the stream has reached EOF. - /// - /// # Errors - /// - /// This function has the same error semantics as [`read_until`] and will also return an error - /// if the read bytes are not valid UTF-8. If an I/O error is encountered then `buf` may - /// contain some bytes already read in the event that all data read so far was valid UTF-8. - /// - /// [`read_until`]: #method.read_until - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::io::BufReader; - /// use async_std::prelude::*; - /// - /// let mut file = BufReader::new(File::open("a.txt").await?); - /// - /// let mut buf = String::new(); - /// file.read_line(&mut buf).await?; - /// # - /// # Ok(()) }) } - /// ``` - fn read_line<'a>( - &'a mut self, - buf: &'a mut String, - ) -> ret!('a, ReadLineFuture, io::Result) + fn read_line<'a>(&'a mut self, buf: &'a mut String) -> ReadLineFuture<'a, Self> where Self: Unpin, { @@ -181,35 +286,6 @@ pub trait BufRead { } } - /// Returns a stream over the lines of this byte stream. - /// - /// The stream returned from this function will yield instances of - /// [`io::Result`]`<`[`String`]`>`. Each string returned will *not* have a newline byte (the - /// 0xA byte) or CRLF (0xD, 0xA bytes) at the end. - /// - /// [`io::Result`]: type.Result.html - /// [`String`]: https://doc.rust-lang.org/std/string/struct.String.html - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::io::BufReader; - /// use async_std::prelude::*; - /// - /// let file = File::open("a.txt").await?; - /// let mut lines = BufReader::new(file).lines(); - /// let mut count = 0; - /// - /// while let Some(line) = lines.next().await { - /// line?; - /// count += 1; - /// } - /// # - /// # Ok(()) }) } - /// ``` fn lines(self) -> Lines where Self: Unpin + Sized, @@ -223,13 +299,9 @@ pub trait BufRead { } } -impl BufRead for T { - fn consume(&mut self, amt: usize) { - AsyncBufRead::consume(Pin::new(self), amt) - } -} +impl BufReadExt for T {} -pub fn read_until_internal( +pub fn read_until_internal( mut reader: Pin<&mut R>, cx: &mut Context<'_>, byte: u8, diff --git a/src/io/buf_read/read_line.rs b/src/io/buf_read/read_line.rs index 7424637..29866be 100644 --- a/src/io/buf_read/read_line.rs +++ b/src/io/buf_read/read_line.rs @@ -2,11 +2,9 @@ use std::mem; use std::pin::Pin; use std::str; -use futures_io::AsyncBufRead; - use super::read_until_internal; use crate::future::Future; -use crate::io; +use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; #[doc(hidden)] @@ -18,7 +16,7 @@ pub struct ReadLineFuture<'a, T: Unpin + ?Sized> { pub(crate) read: usize, } -impl Future for ReadLineFuture<'_, T> { +impl Future for ReadLineFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/buf_read/read_until.rs b/src/io/buf_read/read_until.rs index c57a820..72385ab 100644 --- a/src/io/buf_read/read_until.rs +++ b/src/io/buf_read/read_until.rs @@ -1,10 +1,8 @@ use std::pin::Pin; -use futures_io::AsyncBufRead; - use super::read_until_internal; use crate::future::Future; -use crate::io; +use crate::io::{self, BufRead}; use crate::task::{Context, Poll}; #[doc(hidden)] @@ -16,7 +14,7 @@ pub struct ReadUntilFuture<'a, T: Unpin + ?Sized> { pub(crate) read: usize, } -impl Future for ReadUntilFuture<'_, T> { +impl Future for ReadUntilFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index 6329a1c..13cb4cc 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -2,9 +2,7 @@ use std::io::{IoSliceMut, Read as _}; use std::pin::Pin; use std::{cmp, fmt}; -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, Initializer}; - -use crate::io::{self, SeekFrom}; +use crate::io::{self, BufRead, Read, Seek, SeekFrom}; use crate::task::{Context, Poll}; const DEFAULT_CAPACITY: usize = 8 * 1024; @@ -193,7 +191,7 @@ impl BufReader { } } -impl AsyncRead for BufReader { +impl Read for BufReader { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -229,14 +227,9 @@ impl AsyncRead for BufReader { self.consume(nread); Poll::Ready(Ok(nread)) } - - // we can't skip unconditionally because of the large buffer case in read. - unsafe fn initializer(&self) -> Initializer { - self.inner.initializer() - } } -impl AsyncBufRead for BufReader { +impl BufRead for BufReader { fn poll_fill_buf<'a>( self: Pin<&'a mut Self>, cx: &mut Context<'_>, @@ -278,7 +271,7 @@ impl fmt::Debug for BufReader { } } -impl AsyncSeek for BufReader { +impl Seek for BufReader { /// Seeks to an offset, in bytes, in the underlying reader. /// /// The position used for seeking with `SeekFrom::Current(_)` is the position the underlying diff --git a/src/io/copy.rs b/src/io/copy.rs index d046b4b..3840d2a 100644 --- a/src/io/copy.rs +++ b/src/io/copy.rs @@ -1,9 +1,7 @@ use std::pin::Pin; -use futures_io::{AsyncBufRead, AsyncRead, AsyncWrite}; - use crate::future::Future; -use crate::io::{self, BufReader}; +use crate::io::{self, BufRead, BufReader, Read, Write}; use crate::task::{Context, Poll}; /// Copies the entire contents of a reader into a writer. @@ -45,8 +43,8 @@ use crate::task::{Context, Poll}; /// ``` pub async fn copy(reader: &mut R, writer: &mut W) -> io::Result where - R: AsyncRead + Unpin + ?Sized, - W: AsyncWrite + Unpin + ?Sized, + R: Read + Unpin + ?Sized, + W: Write + Unpin + ?Sized, { pub struct CopyFuture<'a, R, W: ?Sized> { reader: R, @@ -69,8 +67,8 @@ where impl Future for CopyFuture<'_, R, W> where - R: AsyncBufRead, - W: AsyncWrite + Unpin + ?Sized, + R: BufRead, + W: Write + Unpin + ?Sized, { type Output = io::Result; diff --git a/src/io/cursor.rs b/src/io/cursor.rs index c890c2f..8975840 100644 --- a/src/io/cursor.rs +++ b/src/io/cursor.rs @@ -1,8 +1,7 @@ -use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; - -use std::io::{self, IoSlice, IoSliceMut, SeekFrom}; use std::pin::Pin; -use std::task::{Context, Poll}; + +use crate::io::{self, IoSlice, IoSliceMut, Seek, SeekFrom, BufRead, Read, Write}; +use crate::task::{Context, Poll}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -153,7 +152,7 @@ impl Cursor { } } -impl AsyncSeek for Cursor +impl Seek for Cursor where T: AsRef<[u8]> + Unpin, { @@ -162,11 +161,11 @@ where _: &mut Context<'_>, pos: SeekFrom, ) -> Poll> { - Poll::Ready(io::Seek::seek(&mut self.inner, pos)) + Poll::Ready(std::io::Seek::seek(&mut self.inner, pos)) } } -impl AsyncRead for Cursor +impl Read for Cursor where T: AsRef<[u8]> + Unpin, { @@ -175,7 +174,7 @@ where _cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Poll::Ready(io::Read::read(&mut self.inner, buf)) + Poll::Ready(std::io::Read::read(&mut self.inner, buf)) } fn poll_read_vectored( @@ -183,30 +182,30 @@ where _: &mut Context<'_>, bufs: &mut [IoSliceMut<'_>], ) -> Poll> { - Poll::Ready(io::Read::read_vectored(&mut self.inner, bufs)) + Poll::Ready(std::io::Read::read_vectored(&mut self.inner, bufs)) } } -impl AsyncBufRead for Cursor +impl BufRead for Cursor where T: AsRef<[u8]> + Unpin, { fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(io::BufRead::fill_buf(&mut self.get_mut().inner)) + Poll::Ready(std::io::BufRead::fill_buf(&mut self.get_mut().inner)) } fn consume(mut self: Pin<&mut Self>, amt: usize) { - io::BufRead::consume(&mut self.inner, amt) + std::io::BufRead::consume(&mut self.inner, amt) } } -impl AsyncWrite for Cursor<&mut [u8]> { +impl Write for Cursor<&mut [u8]> { fn poll_write( mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Poll::Ready(io::Write::write(&mut self.inner, buf)) + Poll::Ready(std::io::Write::write(&mut self.inner, buf)) } fn poll_write_vectored( @@ -214,11 +213,11 @@ impl AsyncWrite for Cursor<&mut [u8]> { _: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll> { - Poll::Ready(io::Write::write_vectored(&mut self.inner, bufs)) + Poll::Ready(std::io::Write::write_vectored(&mut self.inner, bufs)) } fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(io::Write::flush(&mut self.inner)) + Poll::Ready(std::io::Write::flush(&mut self.inner)) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -226,13 +225,13 @@ impl AsyncWrite for Cursor<&mut [u8]> { } } -impl AsyncWrite for Cursor<&mut Vec> { +impl Write for Cursor<&mut Vec> { fn poll_write( mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Poll::Ready(io::Write::write(&mut self.inner, buf)) + Poll::Ready(std::io::Write::write(&mut self.inner, buf)) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -240,17 +239,17 @@ impl AsyncWrite for Cursor<&mut Vec> { } fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(io::Write::flush(&mut self.inner)) + Poll::Ready(std::io::Write::flush(&mut self.inner)) } } -impl AsyncWrite for Cursor> { +impl Write for Cursor> { fn poll_write( mut self: Pin<&mut Self>, _: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Poll::Ready(io::Write::write(&mut self.inner, buf)) + Poll::Ready(std::io::Write::write(&mut self.inner, buf)) } fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { @@ -258,6 +257,6 @@ impl AsyncWrite for Cursor> { } fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { - Poll::Ready(io::Write::flush(&mut self.inner)) + Poll::Ready(std::io::Write::flush(&mut self.inner)) } } diff --git a/src/io/empty.rs b/src/io/empty.rs index 2668dcc..d8d768e 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -1,9 +1,7 @@ use std::fmt; use std::pin::Pin; -use futures_io::{AsyncBufRead, AsyncRead, Initializer}; - -use crate::io; +use crate::io::{self, BufRead, Read}; use crate::task::{Context, Poll}; /// Creates a reader that contains no data. @@ -43,7 +41,7 @@ impl fmt::Debug for Empty { } } -impl AsyncRead for Empty { +impl Read for Empty { #[inline] fn poll_read( self: Pin<&mut Self>, @@ -52,14 +50,9 @@ impl AsyncRead for Empty { ) -> Poll> { Poll::Ready(Ok(0)) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } -impl AsyncBufRead for Empty { +impl BufRead for Empty { #[inline] fn poll_fill_buf<'a>( self: Pin<&'a mut Self>, diff --git a/src/io/mod.rs b/src/io/mod.rs index 97b1499..301cfa5 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -20,25 +20,25 @@ //! # Ok(()) }) } //! ``` -pub mod prelude; - #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; -pub use buf_read::{BufRead, Lines}; +pub use buf_read::{BufRead, BufReadExt, Lines}; pub use buf_reader::BufReader; pub use copy::copy; pub use cursor::Cursor; pub use empty::{empty, Empty}; -pub use read::Read; +pub use read::{Read, ReadExt}; pub use repeat::{repeat, Repeat}; -pub use seek::Seek; +pub use seek::{Seek, SeekExt}; pub use sink::{sink, Sink}; pub use stderr::{stderr, Stderr}; pub use stdin::{stdin, Stdin}; pub use stdout::{stdout, Stdout}; pub use timeout::timeout; -pub use write::Write; +pub use write::{Write, WriteExt}; + +pub mod prelude; mod buf_read; mod buf_reader; diff --git a/src/io/prelude.rs b/src/io/prelude.rs index 490be04..eeb0ced 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -9,10 +9,19 @@ //! ``` #[doc(no_inline)] -pub use super::BufRead; +pub use crate::io::BufRead; #[doc(no_inline)] -pub use super::Read; +pub use crate::io::Read; #[doc(no_inline)] -pub use super::Seek; +pub use crate::io::Seek; #[doc(no_inline)] -pub use super::Write; +pub use crate::io::Write; + +#[doc(hidden)] +pub use crate::io::BufReadExt as _; +#[doc(hidden)] +pub use crate::io::ReadExt as _; +#[doc(hidden)] +pub use crate::io::SeekExt as _; +#[doc(hidden)] +pub use crate::io::WriteExt as _; diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index bc6671c..1983823 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -10,119 +10,294 @@ use read_to_end::{read_to_end_internal, ReadToEndFuture}; use read_to_string::ReadToStringFuture; use read_vectored::ReadVectoredFuture; -use std::io; use std::mem; use cfg_if::cfg_if; -use futures_io::AsyncRead; + +use crate::io::IoSliceMut; cfg_if! { if #[cfg(feature = "docs")] { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; + + use crate::io; + use crate::task::{Context, Poll}; + #[doc(hidden)] pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); + /// Allows reading from a byte stream. + /// + /// This trait is a re-export of [`futures::io::AsyncRead`] and is an async version of + /// [`std::io::Read`]. + /// + /// Methods other than [`poll_read`] and [`poll_read_vectored`] do not really exist in the + /// trait itself, but they become available when the prelude is imported: + /// + /// ``` + /// # #[allow(unused_imports)] + /// use async_std::prelude::*; + /// ``` + /// + /// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html + /// [`futures::io::AsyncRead`]: + /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html + /// [`poll_read`]: #tymethod.poll_read + /// [`poll_read_vectored`]: #method.poll_read_vectored + pub trait Read { + /// Attempt to read from the `AsyncRead` into `buf`. + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll>; + + /// Attempt to read from the `AsyncRead` into `bufs` using vectored IO operations. + fn poll_read_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + unreachable!() + } + + /// Reads some bytes from the byte stream. + /// + /// Returns the number of bytes read from the start of the buffer. + /// + /// If the return value is `Ok(n)`, then it must be guaranteed that + /// `0 <= n <= buf.len()`. A nonzero `n` value indicates that the buffer has been + /// filled in with `n` bytes of data. If `n` is `0`, then it can indicate one of two + /// scenarios: + /// + /// 1. This reader has reached its "end of file" and will likely no longer be able to + /// produce bytes. Note that this does not mean that the reader will always no + /// longer be able to produce bytes. + /// 2. The buffer specified was 0 bytes in length. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::open("a.txt").await?; + /// + /// let mut buf = vec![0; 1024]; + /// let n = file.read(&mut buf).await?; + /// # + /// # Ok(()) }) } + /// ``` + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ImplFuture<'a, io::Result> + where + Self: Unpin + { + unreachable!() + } + + /// Like [`read`], except that it reads into a slice of buffers. + /// + /// Data is copied to fill each buffer in order, with the final buffer written to + /// possibly being only partially filled. This method must behave as a single call to + /// [`read`] with the buffers concatenated would. + /// + /// The default implementation calls [`read`] with either the first nonempty buffer + /// provided, or an empty one if none exists. + /// + /// [`read`]: #tymethod.read + fn read_vectored<'a>( + &'a mut self, + bufs: &'a mut [IoSliceMut<'a>], + ) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Reads all bytes from the byte stream. + /// + /// All bytes read from this stream will be appended to the specified buffer `buf`. + /// This function will continuously call [`read`] to append more data to `buf` until + /// [`read`] returns either `Ok(0)` or an error. + /// + /// If successful, this function will return the total number of bytes read. + /// + /// [`read`]: #tymethod.read + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::open("a.txt").await?; + /// + /// let mut buf = Vec::new(); + /// file.read_to_end(&mut buf).await?; + /// # + /// # Ok(()) }) } + /// ``` + fn read_to_end<'a>( + &'a mut self, + buf: &'a mut Vec, + ) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Reads all bytes from the byte stream and appends them into a string. + /// + /// If successful, this function will return the number of bytes read. + /// + /// If the data in this stream is not valid UTF-8 then an error will be returned and + /// `buf` will be left unmodified. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::open("a.txt").await?; + /// + /// let mut buf = String::new(); + /// file.read_to_string(&mut buf).await?; + /// # + /// # Ok(()) }) } + /// ``` + fn read_to_string<'a>( + &'a mut self, + buf: &'a mut String, + ) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Reads the exact number of bytes required to fill `buf`. + /// + /// This function reads as many bytes as necessary to completely fill the specified + /// buffer `buf`. + /// + /// No guarantees are provided about the contents of `buf` when this function is + /// called, implementations cannot rely on any property of the contents of `buf` being + /// true. It is recommended that implementations only write data to `buf` instead of + /// reading its contents. + /// + /// If this function encounters an "end of file" before completely filling the buffer, + /// it returns an error of the kind [`ErrorKind::UnexpectedEof`]. The contents of + /// `buf` are unspecified in this case. + /// + /// If any other read error is encountered then this function immediately returns. The + /// contents of `buf` are unspecified in this case. + /// + /// If this function returns an error, it is unspecified how many bytes it has read, + /// but it will never read more than would be necessary to completely fill the buffer. + /// + /// [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::open("a.txt").await?; + /// + /// let mut buf = vec![0; 10]; + /// file.read_exact(&mut buf).await?; + /// # + /// # Ok(()) }) } + /// ``` + fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ImplFuture<'a, io::Result<()>> + where + Self: Unpin, + { + unreachable!() + } + } + + impl Read for Box { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + unreachable!() + } + } + + impl Read for &mut T { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + unreachable!() + } + } + + impl

Read for Pin

+ where + P: DerefMut + Unpin, +

::Target: Read, + { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + unreachable!() + } + } + + impl Read for &[u8] { + fn poll_read( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + unreachable!() + } } } else { - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); - } + pub use futures_io::AsyncRead as Read; } } -/// Allows reading from a byte stream. -/// -/// This trait is an async version of [`std::io::Read`]. -/// -/// While it is currently not possible to implement this trait directly, it gets implemented -/// automatically for all types that implement [`futures::io::AsyncRead`]. -/// -/// [`std::io::Read`]: https://doc.rust-lang.org/std/io/trait.Read.html -/// [`futures::io::AsyncRead`]: -/// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncRead.html -pub trait Read { - /// Reads some bytes from the byte stream. - /// - /// Returns the number of bytes read from the start of the buffer. - /// - /// If the return value is `Ok(n)`, then it must be guaranteed that `0 <= n <= buf.len()`. A - /// nonzero `n` value indicates that the buffer has been filled in with `n` bytes of data. If - /// `n` is `0`, then it can indicate one of two scenarios: - /// - /// 1. This reader has reached its "end of file" and will likely no longer be able to produce - /// bytes. Note that this does not mean that the reader will always no longer be able to - /// produce bytes. - /// 2. The buffer specified was 0 bytes in length. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::open("a.txt").await?; - /// - /// let mut buf = vec![0; 1024]; - /// let n = file.read(&mut buf).await?; - /// # - /// # Ok(()) }) } - /// ``` - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ret!('a, ReadFuture, io::Result) +#[doc(hidden)] +pub trait ReadExt: futures_io::AsyncRead { + fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadFuture<'a, Self> where - Self: Unpin; + Self: Unpin, + { + ReadFuture { reader: self, buf } + } - /// Like [`read`], except that it reads into a slice of buffers. - /// - /// Data is copied to fill each buffer in order, with the final buffer written to possibly - /// being only partially filled. This method must behave as a single call to [`read`] with the - /// buffers concatenated would. - /// - /// The default implementation calls [`read`] with either the first nonempty buffer provided, - /// or an empty one if none exists. - /// - /// [`read`]: #tymethod.read fn read_vectored<'a>( &'a mut self, - bufs: &'a mut [io::IoSliceMut<'a>], - ) -> ret!('a, ReadVectoredFuture, io::Result) + bufs: &'a mut [IoSliceMut<'a>], + ) -> ReadVectoredFuture<'a, Self> where Self: Unpin, { ReadVectoredFuture { reader: self, bufs } } - /// Reads all bytes from the byte stream. - /// - /// All bytes read from this stream will be appended to the specified buffer `buf`. This - /// function will continuously call [`read`] to append more data to `buf` until [`read`] - /// returns either `Ok(0)` or an error. - /// - /// If successful, this function will return the total number of bytes read. - /// - /// [`read`]: #tymethod.read - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::open("a.txt").await?; - /// - /// let mut buf = Vec::new(); - /// file.read_to_end(&mut buf).await?; - /// # - /// # Ok(()) }) } - /// ``` - fn read_to_end<'a>( - &'a mut self, - buf: &'a mut Vec, - ) -> ret!('a, ReadToEndFuture, io::Result) + fn read_to_end<'a>(&'a mut self, buf: &'a mut Vec) -> ReadToEndFuture<'a, Self> where Self: Unpin, { @@ -134,32 +309,7 @@ pub trait Read { } } - /// Reads all bytes from the byte stream and appends them into a string. - /// - /// If successful, this function will return the number of bytes read. - /// - /// If the data in this stream is not valid UTF-8 then an error will be returned and `buf` will - /// be left unmodified. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::open("a.txt").await?; - /// - /// let mut buf = String::new(); - /// file.read_to_string(&mut buf).await?; - /// # - /// # Ok(()) }) } - /// ``` - fn read_to_string<'a>( - &'a mut self, - buf: &'a mut String, - ) -> ret!('a, ReadToStringFuture, io::Result) + fn read_to_string<'a>(&'a mut self, buf: &'a mut String) -> ReadToStringFuture<'a, Self> where Self: Unpin, { @@ -172,43 +322,7 @@ pub trait Read { } } - /// Reads the exact number of bytes required to fill `buf`. - /// - /// This function reads as many bytes as necessary to completely fill the specified buffer - /// `buf`. - /// - /// No guarantees are provided about the contents of `buf` when this function is called, - /// implementations cannot rely on any property of the contents of `buf` being true. It is - /// recommended that implementations only write data to `buf` instead of reading its contents. - /// - /// If this function encounters an "end of file" before completely filling the buffer, it - /// returns an error of the kind [`ErrorKind::UnexpectedEof`]. The contents of `buf` are - /// unspecified in this case. - /// - /// If any other read error is encountered then this function immediately returns. The contents - /// of `buf` are unspecified in this case. - /// - /// If this function returns an error, it is unspecified how many bytes it has read, but it - /// will never read more than would be necessary to completely fill the buffer. - /// - /// [`ErrorKind::UnexpectedEof`]: enum.ErrorKind.html#variant.UnexpectedEof - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::open("a.txt").await?; - /// - /// let mut buf = vec![0; 10]; - /// file.read_exact(&mut buf).await?; - /// # - /// # Ok(()) }) } - /// ``` - fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ret!('a, ReadExactFuture, io::Result<()>) + fn read_exact<'a>(&'a mut self, buf: &'a mut [u8]) -> ReadExactFuture<'a, Self> where Self: Unpin, { @@ -216,8 +330,4 @@ pub trait Read { } } -impl Read for T { - fn read<'a>(&'a mut self, buf: &'a mut [u8]) -> ret!('a, ReadFuture, io::Result) { - ReadFuture { reader: self, buf } - } -} +impl ReadExt for T {} diff --git a/src/io/read/read.rs b/src/io/read/read.rs index 0f88353..c46aff6 100644 --- a/src/io/read/read.rs +++ b/src/io/read/read.rs @@ -1,10 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::pin::Pin; -use futures_io::AsyncRead; +use crate::future::Future; +use crate::io::{self, Read}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -13,7 +11,7 @@ pub struct ReadFuture<'a, T: Unpin + ?Sized> { pub(crate) buf: &'a mut [u8], } -impl Future for ReadFuture<'_, T> { +impl Future for ReadFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs index ffc3600..d6f0218 100644 --- a/src/io/read/read_exact.rs +++ b/src/io/read/read_exact.rs @@ -1,11 +1,9 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::mem; use std::pin::Pin; -use futures_io::AsyncRead; +use crate::future::Future; +use crate::task::{Context, Poll}; +use crate::io::{self, Read}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -14,7 +12,7 @@ pub struct ReadExactFuture<'a, T: Unpin + ?Sized> { pub(crate) buf: &'a mut [u8], } -impl Future for ReadExactFuture<'_, T> { +impl Future for ReadExactFuture<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/read/read_to_end.rs b/src/io/read/read_to_end.rs index d51f599..331f26f 100644 --- a/src/io/read/read_to_end.rs +++ b/src/io/read/read_to_end.rs @@ -1,10 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::pin::Pin; -use futures_io::AsyncRead; +use crate::future::Future; +use crate::io::{self, Read}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -14,7 +12,7 @@ pub struct ReadToEndFuture<'a, T: Unpin + ?Sized> { pub(crate) start_len: usize, } -impl Future for ReadToEndFuture<'_, T> { +impl Future for ReadToEndFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { @@ -36,7 +34,7 @@ impl Future for ReadToEndFuture<'_, T> { // // Because we're extending the buffer with uninitialized data for trusted // readers, we need to make sure to truncate that if any of this panics. -pub fn read_to_end_internal( +pub fn read_to_end_internal( mut rd: Pin<&mut R>, cx: &mut Context<'_>, buf: &mut Vec, diff --git a/src/io/read/read_to_string.rs b/src/io/read/read_to_string.rs index 9a3bced..60773e0 100644 --- a/src/io/read/read_to_string.rs +++ b/src/io/read/read_to_string.rs @@ -1,13 +1,11 @@ -use super::read_to_end_internal; -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::mem; use std::pin::Pin; use std::str; -use futures_io::AsyncRead; +use super::read_to_end_internal; +use crate::future::Future; +use crate::io::{self, Read}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -18,7 +16,7 @@ pub struct ReadToStringFuture<'a, T: Unpin + ?Sized> { pub(crate) start_len: usize, } -impl Future for ReadToStringFuture<'_, T> { +impl Future for ReadToStringFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/read/read_vectored.rs b/src/io/read/read_vectored.rs index b65b79f..15fecfa 100644 --- a/src/io/read/read_vectored.rs +++ b/src/io/read/read_vectored.rs @@ -1,12 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io::IoSliceMut; use std::pin::Pin; -use futures_io::AsyncRead; - -use crate::io; +use crate::io::{self, Read, IoSliceMut}; +use crate::future::Future; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -15,7 +11,7 @@ pub struct ReadVectoredFuture<'a, T: Unpin + ?Sized> { pub(crate) bufs: &'a mut [IoSliceMut<'a>], } -impl Future for ReadVectoredFuture<'_, T> { +impl Future for ReadVectoredFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/repeat.rs b/src/io/repeat.rs index 7f8b3a6..a82e21b 100644 --- a/src/io/repeat.rs +++ b/src/io/repeat.rs @@ -1,9 +1,7 @@ use std::fmt; use std::pin::Pin; -use futures_io::{AsyncRead, Initializer}; - -use crate::io; +use crate::io::{self, Read}; use crate::task::{Context, Poll}; /// Creates an instance of a reader that infinitely repeats one byte. @@ -44,7 +42,7 @@ impl fmt::Debug for Repeat { } } -impl AsyncRead for Repeat { +impl Read for Repeat { #[inline] fn poll_read( self: Pin<&mut Self>, @@ -56,9 +54,4 @@ impl AsyncRead for Repeat { } Poll::Ready(Ok(buf.len())) } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } diff --git a/src/io/seek.rs b/src/io/seek.rs index b16da75..f2dd4d9 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -1,7 +1,6 @@ use std::pin::Pin; use cfg_if::cfg_if; -use futures_io::AsyncSeek; use crate::future::Future; use crate::io::{self, SeekFrom}; @@ -9,63 +8,116 @@ use crate::task::{Context, Poll}; cfg_if! { if #[cfg(feature = "docs")] { - #[doc(hidden)] - pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); + use std::ops::{Deref, DerefMut}; - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); + #[doc(hidden)] + pub struct ImplFuture(std::marker::PhantomData); + + /// Allows seeking through a byte stream. + /// + /// This trait is a re-export of [`futures::io::AsyncSeek`] and is an async version of + /// [`std::io::Seek`]. + /// + /// The [provided methods] do not really exist in the trait itself, but they become + /// available when the prelude is imported: + /// + /// ``` + /// # #[allow(unused_imports)] + /// use async_std::prelude::*; + /// ``` + /// + /// [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html + /// [`futures::io::AsyncSeek`]: + /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html + /// [provided methods]: #provided-methods + pub trait Seek { + /// Attempt to seek to an offset, in bytes, in a stream. + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll>; + + /// Seeks to a new position in a byte stream. + /// + /// Returns the new position in the byte stream. + /// + /// A seek beyond the end of stream is allowed, but behavior is defined by the + /// implementation. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::io::SeekFrom; + /// use async_std::prelude::*; + /// + /// let mut file = File::open("a.txt").await?; + /// + /// let file_len = file.seek(SeekFrom::End(0)).await?; + /// # + /// # Ok(()) }) } + /// ``` + fn seek(&mut self, pos: SeekFrom) -> ImplFuture> + where + Self: Unpin + { + unreachable!() + } + } + + impl Seek for Box { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + unreachable!() + } + } + + impl Seek for &mut T { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + unreachable!() + } + } + + impl

Seek for Pin

+ where + P: DerefMut + Unpin, +

::Target: Seek, + { + fn poll_seek( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + unreachable!() + } } } else { - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); - } + pub use futures_io::AsyncSeek as Seek; } } -/// Allows seeking through a byte stream. -/// -/// This trait is an async version of [`std::io::Seek`]. -/// -/// While it is currently not possible to implement this trait directly, it gets implemented -/// automatically for all types that implement [`futures::io::AsyncSeek`]. -/// -/// [`std::io::Seek`]: https://doc.rust-lang.org/std/io/trait.Seek.html -/// [`futures::io::AsyncSeek`]: -/// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncSeek.html -pub trait Seek { - /// Seeks to a new position in a byte stream. - /// - /// Returns the new position in the byte stream. - /// - /// A seek beyond the end of stream is allowed, but behavior is defined by the - /// implementation. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::io::SeekFrom; - /// use async_std::prelude::*; - /// - /// let mut file = File::open("a.txt").await?; - /// - /// let file_len = file.seek(SeekFrom::End(0)).await?; - /// # - /// # Ok(()) }) } - /// ``` - fn seek(&mut self, pos: SeekFrom) -> ret!('_, SeekFuture, io::Result) +#[doc(hidden)] +pub trait SeekExt: futures_io::AsyncSeek { + fn seek(&mut self, pos: SeekFrom) -> SeekFuture<'_, Self> where - Self: Unpin; -} - -impl Seek for T { - fn seek(&mut self, pos: SeekFrom) -> ret!('_, SeekFuture, io::Result) { + Self: Unpin, + { SeekFuture { seeker: self, pos } } } +impl SeekExt for T {} + #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct SeekFuture<'a, T: Unpin + ?Sized> { @@ -73,7 +125,7 @@ pub struct SeekFuture<'a, T: Unpin + ?Sized> { pos: SeekFrom, } -impl Future for SeekFuture<'_, T> { +impl Future for SeekFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/sink.rs b/src/io/sink.rs index 071f6ed..faa763c 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -1,9 +1,7 @@ use std::fmt; use std::pin::Pin; -use futures_io::AsyncWrite; - -use crate::io; +use crate::io::{self, Write}; use crate::task::{Context, Poll}; /// Creates a writer that consumes and drops all data. @@ -40,7 +38,7 @@ impl fmt::Debug for Sink { } } -impl AsyncWrite for Sink { +impl Write for Sink { #[inline] fn poll_write( self: Pin<&mut Self>, diff --git a/src/io/stderr.rs b/src/io/stderr.rs index bb2318f..64e0b18 100644 --- a/src/io/stderr.rs +++ b/src/io/stderr.rs @@ -1,11 +1,10 @@ -use std::io; use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures_io::AsyncWrite; use crate::future::Future; +use crate::io::{self, Write}; use crate::task::{blocking, Context, Poll}; /// Constructs a new handle to the standard error of the current process. @@ -29,7 +28,7 @@ use crate::task::{blocking, Context, Poll}; /// ``` pub fn stderr() -> Stderr { Stderr(Mutex::new(State::Idle(Some(Inner { - stderr: io::stderr(), + stderr: std::io::stderr(), buf: Vec::new(), last_op: None, })))) @@ -64,7 +63,7 @@ enum State { #[derive(Debug)] struct Inner { /// The blocking stderr handle. - stderr: io::Stderr, + stderr: std::io::Stderr, /// The write buffer. buf: Vec, @@ -80,7 +79,7 @@ enum Operation { Flush(io::Result<()>), } -impl AsyncWrite for Stderr { +impl Write for Stderr { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -118,7 +117,7 @@ impl AsyncWrite for Stderr { // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { - let res = io::Write::write(&mut inner.stderr, &mut inner.buf); + let res = std::io::Write::write(&mut inner.stderr, &mut inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) })); @@ -146,7 +145,7 @@ impl AsyncWrite for Stderr { // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { - let res = io::Write::flush(&mut inner.stderr); + let res = std::io::Write::flush(&mut inner.stderr); inner.last_op = Some(Operation::Flush(res)); State::Idle(Some(inner)) })); @@ -179,7 +178,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for Stderr { fn as_raw_fd(&self) -> RawFd { - io::stderr().as_raw_fd() + std::io::stderr().as_raw_fd() } } } @@ -190,7 +189,7 @@ cfg_if! { if #[cfg(any(windows, feature = "docs"))] { impl AsRawHandle for Stderr { fn as_raw_handle(&self) -> RawHandle { - io::stderr().as_raw_handle() + std::io::stderr().as_raw_handle() } } } diff --git a/src/io/stdin.rs b/src/io/stdin.rs index 9fe432d..cbf7fc4 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -1,10 +1,9 @@ -use std::io; use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures_io::{AsyncRead, Initializer}; +use crate::io::{self, Read}; use crate::future::{self, Future}; use crate::task::{blocking, Context, Poll}; @@ -29,7 +28,7 @@ use crate::task::{blocking, Context, Poll}; /// ``` pub fn stdin() -> Stdin { Stdin(Mutex::new(State::Idle(Some(Inner { - stdin: io::stdin(), + stdin: std::io::stdin(), line: String::new(), buf: Vec::new(), last_op: None, @@ -65,7 +64,7 @@ enum State { #[derive(Debug)] struct Inner { /// The blocking stdin handle. - stdin: io::Stdin, + stdin: std::io::Stdin, /// The line buffer. line: String, @@ -137,7 +136,7 @@ impl Stdin { } } -impl AsyncRead for Stdin { +impl Read for Stdin { fn poll_read( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -174,7 +173,7 @@ impl AsyncRead for Stdin { // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { - let res = io::Read::read(&mut inner.stdin, &mut inner.buf); + let res = std::io::Read::read(&mut inner.stdin, &mut inner.buf); inner.last_op = Some(Operation::Read(res)); State::Idle(Some(inner)) })); @@ -185,11 +184,6 @@ impl AsyncRead for Stdin { } } } - - #[inline] - unsafe fn initializer(&self) -> Initializer { - Initializer::nop() - } } cfg_if! { @@ -208,7 +202,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for Stdin { fn as_raw_fd(&self) -> RawFd { - io::stdin().as_raw_fd() + std::io::stdin().as_raw_fd() } } } @@ -219,7 +213,7 @@ cfg_if! { if #[cfg(any(windows, feature = "docs"))] { impl AsRawHandle for Stdin { fn as_raw_handle(&self) -> RawHandle { - io::stdin().as_raw_handle() + std::io::stdin().as_raw_handle() } } } diff --git a/src/io/stdout.rs b/src/io/stdout.rs index f62f3df..b5fcccf 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -1,12 +1,11 @@ -use std::io; use std::pin::Pin; use std::sync::Mutex; use cfg_if::cfg_if; -use futures_io::AsyncWrite; use crate::future::Future; use crate::task::{blocking, Context, Poll}; +use crate::io::{self, Write}; /// Constructs a new handle to the standard output of the current process. /// @@ -29,7 +28,7 @@ use crate::task::{blocking, Context, Poll}; /// ``` pub fn stdout() -> Stdout { Stdout(Mutex::new(State::Idle(Some(Inner { - stdout: io::stdout(), + stdout: std::io::stdout(), buf: Vec::new(), last_op: None, })))) @@ -64,7 +63,7 @@ enum State { #[derive(Debug)] struct Inner { /// The blocking stdout handle. - stdout: io::Stdout, + stdout: std::io::Stdout, /// The write buffer. buf: Vec, @@ -80,7 +79,7 @@ enum Operation { Flush(io::Result<()>), } -impl AsyncWrite for Stdout { +impl Write for Stdout { fn poll_write( mut self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -118,7 +117,7 @@ impl AsyncWrite for Stdout { // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { - let res = io::Write::write(&mut inner.stdout, &mut inner.buf); + let res = std::io::Write::write(&mut inner.stdout, &mut inner.buf); inner.last_op = Some(Operation::Write(res)); State::Idle(Some(inner)) })); @@ -146,7 +145,7 @@ impl AsyncWrite for Stdout { // Start the operation asynchronously. *state = State::Busy(blocking::spawn(async move { - let res = io::Write::flush(&mut inner.stdout); + let res = std::io::Write::flush(&mut inner.stdout); inner.last_op = Some(Operation::Flush(res)); State::Idle(Some(inner)) })); @@ -179,7 +178,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for Stdout { fn as_raw_fd(&self) -> RawFd { - io::stdout().as_raw_fd() + std::io::stdout().as_raw_fd() } } } @@ -190,7 +189,7 @@ cfg_if! { if #[cfg(any(windows, feature = "docs"))] { impl AsRawHandle for Stdout { fn as_raw_handle(&self) -> RawHandle { - io::stdout().as_raw_handle() + std::io::stdout().as_raw_handle() } } } diff --git a/src/io/write/flush.rs b/src/io/write/flush.rs index 21ada15..08f2b5b 100644 --- a/src/io/write/flush.rs +++ b/src/io/write/flush.rs @@ -1,10 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::pin::Pin; -use futures_io::AsyncWrite; +use crate::future::Future; +use crate::io::{self, Write}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -12,7 +10,7 @@ pub struct FlushFuture<'a, T: Unpin + ?Sized> { pub(crate) writer: &'a mut T, } -impl Future for FlushFuture<'_, T> { +impl Future for FlushFuture<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 63a3cd8..f5f1602 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -8,130 +8,277 @@ use write::WriteFuture; use write_all::WriteAllFuture; use write_vectored::WriteVectoredFuture; -use std::io; - use cfg_if::cfg_if; -use futures_io::AsyncWrite; + +use crate::io::IoSlice; cfg_if! { if #[cfg(feature = "docs")] { + use std::pin::Pin; + use std::ops::{Deref, DerefMut}; + + use crate::io; + use crate::task::{Context, Poll}; + #[doc(hidden)] pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); + /// Allows writing to a byte stream. + /// + /// This trait is a re-export of [`futures::io::AsyncWrite`] and is an async version of + /// [`std::io::Write`]. + /// + /// Methods other than [`poll_write`], [`poll_write_vectored`], [`poll_flush`], and + /// [`poll_close`] do not really exist in the trait itself, but they become available when + /// the prelude is imported: + /// + /// ``` + /// # #[allow(unused_imports)] + /// use async_std::prelude::*; + /// ``` + /// + /// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html + /// [`futures::io::AsyncWrite`]: + /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html + /// [`poll_write`]: #tymethod.poll_write + /// [`poll_write_vectored`]: #method.poll_write_vectored + /// [`poll_flush`]: #tymethod.poll_flush + /// [`poll_close`]: #tymethod.poll_close + pub trait Write { + /// Attempt to write bytes from `buf` into the object. + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll>; + + /// Attempt to write bytes from `bufs` into the object using vectored + /// IO operations. + fn poll_write_vectored( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + bufs: &[IoSlice<'_>] + ) -> Poll> { + unreachable!() + } + + /// Attempt to flush the object, ensuring that any buffered data reach + /// their destination. + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + /// Attempt to close the object. + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + /// Writes some bytes into the byte stream. + /// + /// Returns the number of bytes written from the start of the buffer. + /// + /// If the return value is `Ok(n)` then it must be guaranteed that + /// `0 <= n <= buf.len()`. A return value of `0` typically means that the underlying + /// object is no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::create("a.txt").await?; + /// + /// let n = file.write(b"hello world").await?; + /// # + /// # Ok(()) }) } + /// ``` + fn write<'a>(&'a mut self, buf: &'a [u8]) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Flushes the stream to ensure that all buffered contents reach their destination. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::create("a.txt").await?; + /// + /// file.write_all(b"hello world").await?; + /// file.flush().await?; + /// # + /// # Ok(()) }) } + /// ``` + fn flush(&mut self) -> ImplFuture<'_, io::Result<()>> + where + Self: Unpin, + { + unreachable!() + } + + /// Like [`write`], except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer read from possibly + /// being only partially consumed. This method must behave as a call to [`write`] with + /// the buffers concatenated would. + /// + /// The default implementation calls [`write`] with either the first nonempty buffer + /// provided, or an empty one if none exists. + /// + /// [`write`]: #tymethod.write + fn write_vectored<'a>( + &'a mut self, + bufs: &'a [IoSlice<'a>], + ) -> ImplFuture<'a, io::Result> + where + Self: Unpin, + { + unreachable!() + } + + /// Writes an entire buffer into the byte stream. + /// + /// This method will continuously call [`write`] until there is no more data to be + /// written or an error is returned. This method will not return until the entire + /// buffer has been successfully written or such an error occurs. + /// + /// [`write`]: #tymethod.write + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs::File; + /// use async_std::prelude::*; + /// + /// let mut file = File::create("a.txt").await?; + /// + /// file.write_all(b"hello world").await?; + /// # + /// # Ok(()) }) } + /// ``` + /// + /// [`write`]: #tymethod.write + fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> ImplFuture<'a, io::Result<()>> + where + Self: Unpin, + { + unreachable!() + } + } + + impl Write for Box { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + unreachable!() + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl Write for &mut T { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + unreachable!() + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl

Write for Pin

+ where + P: DerefMut + Unpin, +

::Target: Write, + { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + unreachable!() + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl Write for Vec { + fn poll_write( + self: Pin<&mut Self>, + cx: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + unreachable!() + } + + fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } } } else { - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); - } + pub use futures_io::AsyncWrite as Write; } } -/// Allows writing to a byte stream. -/// -/// This trait is an async version of [`std::io::Write`]. -/// -/// While it is currently not possible to implement this trait directly, it gets implemented -/// automatically for all types that implement [`futures::io::AsyncWrite`]. -/// -/// [`std::io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html -/// [`futures::io::AsyncWrite`]: -/// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncWrite.html -pub trait Write { - /// Writes some bytes into the byte stream. - /// - /// Returns the number of bytes written from the start of the buffer. - /// - /// If the return value is `Ok(n)` then it must be guaranteed that `0 <= n <= buf.len()`. A - /// return value of `0` typically means that the underlying object is no longer able to accept - /// bytes and will likely not be able to in the future as well, or that the buffer provided is - /// empty. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::create("a.txt").await?; - /// - /// let n = file.write(b"hello world").await?; - /// # - /// # Ok(()) }) } - /// ``` - fn write<'a>(&'a mut self, buf: &'a [u8]) -> ret!('a, WriteFuture, io::Result) +#[doc(hidden)] +pub trait WriteExt: Write { + fn write<'a>(&'a mut self, buf: &'a [u8]) -> WriteFuture<'a, Self> where - Self: Unpin; + Self: Unpin, + { + WriteFuture { writer: self, buf } + } - /// Flushes the stream to ensure that all buffered contents reach their destination. - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::create("a.txt").await?; - /// - /// file.write_all(b"hello world").await?; - /// file.flush().await?; - /// # - /// # Ok(()) }) } - /// ``` - fn flush(&mut self) -> ret!('_, FlushFuture, io::Result<()>) + fn flush(&mut self) -> FlushFuture<'_, Self> where - Self: Unpin; + Self: Unpin, + { + FlushFuture { writer: self } + } - /// Like [`write`], except that it writes from a slice of buffers. - /// - /// Data is copied from each buffer in order, with the final buffer read from possibly being - /// only partially consumed. This method must behave as a call to [`write`] with the buffers - /// concatenated would. - /// - /// The default implementation calls [`write`] with either the first nonempty buffer provided, - /// or an empty one if none exists. - /// - /// [`write`]: #tymethod.write - fn write_vectored<'a>( - &'a mut self, - bufs: &'a [io::IoSlice<'a>], - ) -> ret!('a, WriteVectoredFuture, io::Result) + fn write_vectored<'a>(&'a mut self, bufs: &'a [IoSlice<'a>]) -> WriteVectoredFuture<'a, Self> where Self: Unpin, { WriteVectoredFuture { writer: self, bufs } } - /// Writes an entire buffer into the byte stream. - /// - /// This method will continuously call [`write`] until there is no more data to be written or - /// an error is returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. - /// - /// [`write`]: #tymethod.write - /// - /// # Examples - /// - /// ```no_run - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use async_std::fs::File; - /// use async_std::prelude::*; - /// - /// let mut file = File::create("a.txt").await?; - /// - /// file.write_all(b"hello world").await?; - /// # - /// # Ok(()) }) } - /// ``` - /// - /// [`write`]: #tymethod.write - fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> ret!('a, WriteAllFuture, io::Result<()>) + fn write_all<'a>(&'a mut self, buf: &'a [u8]) -> WriteAllFuture<'a, Self> where Self: Unpin, { @@ -139,12 +286,4 @@ pub trait Write { } } -impl Write for T { - fn write<'a>(&'a mut self, buf: &'a [u8]) -> ret!('a, WriteFuture, io::Result) { - WriteFuture { writer: self, buf } - } - - fn flush(&mut self) -> ret!('_, FlushFuture, io::Result<()>) { - FlushFuture { writer: self } - } -} +impl WriteExt for T {} diff --git a/src/io/write/write.rs b/src/io/write/write.rs index 361e6af..da6e5c5 100644 --- a/src/io/write/write.rs +++ b/src/io/write/write.rs @@ -1,10 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::pin::Pin; -use futures_io::AsyncWrite; +use crate::future::Future; +use crate::io::{self, Write}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -13,7 +11,7 @@ pub struct WriteFuture<'a, T: Unpin + ?Sized> { pub(crate) buf: &'a [u8], } -impl Future for WriteFuture<'_, T> { +impl Future for WriteFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/write/write_all.rs b/src/io/write/write_all.rs index d43854a..5353f7a 100644 --- a/src/io/write/write_all.rs +++ b/src/io/write/write_all.rs @@ -1,11 +1,9 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; use std::mem; use std::pin::Pin; -use futures_io::AsyncWrite; +use crate::future::Future; +use crate::io::{self, Write}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -14,7 +12,7 @@ pub struct WriteAllFuture<'a, T: Unpin + ?Sized> { pub(crate) buf: &'a [u8], } -impl Future for WriteAllFuture<'_, T> { +impl Future for WriteAllFuture<'_, T> { type Output = io::Result<()>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs index 0f3e49a..d4e2da7 100644 --- a/src/io/write/write_vectored.rs +++ b/src/io/write/write_vectored.rs @@ -1,11 +1,8 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - -use std::io; -use std::io::IoSlice; use std::pin::Pin; -use futures_io::AsyncWrite; +use crate::future::Future; +use crate::task::{Context, Poll}; +use crate::io::{self, Write, IoSlice}; #[doc(hidden)] #[allow(missing_debug_implementations)] @@ -14,7 +11,7 @@ pub struct WriteVectoredFuture<'a, T: Unpin + ?Sized> { pub(crate) bufs: &'a [IoSlice<'a>], } -impl Future for WriteVectoredFuture<'_, T> { +impl Future for WriteVectoredFuture<'_, T> { type Output = io::Result; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/lib.rs b/src/lib.rs index d4ed7c2..31635ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,12 @@ //! //! # Features //! -//! Unstable APIs in this crate are available when the `unstable` Cargo feature is enabled: +//! Items marked with +//! unstable +//! are available only when the `unstable` Cargo feature is enabled: //! //! ```toml //! [dependencies.async-std] @@ -58,6 +63,7 @@ cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub mod pin; + mod vec; mod result; } diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 4afa574..26e19d7 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -8,6 +8,7 @@ use crate::future::{self, Future}; use crate::io; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; +use crate::stream::Stream; use crate::task::{Context, Poll}; /// A TCP socket server, listening for connections. @@ -190,7 +191,7 @@ impl TcpListener { #[derive(Debug)] pub struct Incoming<'a>(&'a TcpListener); -impl<'a> futures_core::stream::Stream for Incoming<'a> { +impl<'a> Stream for Incoming<'a> { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index 7c10602..1056943 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -3,10 +3,9 @@ use std::net::SocketAddr; use std::pin::Pin; use cfg_if::cfg_if; -use futures_io::{AsyncRead, AsyncWrite}; use crate::future; -use crate::io; +use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; use crate::task::blocking; @@ -285,7 +284,7 @@ impl TcpStream { } } -impl AsyncRead for TcpStream { +impl Read for TcpStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -303,7 +302,7 @@ impl AsyncRead for TcpStream { } } -impl AsyncRead for &TcpStream { +impl Read for &TcpStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -313,7 +312,7 @@ impl AsyncRead for &TcpStream { } } -impl AsyncWrite for TcpStream { +impl Write for TcpStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -339,7 +338,7 @@ impl AsyncWrite for TcpStream { } } -impl AsyncWrite for &TcpStream { +impl Write for &TcpStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 28f43eb..ae30b5b 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -6,11 +6,10 @@ use std::net::Shutdown; use std::path::Path; use std::pin::Pin; -use futures_io::{AsyncRead, AsyncWrite}; use mio_uds; use super::SocketAddr; -use crate::io; +use crate::io::{self, Read, Write}; use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::task::{blocking, Context, Poll}; @@ -154,7 +153,7 @@ impl UnixStream { } } -impl AsyncRead for UnixStream { +impl Read for UnixStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -164,7 +163,7 @@ impl AsyncRead for UnixStream { } } -impl AsyncRead for &UnixStream { +impl Read for &UnixStream { fn poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -174,7 +173,7 @@ impl AsyncRead for &UnixStream { } } -impl AsyncWrite for UnixStream { +impl Write for UnixStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, @@ -192,7 +191,7 @@ impl AsyncWrite for UnixStream { } } -impl AsyncWrite for &UnixStream { +impl Write for &UnixStream { fn poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, diff --git a/src/prelude.rs b/src/prelude.rs index a50e123..7ad894f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -25,3 +25,14 @@ pub use crate::io::Write as _; pub use crate::stream::Stream; #[doc(no_inline)] pub use crate::task_local; + +#[doc(hidden)] +pub use crate::io::BufReadExt as _; +#[doc(hidden)] +pub use crate::io::ReadExt as _; +#[doc(hidden)] +pub use crate::io::SeekExt as _; +#[doc(hidden)] +pub use crate::io::WriteExt as _; +#[doc(hidden)] +pub use crate::stream::stream::Stream as _; diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index 71cf61d..74cc567 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -1,8 +1,9 @@ -use crate::stream::{FromStream, IntoStream, Stream}; - use std::pin::Pin; -impl FromStream> for Result +use crate::prelude::*; +use crate::stream::{FromStream, IntoStream}; + +impl FromStream> for Result where V: FromStream, { @@ -12,9 +13,9 @@ where #[inline] fn from_stream<'a, S: IntoStream>>( stream: S, - ) -> Pin + Send + 'a>> + ) -> Pin + 'a>> where - ::IntoStream: Send + 'a, + ::IntoStream: 'a, { let stream = stream.into_stream(); diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index d046e3b..984e5c8 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -12,7 +12,7 @@ use std::pin::Pin; /// [`IntoStream`]: trait.IntoStream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(any(feature = "unstable", feature = "docs"))] -pub trait FromStream { +pub trait FromStream { /// Creates a value from a stream. /// /// # Examples @@ -24,7 +24,7 @@ pub trait FromStream { /// /// // let _five_fives = async_std::stream::repeat(5).take(5); /// ``` - fn from_stream<'a, S: IntoStream + Send + 'a>( + fn from_stream<'a, S: IntoStream + 'a>( stream: S, - ) -> Pin + Send + 'a>>; + ) -> Pin + 'a>>; } diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 5e4311e..7d1be4a 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -20,13 +20,13 @@ pub trait IntoStream { type Item; /// Which kind of stream are we turning this into? - type IntoStream: Stream + Send; + type IntoStream: Stream; /// Creates a stream from a value. fn into_stream(self) -> Self::IntoStream; } -impl IntoStream for I { +impl IntoStream for I { type Item = I::Item; type IntoStream = I; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 5e17aa5..da79b19 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -26,12 +26,20 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Fuse, Scan, Stream, Take, Zip}; +pub use stream::{Fuse, Scan, Take, Zip}; mod empty; mod once; mod repeat; -mod stream; +pub(crate) mod stream; + +cfg_if! { + if #[cfg(feature = "docs")] { + pub use stream::Stream; + } else { + pub use futures_core::stream::Stream; + } +} cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ddab21..7527deb 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -60,8 +60,8 @@ use std::task::{Context, Poll}; use cfg_if::cfg_if; cfg_if! { - if #[cfg(any(feature = "unstable", feature = "docs"))] { - use crate::stream::FromStream; + if #[cfg(feature = "unstable")] { + use crate::future::Future; } } @@ -75,7 +75,7 @@ cfg_if! { ($a:lifetime, $f:tt, $o:ty, $t1:ty) => (ImplFuture<$a, $o>); ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => (ImplFuture<$a, $o>); ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty, $t3:ty) => (ImplFuture<$a, $o>); - + ($f:ty, $o:ty) => (ImplFuture<'static, $o>); } } else { macro_rules! ret { @@ -83,36 +83,34 @@ cfg_if! { ($a:lifetime, $f:tt, $o:ty, $t1:ty) => ($f<$a, Self, $t1>); ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => ($f<$a, Self, $t1, $t2>); ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty, $t3:ty) => ($f<$a, Self, $t1, $t2, $t3>); + ($f:ty, $o:ty) => ($f); } } } cfg_if! { - if #[cfg(feature = "docs")] { - #[doc(hidden)] - pub struct DynFuture<'a, T>(std::marker::PhantomData<&'a T>); - - macro_rules! dyn_ret { - ($a:lifetime, $o:ty) => (DynFuture<$a, $o>); - } - } else { - #[allow(unused_macros)] - macro_rules! dyn_ret { - ($a:lifetime, $o:ty) => (Pin + Send + 'a>>) - } + if #[cfg(any(feature = "unstable", feature = "docs"))] { + use crate::stream::FromStream; } } /// An asynchronous stream of values. /// -/// This trait is an async version of [`std::iter::Iterator`]. +/// This trait is a re-export of [`futures::stream::Stream`] and is an async version of +/// [`std::iter::Iterator`]. /// -/// While it is currently not possible to implement this trait directly, it gets implemented -/// automatically for all types that implement [`futures::stream::Stream`]. +/// The [provided methods] do not really exist in the trait itself, but they become available when +/// the prelude is imported: +/// +/// ``` +/// # #[allow(unused_imports)] +/// use async_std::prelude::*; +/// ``` /// /// [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html /// [`futures::stream::Stream`]: /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html +/// [provided methods]: #provided-methods pub trait Stream { /// The type of items yielded by this stream. type Item; @@ -345,7 +343,7 @@ pub trait Stream { /// # /// # }) } /// ``` - fn min_by(self, compare: F) -> MinByFuture + fn min_by(self, compare: F) -> ret!(MinByFuture, Self::Item) where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, @@ -555,7 +553,7 @@ pub trait Stream { /// # /// # }) } /// ``` - fn fold(self, init: B, f: F) -> FoldFuture + fn fold(self, init: B, f: F) -> ret!(FoldFuture, Self::Item) where Self: Sized, F: FnMut(B, Self::Item) -> B, @@ -753,13 +751,12 @@ pub trait Stream { /// ``` /// /// [`stream`]: trait.Stream.html#tymethod.next - #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] - #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(any(feature = "unstable", feature = "docs"))] - fn collect<'a, B>(self) -> dyn_ret!('a, B) + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] + fn collect<'a, B>(self) -> ret!(Pin + 'a>>, B) where - Self: futures_core::stream::Stream + Sized + Send + 'a, - ::Item: Send, + Self: futures_core::stream::Stream + Sized + 'a, B: FromStream<::Item>, { FromStream::from_stream(self) diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 222022b..cd44788 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -24,9 +24,9 @@ impl Scan { impl Unpin for Scan {} -impl futures_core::stream::Stream for Scan +impl Stream for Scan where - S: Stream, + S: crate::stream::Stream, F: FnMut(&mut St, S::Item) -> Option, { type Item = B; diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 8f7c9ab..706dbfa 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -5,7 +5,7 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; /// An iterator that iterates two other iterators simultaneously. -pub struct Zip { +pub struct Zip { item_slot: Option, first: A, second: B, @@ -22,7 +22,7 @@ impl fmt::Debug for Zip { impl Unpin for Zip {} -impl Zip { +impl Zip { pub(crate) fn new(first: A, second: B) -> Self { Zip { item_slot: None, diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index f603d0d..ad82797 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -1,14 +1,15 @@ -use crate::stream::{FromStream, IntoStream, Stream}; - use std::pin::Pin; -impl FromStream for Vec { +use crate::prelude::*; +use crate::stream::{FromStream, IntoStream}; + +impl FromStream for Vec { #[inline] fn from_stream<'a, S: IntoStream>( stream: S, - ) -> Pin + Send + 'a>> + ) -> Pin + 'a>> where - ::IntoStream: Send + 'a, + ::IntoStream: 'a, { let stream = stream.into_stream(); From 1fa196812aec1697a55563a1dd4e929ad748ffa8 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 21 Sep 2019 15:05:57 +0200 Subject: [PATCH 173/194] Fix compilation errors around Stream --- src/fs/file.rs | 2 +- src/io/cursor.rs | 2 +- src/io/read/read_exact.rs | 2 +- src/io/read/read_vectored.rs | 2 +- src/io/stdin.rs | 2 +- src/io/stdout.rs | 2 +- src/io/write/write_vectored.rs | 2 +- src/prelude.rs | 4 +--- src/stream/mod.rs | 12 ++---------- src/stream/stream/scan.rs | 4 ++-- src/stream/stream/zip.rs | 8 ++++---- 11 files changed, 16 insertions(+), 26 deletions(-) diff --git a/src/fs/file.rs b/src/fs/file.rs index b2a897e..bdf9347 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -12,7 +12,7 @@ use cfg_if::cfg_if; use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::io::{self, Seek, SeekFrom, Read, Write}; +use crate::io::{self, Read, Seek, SeekFrom, Write}; use crate::prelude::*; use crate::task::{self, blocking, Context, Poll, Waker}; diff --git a/src/io/cursor.rs b/src/io/cursor.rs index 8975840..2dfc8a7 100644 --- a/src/io/cursor.rs +++ b/src/io/cursor.rs @@ -1,6 +1,6 @@ use std::pin::Pin; -use crate::io::{self, IoSlice, IoSliceMut, Seek, SeekFrom, BufRead, Read, Write}; +use crate::io::{self, BufRead, IoSlice, IoSliceMut, Read, Seek, SeekFrom, Write}; use crate::task::{Context, Poll}; /// A `Cursor` wraps an in-memory buffer and provides it with a diff --git a/src/io/read/read_exact.rs b/src/io/read/read_exact.rs index d6f0218..c970f43 100644 --- a/src/io/read/read_exact.rs +++ b/src/io/read/read_exact.rs @@ -2,8 +2,8 @@ use std::mem; use std::pin::Pin; use crate::future::Future; -use crate::task::{Context, Poll}; use crate::io::{self, Read}; +use crate::task::{Context, Poll}; #[doc(hidden)] #[allow(missing_debug_implementations)] diff --git a/src/io/read/read_vectored.rs b/src/io/read/read_vectored.rs index 15fecfa..8e52ba2 100644 --- a/src/io/read/read_vectored.rs +++ b/src/io/read/read_vectored.rs @@ -1,7 +1,7 @@ use std::pin::Pin; -use crate::io::{self, Read, IoSliceMut}; use crate::future::Future; +use crate::io::{self, IoSliceMut, Read}; use crate::task::{Context, Poll}; #[doc(hidden)] diff --git a/src/io/stdin.rs b/src/io/stdin.rs index cbf7fc4..d2e9ec0 100644 --- a/src/io/stdin.rs +++ b/src/io/stdin.rs @@ -3,8 +3,8 @@ use std::sync::Mutex; use cfg_if::cfg_if; -use crate::io::{self, Read}; use crate::future::{self, Future}; +use crate::io::{self, Read}; use crate::task::{blocking, Context, Poll}; /// Constructs a new handle to the standard input of the current process. diff --git a/src/io/stdout.rs b/src/io/stdout.rs index b5fcccf..b7fee70 100644 --- a/src/io/stdout.rs +++ b/src/io/stdout.rs @@ -4,8 +4,8 @@ use std::sync::Mutex; use cfg_if::cfg_if; use crate::future::Future; -use crate::task::{blocking, Context, Poll}; use crate::io::{self, Write}; +use crate::task::{blocking, Context, Poll}; /// Constructs a new handle to the standard output of the current process. /// diff --git a/src/io/write/write_vectored.rs b/src/io/write/write_vectored.rs index d4e2da7..5f8492b 100644 --- a/src/io/write/write_vectored.rs +++ b/src/io/write/write_vectored.rs @@ -1,8 +1,8 @@ use std::pin::Pin; use crate::future::Future; +use crate::io::{self, IoSlice, Write}; use crate::task::{Context, Poll}; -use crate::io::{self, Write, IoSlice}; #[doc(hidden)] #[allow(missing_debug_implementations)] diff --git a/src/prelude.rs b/src/prelude.rs index 7ad894f..d28ec64 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -21,7 +21,7 @@ pub use crate::io::Read as _; pub use crate::io::Seek as _; #[doc(no_inline)] pub use crate::io::Write as _; -#[doc(no_inline)] +#[doc(hidden)] pub use crate::stream::Stream; #[doc(no_inline)] pub use crate::task_local; @@ -34,5 +34,3 @@ pub use crate::io::ReadExt as _; pub use crate::io::SeekExt as _; #[doc(hidden)] pub use crate::io::WriteExt as _; -#[doc(hidden)] -pub use crate::stream::stream::Stream as _; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index da79b19..5e17aa5 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -26,20 +26,12 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Fuse, Scan, Take, Zip}; +pub use stream::{Fuse, Scan, Stream, Take, Zip}; mod empty; mod once; mod repeat; -pub(crate) mod stream; - -cfg_if! { - if #[cfg(feature = "docs")] { - pub use stream::Stream; - } else { - pub use futures_core::stream::Stream; - } -} +mod stream; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index cd44788..222022b 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -24,9 +24,9 @@ impl Scan { impl Unpin for Scan {} -impl Stream for Scan +impl futures_core::stream::Stream for Scan where - S: crate::stream::Stream, + S: Stream, F: FnMut(&mut St, S::Item) -> Option, { type Item = B; diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 706dbfa..05f9967 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -5,13 +5,13 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; /// An iterator that iterates two other iterators simultaneously. -pub struct Zip { +pub struct Zip { item_slot: Option, first: A, second: B, } -impl fmt::Debug for Zip { +impl fmt::Debug for Zip { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { fmt.debug_struct("Zip") .field("first", &self.first) @@ -20,9 +20,9 @@ impl fmt::Debug for Zip { } } -impl Unpin for Zip {} +impl Unpin for Zip {} -impl Zip { +impl Zip { pub(crate) fn new(first: A, second: B) -> Self { Zip { item_slot: None, From 53ce30ae6655c1dc107a171a4364cc4055493f26 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 21 Sep 2019 15:17:49 +0200 Subject: [PATCH 174/194] Fix async_std imports in metadata.rs --- src/fs/metadata.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 6bb9993..2c9e41e 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -70,7 +70,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{:?}", metadata.file_type()); @@ -90,7 +90,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata(".").await?; /// println!("{:?}", metadata.is_dir()); @@ -110,7 +110,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{:?}", metadata.is_file()); @@ -128,7 +128,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{}", metadata.len()); @@ -146,7 +146,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{:?}", metadata.permissions()); @@ -169,7 +169,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{:?}", metadata.modified()); @@ -192,7 +192,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{:?}", metadata.accessed()); @@ -215,7 +215,7 @@ cfg_if! { /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { /// # - /// use std::fs; + /// use async_std::fs; /// /// let metadata = fs::metadata("a.txt").await?; /// println!("{:?}", metadata.created()); From a97d26ca13d8199440e15d8da954b611f68b7563 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 21 Sep 2019 15:22:50 +0200 Subject: [PATCH 175/194] Fix imports in the book --- docs/src/concepts/futures.md | 3 +-- docs/src/concepts/tasks.md | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md index 02a37ba..a23ec07 100644 --- a/docs/src/concepts/futures.md +++ b/docs/src/concepts/futures.md @@ -112,8 +112,7 @@ While the `Future` trait has existed in Rust for a while, it was inconvenient to ```rust,edition2018 # extern crate async_std; -# use async_std::{fs::File, io::Read}; -# use std::io; +# use async_std::{fs::File, io, prelude::*}; # async fn read_file(path: &str) -> Result { let mut file = File::open(path).await?; diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index 0711c12..15bb82f 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -6,7 +6,7 @@ In `async-std`, the [`tasks`][tasks] module is responsible for this. The simples ```rust,edition2018 # extern crate async_std; -use async_std::{io, task, fs::File, io::Read}; +use async_std::{fs::File, io, prelude::*, task}; async fn read_file(path: &str) -> Result { let mut file = File::open(path).await?; @@ -33,8 +33,7 @@ This asks the runtime baked into `async_std` to execute the code that reads a fi ```rust,edition2018 # extern crate async_std; -# use async_std::{fs::File, io::Read, task}; -# use std::io; +# use async_std::{fs::File, prelude::*, task}; # # async fn read_file(path: &str) -> Result { # let mut file = File::open(path).await?; From 93463e8df362d84caee872224cf734a49e1f074e Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 16:34:51 +0300 Subject: [PATCH 176/194] export SkipWhile type --- src/stream/stream/mod.rs | 2 +- src/stream/stream/skip_while.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 5444312..6aa92c7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -39,6 +39,7 @@ mod zip; pub use fuse::Fuse; pub use scan::Scan; +pub use skip_while::SkipWhile; pub use take::Take; pub use zip::Zip; @@ -52,7 +53,6 @@ use fold::FoldFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; -use skip_while::SkipWhile; use std::cmp::Ordering; use std::marker::PhantomData; diff --git a/src/stream/stream/skip_while.rs b/src/stream/stream/skip_while.rs index 59f564a..a54b051 100644 --- a/src/stream/stream/skip_while.rs +++ b/src/stream/stream/skip_while.rs @@ -4,8 +4,8 @@ use std::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] +/// A stream to skip elements of another stream based on a predicate. +#[derive(Debug)] pub struct SkipWhile { stream: S, predicate: Option

, From 75da138696b495b65775aab356ad8146f1c2b8e8 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 16:37:30 +0300 Subject: [PATCH 177/194] export Skip type --- src/stream/stream/mod.rs | 2 +- src/stream/stream/skip.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 5f4010c..6444651 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -39,6 +39,7 @@ mod zip; pub use fuse::Fuse; pub use scan::Scan; +use skip::Skip; pub use take::Take; pub use zip::Zip; @@ -52,7 +53,6 @@ use fold::FoldFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; -use skip::Skip; use std::cmp::Ordering; use std::marker::PhantomData; diff --git a/src/stream/stream/skip.rs b/src/stream/stream/skip.rs index 09f5cab..8a2d966 100644 --- a/src/stream/stream/skip.rs +++ b/src/stream/stream/skip.rs @@ -3,8 +3,8 @@ use std::task::{Context, Poll}; use crate::stream::Stream; -#[doc(hidden)] -#[allow(missing_debug_implementations)] +/// A stream to skip first n elements of another stream. +#[derive(Debug)] pub struct Skip { stream: S, n: usize, From e430851bc400f6bf3c5dc4562c44ae0a012e23f2 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 16:40:01 +0300 Subject: [PATCH 178/194] export Filter type --- src/stream/stream/filter.rs | 4 ++-- src/stream/stream/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 135d75d..68211f7 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -4,8 +4,8 @@ use std::pin::Pin; use crate::stream::Stream; use crate::task::{Context, Poll}; -#[doc(hidden)] -#[allow(missing_debug_implementations)] +/// A stream to filter elements of another stream with a predicate. +#[derive(Debug)] pub struct Filter { stream: S, predicate: P, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 195f2eb..66645ce 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -37,6 +37,7 @@ mod scan; mod take; mod zip; +pub use filter::Filter; pub use fuse::Fuse; pub use scan::Scan; pub use take::Take; @@ -45,7 +46,6 @@ pub use zip::Zip; use all::AllFuture; use any::AnyFuture; use enumerate::Enumerate; -use filter::Filter; use filter_map::FilterMap; use find::FindFuture; use find_map::FindMapFuture; From fdd81e1b2a04d174fb8f4a0f1f7e170dcffa501d Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 16:40:58 +0300 Subject: [PATCH 179/194] Actually export Skip --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 6444651..6284062 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -39,7 +39,7 @@ mod zip; pub use fuse::Fuse; pub use scan::Scan; -use skip::Skip; +pub use skip::Skip; pub use take::Take; pub use zip::Zip; From e74c0cec1f3dd84e2857d12109b7e6d234386fdc Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 17:37:25 +0300 Subject: [PATCH 180/194] adds stream::step_by combinator --- src/stream/stream/mod.rs | 36 ++++++++++++++++++++++++++ src/stream/stream/step_by.rs | 50 ++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 src/stream/stream/step_by.rs diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 429241d..754dc19 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -35,6 +35,7 @@ mod next; mod nth; mod scan; mod skip; +mod step_by; mod take; mod zip; @@ -42,6 +43,7 @@ pub use filter::Filter; pub use fuse::Fuse; pub use scan::Scan; pub use skip::Skip; +pub use step_by::StepBy; pub use take::Take; pub use zip::Zip; @@ -228,6 +230,40 @@ pub trait Stream { } } + /// Creates a stream that yields each `step`th element. + /// + /// # Panics + /// + /// This method will panic if the given step is `0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); + /// let mut stepped = s.step_by(2); + /// + /// assert_eq!(stepped.next().await, Some(0)); + /// assert_eq!(stepped.next().await, Some(2)); + /// assert_eq!(stepped.next().await, Some(4)); + /// assert_eq!(stepped.next().await, None); + /// + /// # + /// # }) } + /// ``` + fn step_by(self, step: usize) -> StepBy + where + Self: Sized, + { + StepBy::new(self, step) + } + /// Creates a stream that gives the current element's count as well as the next value. /// /// # Overflow behaviour. diff --git a/src/stream/stream/step_by.rs b/src/stream/stream/step_by.rs new file mode 100644 index 0000000..f84feec --- /dev/null +++ b/src/stream/stream/step_by.rs @@ -0,0 +1,50 @@ +use std::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that steps a given amount of elements of another stream. +#[derive(Debug)] +pub struct StepBy { + stream: S, + step: usize, + i: usize, +} + +impl StepBy { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(step: usize); + pin_utils::unsafe_unpinned!(i: usize); + + pub(crate) fn new(stream: S, step: usize) -> Self { + StepBy { + stream, + step: step.checked_sub(1).unwrap(), + i: 0, + } + } +} + +impl Stream for StepBy +where + S: Stream, +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + loop { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => match self.i { + 0 => { + *self.as_mut().i() = self.step; + return Poll::Ready(Some(v)); + } + _ => *self.as_mut().i() -= 1, + }, + None => return Poll::Ready(None), + } + } + } +} From bf7121d2d4ee2cb2e2749c6ae42f856f6e7fdac3 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sat, 21 Sep 2019 18:18:29 +0300 Subject: [PATCH 181/194] adds stream::inspect combinator --- src/stream/stream/inspect.rs | 43 ++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 33 +++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 src/stream/stream/inspect.rs diff --git a/src/stream/stream/inspect.rs b/src/stream/stream/inspect.rs new file mode 100644 index 0000000..e63b584 --- /dev/null +++ b/src/stream/stream/inspect.rs @@ -0,0 +1,43 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// A stream that does something with each element of another stream. +#[derive(Debug)] +pub struct Inspect { + stream: S, + f: F, + __t: PhantomData, +} + +impl Inspect { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(f: F); + + pub(super) fn new(stream: S, f: F) -> Self { + Inspect { + stream, + f, + __t: PhantomData, + } + } +} + +impl Stream for Inspect +where + S: Stream, + F: FnMut(&S::Item), +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + Poll::Ready(next.and_then(|x| { + (self.as_mut().f())(&x); + Some(x) + })) + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d79605e..e4c29bc 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -30,6 +30,7 @@ mod find; mod find_map; mod fold; mod fuse; +mod inspect; mod min_by; mod next; mod nth; @@ -41,6 +42,7 @@ mod zip; pub use filter::Filter; pub use fuse::Fuse; +pub use inspect::Inspect; pub use scan::Scan; pub use skip::Skip; pub use skip_while::SkipWhile; @@ -260,6 +262,37 @@ pub trait Stream { Enumerate::new(self) } + /// A combinator that does something with each element in the stream, passing the value on. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let a: VecDeque<_> = vec![1u8, 2, 3, 4, 5].into_iter().collect(); + /// let sum = a + /// .inspect(|x| println!("about to filter {}", x)) + /// .filter(|x| x % 2 == 0) + /// .inspect(|x| println!("made it through filter: {}", x)) + /// .fold(0, |sum, i| sum + i).await; + /// + /// assert_eq!(sum, 6); + /// # + /// # }) } + /// ``` + fn inspect(self, f: F) -> Inspect + where + Self: Sized, + F: FnMut(&Self::Item), + { + Inspect::new(self, f) + } + /// Transforms this `Stream` into a "fused" `Stream` such that after the first time `poll` /// returns `Poll::Ready(None)`, all future calls to `poll` will also return /// `Poll::Ready(None)`. From 89fd473da0eab8eb20b1c36a3e22678b57424337 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 22 Sep 2019 09:51:54 +0300 Subject: [PATCH 182/194] fixes merge artifacts in stream docs --- src/stream/stream/mod.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f126455..65992eb 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -275,6 +275,7 @@ pub trait Stream { /// This combinator does no guarding against overflows. /// /// # Examples + /// /// ``` /// # fn main() { async_std::task::block_on(async { /// # @@ -291,6 +292,7 @@ pub trait Stream { /// /// # /// # }) } + /// ``` fn enumerate(self) -> Enumerate where Self: Sized, @@ -357,6 +359,7 @@ pub trait Stream { done: false, } } + /// Creates a stream that uses a predicate to determine if an element /// should be yeilded. /// @@ -364,7 +367,7 @@ pub trait Stream { /// /// Basic usage: /// - ///``` + /// ``` /// # fn main() { async_std::task::block_on(async { /// # /// use std::collections::VecDeque; @@ -378,6 +381,7 @@ pub trait Stream { /// assert_eq!(s.next().await, None); /// # /// # }) } + /// ``` fn filter

(self, predicate: P) -> Filter where Self: Sized, @@ -415,6 +419,7 @@ pub trait Stream { /// assert_eq!(end, None); /// # /// # }) } + /// ``` fn filter_map(self, f: F) -> FilterMap where Self: Sized, @@ -801,6 +806,11 @@ pub trait Stream { /// /// ## Examples /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); /// let mut skipped = s.skip(2); /// From f2ca3f37a96352ebabf1f88529e9779383ae5c13 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 11:47:39 +0200 Subject: [PATCH 183/194] Fix build errors in docs --- docs/src/concepts/futures.md | 12 ++++++------ docs/src/concepts/tasks.md | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md index a23ec07..05cc040 100644 --- a/docs/src/concepts/futures.md +++ b/docs/src/concepts/futures.md @@ -51,9 +51,9 @@ Remember the talk about "deferred computation" in the intro? That's all it is. I Let's have a look at a simple function, specifically the return value: ```rust,edition2018 -# use std::{fs::File, io::{self, Read}}; +# use std::{fs::File, io, io::prelude::*}; # -fn read_file(path: &str) -> Result { +fn read_file(path: &str) -> io::Result { let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; @@ -69,7 +69,7 @@ But we wanted to abstract over *computation* and let someone else choose how to ```rust,edition2018 # use std::{fs::File, io::{self, Read}}; # -fn read_file(path: &str) -> Result { +fn read_file(path: &str) -> io::Result { let mut file = File::open(path)?; let mut contents = String::new(); file.read_to_string(&mut contents)?; @@ -112,9 +112,9 @@ While the `Future` trait has existed in Rust for a while, it was inconvenient to ```rust,edition2018 # extern crate async_std; -# use async_std::{fs::File, io, prelude::*}; +# use async_std::{fs::File, io, io::prelude::*}; # -async fn read_file(path: &str) -> Result { +async fn read_file(path: &str) -> io::Result { let mut file = File::open(path).await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; @@ -124,7 +124,7 @@ async fn read_file(path: &str) -> Result { Amazingly little difference, right? All we did is label the function `async` and insert 2 special commands: `.await`. -This `async` function sets up a deferred computation. When this function is called, it will produce a `Future>` instead of immediately returning a `Result`. (Or, more precisely, generate a type for you that implements `Future>`.) +This `async` function sets up a deferred computation. When this function is called, it will produce a `Future>` instead of immediately returning a `io::Result`. (Or, more precisely, generate a type for you that implements `Future>`.) ## What does `.await` do? diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index 15bb82f..d4037a3 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -8,7 +8,7 @@ In `async-std`, the [`tasks`][tasks] module is responsible for this. The simples # extern crate async_std; use async_std::{fs::File, io, prelude::*, task}; -async fn read_file(path: &str) -> Result { +async fn read_file(path: &str) -> io::Result { let mut file = File::open(path).await?; let mut contents = String::new(); file.read_to_string(&mut contents).await?; @@ -33,9 +33,9 @@ This asks the runtime baked into `async_std` to execute the code that reads a fi ```rust,edition2018 # extern crate async_std; -# use async_std::{fs::File, prelude::*, task}; +# use async_std::{fs::File, io, prelude::*, task}; # -# async fn read_file(path: &str) -> Result { +# async fn read_file(path: &str) -> io::Result { # let mut file = File::open(path).await?; # let mut contents = String::new(); # file.read_to_string(&mut contents).await?; From 217e435e8e20dcff4852597f22b440d59bf628c5 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 12:03:56 +0200 Subject: [PATCH 184/194] Fix more compilation errors in the book --- docs/src/concepts/futures.md | 2 +- docs/src/tutorial/accept_loop.md | 4 ++-- docs/src/tutorial/connecting_readers_and_writers.md | 3 +-- docs/src/tutorial/handling_disconnection.md | 5 +++-- docs/src/tutorial/implementing_a_client.md | 3 ++- docs/src/tutorial/receiving_messages.md | 10 +++++----- docs/src/tutorial/sending_messages.md | 3 +-- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/src/concepts/futures.md b/docs/src/concepts/futures.md index 05cc040..67db720 100644 --- a/docs/src/concepts/futures.md +++ b/docs/src/concepts/futures.md @@ -67,7 +67,7 @@ Note that this return value talks about the past. The past has a drawback: all d But we wanted to abstract over *computation* and let someone else choose how to run it. That's fundamentally incompatible with looking at the results of previous computation all the time. So, let's find a type that *describes* a computation without running it. Let's look at the function again: ```rust,edition2018 -# use std::{fs::File, io::{self, Read}}; +# use std::{fs::File, io, io::prelude::*}; # fn read_file(path: &str) -> io::Result { let mut file = File::open(path)?; diff --git a/docs/src/tutorial/accept_loop.md b/docs/src/tutorial/accept_loop.md index 96c15ba..50a6b5f 100644 --- a/docs/src/tutorial/accept_loop.md +++ b/docs/src/tutorial/accept_loop.md @@ -29,7 +29,7 @@ Now we can write the server's accept loop: # extern crate async_std; # use async_std::{ # net::{TcpListener, ToSocketAddrs}, -# prelude::Stream, +# prelude::*, # }; # # type Result = std::result::Result>; @@ -66,7 +66,7 @@ Finally, let's add main: # extern crate async_std; # use async_std::{ # net::{TcpListener, ToSocketAddrs}, -# prelude::Stream, +# prelude::*, # task, # }; # diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index d5da471..5f98ac2 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -15,9 +15,8 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined # extern crate futures_channel; # extern crate futures_util; # use async_std::{ -# io::{Write}, # net::TcpStream, -# prelude::{Future, Stream}, +# prelude::*, # task, # }; # use futures_channel::mpsc; diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 27c5052..9d7c572 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -72,7 +72,7 @@ We use the `select` macro for this purpose: # extern crate async_std; # extern crate futures_channel; # extern crate futures_util; -# use async_std::{io::Write, net::TcpStream}; +# use async_std::{net::TcpStream, prelude::*}; use futures_channel::mpsc; use futures_util::{select, FutureExt, StreamExt}; # use std::sync::Arc; @@ -125,8 +125,9 @@ The final code looks like this: # extern crate futures_channel; # extern crate futures_util; use async_std::{ - io::{BufReader, BufRead, Write}, + io::BufReader, net::{TcpListener, TcpStream, ToSocketAddrs}, + prelude::*, task, }; use futures_channel::mpsc; diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index a3ba93a..8eab22f 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -18,8 +18,9 @@ With async, we can just use the `select!` macro. # extern crate async_std; # extern crate futures_util; use async_std::{ - io::{stdin, BufRead, BufReader, Write}, + io::{stdin, BufReader}, net::{TcpStream, ToSocketAddrs}, + prelude::*, task, }; use futures_util::{select, FutureExt, StreamExt}; diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 667cf1c..182e55c 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -10,9 +10,9 @@ We need to: ```rust,edition2018 # extern crate async_std; # use async_std::{ -# io::{BufRead, BufReader}, +# io::BufReader, # net::{TcpListener, TcpStream, ToSocketAddrs}, -# prelude::Stream, +# prelude::*, # task, # }; # @@ -75,9 +75,9 @@ We can "fix" it by waiting for the task to be joined, like this: # #![feature(async_closure)] # extern crate async_std; # use async_std::{ -# io::{BufRead, BufReader}, +# io::BufReader, # net::{TcpListener, TcpStream, ToSocketAddrs}, -# prelude::Stream, +# prelude::*, # task, # }; # @@ -125,7 +125,7 @@ So let's use a helper function for this: # extern crate async_std; # use async_std::{ # io, -# prelude::Future, +# prelude::*, # task, # }; fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index 6fec8c9..028970e 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -16,9 +16,8 @@ if Alice and Charley send two messages to Bob at the same time, Bob will see the # extern crate futures_channel; # extern crate futures_util; # use async_std::{ -# io::Write, # net::TcpStream, -# prelude::Stream, +# prelude::*, # }; use futures_channel::mpsc; // 1 use futures_util::sink::SinkExt; From bfab20da03e903b0d8d08e73d16d77f2b2fb0853 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 12:08:08 +0200 Subject: [PATCH 185/194] Don't re-export ext traits in async_std::io --- src/io/mod.rs | 17 +++++++++-------- src/io/prelude.rs | 8 ++++---- src/prelude.rs | 8 ++++---- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index 301cfa5..8dd1ad1 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -23,34 +23,35 @@ #[doc(inline)] pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; -pub use buf_read::{BufRead, BufReadExt, Lines}; +pub use buf_read::{BufRead, Lines}; pub use buf_reader::BufReader; pub use copy::copy; pub use cursor::Cursor; pub use empty::{empty, Empty}; -pub use read::{Read, ReadExt}; +pub use read::Read; pub use repeat::{repeat, Repeat}; -pub use seek::{Seek, SeekExt}; +pub use seek::Seek; pub use sink::{sink, Sink}; pub use stderr::{stderr, Stderr}; pub use stdin::{stdin, Stdin}; pub use stdout::{stdout, Stdout}; pub use timeout::timeout; -pub use write::{Write, WriteExt}; +pub use write::Write; pub mod prelude; -mod buf_read; +pub(crate) mod buf_read; +pub(crate) mod read; +pub(crate) mod seek; +pub(crate) mod write; + mod buf_reader; mod copy; mod cursor; mod empty; -mod read; mod repeat; -mod seek; mod sink; mod stderr; mod stdin; mod stdout; mod timeout; -mod write; diff --git a/src/io/prelude.rs b/src/io/prelude.rs index eeb0ced..fb1b945 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -18,10 +18,10 @@ pub use crate::io::Seek; pub use crate::io::Write; #[doc(hidden)] -pub use crate::io::BufReadExt as _; +pub use crate::io::buf_read::BufReadExt as _; #[doc(hidden)] -pub use crate::io::ReadExt as _; +pub use crate::io::read::ReadExt as _; #[doc(hidden)] -pub use crate::io::SeekExt as _; +pub use crate::io::seek::SeekExt as _; #[doc(hidden)] -pub use crate::io::WriteExt as _; +pub use crate::io::write::WriteExt as _; diff --git a/src/prelude.rs b/src/prelude.rs index d28ec64..6efd747 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -27,10 +27,10 @@ pub use crate::stream::Stream; pub use crate::task_local; #[doc(hidden)] -pub use crate::io::BufReadExt as _; +pub use crate::io::buf_read::BufReadExt as _; #[doc(hidden)] -pub use crate::io::ReadExt as _; +pub use crate::io::read::ReadExt as _; #[doc(hidden)] -pub use crate::io::SeekExt as _; +pub use crate::io::seek::SeekExt as _; #[doc(hidden)] -pub use crate::io::WriteExt as _; +pub use crate::io::write::WriteExt as _; From 797a6b2d905ebfa3958966454caf9f48952f65b1 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 12:16:35 +0200 Subject: [PATCH 186/194] Add a missing assert in a doc example --- src/task/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/task/mod.rs b/src/task/mod.rs index 1844cca..7424502 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -17,6 +17,7 @@ //! let handle = task::spawn(async { //! 1 + 2 //! }); +//! assert_eq!(handle.await, 3); //! # //! # }) } //! ``` From 0e3c47c3bfd74959b336865c66ef208e77f56060 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 12:41:04 +0200 Subject: [PATCH 187/194] Fix imports in docs --- docs/src/tutorial/handling_disconnection.md | 4 ++-- docs/src/tutorial/implementing_a_client.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 9d7c572..21f67ab 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -74,7 +74,7 @@ We use the `select` macro for this purpose: # extern crate futures_util; # use async_std::{net::TcpStream, prelude::*}; use futures_channel::mpsc; -use futures_util::{select, FutureExt, StreamExt}; +use futures_util::{select, FutureExt}; # use std::sync::Arc; # type Receiver = mpsc::UnboundedReceiver; @@ -131,7 +131,7 @@ use async_std::{ task, }; use futures_channel::mpsc; -use futures_util::{select, FutureExt, SinkExt, StreamExt}; +use futures_util::{select, FutureExt, SinkExt}; use std::{ collections::hash_map::{Entry, HashMap}, future::Future, diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 8eab22f..4510398 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -23,7 +23,7 @@ use async_std::{ prelude::*, task, }; -use futures_util::{select, FutureExt, StreamExt}; +use futures_util::{select, FutureExt}; type Result = std::result::Result>; From d55cfb1da82a247d3a3d52b7a3e957e9d9bae2cd Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 12:44:46 +0200 Subject: [PATCH 188/194] impl FusedStream for Fuse --- src/stream/stream/fuse.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 3541937..13850c5 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -31,3 +31,9 @@ impl futures_core::Stream for Fuse { } } } + +impl futures_core::stream::FusedStream for Fuse { + fn is_terminated(&self) -> bool { + self.done + } +} From 2a2a473889f8a9905016cedb555c76cbcc0c3911 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Sun, 22 Sep 2019 09:46:26 +0300 Subject: [PATCH 189/194] adds stream::chain combinator --- src/stream/stream/chain.rs | 50 ++++++++++++++++++++++++++++++++++++++ src/stream/stream/fuse.rs | 8 +++--- src/stream/stream/mod.rs | 35 ++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 src/stream/stream/chain.rs diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs new file mode 100644 index 0000000..080d9b4 --- /dev/null +++ b/src/stream/stream/chain.rs @@ -0,0 +1,50 @@ +use std::pin::Pin; + +use super::fuse::Fuse; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// Chains two streams one after another. +#[derive(Debug)] +pub struct Chain { + first: Fuse, + second: Fuse, +} + +impl Chain { + pin_utils::unsafe_pinned!(first: Fuse); + pin_utils::unsafe_pinned!(second: Fuse); + + pub(super) fn new(first: S, second: U) -> Self { + Chain { + first: first.fuse(), + second: second.fuse(), + } + } +} + +impl> Stream for Chain { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if !self.first.done { + let next = futures_core::ready!(self.as_mut().first().poll_next(cx)); + if let Some(next) = next { + return Poll::Ready(Some(next)); + } + } + + if !self.second.done { + let next = futures_core::ready!(self.as_mut().second().poll_next(cx)); + if let Some(next) = next { + return Poll::Ready(Some(next)); + } + } + + if self.first.done && self.second.done { + return Poll::Ready(None); + } + + Poll::Pending + } +} diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 3541937..ff5bdab 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,5 +1,7 @@ use std::pin::Pin; -use std::task::{Context, Poll}; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; /// A `Stream` that is permanently closed once a single call to `poll` results in /// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. @@ -11,12 +13,12 @@ pub struct Fuse { impl Unpin for Fuse {} -impl Fuse { +impl Fuse { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(done: bool); } -impl futures_core::Stream for Fuse { +impl Stream for Fuse { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 65992eb..0979d82 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -23,6 +23,7 @@ mod all; mod any; +mod chain; mod enumerate; mod filter; mod filter_map; @@ -41,6 +42,7 @@ mod step_by; mod take; mod zip; +pub use chain::Chain; pub use filter::Filter; pub use fuse::Fuse; pub use inspect::Inspect; @@ -268,6 +270,39 @@ pub trait Stream { StepBy::new(self, step) } + /// Takes two streams and creates a new stream over both in sequence. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let first: VecDeque<_> = vec![0u8, 1].into_iter().collect(); + /// let second: VecDeque<_> = vec![2, 3].into_iter().collect(); + /// let mut c = first.chain(second); + /// + /// assert_eq!(c.next().await, Some(0)); + /// assert_eq!(c.next().await, Some(1)); + /// assert_eq!(c.next().await, Some(2)); + /// assert_eq!(c.next().await, Some(3)); + /// assert_eq!(c.next().await, None); + /// + /// # + /// # }) } + /// ``` + fn chain(self, other: U) -> Chain + where + Self: Sized, + U: Stream + Sized, + { + Chain::new(self, other) + } + /// Creates a stream that gives the current element's count as well as the next value. /// /// # Overflow behaviour. From 17534cfffc72f6022d8ffd3bb0e709eba2734acc Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 15:19:54 +0200 Subject: [PATCH 190/194] Fuse next() future --- docs/src/tutorial/handling_disconnection.md | 12 ++++++------ src/stream/stream/fuse.rs | 6 ------ 2 files changed, 6 insertions(+), 12 deletions(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 21f67ab..e1eb8e9 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -94,11 +94,11 @@ async fn client_writer( let mut shutdown = shutdown.fuse(); loop { // 2 select! { - msg = messages.next() => match msg { + msg = messages.next().fuse() => match msg { Some(msg) => stream.write_all(msg.as_bytes()).await?, None => break, }, - void = shutdown.next() => match void { + void = shutdown.next().fuse() => match void { Some(void) => match void {}, // 3 None => break, } @@ -210,11 +210,11 @@ async fn client_writer( let mut shutdown = shutdown.fuse(); loop { select! { - msg = messages.next() => match msg { + msg = messages.next().fuse() => match msg { Some(msg) => stream.write_all(msg.as_bytes()).await?, None => break, }, - void = shutdown.next() => match void { + void = shutdown.next().fuse() => match void { Some(void) => match void {}, None => break, } @@ -244,11 +244,11 @@ async fn broker(events: Receiver) { let mut events = events.fuse(); loop { let event = select! { - event = events.next() => match event { + event = events.next().fuse() => match event { None => break, // 2 Some(event) => event, }, - disconnect = disconnect_receiver.next() => { + disconnect = disconnect_receiver.next().fuse() => { let (name, _pending_messages) = disconnect.unwrap(); // 3 assert!(peers.remove(&name).is_some()); continue; diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 13850c5..3541937 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -31,9 +31,3 @@ impl futures_core::Stream for Fuse { } } } - -impl futures_core::stream::FusedStream for Fuse { - fn is_terminated(&self) -> bool { - self.done - } -} From 85b80cfe9a0a20a6e4d9091ad48027dcbf7ff656 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 15:35:53 +0200 Subject: [PATCH 191/194] Fuse futures in select! --- docs/src/tutorial/implementing_a_client.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/tutorial/implementing_a_client.md b/docs/src/tutorial/implementing_a_client.md index 4510398..3aac67f 100644 --- a/docs/src/tutorial/implementing_a_client.md +++ b/docs/src/tutorial/implementing_a_client.md @@ -39,14 +39,14 @@ async fn try_run(addr: impl ToSocketAddrs) -> Result<()> { let mut lines_from_stdin = BufReader::new(stdin()).lines().fuse(); // 2 loop { select! { // 3 - line = lines_from_server.next() => match line { + line = lines_from_server.next().fuse() => match line { Some(line) => { let line = line?; println!("{}", line); }, None => break, }, - line = lines_from_stdin.next() => match line { + line = lines_from_stdin.next().fuse() => match line { Some(line) => { let line = line?; writer.write_all(line.as_bytes()).await?; From 73d7fea93774ab941953ff94df2e13becc84703c Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 16:34:07 +0200 Subject: [PATCH 192/194] Re-export Stream from futures --- src/fs/read_dir.rs | 3 +- src/io/buf_read/lines.rs | 3 +- src/os/unix/net/listener.rs | 3 +- src/prelude.rs | 2 + src/stream/empty.rs | 3 +- src/stream/into_stream.rs | 2 +- src/stream/mod.rs | 5 +- src/stream/once.rs | 3 +- src/stream/repeat.rs | 3 +- src/stream/stream/chain.rs | 2 +- src/stream/stream/enumerate.rs | 2 +- src/stream/stream/filter.rs | 2 +- src/stream/stream/filter_map.rs | 2 +- src/stream/stream/mod.rs | 1658 +++++++++++++++++-------------- src/stream/stream/scan.rs | 2 +- src/stream/stream/take.rs | 2 +- src/stream/stream/zip.rs | 2 +- 17 files changed, 951 insertions(+), 748 deletions(-) diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index ea0caec..c6f80fe 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -4,6 +4,7 @@ use std::pin::Pin; use crate::fs::DirEntry; use crate::future::Future; use crate::io; +use crate::stream::Stream; use crate::task::{blocking, Context, Poll}; /// Returns a stream of entries in a directory. @@ -80,7 +81,7 @@ impl ReadDir { } } -impl futures_core::stream::Stream for ReadDir { +impl Stream for ReadDir { type Item = io::Result; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index a761eb4..6cb4a07 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -4,6 +4,7 @@ use std::str; use super::read_until_internal; use crate::io::{self, BufRead}; +use crate::stream::Stream; use crate::task::{Context, Poll}; /// A stream of lines in a byte stream. @@ -23,7 +24,7 @@ pub struct Lines { pub(crate) read: usize, } -impl futures_core::stream::Stream for Lines { +impl Stream for Lines { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 78142a4..ed4f1f4 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -12,6 +12,7 @@ use crate::future::{self, Future}; use crate::io; use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; +use crate::stream::Stream; use crate::task::{blocking, Context, Poll}; /// A Unix domain socket server, listening for connections. @@ -185,7 +186,7 @@ impl fmt::Debug for UnixListener { #[derive(Debug)] pub struct Incoming<'a>(&'a UnixListener); -impl futures_core::stream::Stream for Incoming<'_> { +impl Stream for Incoming<'_> { type Item = io::Result; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/prelude.rs b/src/prelude.rs index 6efd747..4c26a28 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -34,3 +34,5 @@ pub use crate::io::read::ReadExt as _; pub use crate::io::seek::SeekExt as _; #[doc(hidden)] pub use crate::io::write::WriteExt as _; +#[doc(hidden)] +pub use crate::stream::stream::StreamExt as _; diff --git a/src/stream/empty.rs b/src/stream/empty.rs index f4cf552..c9deea8 100644 --- a/src/stream/empty.rs +++ b/src/stream/empty.rs @@ -1,6 +1,7 @@ use std::marker::PhantomData; use std::pin::Pin; +use crate::stream::Stream; use crate::task::{Context, Poll}; /// Creates a stream that doesn't yield any items. @@ -35,7 +36,7 @@ pub struct Empty { _marker: PhantomData, } -impl futures_core::stream::Stream for Empty { +impl Stream for Empty { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 7d1be4a..9233181 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -1,4 +1,4 @@ -use futures_core::stream::Stream; +use crate::stream::Stream; /// Conversion into a `Stream`. /// diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 5e17aa5..c5da997 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -26,12 +26,13 @@ use cfg_if::cfg_if; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Fuse, Scan, Stream, Take, Zip}; +pub use stream::{Chain, Filter, Fuse, Inspect, Scan, Skip, SkipWhile, StepBy, Stream, Take, Zip}; + +pub(crate) mod stream; mod empty; mod once; mod repeat; -mod stream; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { diff --git a/src/stream/once.rs b/src/stream/once.rs index 09811d8..133a155 100644 --- a/src/stream/once.rs +++ b/src/stream/once.rs @@ -1,5 +1,6 @@ use std::pin::Pin; +use crate::stream::Stream; use crate::task::{Context, Poll}; /// Creates a stream that yields a single item. @@ -33,7 +34,7 @@ pub struct Once { value: Option, } -impl futures_core::stream::Stream for Once { +impl Stream for Once { type Item = T; fn poll_next(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { diff --git a/src/stream/repeat.rs b/src/stream/repeat.rs index c2ee97a..1a6da41 100644 --- a/src/stream/repeat.rs +++ b/src/stream/repeat.rs @@ -1,5 +1,6 @@ use std::pin::Pin; +use crate::stream::Stream; use crate::task::{Context, Poll}; /// Creates a stream that yields the same item repeatedly. @@ -36,7 +37,7 @@ pub struct Repeat { item: T, } -impl futures_core::stream::Stream for Repeat { +impl Stream for Repeat { type Item = T; fn poll_next(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { diff --git a/src/stream/stream/chain.rs b/src/stream/stream/chain.rs index 080d9b4..2693382 100644 --- a/src/stream/stream/chain.rs +++ b/src/stream/stream/chain.rs @@ -1,7 +1,7 @@ use std::pin::Pin; use super::fuse::Fuse; -use crate::stream::Stream; +use crate::prelude::*; use crate::task::{Context, Poll}; /// Chains two streams one after another. diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index a29fc80..7d5a3d6 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -19,7 +19,7 @@ impl Enumerate { } } -impl futures_core::stream::Stream for Enumerate +impl Stream for Enumerate where S: Stream, { diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs index 68211f7..3fd5453 100644 --- a/src/stream/stream/filter.rs +++ b/src/stream/stream/filter.rs @@ -25,7 +25,7 @@ impl Filter { } } -impl futures_core::stream::Stream for Filter +impl Stream for Filter where S: Stream, P: FnMut(&S::Item) -> bool, diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 2f27515..756efff 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -27,7 +27,7 @@ impl FilterMap { } } -impl futures_core::stream::Stream for FilterMap +impl Stream for FilterMap where S: Stream, F: FnMut(S::Item) -> Option, diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index d74822a..b209f44 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -42,17 +42,6 @@ mod step_by; mod take; mod zip; -pub use chain::Chain; -pub use filter::Filter; -pub use fuse::Fuse; -pub use inspect::Inspect; -pub use scan::Scan; -pub use skip::Skip; -pub use skip_while::SkipWhile; -pub use step_by::StepBy; -pub use take::Take; -pub use zip::Zip; - use all::AllFuture; use any::AnyFuture; use enumerate::Enumerate; @@ -64,41 +53,24 @@ use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; +pub use chain::Chain; +pub use filter::Filter; +pub use fuse::Fuse; +pub use inspect::Inspect; +pub use scan::Scan; +pub use skip::Skip; +pub use skip_while::SkipWhile; +pub use step_by::StepBy; +pub use take::Take; +pub use zip::Zip; + use std::cmp::Ordering; use std::marker::PhantomData; use std::pin::Pin; -use std::task::{Context, Poll}; use cfg_if::cfg_if; -cfg_if! { - if #[cfg(feature = "unstable")] { - use crate::future::Future; - } -} - -cfg_if! { - if #[cfg(feature = "docs")] { - #[doc(hidden)] - pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); - - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => (ImplFuture<$a, $o>); - ($a:lifetime, $f:tt, $o:ty, $t1:ty) => (ImplFuture<$a, $o>); - ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => (ImplFuture<$a, $o>); - ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty, $t3:ty) => (ImplFuture<$a, $o>); - ($f:ty, $o:ty) => (ImplFuture<'static, $o>); - } - } else { - macro_rules! ret { - ($a:lifetime, $f:tt, $o:ty) => ($f<$a, Self>); - ($a:lifetime, $f:tt, $o:ty, $t1:ty) => ($f<$a, Self, $t1>); - ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty) => ($f<$a, Self, $t1, $t2>); - ($a:lifetime, $f:tt, $o:ty, $t1:ty, $t2:ty, $t3:ty) => ($f<$a, Self, $t1, $t2, $t3>); - ($f:ty, $o:ty) => ($f); - } - } -} +use crate::future::Future; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { @@ -106,124 +78,916 @@ cfg_if! { } } -/// An asynchronous stream of values. -/// -/// This trait is a re-export of [`futures::stream::Stream`] and is an async version of -/// [`std::iter::Iterator`]. -/// -/// The [provided methods] do not really exist in the trait itself, but they become available when -/// the prelude is imported: -/// -/// ``` -/// # #[allow(unused_imports)] -/// use async_std::prelude::*; -/// ``` -/// -/// [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html -/// [`futures::stream::Stream`]: -/// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html -/// [provided methods]: #provided-methods -pub trait Stream { - /// The type of items yielded by this stream. - type Item; +cfg_if! { + if #[cfg(feature = "docs")] { + use std::ops::{Deref, DerefMut}; - /// Attempts to receive the next item from the stream. - /// - /// There are several possible return values: - /// - /// * `Poll::Pending` means this stream's next value is not ready yet. - /// * `Poll::Ready(None)` means this stream has been exhausted. - /// * `Poll::Ready(Some(item))` means `item` was received out of the stream. - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::pin::Pin; - /// - /// use async_std::prelude::*; - /// use async_std::stream; - /// use async_std::task::{Context, Poll}; - /// - /// fn increment(s: impl Stream + Unpin) -> impl Stream + Unpin { - /// struct Increment(S); - /// - /// impl + Unpin> Stream for Increment { - /// type Item = S::Item; - /// - /// fn poll_next( - /// mut self: Pin<&mut Self>, - /// cx: &mut Context<'_>, - /// ) -> Poll> { - /// match Pin::new(&mut self.0).poll_next(cx) { - /// Poll::Pending => Poll::Pending, - /// Poll::Ready(None) => Poll::Ready(None), - /// Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), - /// } - /// } - /// } - /// - /// Increment(s) - /// } - /// - /// let mut s = increment(stream::once(7)); - /// - /// assert_eq!(s.next().await, Some(8)); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + use crate::task::{Context, Poll}; - /// Advances the stream and returns the next value. - /// - /// Returns [`None`] when iteration is finished. Individual stream implementations may - /// choose to resume iteration, and so calling `next()` again may or may not eventually - /// start returning more values. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::once(7); - /// - /// assert_eq!(s.next().await, Some(7)); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` - fn next(&mut self) -> ret!('_, NextFuture, Option) + #[doc(hidden)] + pub struct ImplFuture<'a, T>(std::marker::PhantomData<&'a T>); + + /// An asynchronous stream of values. + /// + /// This trait is a re-export of [`futures::stream::Stream`] and is an async version of + /// [`std::iter::Iterator`]. + /// + /// The [provided methods] do not really exist in the trait itself, but they become + /// available when the prelude is imported: + /// + /// ``` + /// # #[allow(unused_imports)] + /// use async_std::prelude::*; + /// ``` + /// + /// [`std::iter::Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html + /// [`futures::stream::Stream`]: + /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/stream/trait.Stream.html + /// [provided methods]: #provided-methods + pub trait Stream { + /// The type of items yielded by this stream. + type Item; + + /// Attempts to receive the next item from the stream. + /// + /// There are several possible return values: + /// + /// * `Poll::Pending` means this stream's next value is not ready yet. + /// * `Poll::Ready(None)` means this stream has been exhausted. + /// * `Poll::Ready(Some(item))` means `item` was received out of the stream. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::pin::Pin; + /// + /// use async_std::prelude::*; + /// use async_std::stream; + /// use async_std::task::{Context, Poll}; + /// + /// fn increment( + /// s: impl Stream + Unpin, + /// ) -> impl Stream + Unpin { + /// struct Increment(S); + /// + /// impl + Unpin> Stream for Increment { + /// type Item = S::Item; + /// + /// fn poll_next( + /// mut self: Pin<&mut Self>, + /// cx: &mut Context<'_>, + /// ) -> Poll> { + /// match Pin::new(&mut self.0).poll_next(cx) { + /// Poll::Pending => Poll::Pending, + /// Poll::Ready(None) => Poll::Ready(None), + /// Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), + /// } + /// } + /// } + /// + /// Increment(s) + /// } + /// + /// let mut s = increment(stream::once(7)); + /// + /// assert_eq!(s.next().await, Some(8)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + + /// Advances the stream and returns the next value. + /// + /// Returns [`None`] when iteration is finished. Individual stream implementations may + /// choose to resume iteration, and so calling `next()` again may or may not eventually + /// start returning more values. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::once(7); + /// + /// assert_eq!(s.next().await, Some(7)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn next(&mut self) -> ImplFuture<'_, Option> + where + Self: Unpin, + { + unreachable!() + } + + /// Creates a stream that yields its first `n` elements. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat(9).take(3); + /// + /// while let Some(v) = s.next().await { + /// assert_eq!(v, 9); + /// } + /// # + /// # }) } + /// ``` + fn take(self, n: usize) -> Take + where + Self: Sized, + { + unreachable!() + } + + /// Creates a stream that yields each `step`th element. + /// + /// # Panics + /// + /// This method will panic if the given step is `0`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); + /// let mut stepped = s.step_by(2); + /// + /// assert_eq!(stepped.next().await, Some(0)); + /// assert_eq!(stepped.next().await, Some(2)); + /// assert_eq!(stepped.next().await, Some(4)); + /// assert_eq!(stepped.next().await, None); + /// + /// # + /// # }) } + /// ``` + fn step_by(self, step: usize) -> StepBy + where + Self: Sized, + { + unreachable!() + } + + /// Takes two streams and creates a new stream over both in sequence. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let first: VecDeque<_> = vec![0u8, 1].into_iter().collect(); + /// let second: VecDeque<_> = vec![2, 3].into_iter().collect(); + /// let mut c = first.chain(second); + /// + /// assert_eq!(c.next().await, Some(0)); + /// assert_eq!(c.next().await, Some(1)); + /// assert_eq!(c.next().await, Some(2)); + /// assert_eq!(c.next().await, Some(3)); + /// assert_eq!(c.next().await, None); + /// + /// # + /// # }) } + /// ``` + fn chain(self, other: U) -> Chain + where + Self: Sized, + U: Stream + Sized, + { + unreachable!() + } + + /// Creates a stream that gives the current element's count as well as the next value. + /// + /// # Overflow behaviour. + /// + /// This combinator does no guarding against overflows. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let s: VecDeque<_> = vec!['a', 'b', 'c'].into_iter().collect(); + /// let mut s = s.enumerate(); + /// + /// assert_eq!(s.next().await, Some((0, 'a'))); + /// assert_eq!(s.next().await, Some((1, 'b'))); + /// assert_eq!(s.next().await, Some((2, 'c'))); + /// assert_eq!(s.next().await, None); + /// + /// # + /// # }) } + /// ``` + fn enumerate(self) -> Enumerate + where + Self: Sized, + { + unreachable!() + } + + /// A combinator that does something with each element in the stream, passing the value + /// on. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let a: VecDeque<_> = vec![1u8, 2, 3, 4, 5].into_iter().collect(); + /// let sum = a + /// .inspect(|x| println!("about to filter {}", x)) + /// .filter(|x| x % 2 == 0) + /// .inspect(|x| println!("made it through filter: {}", x)) + /// .fold(0, |sum, i| sum + i).await; + /// + /// assert_eq!(sum, 6); + /// # + /// # }) } + /// ``` + fn inspect(self, f: F) -> Inspect + where + Self: Sized, + F: FnMut(&Self::Item), + { + unreachable!() + } + + /// Transforms this `Stream` into a "fused" `Stream` such that after the first time + /// `poll` returns `Poll::Ready(None)`, all future calls to `poll` will also return + /// `Poll::Ready(None)`. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::once(1).fuse(); + /// assert_eq!(s.next().await, Some(1)); + /// assert_eq!(s.next().await, None); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn fuse(self) -> Fuse + where + Self: Sized, + { + unreachable!() + } + + /// Creates a stream that uses a predicate to determine if an element should be yielded. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); + /// let mut s = s.filter(|i| i % 2 == 0); + /// + /// assert_eq!(s.next().await, Some(2)); + /// assert_eq!(s.next().await, Some(4)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn filter

(self, predicate: P) -> Filter + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + unreachable!() + } + + /// Both filters and maps a stream. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque<&str> = vec!["1", "lol", "3", "NaN", "5"].into_iter().collect(); + /// + /// let mut parsed = s.filter_map(|a| a.parse::().ok()); + /// + /// let one = parsed.next().await; + /// assert_eq!(one, Some(1)); + /// + /// let three = parsed.next().await; + /// assert_eq!(three, Some(3)); + /// + /// let five = parsed.next().await; + /// assert_eq!(five, Some(5)); + /// + /// let end = parsed.next().await; + /// assert_eq!(end, None); + /// # + /// # }) } + /// ``` + fn filter_map(self, f: F) -> FilterMap + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + unreachable!() + } + + /// Returns the element that gives the minimum value with respect to the + /// specified comparison function. If several elements are equally minimum, + /// the first element is returned. If the stream is empty, `None` is returned. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let min = Stream::min_by(s.clone(), |x, y| x.cmp(y)).await; + /// assert_eq!(min, Some(1)); + /// + /// let min = Stream::min_by(s, |x, y| y.cmp(x)).await; + /// assert_eq!(min, Some(3)); + /// + /// let min = Stream::min_by(VecDeque::::new(), |x, y| x.cmp(y)).await; + /// assert_eq!(min, None); + /// # + /// # }) } + /// ``` + fn min_by(self, compare: F) -> ImplFuture<'static, Option> + where + Self: Sized, + F: FnMut(&Self::Item, &Self::Item) -> Ordering, + { + unreachable!() + } + + /// Returns the nth element of the stream. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let second = s.nth(1).await; + /// assert_eq!(second, Some(2)); + /// # + /// # }) } + /// ``` + /// Calling `nth()` multiple times: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let second = s.nth(0).await; + /// assert_eq!(second, Some(1)); + /// + /// let second = s.nth(0).await; + /// assert_eq!(second, Some(2)); + /// # + /// # }) } + /// ``` + /// Returning `None` if the stream finished before returning `n` elements: + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// + /// let fourth = s.nth(4).await; + /// assert_eq!(fourth, None); + /// # + /// # }) } + /// ``` + fn nth(&mut self, n: usize) -> ImplFuture<'_, Option> + where + Self: Sized, + { + unreachable!() + } + + /// Tests if every element of the stream matches a predicate. + /// + /// `all()` takes a closure that returns `true` or `false`. It applies + /// this closure to each element of the stream, and if they all return + /// `true`, then so does `all()`. If any of them return `false`, it + /// returns `false`. + /// + /// `all()` is short-circuiting; in other words, it will stop processing + /// as soon as it finds a `false`, given that no matter what else happens, + /// the result will also be `false`. + /// + /// An empty stream returns `true`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat::(42).take(3); + /// assert!(s.all(|x| x == 42).await); + /// + /// # + /// # }) } + /// ``` + /// + /// Empty stream: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::empty::(); + /// assert!(s.all(|_| false).await); + /// # + /// # }) } + /// ``` + #[inline] + fn all(&mut self, f: F) -> ImplFuture<'_, bool> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> bool, + { + unreachable!() + } + + /// Searches for an element in a stream that satisfies a predicate. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let res = s.find(|x| *x == 2).await; + /// assert_eq!(res, Some(2)); + /// # + /// # }) } + /// ``` + /// + /// Resuming after a first find: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let res = s.find(|x| *x == 2).await; + /// assert_eq!(res, Some(2)); + /// + /// let next = s.next().await; + /// assert_eq!(next, Some(3)); + /// # + /// # }) } + /// ``` + fn find

(&mut self, p: P) -> ImplFuture<'_, Option> + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + unreachable!() + } + + /// Applies function to the elements of stream and returns the first non-none result. + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let mut s: VecDeque<&str> = vec!["lol", "NaN", "2", "5"].into_iter().collect(); + /// let first_number = s.find_map(|s| s.parse().ok()).await; + /// + /// assert_eq!(first_number, Some(2)); + /// # + /// # }) } + /// ``` + fn find_map(&mut self, f: F) -> ImplFuture<'_, Option> + where + Self: Sized, + F: FnMut(Self::Item) -> Option, + { + unreachable!() + } + + /// A combinator that applies a function to every element in a stream + /// producing a single, final value. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let sum = s.fold(0, |acc, x| acc + x).await; + /// + /// assert_eq!(sum, 6); + /// # + /// # }) } + /// ``` + fn fold(self, init: B, f: F) -> ImplFuture<'static, B> + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + unreachable!() + } + + /// Tests if any element of the stream matches a predicate. + /// + /// `any()` takes a closure that returns `true` or `false`. It applies + /// this closure to each element of the stream, and if any of them return + /// `true`, then so does `any()`. If they all return `false`, it + /// returns `false`. + /// + /// `any()` is short-circuiting; in other words, it will stop processing + /// as soon as it finds a `true`, given that no matter what else happens, + /// the result will also be `true`. + /// + /// An empty stream returns `false`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat::(42).take(3); + /// assert!(s.any(|x| x == 42).await); + /// # + /// # }) } + /// ``` + /// + /// Empty stream: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::empty::(); + /// assert!(!s.any(|_| false).await); + /// # + /// # }) } + /// ``` + #[inline] + fn any(&mut self, f: F) -> ImplFuture<'_, bool> + where + Self: Unpin + Sized, + F: FnMut(Self::Item) -> bool, + { + unreachable!() + } + + /// A stream adaptor similar to [`fold`] that holds internal state and produces a new + /// stream. + /// + /// [`fold`]: #method.fold + /// + /// `scan()` takes two arguments: an initial value which seeds the internal state, and + /// a closure with two arguments, the first being a mutable reference to the internal + /// state and the second a stream element. The closure can assign to the internal state + /// to share state between iterations. + /// + /// On iteration, the closure will be applied to each element of the stream and the + /// return value from the closure, an `Option`, is yielded by the stream. + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let mut s = s.scan(1, |state, x| { + /// *state = *state * x; + /// Some(-*state) + /// }); + /// + /// assert_eq!(s.next().await, Some(-1)); + /// assert_eq!(s.next().await, Some(-2)); + /// assert_eq!(s.next().await, Some(-6)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + #[inline] + fn scan(self, initial_state: St, f: F) -> Scan + where + Self: Sized, + F: FnMut(&mut St, Self::Item) -> Option, + { + unreachable!() + } + + /// Combinator that `skip`s elements based on a predicate. + /// + /// Takes a closure argument. It will call this closure on every element in + /// the stream and ignore elements until it returns `false`. + /// + /// After `false` is returned, `SkipWhile`'s job is over and all further + /// elements in the strem are yielded. + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let a: VecDeque<_> = vec![-1i32, 0, 1].into_iter().collect(); + /// let mut s = a.skip_while(|x| x.is_negative()); + /// + /// assert_eq!(s.next().await, Some(0)); + /// assert_eq!(s.next().await, Some(1)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn skip_while

(self, predicate: P) -> SkipWhile + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + unreachable!() + } + + /// Creates a combinator that skips the first `n` elements. + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let mut skipped = s.skip(2); + /// + /// assert_eq!(skipped.next().await, Some(3)); + /// assert_eq!(skipped.next().await, None); + /// # + /// # }) } + /// ``` + fn skip(self, n: usize) -> Skip + where + Self: Sized, + { + unreachable!() + } + + /// 'Zips up' two streams into a single stream of pairs. + /// + /// `zip()` returns a new stream that will iterate over two other streams, returning a + /// tuple where the first element comes from the first stream, and the second element + /// comes from the second stream. + /// + /// In other words, it zips two streams together, into a single one. + /// + /// If either stream returns [`None`], [`poll_next`] from the zipped stream will return + /// [`None`]. If the first stream returns [`None`], `zip` will short-circuit and + /// `poll_next` will not be called on the second stream. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`poll_next`]: #tymethod.poll_next + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let l: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let r: VecDeque = vec![4, 5, 6, 7].into_iter().collect(); + /// let mut s = l.zip(r); + /// + /// assert_eq!(s.next().await, Some((1, 4))); + /// assert_eq!(s.next().await, Some((2, 5))); + /// assert_eq!(s.next().await, Some((3, 6))); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + #[inline] + fn zip(self, other: U) -> Zip + where + Self: Sized, + U: Stream, + { + unreachable!() + } + + /// Transforms a stream into a collection. + /// + /// `collect()` can take anything streamable, and turn it into a relevant + /// collection. This is one of the more powerful methods in the async + /// standard library, used in a variety of contexts. + /// + /// The most basic pattern in which `collect()` is used is to turn one + /// collection into another. You take a collection, call [`stream`] on it, + /// do a bunch of transformations, and then `collect()` at the end. + /// + /// Because `collect()` is so general, it can cause problems with type + /// inference. As such, `collect()` is one of the few times you'll see + /// the syntax affectionately known as the 'turbofish': `::<>`. This + /// helps the inference algorithm understand specifically which collection + /// you're trying to collect into. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let s = stream::repeat(9u8).take(3); + /// let buf: Vec = s.collect().await; + /// + /// assert_eq!(buf, vec![9; 3]); + /// + /// // You can also collect streams of Result values + /// // into any collection that implements FromStream + /// let s = stream::repeat(Ok(9)).take(3); + /// // We are using Vec here, but other collections + /// // are supported as well + /// let buf: Result, ()> = s.collect().await; + /// + /// assert_eq!(buf, Ok(vec![9; 3])); + /// + /// // The stream will stop on the first Err and + /// // return that instead + /// let s = stream::repeat(Err(5)).take(3); + /// let buf: Result, u8> = s.collect().await; + /// + /// assert_eq!(buf, Err(5)); + /// # + /// # }) } + /// ``` + /// + /// [`stream`]: trait.Stream.html#tymethod.next + #[cfg(any(feature = "unstable", feature = "docs"))] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] + fn collect<'a, B>(self) -> ImplFuture<'a, B> + where + Self: Sized + 'a, + B: FromStream, + { + unreachable!() + } + } + + impl Stream for Box { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl Stream for &mut S { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl

Stream for Pin

+ where + P: DerefMut + Unpin, +

::Target: Stream, + { + type Item = <

::Target as Stream>::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl Stream for std::collections::VecDeque { + type Item = T; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + + impl Stream for std::panic::AssertUnwindSafe { + type Item = S::Item; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + unreachable!() + } + } + } else { + pub use futures_core::stream::Stream; + } +} + +#[doc(hidden)] +pub trait StreamExt: futures_core::stream::Stream { + fn next(&mut self) -> NextFuture<'_, Self> where Self: Unpin, { NextFuture { stream: self } } - /// Creates a stream that yields its first `n` elements. - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::repeat(9).take(3); - /// - /// while let Some(v) = s.next().await { - /// assert_eq!(v, 9); - /// } - /// # - /// # }) } - /// ``` fn take(self, n: usize) -> Take where Self: Sized, @@ -234,33 +998,6 @@ pub trait Stream { } } - /// Creates a stream that yields each `step`th element. - /// - /// # Panics - /// - /// This method will panic if the given step is `0`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let s: VecDeque<_> = vec![0u8, 1, 2, 3, 4].into_iter().collect(); - /// let mut stepped = s.step_by(2); - /// - /// assert_eq!(stepped.next().await, Some(0)); - /// assert_eq!(stepped.next().await, Some(2)); - /// assert_eq!(stepped.next().await, Some(4)); - /// assert_eq!(stepped.next().await, None); - /// - /// # - /// # }) } - /// ``` fn step_by(self, step: usize) -> StepBy where Self: Sized, @@ -268,31 +1005,6 @@ pub trait Stream { StepBy::new(self, step) } - /// Takes two streams and creates a new stream over both in sequence. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let first: VecDeque<_> = vec![0u8, 1].into_iter().collect(); - /// let second: VecDeque<_> = vec![2, 3].into_iter().collect(); - /// let mut c = first.chain(second); - /// - /// assert_eq!(c.next().await, Some(0)); - /// assert_eq!(c.next().await, Some(1)); - /// assert_eq!(c.next().await, Some(2)); - /// assert_eq!(c.next().await, Some(3)); - /// assert_eq!(c.next().await, None); - /// - /// # - /// # }) } - /// ``` fn chain(self, other: U) -> Chain where Self: Sized, @@ -301,31 +1013,6 @@ pub trait Stream { Chain::new(self, other) } - /// Creates a stream that gives the current element's count as well as the next value. - /// - /// # Overflow behaviour. - /// - /// This combinator does no guarding against overflows. - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let s: VecDeque<_> = vec!['a', 'b', 'c'].into_iter().collect(); - /// let mut s = s.enumerate(); - /// - /// assert_eq!(s.next().await, Some((0, 'a'))); - /// assert_eq!(s.next().await, Some((1, 'b'))); - /// assert_eq!(s.next().await, Some((2, 'c'))); - /// assert_eq!(s.next().await, None); - /// - /// # - /// # }) } - /// ``` fn enumerate(self) -> Enumerate where Self: Sized, @@ -333,29 +1020,6 @@ pub trait Stream { Enumerate::new(self) } - /// A combinator that does something with each element in the stream, passing the value on. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let a: VecDeque<_> = vec![1u8, 2, 3, 4, 5].into_iter().collect(); - /// let sum = a - /// .inspect(|x| println!("about to filter {}", x)) - /// .filter(|x| x % 2 == 0) - /// .inspect(|x| println!("made it through filter: {}", x)) - /// .fold(0, |sum, i| sum + i).await; - /// - /// assert_eq!(sum, 6); - /// # - /// # }) } - /// ``` fn inspect(self, f: F) -> Inspect where Self: Sized, @@ -364,25 +1028,6 @@ pub trait Stream { Inspect::new(self, f) } - /// Transforms this `Stream` into a "fused" `Stream` such that after the first time `poll` - /// returns `Poll::Ready(None)`, all future calls to `poll` will also return - /// `Poll::Ready(None)`. - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::once(1).fuse(); - /// assert_eq!(s.next().await, Some(1)); - /// assert_eq!(s.next().await, None); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` fn fuse(self) -> Fuse where Self: Sized, @@ -393,28 +1038,6 @@ pub trait Stream { } } - /// Creates a stream that uses a predicate to determine if an element - /// should be yeilded. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); - /// let mut s = s.filter(|i| i % 2 == 0); - /// - /// assert_eq!(s.next().await, Some(2)); - /// assert_eq!(s.next().await, Some(4)); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` fn filter

(self, predicate: P) -> Filter where Self: Sized, @@ -423,36 +1046,6 @@ pub trait Stream { Filter::new(self, predicate) } - /// Both filters and maps a stream. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let s: VecDeque<&str> = vec!["1", "lol", "3", "NaN", "5"].into_iter().collect(); - /// - /// let mut parsed = s.filter_map(|a| a.parse::().ok()); - /// - /// let one = parsed.next().await; - /// assert_eq!(one, Some(1)); - /// - /// let three = parsed.next().await; - /// assert_eq!(three, Some(3)); - /// - /// let five = parsed.next().await; - /// assert_eq!(five, Some(5)); - /// - /// let end = parsed.next().await; - /// assert_eq!(end, None); - /// # - /// # }) } - /// ``` fn filter_map(self, f: F) -> FilterMap where Self: Sized, @@ -461,32 +1054,7 @@ pub trait Stream { FilterMap::new(self, f) } - /// Returns the element that gives the minimum value with respect to the - /// specified comparison function. If several elements are equally minimum, - /// the first element is returned. If the stream is empty, `None` is returned. - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// - /// let min = Stream::min_by(s.clone(), |x, y| x.cmp(y)).await; - /// assert_eq!(min, Some(1)); - /// - /// let min = Stream::min_by(s, |x, y| y.cmp(x)).await; - /// assert_eq!(min, Some(3)); - /// - /// let min = Stream::min_by(VecDeque::::new(), |x, y| x.cmp(y)).await; - /// assert_eq!(min, None); - /// # - /// # }) } - /// ``` - fn min_by(self, compare: F) -> ret!(MinByFuture, Self::Item) + fn min_by(self, compare: F) -> MinByFuture where Self: Sized, F: FnMut(&Self::Item, &Self::Item) -> Ordering, @@ -494,109 +1062,15 @@ pub trait Stream { MinByFuture::new(self, compare) } - /// Returns the nth element of the stream. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// - /// let second = s.nth(1).await; - /// assert_eq!(second, Some(2)); - /// # - /// # }) } - /// ``` - /// Calling `nth()` multiple times: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// - /// let second = s.nth(0).await; - /// assert_eq!(second, Some(1)); - /// - /// let second = s.nth(0).await; - /// assert_eq!(second, Some(2)); - /// # - /// # }) } - /// ``` - /// Returning `None` if the stream finished before returning `n` elements: - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// - /// let fourth = s.nth(4).await; - /// assert_eq!(fourth, None); - /// # - /// # }) } - /// ``` - fn nth(&mut self, n: usize) -> ret!('_, NthFuture, Option) + fn nth(&mut self, n: usize) -> NthFuture<'_, Self> where Self: Sized, { NthFuture::new(self, n) } - /// Tests if every element of the stream matches a predicate. - /// - /// `all()` takes a closure that returns `true` or `false`. It applies - /// this closure to each element of the stream, and if they all return - /// `true`, then so does `all()`. If any of them return `false`, it - /// returns `false`. - /// - /// `all()` is short-circuiting; in other words, it will stop processing - /// as soon as it finds a `false`, given that no matter what else happens, - /// the result will also be `false`. - /// - /// An empty stream returns `true`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::repeat::(42).take(3); - /// assert!(s.all(|x| x == 42).await); - /// - /// # - /// # }) } - /// ``` - /// - /// Empty stream: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::empty::(); - /// assert!(s.all(|_| false).await); - /// # - /// # }) } - /// ``` #[inline] - fn all(&mut self, f: F) -> ret!('_, AllFuture, bool, F, Self::Item) + fn all(&mut self, f: F) -> AllFuture<'_, Self, F, Self::Item> where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -609,43 +1083,7 @@ pub trait Stream { } } - /// Searches for an element in a stream that satisfies a predicate. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// let res = s.find(|x| *x == 2).await; - /// assert_eq!(res, Some(2)); - /// # - /// # }) } - /// ``` - /// - /// Resuming after a first find: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let mut s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// let res = s.find(|x| *x == 2).await; - /// assert_eq!(res, Some(2)); - /// - /// let next = s.next().await; - /// assert_eq!(next, Some(3)); - /// # - /// # }) } - /// ``` - fn find

(&mut self, p: P) -> ret!('_, FindFuture, Option, P, Self::Item) + fn find

(&mut self, p: P) -> FindFuture<'_, Self, P, Self::Item> where Self: Sized, P: FnMut(&Self::Item) -> bool, @@ -653,22 +1091,7 @@ pub trait Stream { FindFuture::new(self, p) } - /// Applies function to the elements of stream and returns the first non-none result. - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let mut s: VecDeque<&str> = vec!["lol", "NaN", "2", "5"].into_iter().collect(); - /// let first_number = s.find_map(|s| s.parse().ok()).await; - /// - /// assert_eq!(first_number, Some(2)); - /// # - /// # }) } - /// ``` - fn find_map(&mut self, f: F) -> ret!('_, FindMapFuture, Option, F, Self::Item, B) + fn find_map(&mut self, f: F) -> FindMapFuture<'_, Self, F, Self::Item, B> where Self: Sized, F: FnMut(Self::Item) -> Option, @@ -676,27 +1099,7 @@ pub trait Stream { FindMapFuture::new(self, f) } - /// A combinator that applies a function to every element in a stream - /// producing a single, final value. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use std::collections::VecDeque; - /// - /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// let sum = s.fold(0, |acc, x| acc + x).await; - /// - /// assert_eq!(sum, 6); - /// # - /// # }) } - /// ``` - fn fold(self, init: B, f: F) -> ret!(FoldFuture, Self::Item) + fn fold(self, init: B, f: F) -> FoldFuture where Self: Sized, F: FnMut(B, Self::Item) -> B, @@ -704,50 +1107,7 @@ pub trait Stream { FoldFuture::new(self, init, f) } - /// Tests if any element of the stream matches a predicate. - /// - /// `any()` takes a closure that returns `true` or `false`. It applies - /// this closure to each element of the stream, and if any of them return - /// `true`, then so does `any()`. If they all return `false`, it - /// returns `false`. - /// - /// `any()` is short-circuiting; in other words, it will stop processing - /// as soon as it finds a `true`, given that no matter what else happens, - /// the result will also be `true`. - /// - /// An empty stream returns `false`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::repeat::(42).take(3); - /// assert!(s.any(|x| x == 42).await); - /// # - /// # }) } - /// ``` - /// - /// Empty stream: - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let mut s = stream::empty::(); - /// assert!(!s.any(|_| false).await); - /// # - /// # }) } - /// ``` - #[inline] - fn any(&mut self, f: F) -> ret!('_, AnyFuture, bool, F, Self::Item) + fn any(&mut self, f: F) -> AnyFuture<'_, Self, F, Self::Item> where Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, @@ -760,40 +1120,6 @@ pub trait Stream { } } - /// A stream adaptor similar to [`fold`] that holds internal state and produces a new stream. - /// - /// [`fold`]: #method.fold - /// - /// `scan()` takes two arguments: an initial value which seeds the internal state, and a - /// closure with two arguments, the first being a mutable reference to the internal state and - /// the second a stream element. The closure can assign to the internal state to share state - /// between iterations. - /// - /// On iteration, the closure will be applied to each element of the stream and the return - /// value from the closure, an `Option`, is yielded by the stream. - /// - /// ## Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// let mut s = s.scan(1, |state, x| { - /// *state = *state * x; - /// Some(-*state) - /// }); - /// - /// assert_eq!(s.next().await, Some(-1)); - /// assert_eq!(s.next().await, Some(-2)); - /// assert_eq!(s.next().await, Some(-6)); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` - #[inline] fn scan(self, initial_state: St, f: F) -> Scan where Self: Sized, @@ -802,31 +1128,6 @@ pub trait Stream { Scan::new(self, initial_state, f) } - /// Combinator that `skip`s elements based on a predicate. - /// - /// Takes a closure argument. It will call this closure on every element in - /// the stream and ignore elements until it returns `false`. - /// - /// After `false` is returned, `SkipWhile`'s job is over and all further - /// elements in the strem are yeilded. - /// - /// ## Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let a: VecDeque<_> = vec![-1i32, 0, 1].into_iter().collect(); - /// let mut s = a.skip_while(|x| x.is_negative()); - /// - /// assert_eq!(s.next().await, Some(0)); - /// assert_eq!(s.next().await, Some(1)); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` fn skip_while

(self, predicate: P) -> SkipWhile where Self: Sized, @@ -835,23 +1136,6 @@ pub trait Stream { SkipWhile::new(self, predicate) } - /// Creates a combinator that skips the first `n` elements. - /// - /// ## Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// let mut skipped = s.skip(2); - /// - /// assert_eq!(skipped.next().await, Some(3)); - /// assert_eq!(skipped.next().await, None); - /// # - /// # }) } - /// ``` fn skip(self, n: usize) -> Skip where Self: Sized, @@ -859,114 +1143,24 @@ pub trait Stream { Skip::new(self, n) } - /// 'Zips up' two streams into a single stream of pairs. - /// - /// `zip()` returns a new stream that will iterate over two other streams, returning a tuple - /// where the first element comes from the first stream, and the second element comes from the - /// second stream. - /// - /// In other words, it zips two streams together, into a single one. - /// - /// If either stream returns [`None`], [`poll_next`] from the zipped stream will return - /// [`None`]. If the first stream returns [`None`], `zip` will short-circuit and `poll_next` - /// will not be called on the second stream. - /// - /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None - /// [`poll_next`]: #tymethod.poll_next - /// - /// ## Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use std::collections::VecDeque; - /// use async_std::stream::Stream; - /// - /// let l: VecDeque = vec![1, 2, 3].into_iter().collect(); - /// let r: VecDeque = vec![4, 5, 6, 7].into_iter().collect(); - /// let mut s = l.zip(r); - /// - /// assert_eq!(s.next().await, Some((1, 4))); - /// assert_eq!(s.next().await, Some((2, 5))); - /// assert_eq!(s.next().await, Some((3, 6))); - /// assert_eq!(s.next().await, None); - /// # - /// # }) } - /// ``` - #[inline] fn zip(self, other: U) -> Zip where - Self: Sized, + Self: Stream + Sized, U: Stream, { Zip::new(self, other) } - /// Transforms a stream into a collection. - /// - /// `collect()` can take anything streamable, and turn it into a relevant - /// collection. This is one of the more powerful methods in the async - /// standard library, used in a variety of contexts. - /// - /// The most basic pattern in which `collect()` is used is to turn one - /// collection into another. You take a collection, call [`stream`] on it, - /// do a bunch of transformations, and then `collect()` at the end. - /// - /// Because `collect()` is so general, it can cause problems with type - /// inference. As such, `collect()` is one of the few times you'll see - /// the syntax affectionately known as the 'turbofish': `::<>`. This - /// helps the inference algorithm understand specifically which collection - /// you're trying to collect into. - /// - /// # Examples - /// - /// ``` - /// # fn main() { async_std::task::block_on(async { - /// # - /// use async_std::prelude::*; - /// use async_std::stream; - /// - /// let s = stream::repeat(9u8).take(3); - /// let buf: Vec = s.collect().await; - /// - /// assert_eq!(buf, vec![9; 3]); - /// - /// // You can also collect streams of Result values - /// // into any collection that implements FromStream - /// let s = stream::repeat(Ok(9)).take(3); - /// // We are using Vec here, but other collections - /// // are supported as well - /// let buf: Result, ()> = s.collect().await; - /// - /// assert_eq!(buf, Ok(vec![9; 3])); - /// - /// // The stream will stop on the first Err and - /// // return that instead - /// let s = stream::repeat(Err(5)).take(3); - /// let buf: Result, u8> = s.collect().await; - /// - /// assert_eq!(buf, Err(5)); - /// # - /// # }) } - /// ``` - /// - /// [`stream`]: trait.Stream.html#tymethod.next #[cfg(any(feature = "unstable", feature = "docs"))] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] - fn collect<'a, B>(self) -> ret!(Pin + 'a>>, B) + fn collect<'a, B>(self) -> Pin + 'a>> where - Self: futures_core::stream::Stream + Sized + 'a, - B: FromStream<::Item>, + Self: Sized + 'a, + B: FromStream, { FromStream::from_stream(self) } } -impl Stream for T { - type Item = ::Item; - - fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures_core::stream::Stream::poll_next(self, cx) - } -} +impl StreamExt for T {} diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 222022b..7897516 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -24,7 +24,7 @@ impl Scan { impl Unpin for Scan {} -impl futures_core::stream::Stream for Scan +impl Stream for Scan where S: Stream, F: FnMut(&mut St, S::Item) -> Option, diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 0dea1d0..81d48d2 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -17,7 +17,7 @@ impl Take { pin_utils::unsafe_unpinned!(remaining: usize); } -impl futures_core::stream::Stream for Take { +impl Stream for Take { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs index 05f9967..4c66aef 100644 --- a/src/stream/stream/zip.rs +++ b/src/stream/stream/zip.rs @@ -36,7 +36,7 @@ impl Zip { pin_utils::unsafe_pinned!(second: B); } -impl futures_core::stream::Stream for Zip { +impl Stream for Zip { type Item = (A::Item, B::Item); fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { From c62b7a0ba9b3161836a1471599444b42e3bee6b0 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 22 Sep 2019 17:06:00 +0200 Subject: [PATCH 193/194] Fix warnings --- src/stream/stream/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b209f44..0348b6a 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -66,14 +66,14 @@ pub use zip::Zip; use std::cmp::Ordering; use std::marker::PhantomData; -use std::pin::Pin; use cfg_if::cfg_if; -use crate::future::Future; - cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { + use std::pin::Pin; + + use crate::future::Future; use crate::stream::FromStream; } } From 293d992de154be0cec8216a606b59fcba9deaf31 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 24 Sep 2019 05:13:02 +0200 Subject: [PATCH 194/194] Fix stream_extend compilation failures --- src/stream/extend.rs | 4 ++-- src/vec/extend.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 35c9064..b960dac 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -1,7 +1,7 @@ use std::pin::Pin; -use crate::future::Future; -use crate::stream::{IntoStream, Stream}; +use crate::prelude::*; +use crate::stream::IntoStream; /// Extend a collection with the contents of a stream. /// diff --git a/src/vec/extend.rs b/src/vec/extend.rs index 2ebcf34..d85589e 100644 --- a/src/vec/extend.rs +++ b/src/vec/extend.rs @@ -1,7 +1,7 @@ use std::pin::Pin; -use crate::future::Future; -use crate::stream::{Extend, IntoStream, Stream}; +use crate::prelude::*; +use crate::stream::{Extend, IntoStream}; impl Extend for Vec { fn stream_extend<'a, S: IntoStream + 'a>(

(&mut self, p: P) -> ret!('_, FindFuture, Option, P, Self::Item) + where + Self: Sized, + P: FnMut(&Self::Item) -> bool, + { + FindFuture::new(self, p) + } + /// Applies function to the elements of stream and returns the first non-none result. /// /// ``` From 5b720ab1e26cf644f55f953522e34df736be7424 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 09:54:25 +0300 Subject: [PATCH 085/194] adds stream::fold combinator --- src/stream/stream/fold.rs | 61 +++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 30 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/stream/stream/fold.rs diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs new file mode 100644 index 0000000..0e3dd67 --- /dev/null +++ b/src/stream/stream/fold.rs @@ -0,0 +1,61 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::future::Future; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct FoldFuture { + stream: S, + f: F, + acc: Option, + __t: PhantomData, +} + +impl FoldFuture { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(f: F); + pin_utils::unsafe_unpinned!(acc: Option); + + pub(super) fn new(stream: S, init: B, f: F) -> Self { + FoldFuture { + stream, + f, + acc: Some(init), + __t: PhantomData, + } + } +} + +impl Future for FoldFuture +where + S: futures_core::stream::Stream + Unpin + Sized, + F: FnMut(B, S::Item) -> B, +{ + type Output = B; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => { + cx.waker().wake_by_ref(); + let old = self + .as_mut() + .acc() + .take() + .expect("FoldFuture should never contain None"); + let new = (self.as_mut().f())(old, v); + *self.as_mut().acc() = Some(new); + Poll::Pending + } + None => Poll::Ready( + self.as_mut() + .acc() + .take() + .expect("FoldFuture should never contain None"), + ), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index eddafe2..7825396 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -25,6 +25,7 @@ mod all; mod any; mod filter_map; mod find_map; +mod fold; mod min_by; mod next; mod nth; @@ -36,6 +37,7 @@ use all::AllFuture; use any::AnyFuture; use filter_map::FilterMap; use find_map::FindMapFuture; +use fold::FoldFuture; use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; @@ -344,6 +346,34 @@ pub trait Stream { FindMapFuture::new(self, f) } + /// A combinator that applies a function to every element in a stream + /// producing a single, final value. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use std::collections::VecDeque; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let sum = s.fold(0, |acc, x| acc + x).await; + /// + /// assert_eq!(sum, 6); + /// # + /// # }) } + /// ``` + fn fold(self, init: B, f: F) -> FoldFuture + where + Self: Sized, + F: FnMut(B, Self::Item) -> B, + { + FoldFuture::new(self, init, f) + } + /// Tests if any element of the stream matches a predicate. /// /// `any()` takes a closure that returns `true` or `false`. It applies From 2d75ffacc4cddad597c49007d1eefbd49b616dc7 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 10:08:08 +0300 Subject: [PATCH 086/194] fixes example to resemble std more --- src/stream/stream/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 4ca42c6..3fe565b 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -149,16 +149,15 @@ pub trait Stream { /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; - /// use async_std::stream; + /// use std::collections::VecDeque; /// - /// let mut s = stream::repeat(9).take(4).enumerate(); - /// let mut c: usize = 0; + /// let s: VecDeque<_> = vec!['a', 'b', 'c'].into_iter().collect(); + /// let mut s = s.enumerate(); + /// + /// assert_eq!(s.next().await, Some((0, 'a'))); + /// assert_eq!(s.next().await, Some((1, 'b'))); + /// assert_eq!(s.next().await, Some((2, 'c'))); /// - /// while let Some((i, v)) = s.next().await { - /// assert_eq!(c, i); - /// assert_eq!(v, 9); - /// c += 1; - /// } /// # /// # }) } fn enumerate(self) -> Enumerate From cdd4215e8fdd0298c9346f9bcaf7b1dabc48e1d3 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 10:09:52 +0300 Subject: [PATCH 087/194] forgot None case --- src/stream/stream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 3fe565b..68df3e7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -157,6 +157,7 @@ pub trait Stream { /// assert_eq!(s.next().await, Some((0, 'a'))); /// assert_eq!(s.next().await, Some((1, 'b'))); /// assert_eq!(s.next().await, Some((2, 'c'))); + /// assert_eq!(s.next().await, None); /// /// # /// # }) } From 06f2569d23732f2bb48427770ae6ef1861e460cc Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Thu, 12 Sep 2019 00:02:57 +0900 Subject: [PATCH 088/194] Add BufRead::fill_buf (#176) * Add BufRead::fill_buf * Make FillBufFuture constructor pub(crate) * Give more information about the transmutation source type --- src/io/buf_read/fill_buf.rs | 32 ++++++++++++++++++++++++++++++++ src/io/buf_read/mod.rs | 22 ++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 src/io/buf_read/fill_buf.rs diff --git a/src/io/buf_read/fill_buf.rs b/src/io/buf_read/fill_buf.rs new file mode 100644 index 0000000..0ce58cf --- /dev/null +++ b/src/io/buf_read/fill_buf.rs @@ -0,0 +1,32 @@ +use std::pin::Pin; + +use futures_io::AsyncBufRead; + +use crate::future::Future; +use crate::io; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct FillBufFuture<'a, R: ?Sized> { + reader: &'a mut R, +} + +impl<'a, R: ?Sized> FillBufFuture<'a, R> { + pub(crate) fn new(reader: &'a mut R) -> Self { + Self { reader } + } +} + +impl<'a, R: AsyncBufRead + Unpin + ?Sized> Future for FillBufFuture<'a, R> { + type Output = io::Result<&'a [u8]>; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let Self { reader } = &mut *self; + let result = Pin::new(reader).poll_fill_buf(cx); + // This is safe because: + // 1. The buffer is valid for the lifetime of the reader. + // 2. Output is unrelated to the wrapper (Self). + result.map_ok(|buf| unsafe { std::mem::transmute::<&'_ [u8], &'a [u8]>(buf) }) + } +} diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index e320375..6018439 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -1,7 +1,9 @@ +mod fill_buf; mod lines; mod read_line; mod read_until; +use fill_buf::FillBufFuture; pub use lines::Lines; use read_line::ReadLineFuture; use read_until::ReadUntilFuture; @@ -41,6 +43,26 @@ cfg_if! { /// [`futures::io::AsyncBufRead`]: /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html pub trait BufRead { + /// Returns the contents of the internal buffer, filling it with more data from the inner + /// reader if it is empty. + /// + /// This function is a lower-level call. It needs to be paired with the [`consume`] method to + /// function properly. When calling this method, none of the contents will be "read" in the + /// sense that later calling `read` may return the same contents. As such, [`consume`] must be + /// called with the number of bytes that are consumed from this buffer to ensure that the bytes + /// are never returned twice. + /// + /// [`consume`]: #tymethod.consume + /// + /// An empty buffer returned indicates that the stream has reached EOF. + // TODO: write a proper doctest with `consume` + fn fill_buf<'a>(&'a mut self) -> ret!('a, FillBufFuture, io::Result<&'a [u8]>) + where + Self: Unpin, + { + FillBufFuture::new(self) + } + /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. /// /// This function will read bytes from the underlying stream until the delimiter or EOF is From 724a9f4eb03e59b4873954bde45949e6d71a92c6 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 11 Sep 2019 17:06:02 +0200 Subject: [PATCH 089/194] Add Stream::poll_next --- src/stream/stream/all.rs | 37 ++++++++----------- src/stream/stream/any.rs | 37 ++++++++----------- src/stream/stream/min_by.rs | 5 +-- src/stream/stream/mod.rs | 72 +++++++++++++++++++++++++++++++------ src/stream/stream/next.rs | 8 +++-- src/stream/stream/take.rs | 9 ++--- 6 files changed, 105 insertions(+), 63 deletions(-) diff --git a/src/stream/stream/all.rs b/src/stream/stream/all.rs index 6271024..3b65fc7 100644 --- a/src/stream/stream/all.rs +++ b/src/stream/stream/all.rs @@ -1,43 +1,36 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - use std::marker::PhantomData; use std::pin::Pin; -#[derive(Debug)] -pub struct AllFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct AllFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, pub(crate) result: bool, - pub(crate) __item: PhantomData, + pub(crate) _marker: PhantomData, } -impl<'a, S, F, T> AllFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(result: bool); - pin_utils::unsafe_unpinned!(f: F); -} +impl Unpin for AllFuture<'_, S, F, T> {} impl Future for AllFuture<'_, S, F, S::Item> where - S: futures_core::stream::Stream + Unpin + Sized, + S: Stream + Unpin + Sized, F: FnMut(S::Item) -> bool, { type Output = bool; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(Pin::new(&mut *self.stream).poll_next(cx)); + match next { Some(v) => { - let result = (self.as_mut().f())(v); - *self.as_mut().result() = result; + let result = (&mut self.f)(v); + self.result = result; + if result { // don't forget to wake this task again to pull the next item from stream cx.waker().wake_by_ref(); diff --git a/src/stream/stream/any.rs b/src/stream/stream/any.rs index f1f551a..a23adf4 100644 --- a/src/stream/stream/any.rs +++ b/src/stream/stream/any.rs @@ -1,43 +1,36 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; - use std::marker::PhantomData; use std::pin::Pin; -#[derive(Debug)] -pub struct AnyFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct AnyFuture<'a, S, F, T> { pub(crate) stream: &'a mut S, pub(crate) f: F, pub(crate) result: bool, - pub(crate) __item: PhantomData, + pub(crate) _marker: PhantomData, } -impl<'a, S, F, T> AnyFuture<'a, S, F, T> -where - F: FnMut(T) -> bool, -{ - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(result: bool); - pin_utils::unsafe_unpinned!(f: F); -} +impl Unpin for AnyFuture<'_, S, F, T> {} impl Future for AnyFuture<'_, S, F, S::Item> where - S: futures_core::stream::Stream + Unpin + Sized, + S: Stream + Unpin + Sized, F: FnMut(S::Item) -> bool, { type Output = bool; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(Pin::new(&mut *self.stream).poll_next(cx)); + match next { Some(v) => { - let result = (self.as_mut().f())(v); - *self.as_mut().result() = result; + let result = (&mut self.f)(v); + self.result = result; + if result { Poll::Ready(true) } else { diff --git a/src/stream/stream/min_by.rs b/src/stream/stream/min_by.rs index b65d88d..3223af9 100644 --- a/src/stream/stream/min_by.rs +++ b/src/stream/stream/min_by.rs @@ -6,7 +6,8 @@ use crate::stream::Stream; use crate::task::{Context, Poll}; /// A future that yields the minimum item in a stream by a given comparison function. -#[derive(Clone, Debug)] +#[doc(hidden)] +#[allow(missing_debug_implementations)] pub struct MinByFuture { stream: S, compare: F, @@ -27,7 +28,7 @@ impl MinByFuture { impl Future for MinByFuture where - S: futures_core::stream::Stream + Unpin, + S: Stream + Unpin, S::Item: Copy, F: FnMut(&S::Item, &S::Item) -> Ordering, { diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 91b111e..17ba524 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -36,9 +36,12 @@ use next::NextFuture; use std::cmp::Ordering; use std::marker::PhantomData; +use std::pin::Pin; use cfg_if::cfg_if; +use crate::task::{Context, Poll}; + cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] @@ -73,6 +76,55 @@ pub trait Stream { /// The type of items yielded by this stream. type Item; + /// Attempts to receive the next item from the stream. + /// + /// There are several possible return values: + /// + /// * `Poll::Pending` means this stream's next value is not ready yet. + /// * `Poll::Ready(None)` means this stream has been exhausted. + /// * `Poll::Ready(Some(item))` means `item` was received out of the stream. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::pin::Pin; + /// + /// use async_std::prelude::*; + /// use async_std::stream; + /// use async_std::task::{Context, Poll}; + /// + /// fn increment(s: impl Stream + Unpin) -> impl Stream + Unpin { + /// struct Increment(S); + /// + /// impl + Unpin> Stream for Increment { + /// type Item = S::Item; + /// + /// fn poll_next( + /// mut self: Pin<&mut Self>, + /// cx: &mut Context<'_>, + /// ) -> Poll> { + /// match Pin::new(&mut self.0).poll_next(cx) { + /// Poll::Pending => Poll::Pending, + /// Poll::Ready(None) => Poll::Ready(None), + /// Poll::Ready(Some(item)) => Poll::Ready(Some(item + 1)), + /// } + /// } + /// } + /// + /// Increment(s) + /// } + /// + /// let mut s = increment(stream::once(7)); + /// + /// assert_eq!(s.next().await, Some(8)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; + /// Advances the stream and returns the next value. /// /// Returns [`None`] when iteration is finished. Individual stream implementations may @@ -98,7 +150,10 @@ pub trait Stream { /// ``` fn next(&mut self) -> ret!('_, NextFuture, Option) where - Self: Unpin; + Self: Unpin, + { + NextFuture { stream: self } + } /// Creates a stream that yields its first `n` elements. /// @@ -207,13 +262,13 @@ pub trait Stream { #[inline] fn all(&mut self, f: F) -> ret!('_, AllFuture, bool, F, Self::Item) where - Self: Sized, + Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, { AllFuture { stream: self, result: true, // the default if the empty stream - __item: PhantomData, + _marker: PhantomData, f, } } @@ -264,13 +319,13 @@ pub trait Stream { #[inline] fn any(&mut self, f: F) -> ret!('_, AnyFuture, bool, F, Self::Item) where - Self: Sized, + Self: Unpin + Sized, F: FnMut(Self::Item) -> bool, { AnyFuture { stream: self, result: false, // the default if the empty stream - __item: PhantomData, + _marker: PhantomData, f, } } @@ -279,10 +334,7 @@ pub trait Stream { impl Stream for T { type Item = ::Item; - fn next(&mut self) -> ret!('_, NextFuture, Option) - where - Self: Unpin, - { - NextFuture { stream: self } + fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + futures_core::stream::Stream::poll_next(self, cx) } } diff --git a/src/stream/stream/next.rs b/src/stream/stream/next.rs index b64750d..de75f5e 100644 --- a/src/stream/stream/next.rs +++ b/src/stream/stream/next.rs @@ -1,14 +1,16 @@ -use crate::future::Future; -use crate::task::{Context, Poll}; use std::pin::Pin; +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct NextFuture<'a, T: Unpin + ?Sized> { pub(crate) stream: &'a mut T, } -impl Future for NextFuture<'_, T> { +impl Future for NextFuture<'_, T> { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { diff --git a/src/stream/stream/take.rs b/src/stream/stream/take.rs index 0499a6a..0dea1d0 100644 --- a/src/stream/stream/take.rs +++ b/src/stream/stream/take.rs @@ -1,7 +1,8 @@ -use crate::task::{Context, Poll}; - use std::pin::Pin; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + /// A stream that yields the first `n` items of another stream. #[derive(Clone, Debug)] pub struct Take { @@ -11,12 +12,12 @@ pub struct Take { impl Unpin for Take {} -impl Take { +impl Take { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(remaining: usize); } -impl futures_core::stream::Stream for Take { +impl futures_core::stream::Stream for Take { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { From ab1e2b403a8a45a92d889aed4acc6d6493496c37 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 11 Sep 2019 17:17:20 +0200 Subject: [PATCH 090/194] Fix compilation errors on latest nightly --- docs/src/tutorial/all_together.md | 3 ++- docs/src/tutorial/clean_shutdown.md | 6 ++++-- docs/src/tutorial/connecting_readers_and_writers.md | 3 ++- docs/src/tutorial/handling_disconnection.md | 3 ++- examples/a-chat/server.rs | 5 ++--- examples/socket-timeouts.rs | 2 +- 6 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index a638e02..641c7da 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -115,7 +115,8 @@ async fn broker(mut events: Receiver) -> Result<()> { Event::Message { from, to, msg } => { for addr in to { if let Some(peer) = peers.get_mut(&addr) { - peer.send(format!("from {}: {}\n", from, msg)).await? + let msg = format!("from {}: {}\n", from, msg); + peer.send(msg).await? } } } diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index f61adf2..992a35d 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -115,7 +115,8 @@ Let's add waiting to the server: # Event::Message { from, to, msg } => { # for addr in to { # if let Some(peer) = peers.get_mut(&addr) { -# peer.send(format!("from {}: {}\n", from, msg)).await? +# let msg = format!("from {}: {}\n", from, msg); +# peer.send(msg).await? # } # } # } @@ -217,7 +218,8 @@ async fn broker(mut events: Receiver) -> Result<()> { Event::Message { from, to, msg } => { for addr in to { if let Some(peer) = peers.get_mut(&addr) { - peer.send(format!("from {}: {}\n", from, msg)).await? + let msg = format!("from {}: {}\n", from, msg); + peer.send(msg).await? } } } diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index 7399cec..d5da471 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -73,7 +73,8 @@ async fn broker(mut events: Receiver) -> Result<()> { Event::Message { from, to, msg } => { // 3 for addr in to { if let Some(peer) = peers.get_mut(&addr) { - peer.send(format!("from {}: {}\n", from, msg)).await? + let msg = format!("from {}: {}\n", from, msg); + peer.send(msg).await? } } } diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 351f253..82bbef5 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -257,7 +257,8 @@ async fn broker(events: Receiver) { Event::Message { from, to, msg } => { for addr in to { if let Some(peer) = peers.get_mut(&addr) { - peer.send(format!("from {}: {}\n", from, msg)).await + let msg = format!("from {}: {}\n", from, msg); + peer.send(fmt).await .unwrap() // 6 } } diff --git a/examples/a-chat/server.rs b/examples/a-chat/server.rs index 911d160..77ebfd1 100644 --- a/examples/a-chat/server.rs +++ b/examples/a-chat/server.rs @@ -139,9 +139,8 @@ async fn broker_loop(mut events: Receiver) { Event::Message { from, to, msg } => { for addr in to { if let Some(peer) = peers.get_mut(&addr) { - peer.send(format!("from {}: {}\n", from, msg)) - .await - .unwrap() + let msg = format!("from {}: {}\n", from, msg); + peer.send(msg).await.unwrap(); } } } diff --git a/examples/socket-timeouts.rs b/examples/socket-timeouts.rs index b2f770e..894206c 100644 --- a/examples/socket-timeouts.rs +++ b/examples/socket-timeouts.rs @@ -10,7 +10,7 @@ async fn get() -> io::Result> { let mut buf = vec![]; - io::timeout(Duration::from_secs(5), async { + io::timeout(Duration::from_secs(5), async move { stream.read_to_end(&mut buf).await?; Ok(buf) }) From 0f4f0fb77e1bee5b51409f0d25312a1581cb6339 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 11 Sep 2019 17:29:33 +0200 Subject: [PATCH 091/194] Fix a typo --- docs/src/tutorial/handling_disconnection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 82bbef5..27c5052 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -258,7 +258,7 @@ async fn broker(events: Receiver) { for addr in to { if let Some(peer) = peers.get_mut(&addr) { let msg = format!("from {}: {}\n", from, msg); - peer.send(fmt).await + peer.send(msg).await .unwrap() // 6 } } From 18428d6bfef00f87eb3d38a4fd021a2cef32a37b Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 21:47:52 +0300 Subject: [PATCH 092/194] housekeeping after 145 --- src/stream/stream/filter_map.rs | 8 +++++--- src/stream/stream/find.rs | 20 ++++++++++---------- src/stream/stream/find_map.rs | 19 ++++++++++--------- src/stream/stream/nth.rs | 19 ++++++++++--------- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/src/stream/stream/filter_map.rs b/src/stream/stream/filter_map.rs index 626a8ec..2f27515 100644 --- a/src/stream/stream/filter_map.rs +++ b/src/stream/stream/filter_map.rs @@ -2,8 +2,10 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; -/// A stream that both filters and maps. -#[derive(Clone, Debug)] +use crate::stream::Stream; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] pub struct FilterMap { stream: S, f: F, @@ -27,7 +29,7 @@ impl FilterMap { impl futures_core::stream::Stream for FilterMap where - S: futures_core::stream::Stream, + S: Stream, F: FnMut(S::Item) -> Option, { type Item = B; diff --git a/src/stream/stream/find.rs b/src/stream/stream/find.rs index dfab089..014c8b7 100644 --- a/src/stream/stream/find.rs +++ b/src/stream/stream/find.rs @@ -1,7 +1,10 @@ -use crate::task::{Context, Poll}; use std::marker::PhantomData; use std::pin::Pin; +use crate::future::Future; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct FindFuture<'a, S, P, T> { @@ -11,9 +14,6 @@ pub struct FindFuture<'a, S, P, T> { } impl<'a, S, P, T> FindFuture<'a, S, P, T> { - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(p: P); - pub(super) fn new(stream: &'a mut S, p: P) -> Self { FindFuture { stream, @@ -23,20 +23,20 @@ impl<'a, S, P, T> FindFuture<'a, S, P, T> { } } -impl<'a, S, P> futures_core::future::Future for FindFuture<'a, S, P, S::Item> +impl Unpin for FindFuture<'_, S, P, T> {} + +impl<'a, S, P> Future for FindFuture<'a, S, P, S::Item> where - S: futures_core::stream::Stream + Unpin + Sized, + S: Stream + Unpin + Sized, P: FnMut(&S::Item) -> bool, { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - - let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let item = futures_core::ready!(Pin::new(&mut *self.stream).poll_next(cx)); match item { - Some(v) => match (self.as_mut().p())(&v) { + Some(v) => match (&mut self.p)(&v) { true => Poll::Ready(Some(v)), false => { cx.waker().wake_by_ref(); diff --git a/src/stream/stream/find_map.rs b/src/stream/stream/find_map.rs index dcc29d8..dfcf92d 100644 --- a/src/stream/stream/find_map.rs +++ b/src/stream/stream/find_map.rs @@ -2,6 +2,10 @@ use std::marker::PhantomData; use std::pin::Pin; use std::task::{Context, Poll}; +use crate::future::Future; +use crate::stream::Stream; + +#[doc(hidden)] #[allow(missing_debug_implementations)] pub struct FindMapFuture<'a, S, F, T, B> { stream: &'a mut S, @@ -11,9 +15,6 @@ pub struct FindMapFuture<'a, S, F, T, B> { } impl<'a, S, B, F, T> FindMapFuture<'a, S, F, T, B> { - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(f: F); - pub(super) fn new(stream: &'a mut S, f: F) -> Self { FindMapFuture { stream, @@ -24,20 +25,20 @@ impl<'a, S, B, F, T> FindMapFuture<'a, S, F, T, B> { } } -impl<'a, S, B, F> futures_core::future::Future for FindMapFuture<'a, S, F, S::Item, B> +impl Unpin for FindMapFuture<'_, S, F, T, B> {} + +impl<'a, S, B, F> Future for FindMapFuture<'a, S, F, S::Item, B> where - S: futures_core::stream::Stream + Unpin + Sized, + S: Stream + Unpin + Sized, F: FnMut(S::Item) -> Option, { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - - let item = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let item = futures_core::ready!(Pin::new(&mut *self.stream).poll_next(cx)); match item { - Some(v) => match (self.as_mut().f())(v) { + Some(v) => match (&mut self.f)(v) { Some(v) => Poll::Ready(Some(v)), None => { cx.waker().wake_by_ref(); diff --git a/src/stream/stream/nth.rs b/src/stream/stream/nth.rs index 169ded5..e7e042a 100644 --- a/src/stream/stream/nth.rs +++ b/src/stream/stream/nth.rs @@ -1,36 +1,37 @@ use std::pin::Pin; use std::task::{Context, Poll}; +use crate::future::Future; +use crate::stream::Stream; + +#[doc(hidden)] #[allow(missing_debug_implementations)] pub struct NthFuture<'a, S> { stream: &'a mut S, n: usize, } -impl<'a, S> NthFuture<'a, S> { - pin_utils::unsafe_pinned!(stream: &'a mut S); - pin_utils::unsafe_unpinned!(n: usize); +impl Unpin for NthFuture<'_, S> {} +impl<'a, S> NthFuture<'a, S> { pub(crate) fn new(stream: &'a mut S, n: usize) -> Self { NthFuture { stream, n } } } -impl<'a, S> futures_core::future::Future for NthFuture<'a, S> +impl<'a, S> Future for NthFuture<'a, S> where - S: futures_core::stream::Stream + Unpin + Sized, + S: Stream + Unpin + Sized, { type Output = Option; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - use futures_core::stream::Stream; - - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(Pin::new(&mut *self.stream).poll_next(cx)); match next { Some(v) => match self.n { 0 => Poll::Ready(Some(v)), _ => { - *self.as_mut().n() -= 1; + self.n -= 1; cx.waker().wake_by_ref(); Poll::Pending } From 3dc33f54b49f5b3ccf129730e03ab8de39fcce9d Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 22:03:44 +0300 Subject: [PATCH 093/194] fixes after #145 --- src/stream/stream/enumerate.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index de29578..8576da8 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -1,6 +1,8 @@ use crate::task::{Context, Poll}; use std::pin::Pin; +use crate::stream::Stream; + #[doc(hidden)] #[allow(missing_debug_implementations)] pub struct Enumerate { @@ -19,7 +21,7 @@ impl Enumerate { impl futures_core::stream::Stream for Enumerate where - S: futures_core::stream::Stream, + S: Stream + Unpin + Sized, { type Item = (usize, S::Item); From 6c3f8af62d2b6899aa16867164a48b5c4184f27c Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 22:07:20 +0300 Subject: [PATCH 094/194] fixes after #145 --- src/stream/stream/fold.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 0e3dd67..91a1c8c 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -2,6 +2,7 @@ use std::marker::PhantomData; use std::pin::Pin; use crate::future::Future; +use crate::stream::Stream; use crate::task::{Context, Poll}; #[doc(hidden)] @@ -30,7 +31,7 @@ impl FoldFuture { impl Future for FoldFuture where - S: futures_core::stream::Stream + Unpin + Sized, + S: Stream + Unpin + Sized, F: FnMut(B, S::Item) -> B, { type Output = B; From 55669f5ff4bd1e5d29f8f57f92f8c1169d550cad Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 12 Sep 2019 13:08:15 +0200 Subject: [PATCH 095/194] Prepare release for 0.99.5 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 36 ++++++++++++++++++++++++++++++++++-- Cargo.toml | 2 +- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fbe971..930e3f6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,38 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] -- Expose `fs::create_dir_all` +# [0.99.5] - 2019-09-12 + +## Added + +- Added tests for `io::timeout` +- Added `io::BufRead::fill_buf`, an `async fn` counterpart to `poll_fill_buf` +- Added `fs::create_dir_all` +- Added `future::timeout`, a free function to time out futures after a threshold +- Added `io::prelude` +- Added `net::ToSocketAddrs`, a non-blocking version of std's `ToSocketAddrs` +- Added `stream::Stream::all` +- Added `stream::Stream::filter_map` +- Added `stream::Stream::find_map` +- Added `stream::Stream::find` +- Added `stream::Stream::min_by` +- Added `stream::Stream::nth` + +## Changed + +- Polished the text and examples of the tutorial +- `cargo fmt` on all examples +- Simplified internals of `TcpStream::connect_to` +- Modularized our CI setup, enabled a rustfmt fallback, and improved caching +- Reduced our dependency on the `futures-rs` crate, improving compilation times +- Split `io::Read`, `io::Write`, `io::BufRead`, and `stream::Stream` into + multiple files +- `fs::File` now flushes more often to prevent flushes during `seek` +- Updated all dependencies +- Fixed a bug in the conversion of `File` into raw handle +- Fixed compilation errors on the latest nightly + +## Removed # [0.99.4] - 2019-08-21 @@ -21,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview - Initial beta release -[Unreleased]: https://github.com/olivierlacan/keep-a-changelog/compare/v0.99.3...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.5...HEAD +[0.99.5]: https://github.com/async-rs/async-std/compare/v0.99.4...v0.99.5 [0.99.4]: https://github.com/async-rs/async-std/compare/v0.99.3...v0.99.4 [0.99.3]: https://github.com/async-rs/async-std/tree/v0.99.3 diff --git a/Cargo.toml b/Cargo.toml index ca0f3fc..2d026fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.4" +version = "0.99.5" authors = [ "Stjepan Glavina ", "The async-std Project Developers", From 0080a0da8ceda7b7b00af0a512cffa3d9dee8b09 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Thu, 12 Sep 2019 18:15:20 +0300 Subject: [PATCH 096/194] change expect to unwrap --- src/stream/stream/fold.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 91a1c8c..05d4937 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -42,21 +42,12 @@ where match next { Some(v) => { cx.waker().wake_by_ref(); - let old = self - .as_mut() - .acc() - .take() - .expect("FoldFuture should never contain None"); + let old = self.as_mut().acc().take().unwrap(); let new = (self.as_mut().f())(old, v); *self.as_mut().acc() = Some(new); Poll::Pending } - None => Poll::Ready( - self.as_mut() - .acc() - .take() - .expect("FoldFuture should never contain None"), - ), + None => Poll::Ready(self.as_mut().acc().take().unwrap()), } } } From d25dae54191c8a922410cd2e3ca39e631d28982b Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 12 Sep 2019 18:39:00 +0200 Subject: [PATCH 097/194] Refactor the networking driver --- src/net/driver/mod.rs | 253 +++++++++--------------------------- src/net/tcp/listener.rs | 77 +++-------- src/net/tcp/stream.rs | 147 +++++---------------- src/net/udp/mod.rs | 127 +++++------------- src/os/unix/net/datagram.rs | 76 +++-------- src/os/unix/net/listener.rs | 34 ++--- src/os/unix/net/stream.rs | 78 ++++------- 7 files changed, 192 insertions(+), 600 deletions(-) diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index 255e35a..be31ec9 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -1,10 +1,6 @@ use std::fmt; -use std::io::{Read as _, Write as _}; -use std::pin::Pin; -use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use futures_io::{AsyncRead, AsyncWrite}; use lazy_static::lazy_static; use mio::{self, Evented}; use slab::Slab; @@ -19,9 +15,6 @@ struct Entry { /// A unique identifier. token: mio::Token, - /// Indicates whether this I/O handle is ready for reading, writing, or if it is disconnected. - readiness: AtomicUsize, - /// Tasks that are blocked on reading from this I/O handle. readers: Mutex>, @@ -75,7 +68,6 @@ impl Reactor { // Allocate an entry and insert it into the slab. let entry = Arc::new(Entry { token, - readiness: AtomicUsize::new(mio::Ready::empty().as_usize()), readers: Mutex::new(Vec::new()), writers: Mutex::new(Vec::new()), }); @@ -151,9 +143,6 @@ fn main_loop() -> io::Result<()> { if let Some(entry) = entries.get(token.0) { // Set the readiness flags from this I/O event. let readiness = event.readiness(); - entry - .readiness - .fetch_or(readiness.as_usize(), Ordering::SeqCst); // Wake up reader tasks blocked on this I/O handle. if !(readiness & reader_interests()).is_empty() { @@ -178,7 +167,7 @@ fn main_loop() -> io::Result<()> { /// /// This handle wraps an I/O event source and exposes a "futurized" interface on top of it, /// implementing traits `AsyncRead` and `AsyncWrite`. -pub struct IoHandle { +pub struct Watcher { /// Data associated with the I/O handle. entry: Arc, @@ -186,13 +175,13 @@ pub struct IoHandle { source: Option, } -impl IoHandle { +impl Watcher { /// 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) -> IoHandle { - IoHandle { + pub fn new(source: T) -> Watcher { + Watcher { entry: REACTOR .register(&source) .expect("cannot register an I/O event source"), @@ -205,91 +194,75 @@ impl IoHandle { self.source.as_ref().unwrap() } - /// Polls the I/O handle for reading. + /// Polls the inner I/O source for a non-blocking read operation. /// - /// If reading from the I/O handle would block, `Poll::Pending` will be returned. - pub fn poll_readable(&self, cx: &mut Context<'_>) -> Poll> { - let mask = reader_interests(); - let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst)); - - if (readiness & mask).is_empty() { - let mut list = self.entry.readers.lock().unwrap(); - if list.iter().all(|w| !w.will_wake(cx.waker())) { - list.push(cx.waker().clone()); - } - - readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst)); + /// 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> + where + F: FnMut(&'a T) -> io::Result, + { + // 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), } - if (readiness & mask).is_empty() { - Poll::Pending - } else { - Poll::Ready(Ok(())) + // 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 } - /// Clears the readability status. + /// Polls the inner I/O source for a non-blocking write operation. /// - /// This method is usually called when an attempt at reading from the OS-level I/O handle - /// returns `io::ErrorKind::WouldBlock`. - pub fn clear_readable(&self, cx: &mut Context<'_>) -> io::Result<()> { - let mask = reader_interests() - hup(); - self.entry - .readiness - .fetch_and(!mask.as_usize(), Ordering::SeqCst); - - if self.poll_readable(cx)?.is_ready() { - // Wake the current task. - cx.waker().wake_by_ref(); + /// 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> + where + F: FnMut(&'a T) -> io::Result, + { + // 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), } - Ok(()) - } + // Lock the waker list. + let mut list = self.entry.writers.lock().unwrap(); - /// Polls the I/O handle for writing. - /// - /// If writing into the I/O handle would block, `Poll::Pending` will be returned. - pub fn poll_writable(&self, cx: &mut Context<'_>) -> Poll> { - let mask = writer_interests(); - let mut readiness = mio::Ready::from_usize(self.entry.readiness.load(Ordering::SeqCst)); - - if (readiness & mask).is_empty() { - let mut list = self.entry.writers.lock().unwrap(); - if list.iter().all(|w| !w.will_wake(cx.waker())) { - list.push(cx.waker().clone()); - } - - readiness = mio::Ready::from_usize(self.entry.readiness.fetch_or(0, Ordering::SeqCst)); + // 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), } - if (readiness & mask).is_empty() { - Poll::Pending - } else { - Poll::Ready(Ok(())) - } - } - - /// Clears the writability status. - /// - /// This method is usually called when an attempt at writing from the OS-level I/O handle - /// returns `io::ErrorKind::WouldBlock`. - pub fn clear_writable(&self, cx: &mut Context<'_>) -> io::Result<()> { - let mask = writer_interests() - hup(); - self.entry - .readiness - .fetch_and(!mask.as_usize(), Ordering::SeqCst); - - if self.poll_writable(cx)?.is_ready() { - // Wake the current task. - cx.waker().wake_by_ref(); + // Register the task if it isn't registered already. + if list.iter().all(|w| !w.will_wake(cx.waker())) { + list.push(cx.waker().clone()); } - Ok(()) + Poll::Pending } /// Deregisters and returns the inner I/O source. /// - /// This method is typically used to convert `IoHandle`s to raw file descriptors/handles. + /// This method is typically used to convert `Watcher`s to raw file descriptors/handles. pub fn into_inner(mut self) -> T { let source = self.source.take().unwrap(); REACTOR @@ -299,7 +272,7 @@ impl IoHandle { } } -impl Drop for IoHandle { +impl Drop for Watcher { fn drop(&mut self) { if let Some(ref source) = self.source { REACTOR @@ -309,125 +282,15 @@ impl Drop for IoHandle { } } -impl fmt::Debug for IoHandle { +impl fmt::Debug for Watcher { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("IoHandle") + f.debug_struct("Watcher") .field("entry", &self.entry) .field("source", &self.source) .finish() } } -impl AsyncRead for IoHandle { - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - futures_core::ready!(Pin::new(&mut *self).poll_readable(cx)?); - - match self.source.as_mut().unwrap().read(buf) { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.clear_readable(cx)?; - Poll::Pending - } - res => Poll::Ready(res), - } - } -} - -impl<'a, T: Evented + Unpin> AsyncRead for &'a IoHandle -where - &'a T: std::io::Read, -{ - fn poll_read( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &mut [u8], - ) -> Poll> { - futures_core::ready!(Pin::new(&mut *self).poll_readable(cx)?); - - match self.source.as_ref().unwrap().read(buf) { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.clear_readable(cx)?; - Poll::Pending - } - res => Poll::Ready(res), - } - } -} - -impl AsyncWrite for IoHandle { - fn poll_write( - mut self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - futures_core::ready!(self.poll_writable(cx)?); - - match self.source.as_mut().unwrap().write(buf) { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.clear_writable(cx)?; - Poll::Pending - } - res => Poll::Ready(res), - } - } - - fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures_core::ready!(self.poll_writable(cx)?); - - match self.source.as_mut().unwrap().flush() { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.clear_writable(cx)?; - Poll::Pending - } - res => Poll::Ready(res), - } - } - - fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - -impl<'a, T: Evented + Unpin> AsyncWrite for &'a IoHandle -where - &'a T: std::io::Write, -{ - fn poll_write( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - buf: &[u8], - ) -> Poll> { - futures_core::ready!(self.poll_writable(cx)?); - - match self.get_ref().write(buf) { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.clear_writable(cx)?; - Poll::Pending - } - res => Poll::Ready(res), - } - } - - fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - futures_core::ready!(self.poll_writable(cx)?); - - match self.get_ref().flush() { - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.clear_writable(cx)?; - Poll::Pending - } - res => Poll::Ready(res), - } - } - - fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } -} - /// Returns a mask containing flags that interest tasks reading from I/O handles. #[inline] fn reader_interests() -> mio::Ready { diff --git a/src/net/tcp/listener.rs b/src/net/tcp/listener.rs index 7f1ebcd..4afa574 100644 --- a/src/net/tcp/listener.rs +++ b/src/net/tcp/listener.rs @@ -6,7 +6,7 @@ use cfg_if::cfg_if; use super::TcpStream; use crate::future::{self, Future}; use crate::io; -use crate::net::driver::IoHandle; +use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; use crate::task::{Context, Poll}; @@ -49,9 +49,7 @@ use crate::task::{Context, Poll}; /// ``` #[derive(Debug)] pub struct TcpListener { - io_handle: IoHandle, - // #[cfg(windows)] - // raw_socket: std::os::windows::io::RawSocket, + watcher: Watcher, } impl TcpListener { @@ -82,17 +80,9 @@ impl TcpListener { for addr in addrs.to_socket_addrs().await? { match mio::net::TcpListener::bind(&addr) { Ok(mio_listener) => { - #[cfg(unix)] - let listener = TcpListener { - io_handle: IoHandle::new(mio_listener), - }; - - #[cfg(windows)] - let listener = TcpListener { - // raw_socket: mio_listener.as_raw_socket(), - io_handle: IoHandle::new(mio_listener), - }; - return Ok(listener); + return Ok(TcpListener { + watcher: Watcher::new(mio_listener), + }); } Err(err) => last_err = Some(err), } @@ -123,34 +113,15 @@ impl TcpListener { /// # Ok(()) }) } /// ``` pub async fn accept(&self) -> io::Result<(TcpStream, SocketAddr)> { - future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_readable(cx)?); + let (io, addr) = + future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.accept_std())) + .await?; - match self.io_handle.get_ref().accept_std() { - Ok((io, addr)) => { - let mio_stream = mio::net::TcpStream::from_stream(io)?; - - #[cfg(unix)] - let stream = TcpStream { - io_handle: IoHandle::new(mio_stream), - }; - - #[cfg(windows)] - let stream = TcpStream { - // raw_socket: mio_stream.as_raw_socket(), - io_handle: IoHandle::new(mio_stream), - }; - - Poll::Ready(Ok((stream, addr))) - } - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } - }) - .await + let mio_stream = mio::net::TcpStream::from_stream(io)?; + let stream = TcpStream { + watcher: Watcher::new(mio_stream), + }; + Ok((stream, addr)) } /// Returns a stream of incoming connections. @@ -201,7 +172,7 @@ impl TcpListener { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.io_handle.get_ref().local_addr() + self.watcher.get_ref().local_addr() } } @@ -235,19 +206,9 @@ impl From for TcpListener { /// Converts a `std::net::TcpListener` into its asynchronous equivalent. fn from(listener: std::net::TcpListener) -> TcpListener { let mio_listener = mio::net::TcpListener::from_std(listener).unwrap(); - - #[cfg(unix)] - let listener = TcpListener { - io_handle: IoHandle::new(mio_listener), - }; - - #[cfg(windows)] - let listener = TcpListener { - // raw_socket: mio_listener.as_raw_socket(), - io_handle: IoHandle::new(mio_listener), - }; - - listener + TcpListener { + watcher: Watcher::new(mio_listener), + } } } @@ -267,7 +228,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for TcpListener { fn as_raw_fd(&self) -> RawFd { - self.io_handle.get_ref().as_raw_fd() + self.watcher.get_ref().as_raw_fd() } } @@ -279,7 +240,7 @@ cfg_if! { impl IntoRawFd for TcpListener { fn into_raw_fd(self) -> RawFd { - self.io_handle.into_inner().into_raw_fd() + self.watcher.into_inner().into_raw_fd() } } } diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index fd8de9c..e4bef93 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -1,5 +1,4 @@ -use std::io::{IoSlice, IoSliceMut}; -use std::mem; +use std::io::{IoSlice, IoSliceMut, Read as _, Write as _}; use std::net::SocketAddr; use std::pin::Pin; @@ -8,8 +7,9 @@ use futures_io::{AsyncRead, AsyncWrite}; use crate::future; use crate::io; -use crate::net::driver::IoHandle; +use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; +use crate::task::blocking; use crate::task::{Context, Poll}; /// A TCP stream between a local and a remote socket. @@ -50,9 +50,7 @@ use crate::task::{Context, Poll}; /// ``` #[derive(Debug)] pub struct TcpStream { - pub(super) io_handle: IoHandle, - // #[cfg(windows)] - // pub(super) raw_socket: std::os::windows::io::RawSocket, + pub(super) watcher: Watcher, } impl TcpStream { @@ -79,7 +77,14 @@ impl TcpStream { let mut last_err = None; for addr in addrs.to_socket_addrs().await? { - let res = Self::connect_to(addr).await; + let res = blocking::spawn(async move { + let std_stream = std::net::TcpStream::connect(addr)?; + let mio_stream = mio::net::TcpStream::from_stream(std_stream)?; + Ok(TcpStream { + watcher: Watcher::new(mio_stream), + }) + }) + .await; match res { Ok(stream) => return Ok(stream), @@ -95,59 +100,6 @@ impl TcpStream { })) } - /// Creates a new TCP stream connected to the specified address. - async fn connect_to(addr: SocketAddr) -> io::Result { - let stream = mio::net::TcpStream::connect(&addr).map(|mio_stream| { - #[cfg(unix)] - let stream = TcpStream { - io_handle: IoHandle::new(mio_stream), - }; - - #[cfg(windows)] - let stream = TcpStream { - // raw_socket: mio_stream.as_raw_socket(), - io_handle: IoHandle::new(mio_stream), - }; - - stream - }); - - enum State { - Waiting(TcpStream), - Error(io::Error), - Done, - } - let mut state = match stream { - Ok(stream) => State::Waiting(stream), - Err(err) => State::Error(err), - }; - future::poll_fn(|cx| { - match mem::replace(&mut state, State::Done) { - State::Waiting(stream) => { - // Once we've connected, wait for the stream to be writable as that's when - // the actual connection has been initiated. Once we're writable we check - // for `take_socket_error` to see if the connect actually hit an error or - // not. - // - // If all that succeeded then we ship everything on up. - if let Poll::Pending = stream.io_handle.poll_writable(cx)? { - state = State::Waiting(stream); - return Poll::Pending; - } - - if let Some(err) = stream.io_handle.get_ref().take_error()? { - return Poll::Ready(Err(err)); - } - - Poll::Ready(Ok(stream)) - } - State::Error(err) => Poll::Ready(Err(err)), - State::Done => panic!("`TcpStream::connect_to()` future polled after completion"), - } - }) - .await - } - /// Returns the local address that this stream is connected to. /// /// ## Examples @@ -163,7 +115,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.io_handle.get_ref().local_addr() + self.watcher.get_ref().local_addr() } /// Returns the remote address that this stream is connected to. @@ -181,7 +133,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn peer_addr(&self) -> io::Result { - self.io_handle.get_ref().peer_addr() + self.watcher.get_ref().peer_addr() } /// Gets the value of the `IP_TTL` option for this socket. @@ -205,7 +157,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn ttl(&self) -> io::Result { - self.io_handle.get_ref().ttl() + self.watcher.get_ref().ttl() } /// Sets the value for the `IP_TTL` option on this socket. @@ -228,7 +180,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.io_handle.get_ref().set_ttl(ttl) + self.watcher.get_ref().set_ttl(ttl) } /// Receives data on the socket from the remote address to which it is connected, without @@ -254,20 +206,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub async fn peek(&self, buf: &mut [u8]) -> io::Result { - let res = future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_readable(cx)?); - - match self.io_handle.get_ref().peek(buf) { - Ok(len) => Poll::Ready(Ok(len)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(e) => Poll::Ready(Err(e)), - } - }) - .await?; - Ok(res) + future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.peek(buf))).await } /// Gets the value of the `TCP_NODELAY` option on this socket. @@ -291,7 +230,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn nodelay(&self) -> io::Result { - self.io_handle.get_ref().nodelay() + self.watcher.get_ref().nodelay() } /// Sets the value of the `TCP_NODELAY` option on this socket. @@ -317,7 +256,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> { - self.io_handle.get_ref().set_nodelay(nodelay) + self.watcher.get_ref().set_nodelay(nodelay) } /// Shuts down the read, write, or both halves of this connection. @@ -342,7 +281,7 @@ impl TcpStream { /// # Ok(()) }) } /// ``` pub fn shutdown(&self, how: std::net::Shutdown) -> std::io::Result<()> { - self.io_handle.get_ref().shutdown(how) + self.watcher.get_ref().shutdown(how) } } @@ -370,15 +309,7 @@ impl AsyncRead for &TcpStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Pin::new(&mut &self.io_handle).poll_read(cx, buf) - } - - fn poll_read_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &mut [IoSliceMut<'_>], - ) -> Poll> { - Pin::new(&mut &self.io_handle).poll_read_vectored(cx, bufs) + self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) } } @@ -414,23 +345,15 @@ impl AsyncWrite for &TcpStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Pin::new(&mut &self.io_handle).poll_write(cx, buf) - } - - fn poll_write_vectored( - self: Pin<&mut Self>, - cx: &mut Context<'_>, - bufs: &[IoSlice<'_>], - ) -> Poll> { - Pin::new(&mut &self.io_handle).poll_write_vectored(cx, bufs) + self.watcher.poll_write_with(cx, |mut inner| inner.write(buf)) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut &self.io_handle).poll_flush(cx) + self.watcher.poll_write_with(cx, |mut inner| inner.flush()) } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut &self.io_handle).poll_close(cx) + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } } @@ -438,19 +361,9 @@ impl From for TcpStream { /// Converts a `std::net::TcpStream` into its asynchronous equivalent. fn from(stream: std::net::TcpStream) -> TcpStream { let mio_stream = mio::net::TcpStream::from_stream(stream).unwrap(); - - #[cfg(unix)] - let stream = TcpStream { - io_handle: IoHandle::new(mio_stream), - }; - - #[cfg(windows)] - let stream = TcpStream { - // raw_socket: mio_stream.as_raw_socket(), - io_handle: IoHandle::new(mio_stream), - }; - - stream + TcpStream { + watcher: Watcher::new(mio_stream), + } } } @@ -470,7 +383,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for TcpStream { fn as_raw_fd(&self) -> RawFd { - self.io_handle.get_ref().as_raw_fd() + self.watcher.get_ref().as_raw_fd() } } @@ -482,7 +395,7 @@ cfg_if! { impl IntoRawFd for TcpStream { fn into_raw_fd(self) -> RawFd { - self.io_handle.into_inner().into_raw_fd() + self.watcher.into_inner().into_raw_fd() } } } diff --git a/src/net/udp/mod.rs b/src/net/udp/mod.rs index 9240484..a750899 100644 --- a/src/net/udp/mod.rs +++ b/src/net/udp/mod.rs @@ -5,9 +5,8 @@ use cfg_if::cfg_if; use std::net::{Ipv4Addr, Ipv6Addr}; use crate::future; -use crate::net::driver::IoHandle; +use crate::net::driver::Watcher; use crate::net::ToSocketAddrs; -use crate::task::Poll; /// A UDP socket. /// @@ -47,9 +46,7 @@ use crate::task::Poll; /// ``` #[derive(Debug)] pub struct UdpSocket { - io_handle: IoHandle, - // #[cfg(windows)] - // raw_socket: std::os::windows::io::RawSocket, + watcher: Watcher, } impl UdpSocket { @@ -77,18 +74,9 @@ impl UdpSocket { for addr in addr.to_socket_addrs().await? { match mio::net::UdpSocket::bind(&addr) { Ok(mio_socket) => { - #[cfg(unix)] - let socket = UdpSocket { - io_handle: IoHandle::new(mio_socket), - }; - - #[cfg(windows)] - let socket = UdpSocket { - // raw_socket: mio_socket.as_raw_socket(), - io_handle: IoHandle::new(mio_socket), - }; - - return Ok(socket); + return Ok(UdpSocket { + watcher: Watcher::new(mio_socket), + }); } Err(err) => last_err = Some(err), } @@ -120,7 +108,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.io_handle.get_ref().local_addr() + self.watcher.get_ref().local_addr() } /// Sends data on the socket to the given address. @@ -161,16 +149,8 @@ impl UdpSocket { }; future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_writable(cx)?); - - match self.io_handle.get_ref().send_to(buf, &addr) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_writable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } + self.watcher + .poll_write_with(cx, |inner| inner.send_to(buf, &addr)) }) .await } @@ -196,16 +176,8 @@ impl UdpSocket { /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_readable(cx)?); - - match self.io_handle.get_ref().recv_from(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } + self.watcher + .poll_read_with(cx, |inner| inner.recv_from(buf)) }) .await } @@ -236,7 +208,8 @@ impl UdpSocket { let mut last_err = None; for addr in addrs.to_socket_addrs().await? { - match self.io_handle.get_ref().connect(addr) { + // TODO(stjepang): connect on the blocking pool + match self.watcher.get_ref().connect(addr) { Ok(()) => return Ok(()), Err(err) => last_err = Some(err), } @@ -277,19 +250,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_writable(cx)?); - - match self.io_handle.get_ref().send(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_writable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } - }) - .await + future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await } /// Receives data from the socket. @@ -312,19 +273,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_readable(cx)?); - - match self.io_handle.get_ref().recv(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } - }) - .await + future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await } /// Gets the value of the `SO_BROADCAST` option for this socket. @@ -333,14 +282,14 @@ impl UdpSocket { /// /// [`set_broadcast`]: #method.set_broadcast pub fn broadcast(&self) -> io::Result { - self.io_handle.get_ref().broadcast() + self.watcher.get_ref().broadcast() } /// Sets the value of the `SO_BROADCAST` option for this socket. /// /// When enabled, this socket is allowed to send packets to a broadcast address. pub fn set_broadcast(&self, on: bool) -> io::Result<()> { - self.io_handle.get_ref().set_broadcast(on) + self.watcher.get_ref().set_broadcast(on) } /// Gets the value of the `IP_MULTICAST_LOOP` option for this socket. @@ -349,7 +298,7 @@ impl UdpSocket { /// /// [`set_multicast_loop_v4`]: #method.set_multicast_loop_v4 pub fn multicast_loop_v4(&self) -> io::Result { - self.io_handle.get_ref().multicast_loop_v4() + self.watcher.get_ref().multicast_loop_v4() } /// Sets the value of the `IP_MULTICAST_LOOP` option for this socket. @@ -360,7 +309,7 @@ impl UdpSocket { /// /// This may not have any affect on IPv6 sockets. pub fn set_multicast_loop_v4(&self, on: bool) -> io::Result<()> { - self.io_handle.get_ref().set_multicast_loop_v4(on) + self.watcher.get_ref().set_multicast_loop_v4(on) } /// Gets the value of the `IP_MULTICAST_TTL` option for this socket. @@ -369,7 +318,7 @@ impl UdpSocket { /// /// [`set_multicast_ttl_v4`]: #method.set_multicast_ttl_v4 pub fn multicast_ttl_v4(&self) -> io::Result { - self.io_handle.get_ref().multicast_ttl_v4() + self.watcher.get_ref().multicast_ttl_v4() } /// Sets the value of the `IP_MULTICAST_TTL` option for this socket. @@ -382,7 +331,7 @@ impl UdpSocket { /// /// This may not have any affect on IPv6 sockets. pub fn set_multicast_ttl_v4(&self, ttl: u32) -> io::Result<()> { - self.io_handle.get_ref().set_multicast_ttl_v4(ttl) + self.watcher.get_ref().set_multicast_ttl_v4(ttl) } /// Gets the value of the `IPV6_MULTICAST_LOOP` option for this socket. @@ -391,7 +340,7 @@ impl UdpSocket { /// /// [`set_multicast_loop_v6`]: #method.set_multicast_loop_v6 pub fn multicast_loop_v6(&self) -> io::Result { - self.io_handle.get_ref().multicast_loop_v6() + self.watcher.get_ref().multicast_loop_v6() } /// Sets the value of the `IPV6_MULTICAST_LOOP` option for this socket. @@ -402,7 +351,7 @@ impl UdpSocket { /// /// This may not have any affect on IPv4 sockets. pub fn set_multicast_loop_v6(&self, on: bool) -> io::Result<()> { - self.io_handle.get_ref().set_multicast_loop_v6(on) + self.watcher.get_ref().set_multicast_loop_v6(on) } /// Gets the value of the `IP_TTL` option for this socket. @@ -411,7 +360,7 @@ impl UdpSocket { /// /// [`set_ttl`]: #method.set_ttl pub fn ttl(&self) -> io::Result { - self.io_handle.get_ref().ttl() + self.watcher.get_ref().ttl() } /// Sets the value for the `IP_TTL` option on this socket. @@ -419,7 +368,7 @@ impl UdpSocket { /// This value sets the time-to-live field that is used in every packet sent /// from this socket. pub fn set_ttl(&self, ttl: u32) -> io::Result<()> { - self.io_handle.get_ref().set_ttl(ttl) + self.watcher.get_ref().set_ttl(ttl) } /// Executes an operation of the `IP_ADD_MEMBERSHIP` type. @@ -447,7 +396,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub fn join_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - self.io_handle + self.watcher .get_ref() .join_multicast_v4(multiaddr, interface) } @@ -476,7 +425,7 @@ impl UdpSocket { /// # Ok(()) }) } /// ``` pub fn join_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - self.io_handle + self.watcher .get_ref() .join_multicast_v6(multiaddr, interface) } @@ -487,7 +436,7 @@ impl UdpSocket { /// /// [`join_multicast_v4`]: #method.join_multicast_v4 pub fn leave_multicast_v4(&self, multiaddr: &Ipv4Addr, interface: &Ipv4Addr) -> io::Result<()> { - self.io_handle + self.watcher .get_ref() .leave_multicast_v4(multiaddr, interface) } @@ -498,7 +447,7 @@ impl UdpSocket { /// /// [`join_multicast_v6`]: #method.join_multicast_v6 pub fn leave_multicast_v6(&self, multiaddr: &Ipv6Addr, interface: u32) -> io::Result<()> { - self.io_handle + self.watcher .get_ref() .leave_multicast_v6(multiaddr, interface) } @@ -508,19 +457,9 @@ impl From for UdpSocket { /// Converts a `std::net::UdpSocket` into its asynchronous equivalent. fn from(socket: std::net::UdpSocket) -> UdpSocket { let mio_socket = mio::net::UdpSocket::from_socket(socket).unwrap(); - - #[cfg(unix)] - let socket = UdpSocket { - io_handle: IoHandle::new(mio_socket), - }; - - #[cfg(windows)] - let socket = UdpSocket { - // raw_socket: mio_socket.as_raw_socket(), - io_handle: IoHandle::new(mio_socket), - }; - - socket + UdpSocket { + watcher: Watcher::new(mio_socket), + } } } @@ -540,7 +479,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl AsRawFd for UdpSocket { fn as_raw_fd(&self) -> RawFd { - self.io_handle.get_ref().as_raw_fd() + self.watcher.get_ref().as_raw_fd() } } @@ -552,7 +491,7 @@ cfg_if! { impl IntoRawFd for UdpSocket { fn into_raw_fd(self) -> RawFd { - self.io_handle.into_inner().into_raw_fd() + self.watcher.into_inner().into_raw_fd() } } } diff --git a/src/os/unix/net/datagram.rs b/src/os/unix/net/datagram.rs index 6b6a7b4..1f971f7 100644 --- a/src/os/unix/net/datagram.rs +++ b/src/os/unix/net/datagram.rs @@ -9,9 +9,9 @@ use mio_uds; use super::SocketAddr; use crate::future; use crate::io; -use crate::net::driver::IoHandle; +use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; -use crate::task::{blocking, Poll}; +use crate::task::blocking; /// A Unix datagram socket. /// @@ -42,15 +42,13 @@ use crate::task::{blocking, Poll}; /// # Ok(()) }) } /// ``` pub struct UnixDatagram { - #[cfg(not(feature = "docs"))] - io_handle: IoHandle, + watcher: Watcher, } impl UnixDatagram { - #[cfg(not(feature = "docs"))] fn new(socket: mio_uds::UnixDatagram) -> UnixDatagram { UnixDatagram { - io_handle: IoHandle::new(socket), + watcher: Watcher::new(socket), } } @@ -137,7 +135,7 @@ impl UnixDatagram { pub async fn connect>(&self, path: P) -> io::Result<()> { // TODO(stjepang): Connect the socket on a blocking pool. let p = path.as_ref(); - self.io_handle.get_ref().connect(p) + self.watcher.get_ref().connect(p) } /// Returns the address of this socket. @@ -155,7 +153,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.io_handle.get_ref().local_addr() + self.watcher.get_ref().local_addr() } /// Returns the address of this socket's peer. @@ -178,7 +176,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn peer_addr(&self) -> io::Result { - self.io_handle.get_ref().peer_addr() + self.watcher.get_ref().peer_addr() } /// Receives data from the socket. @@ -200,16 +198,8 @@ impl UnixDatagram { /// ``` pub async fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, SocketAddr)> { future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_readable(cx)?); - - match self.io_handle.get_ref().recv_from(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } + self.watcher + .poll_read_with(cx, |inner| inner.recv_from(buf)) }) .await } @@ -232,19 +222,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn recv(&self, buf: &mut [u8]) -> io::Result { - future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_writable(cx)?); - - match self.io_handle.get_ref().recv(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_writable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } - }) - .await + future::poll_fn(|cx| self.watcher.poll_read_with(cx, |inner| inner.recv(buf))).await } /// Sends data on the socket to the specified address. @@ -265,16 +243,8 @@ impl UnixDatagram { /// ``` pub async fn send_to>(&self, buf: &[u8], path: P) -> io::Result { future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_writable(cx)?); - - match self.io_handle.get_ref().send_to(buf, path.as_ref()) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_writable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } + self.watcher + .poll_write_with(cx, |inner| inner.send_to(buf, path.as_ref())) }) .await } @@ -297,19 +267,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub async fn send(&self, buf: &[u8]) -> io::Result { - future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_writable(cx)?); - - match self.io_handle.get_ref().send(buf) { - Ok(n) => Poll::Ready(Ok(n)), - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_writable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), - } - }) - .await + future::poll_fn(|cx| self.watcher.poll_write_with(cx, |inner| inner.send(buf))).await } /// Shut down the read, write, or both halves of this connection. @@ -333,7 +291,7 @@ impl UnixDatagram { /// # Ok(()) }) } /// ``` pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.io_handle.get_ref().shutdown(how) + self.watcher.get_ref().shutdown(how) } } @@ -359,14 +317,14 @@ impl From for UnixDatagram { fn from(datagram: std::os::unix::net::UnixDatagram) -> UnixDatagram { let mio_datagram = mio_uds::UnixDatagram::from_datagram(datagram).unwrap(); UnixDatagram { - io_handle: IoHandle::new(mio_datagram), + watcher: Watcher::new(mio_datagram), } } } impl AsRawFd for UnixDatagram { fn as_raw_fd(&self) -> RawFd { - self.io_handle.get_ref().as_raw_fd() + self.watcher.get_ref().as_raw_fd() } } @@ -379,6 +337,6 @@ impl FromRawFd for UnixDatagram { impl IntoRawFd for UnixDatagram { fn into_raw_fd(self) -> RawFd { - self.io_handle.into_inner().into_raw_fd() + self.watcher.into_inner().into_raw_fd() } } diff --git a/src/os/unix/net/listener.rs b/src/os/unix/net/listener.rs index 5710769..78142a4 100644 --- a/src/os/unix/net/listener.rs +++ b/src/os/unix/net/listener.rs @@ -10,7 +10,7 @@ use super::SocketAddr; use super::UnixStream; use crate::future::{self, Future}; use crate::io; -use crate::net::driver::IoHandle; +use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::task::{blocking, Context, Poll}; @@ -48,8 +48,7 @@ use crate::task::{blocking, Context, Poll}; /// # Ok(()) }) } /// ``` pub struct UnixListener { - #[cfg(not(feature = "docs"))] - io_handle: IoHandle, + watcher: Watcher, } impl UnixListener { @@ -71,7 +70,7 @@ impl UnixListener { let listener = blocking::spawn(async move { mio_uds::UnixListener::bind(path) }).await?; Ok(UnixListener { - io_handle: IoHandle::new(listener), + watcher: Watcher::new(listener), }) } @@ -93,25 +92,18 @@ impl UnixListener { /// ``` pub async fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { future::poll_fn(|cx| { - futures_core::ready!(self.io_handle.poll_readable(cx)?); + let res = + futures_core::ready!(self.watcher.poll_read_with(cx, |inner| inner.accept_std())); - match self.io_handle.get_ref().accept_std() { - Ok(Some((io, addr))) => { + match res? { + None => Poll::Pending, + Some((io, addr)) => { let mio_stream = mio_uds::UnixStream::from_stream(io)?; let stream = UnixStream { - io_handle: IoHandle::new(mio_stream), + watcher: Watcher::new(mio_stream), }; Poll::Ready(Ok((stream, addr))) } - Ok(None) => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(ref err) if err.kind() == io::ErrorKind::WouldBlock => { - self.io_handle.clear_readable(cx)?; - Poll::Pending - } - Err(err) => Poll::Ready(Err(err)), } }) .await @@ -162,7 +154,7 @@ impl UnixListener { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.io_handle.get_ref().local_addr() + self.watcher.get_ref().local_addr() } } @@ -210,14 +202,14 @@ impl From for UnixListener { fn from(listener: std::os::unix::net::UnixListener) -> UnixListener { let mio_listener = mio_uds::UnixListener::from_listener(listener).unwrap(); UnixListener { - io_handle: IoHandle::new(mio_listener), + watcher: Watcher::new(mio_listener), } } } impl AsRawFd for UnixListener { fn as_raw_fd(&self) -> RawFd { - self.io_handle.get_ref().as_raw_fd() + self.watcher.get_ref().as_raw_fd() } } @@ -230,6 +222,6 @@ impl FromRawFd for UnixListener { impl IntoRawFd for UnixListener { fn into_raw_fd(self) -> RawFd { - self.io_handle.into_inner().into_raw_fd() + self.watcher.into_inner().into_raw_fd() } } diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index 05a3139..d4e6e64 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -1,18 +1,17 @@ //! Unix-specific networking extensions. use std::fmt; -use std::mem; use std::net::Shutdown; use std::path::Path; +use std::io::{Read as _, Write as _}; use std::pin::Pin; use futures_io::{AsyncRead, AsyncWrite}; use mio_uds; use super::SocketAddr; -use crate::future; use crate::io; -use crate::net::driver::IoHandle; +use crate::net::driver::Watcher; use crate::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; use crate::task::{blocking, Context, Poll}; @@ -40,8 +39,7 @@ use crate::task::{blocking, Context, Poll}; /// # Ok(()) }) } /// ``` pub struct UnixStream { - #[cfg(not(feature = "docs"))] - pub(super) io_handle: IoHandle, + pub(super) watcher: Watcher, } impl UnixStream { @@ -59,46 +57,14 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub async fn connect>(path: P) -> io::Result { - enum State { - Waiting(UnixStream), - Error(io::Error), - Done, - } - let path = path.as_ref().to_owned(); - let mut state = { - match blocking::spawn(async move { mio_uds::UnixStream::connect(path) }).await { - Ok(mio_stream) => State::Waiting(UnixStream { - io_handle: IoHandle::new(mio_stream), - }), - Err(err) => State::Error(err), - } - }; - future::poll_fn(|cx| { - match &mut state { - State::Waiting(stream) => { - futures_core::ready!(stream.io_handle.poll_writable(cx)?); - - if let Some(err) = stream.io_handle.get_ref().take_error()? { - return Poll::Ready(Err(err)); - } - } - State::Error(_) => { - let err = match mem::replace(&mut state, State::Done) { - State::Error(err) => err, - _ => unreachable!(), - }; - - return Poll::Ready(Err(err)); - } - State::Done => panic!("`UnixStream::connect()` future polled after completion"), - } - - match mem::replace(&mut state, State::Done) { - State::Waiting(stream) => Poll::Ready(Ok(stream)), - _ => unreachable!(), - } + blocking::spawn(async move { + 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 } @@ -121,10 +87,10 @@ impl UnixStream { pub fn pair() -> io::Result<(UnixStream, UnixStream)> { let (a, b) = mio_uds::UnixStream::pair()?; let a = UnixStream { - io_handle: IoHandle::new(a), + watcher: Watcher::new(a), }; let b = UnixStream { - io_handle: IoHandle::new(b), + watcher: Watcher::new(b), }; Ok((a, b)) } @@ -144,7 +110,7 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn local_addr(&self) -> io::Result { - self.io_handle.get_ref().local_addr() + self.watcher.get_ref().local_addr() } /// Returns the socket address of the remote half of this connection. @@ -162,7 +128,7 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn peer_addr(&self) -> io::Result { - self.io_handle.get_ref().peer_addr() + self.watcher.get_ref().peer_addr() } /// Shuts down the read, write, or both halves of this connection. @@ -184,7 +150,7 @@ impl UnixStream { /// # Ok(()) }) } /// ``` pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { - self.io_handle.get_ref().shutdown(how) + self.watcher.get_ref().shutdown(how) } } @@ -204,7 +170,7 @@ impl AsyncRead for &UnixStream { cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { - Pin::new(&mut &self.io_handle).poll_read(cx, buf) + self.watcher.poll_read_with(cx, |mut inner| inner.read(buf)) } } @@ -232,15 +198,15 @@ impl AsyncWrite for &UnixStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - Pin::new(&mut &self.io_handle).poll_write(cx, buf) + self.watcher.poll_write_with(cx, |mut inner| inner.write(buf)) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut &self.io_handle).poll_flush(cx) + self.watcher.poll_write_with(cx, |mut inner| inner.flush()) } - fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { - Pin::new(&mut &self.io_handle).poll_close(cx) + fn poll_close(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(Ok(())) } } @@ -266,14 +232,14 @@ impl From for UnixStream { fn from(stream: std::os::unix::net::UnixStream) -> UnixStream { let mio_stream = mio_uds::UnixStream::from_stream(stream).unwrap(); UnixStream { - io_handle: IoHandle::new(mio_stream), + watcher: Watcher::new(mio_stream), } } } impl AsRawFd for UnixStream { fn as_raw_fd(&self) -> RawFd { - self.io_handle.get_ref().as_raw_fd() + self.watcher.get_ref().as_raw_fd() } } @@ -286,6 +252,6 @@ impl FromRawFd for UnixStream { impl IntoRawFd for UnixStream { fn into_raw_fd(self) -> RawFd { - self.io_handle.into_inner().into_raw_fd() + self.watcher.into_inner().into_raw_fd() } } From 5429c2c0a3e2eaf3c2a2855ad9c98157014b5a67 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 12 Sep 2019 18:49:09 +0200 Subject: [PATCH 098/194] cargo fmt --- src/net/tcp/stream.rs | 3 ++- src/os/unix/net/stream.rs | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/net/tcp/stream.rs b/src/net/tcp/stream.rs index e4bef93..7c10602 100644 --- a/src/net/tcp/stream.rs +++ b/src/net/tcp/stream.rs @@ -345,7 +345,8 @@ impl AsyncWrite for &TcpStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.watcher.poll_write_with(cx, |mut inner| inner.write(buf)) + self.watcher + .poll_write_with(cx, |mut inner| inner.write(buf)) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { diff --git a/src/os/unix/net/stream.rs b/src/os/unix/net/stream.rs index d4e6e64..28f43eb 100644 --- a/src/os/unix/net/stream.rs +++ b/src/os/unix/net/stream.rs @@ -1,9 +1,9 @@ //! Unix-specific networking extensions. use std::fmt; +use std::io::{Read as _, Write as _}; use std::net::Shutdown; use std::path::Path; -use std::io::{Read as _, Write as _}; use std::pin::Pin; use futures_io::{AsyncRead, AsyncWrite}; @@ -198,7 +198,8 @@ impl AsyncWrite for &UnixStream { cx: &mut Context<'_>, buf: &[u8], ) -> Poll> { - self.watcher.poll_write_with(cx, |mut inner| inner.write(buf)) + self.watcher + .poll_write_with(cx, |mut inner| inner.write(buf)) } fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { From a7788d461068623acfa734e24c60eccc33cc1ea4 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 12 Sep 2019 18:47:21 +0100 Subject: [PATCH 099/194] Update .travis.yml run CI only on specific branches --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index d633045..19b3e58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,6 +12,13 @@ cache: before_cache: - rm -rf /home/travis/.cargo/registry + +branches: + only: + - master + - staging + - trying + matrix: fast_finish: true include: From 2818c7099fd4e39bf48b25591add106649db1d81 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Thu, 12 Sep 2019 23:24:20 +0200 Subject: [PATCH 100/194] Suppress a lint that makes CI fail on windows --- src/net/driver/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net/driver/mod.rs b/src/net/driver/mod.rs index be31ec9..806acdb 100644 --- a/src/net/driver/mod.rs +++ b/src/net/driver/mod.rs @@ -263,6 +263,7 @@ impl Watcher { /// 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 From ab0a4cb966087fff0520ccb13d3c4c88e6b02c58 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Sep 2019 13:18:07 +0200 Subject: [PATCH 101/194] remove pin bounds from consume Signed-off-by: Yoshua Wuyts --- src/io/buf_read/mod.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 6018439..65741fd 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -63,6 +63,15 @@ pub trait BufRead { FillBufFuture::new(self) } + /// Tells this buffer that `amt` bytes have been consumed from the buffer, so they should no + /// longer be returned in calls to `read`. + fn consume(&mut self, amt: usize) + where + Self: AsyncBufRead + Unpin, + { + AsyncBufRead::consume(Pin::new(self), amt) + } + /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. /// /// This function will read bytes from the underlying stream until the delimiter or EOF is From 10fedfe97fc0c7e6b76790da53c3d5053dde6636 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 13 Sep 2019 02:20:58 +0200 Subject: [PATCH 102/194] implement feedback for bufreader methods Signed-off-by: Yoshua Wuyts --- src/io/buf_read/mod.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/io/buf_read/mod.rs b/src/io/buf_read/mod.rs index 65741fd..b65d177 100644 --- a/src/io/buf_read/mod.rs +++ b/src/io/buf_read/mod.rs @@ -43,6 +43,12 @@ cfg_if! { /// [`futures::io::AsyncBufRead`]: /// https://docs.rs/futures-preview/0.3.0-alpha.17/futures/io/trait.AsyncBufRead.html pub trait BufRead { + /// Tells this buffer that `amt` bytes have been consumed from the buffer, so they should no + /// longer be returned in calls to `read`. + fn consume(&mut self, amt: usize) + where + Self: Unpin; + /// Returns the contents of the internal buffer, filling it with more data from the inner /// reader if it is empty. /// @@ -63,15 +69,6 @@ pub trait BufRead { FillBufFuture::new(self) } - /// Tells this buffer that `amt` bytes have been consumed from the buffer, so they should no - /// longer be returned in calls to `read`. - fn consume(&mut self, amt: usize) - where - Self: AsyncBufRead + Unpin, - { - AsyncBufRead::consume(Pin::new(self), amt) - } - /// Reads all bytes into `buf` until the delimiter `byte` or EOF is reached. /// /// This function will read bytes from the underlying stream until the delimiter or EOF is @@ -226,7 +223,11 @@ pub trait BufRead { } } -impl BufRead for T {} +impl BufRead for T { + fn consume(&mut self, amt: usize) { + AsyncBufRead::consume(Pin::new(self), amt) + } +} pub fn read_until_internal( mut reader: Pin<&mut R>, From d68d6bb05226c72f33927ca394160991dcd81a8d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 13 Sep 2019 17:58:03 +0200 Subject: [PATCH 103/194] links the timeout docs to each other Signed-off-by: Yoshua Wuyts --- src/future/timeout.rs | 3 +++ src/io/timeout.rs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/future/timeout.rs b/src/future/timeout.rs index cf146ae..eb7d8ba 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -10,6 +10,9 @@ use crate::task::{Context, Poll}; /// Awaits a future or times out after a duration of time. /// +/// If you want to await an I/O future consider using +/// [`io::timeout`](../io/fn.timeout.html) instead. +/// /// # Examples /// /// ``` diff --git a/src/io/timeout.rs b/src/io/timeout.rs index 6d77737..2d1b29e 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -7,6 +7,9 @@ use crate::io; /// Awaits an I/O future or times out after a duration of time. /// +/// If you want to await a non I/O future consider using +/// [`future::timeout`](../future/fn.timeout.html) instead. +/// /// # Examples /// /// ```no_run From 23ca060e4c7eadb0f597e4e799503fe1accb692d Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Sep 2019 17:55:07 +0200 Subject: [PATCH 104/194] implement DoubleEndedStream Signed-off-by: Yoshua Wuyts --- src/stream/double_ended_stream.rs | 22 ++++++++++++++++++++++ src/stream/mod.rs | 2 ++ 2 files changed, 24 insertions(+) create mode 100644 src/stream/double_ended_stream.rs diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs new file mode 100644 index 0000000..6f41e61 --- /dev/null +++ b/src/stream/double_ended_stream.rs @@ -0,0 +1,22 @@ +use crate::stream::Stream; + +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// An stream able to yield elements from both ends. +/// +/// Something that implements `DoubleEndedStream` has one extra capability +/// over something that implements [`Stream`]: the ability to also take +/// `Item`s from the back, as well as the front. +/// +/// [`Stream`]: trait.Stream.html +pub trait DoubleEndedStream: Stream { + /// Removes and returns an element from the end of the stream. + /// + /// Returns `None` when there are no more elements. + /// + /// The [trait-level] docs contain more details. + /// + /// [trait-level]: trait.DoubleEndedStream.html + fn poll_next_back(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>; +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 8dcc6d5..38fa206 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -25,8 +25,10 @@ pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use stream::{Stream, Take}; +pub use double_ended_stream::DoubleEndedStream; mod empty; mod once; mod repeat; mod stream; +mod double_ended_stream; From 40fb485ccac720f7359fb15c2b88b020ebe1e4bc Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 11 Sep 2019 14:32:06 +0200 Subject: [PATCH 105/194] cargo fmt Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 38fa206..6081912 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -21,14 +21,14 @@ //! # }) } //! ``` +pub use double_ended_stream::DoubleEndedStream; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use stream::{Stream, Take}; -pub use double_ended_stream::DoubleEndedStream; +mod double_ended_stream; mod empty; mod once; mod repeat; mod stream; -mod double_ended_stream; From fda74ac0ab36156a09ec6ac1a73f6e73444bafb4 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 13 Sep 2019 19:36:57 +0200 Subject: [PATCH 106/194] mark as unstable Signed-off-by: Yoshua Wuyts --- src/stream/double_ended_stream.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index 6f41e61..2287cc6 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -3,13 +3,14 @@ use crate::stream::Stream; use std::pin::Pin; use std::task::{Context, Poll}; -/// An stream able to yield elements from both ends. +/// A stream able to yield elements from both ends. /// /// Something that implements `DoubleEndedStream` has one extra capability /// over something that implements [`Stream`]: the ability to also take /// `Item`s from the back, as well as the front. /// /// [`Stream`]: trait.Stream.html +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait DoubleEndedStream: Stream { /// Removes and returns an element from the end of the stream. /// From 0bc39e6e6c0143a97a6d340d5d7fd8f0ba0bb406 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 9 Sep 2019 00:23:13 +0200 Subject: [PATCH 107/194] add io::cursor Signed-off-by: Yoshua Wuyts --- src/io/cursor.rs | 261 ++++++++++++++++++++++++++++++++++++++++++++++ src/io/mod.rs | 2 + src/io/prelude.rs | 8 +- 3 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 src/io/cursor.rs diff --git a/src/io/cursor.rs b/src/io/cursor.rs new file mode 100644 index 0000000..c271a83 --- /dev/null +++ b/src/io/cursor.rs @@ -0,0 +1,261 @@ +use futures_io::{AsyncRead, AsyncSeek, AsyncWrite}; + +use std::io::{self, IoSlice, IoSliceMut, SeekFrom}; +use std::pin::Pin; +use std::task::{Context, Poll}; + +/// A `Cursor` wraps an in-memory buffer and provides it with a +/// [`Seek`] implementation. +/// +/// `Cursor`s are used with in-memory buffers, anything implementing +/// `AsRef<[u8]>`, to allow them to implement [`Read`] and/or [`Write`], +/// allowing these buffers to be used anywhere you might use a reader or writer +/// that does actual I/O. +/// +/// The standard library implements some I/O traits on various types which +/// are commonly used as a buffer, like `Cursor<`[`Vec`]`>` and +/// `Cursor<`[`&[u8]`][bytes]`>`. +/// +/// [`Seek`]: trait.Seek.html +/// [`Read`]: trait.Read.html +/// [`Write`]: trait.Write.html +/// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html +/// [bytes]: https://doc.rust-lang.org/std/primitive.slice.html +/// [`File`]: struct.File.html +#[derive(Clone, Debug, Default)] +pub struct Cursor { + inner: std::io::Cursor, +} + +impl Cursor { + /// Creates a new cursor wrapping the provided underlying in-memory buffer. + /// + /// Cursor initial position is `0` even if underlying buffer (e.g., `Vec`) + /// is not empty. So writing to cursor starts with overwriting `Vec` + /// content, not with appending to it. + /// + /// # Examples + /// + /// ``` + /// use async_std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// ``` + pub fn new(inner: T) -> Cursor { + Cursor { + inner: std::io::Cursor::new(inner), + } + } + + /// Consumes this cursor, returning the underlying value. + /// + /// # Examples + /// + /// ``` + /// use async_std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let vec = buff.into_inner(); + /// ``` + pub fn into_inner(self) -> T { + self.inner.into_inner() + } + + /// Gets a reference to the underlying value in this cursor. + /// + /// # Examples + /// + /// ``` + /// use async_std::io::Cursor; + /// + /// let buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let reference = buff.get_ref(); + /// ``` + pub fn get_ref(&self) -> &T { + self.inner.get_ref() + } + + /// Gets a mutable reference to the underlying value in this cursor. + /// + /// Care should be taken to avoid modifying the internal I/O state of the + /// underlying value as it may corrupt this cursor's position. + /// + /// # Examples + /// + /// ``` + /// use async_std::io::Cursor; + /// + /// let mut buff = Cursor::new(Vec::new()); + /// # fn force_inference(_: &Cursor>) {} + /// # force_inference(&buff); + /// + /// let reference = buff.get_mut(); + /// ``` + pub fn get_mut(&mut self) -> &mut T { + self.inner.get_mut() + } + + /// Returns the current position of this cursor. + /// + /// # Examples + /// + /// ``` + /// use async_std::io::Cursor; + /// use async_std::io::prelude::*; + /// use async_std::io::SeekFrom; + /// + /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(buff.position(), 0); + /// + /// buff.seek(SeekFrom::Current(2)).unwrap(); + /// assert_eq!(buff.position(), 2); + /// + /// buff.seek(SeekFrom::Current(-1)).unwrap(); + /// assert_eq!(buff.position(), 1); + /// ``` + pub fn position(&self) -> u64 { + self.inner.position() + } + + /// Sets the position of this cursor. + /// + /// # Examples + /// + /// ``` + /// use async_std::io::Cursor; + /// + /// let mut buff = Cursor::new(vec![1, 2, 3, 4, 5]); + /// + /// assert_eq!(buff.position(), 0); + /// + /// buff.set_position(2); + /// assert_eq!(buff.position(), 2); + /// + /// buff.set_position(4); + /// assert_eq!(buff.position(), 4); + /// ``` + pub fn set_position(&mut self, pos: u64) { + self.inner.set_position(pos) + } +} + +impl AsyncSeek for Cursor +where + T: AsRef<[u8]> + Unpin, +{ + fn poll_seek( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + pos: SeekFrom, + ) -> Poll> { + Poll::Ready(io::Seek::seek(&mut self.inner, pos)) + } +} + +impl AsyncRead for Cursor +where + T: AsRef<[u8]> + Unpin, +{ + fn poll_read( + mut self: Pin<&mut Self>, + _cx: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + Poll::Ready(io::Read::read(&mut self.inner, buf)) + } + + fn poll_read_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &mut [IoSliceMut<'_>], + ) -> Poll> { + Poll::Ready(io::Read::read_vectored(&mut self.inner, bufs)) + } +} + +// impl AsyncBufRead for Cursor +// where +// T: AsRef<[u8]> + Unpin, +// { +// fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { +// // let amt = cmp::min(self.position(), self.as_ref().len() as u64); +// // Poll::Ready(Ok(&self.inner.as_ref()[(amt as usize)..])) +// let res = io::BufRead::fill_buf(&mut self.inner); +// Poll::Ready(res) +// } + +// fn consume(mut self: Pin<&mut Self>, amt: usize) { +// io::BufRead::consume(&mut self.inner, amt) +// } +// } + +impl AsyncWrite for Cursor<&mut [u8]> { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(io::Write::write(&mut self.inner, buf)) + } + + fn poll_write_vectored( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + bufs: &[IoSlice<'_>], + ) -> Poll> { + Poll::Ready(io::Write::write_vectored(&mut self.inner, bufs)) + } + + fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(io::Write::flush(&mut self.inner)) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } +} + +impl AsyncWrite for Cursor<&mut Vec> { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(io::Write::write(&mut self.inner, buf)) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } + + fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(io::Write::flush(&mut self.inner)) + } +} + +impl AsyncWrite for Cursor> { + fn poll_write( + mut self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &[u8], + ) -> Poll> { + Poll::Ready(io::Write::write(&mut self.inner, buf)) + } + + fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + self.poll_flush(cx) + } + + fn poll_flush(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(io::Write::flush(&mut self.inner)) + } +} diff --git a/src/io/mod.rs b/src/io/mod.rs index 5152b03..e0e97dd 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -28,6 +28,7 @@ pub use std::io::{Error, ErrorKind, Result, SeekFrom}; pub use buf_read::{BufRead, Lines}; pub use buf_reader::BufReader; pub use copy::copy; +pub use cursor::Cursor; pub use empty::{empty, Empty}; pub use read::Read; pub use seek::Seek; @@ -41,6 +42,7 @@ pub use write::Write; mod buf_read; mod buf_reader; mod copy; +mod cursor; mod empty; mod read; mod seek; diff --git a/src/io/prelude.rs b/src/io/prelude.rs index 65438e1..490be04 100644 --- a/src/io/prelude.rs +++ b/src/io/prelude.rs @@ -9,10 +9,10 @@ //! ``` #[doc(no_inline)] -pub use super::BufRead as _; +pub use super::BufRead; #[doc(no_inline)] -pub use super::Read as _; +pub use super::Read; #[doc(no_inline)] -pub use super::Seek as _; +pub use super::Seek; #[doc(no_inline)] -pub use super::Write as _; +pub use super::Write; From a5b0acb378c0fb0702766764d02fa1f80970a1e2 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Sep 2019 14:20:12 +0200 Subject: [PATCH 108/194] AsyncBufRead for Cursor Signed-off-by: Yoshua Wuyts --- src/io/cursor.rs | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/src/io/cursor.rs b/src/io/cursor.rs index c271a83..33a643b 100644 --- a/src/io/cursor.rs +++ b/src/io/cursor.rs @@ -1,4 +1,4 @@ -use futures_io::{AsyncRead, AsyncSeek, AsyncWrite}; +use futures_io::{AsyncBufRead, AsyncRead, AsyncSeek, AsyncWrite}; use std::io::{self, IoSlice, IoSliceMut, SeekFrom}; use std::pin::Pin; @@ -182,21 +182,18 @@ where } } -// impl AsyncBufRead for Cursor -// where -// T: AsRef<[u8]> + Unpin, -// { -// fn poll_fill_buf(mut self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { -// // let amt = cmp::min(self.position(), self.as_ref().len() as u64); -// // Poll::Ready(Ok(&self.inner.as_ref()[(amt as usize)..])) -// let res = io::BufRead::fill_buf(&mut self.inner); -// Poll::Ready(res) -// } +impl AsyncBufRead for Cursor +where + T: AsRef<[u8]> + Unpin, +{ + fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + Poll::Ready(io::BufRead::fill_buf(&mut self.get_mut().inner)) + } -// fn consume(mut self: Pin<&mut Self>, amt: usize) { -// io::BufRead::consume(&mut self.inner, amt) -// } -// } + fn consume(mut self: Pin<&mut Self>, amt: usize) { + io::BufRead::consume(&mut self.inner, amt) + } +} impl AsyncWrite for Cursor<&mut [u8]> { fn poll_write( From 69c9162558fed95f8b7f47b5088701f7b1c68145 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Sep 2019 14:23:00 +0200 Subject: [PATCH 109/194] fix tests Signed-off-by: Yoshua Wuyts --- src/io/cursor.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/io/cursor.rs b/src/io/cursor.rs index 33a643b..95b13ec 100644 --- a/src/io/cursor.rs +++ b/src/io/cursor.rs @@ -108,6 +108,8 @@ impl Cursor { /// # Examples /// /// ``` + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # /// use async_std::io::Cursor; /// use async_std::io::prelude::*; /// use async_std::io::SeekFrom; @@ -116,11 +118,13 @@ impl Cursor { /// /// assert_eq!(buff.position(), 0); /// - /// buff.seek(SeekFrom::Current(2)).unwrap(); + /// buff.seek(SeekFrom::Current(2)).await?; /// assert_eq!(buff.position(), 2); /// - /// buff.seek(SeekFrom::Current(-1)).unwrap(); + /// buff.seek(SeekFrom::Current(-1)).await?; /// assert_eq!(buff.position(), 1); + /// # + /// # Ok(()) }) } /// ``` pub fn position(&self) -> u64 { self.inner.position() From 3b8e604acc077d49be3df35d2f8319c611d0b41a Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 13 Sep 2019 20:02:31 +0200 Subject: [PATCH 110/194] mark io::cursor as unstable Signed-off-by: Yoshua Wuyts --- src/io/cursor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/io/cursor.rs b/src/io/cursor.rs index 95b13ec..c890c2f 100644 --- a/src/io/cursor.rs +++ b/src/io/cursor.rs @@ -22,6 +22,7 @@ use std::task::{Context, Poll}; /// [`Vec`]: https://doc.rust-lang.org/std/vec/struct.Vec.html /// [bytes]: https://doc.rust-lang.org/std/primitive.slice.html /// [`File`]: struct.File.html +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Clone, Debug, Default)] pub struct Cursor { inner: std::io::Cursor, From a4381230b877992557f3e50a241d914a201284c4 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 11 Sep 2019 16:13:17 +0200 Subject: [PATCH 111/194] Clean up the fs module and a few other places --- Cargo.toml | 3 +- README.md | 2 +- examples/list-dir.rs | 5 +- src/fs/canonicalize.rs | 10 +- src/fs/copy.rs | 26 +++-- src/fs/create_dir.rs | 20 ++-- src/fs/create_dir_all.rs | 14 +-- src/fs/dir_builder.rs | 37 ++++--- src/fs/dir_entry.rs | 106 +++++++++++-------- src/fs/file.rs | 202 +++++++++++++++++++++-------------- src/fs/file_type.rs | 86 +++++++++++++++ src/fs/hard_link.rs | 14 +-- src/fs/metadata.rs | 210 +++++++++++++++++++++++++++++++++++-- src/fs/mod.rs | 13 ++- src/fs/open_options.rs | 158 +++++++++++----------------- src/fs/permissions.rs | 58 ++++++++++ src/fs/read.rs | 19 ++-- src/fs/read_dir.rs | 39 ++++--- src/fs/read_link.rs | 11 +- src/fs/read_to_string.rs | 20 ++-- src/fs/remove_dir.rs | 14 +-- src/fs/remove_dir_all.rs | 14 +-- src/fs/remove_file.rs | 12 +-- src/fs/rename.rs | 15 +-- src/fs/set_permissions.rs | 15 +-- src/fs/symlink_metadata.rs | 17 +-- src/fs/write.rs | 13 +-- src/future/timeout.rs | 4 +- src/io/buf_read/lines.rs | 2 +- src/io/buf_reader.rs | 8 +- src/io/empty.rs | 4 +- src/io/read/mod.rs | 16 +-- src/io/seek.rs | 4 +- src/io/sink.rs | 4 +- src/io/write/mod.rs | 16 +-- src/lib.rs | 26 +++-- src/os/unix/fs.rs | 1 - src/os/unix/io.rs | 1 - src/os/unix/net/mod.rs | 1 - src/os/windows/io.rs | 1 - src/prelude.rs | 11 +- 41 files changed, 842 insertions(+), 410 deletions(-) create mode 100644 src/fs/file_type.rs create mode 100644 src/fs/permissions.rs diff --git a/Cargo.toml b/Cargo.toml index 2d026fa..0229582 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,8 @@ name = "async-std" version = "0.99.5" authors = [ "Stjepan Glavina ", - "The async-std Project Developers", + "Yoshua Wuyts ", + "Contributors to async-std", ] edition = "2018" license = "Apache-2.0/MIT" diff --git a/README.md b/README.md index eb0160f..6d6e0c1 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Async version of Rust's standard library +# Async version of the Rust standard library [![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.com/async-rs/async-std) [![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)](https://github.com/async-rs/async-std) diff --git a/examples/list-dir.rs b/examples/list-dir.rs index 814004a..e4b62fc 100644 --- a/examples/list-dir.rs +++ b/examples/list-dir.rs @@ -13,8 +13,9 @@ fn main() -> io::Result<()> { task::block_on(async { let mut dir = fs::read_dir(&path).await?; - while let Some(entry) = dir.next().await { - println!("{}", entry?.file_name().to_string_lossy()); + while let Some(res) = dir.next().await { + let entry = res?; + println!("{}", entry.file_name().to_string_lossy()); } Ok(()) diff --git a/src/fs/canonicalize.rs b/src/fs/canonicalize.rs index 572a657..c484aee 100644 --- a/src/fs/canonicalize.rs +++ b/src/fs/canonicalize.rs @@ -1,4 +1,3 @@ -use std::fs; use std::path::{Path, PathBuf}; use crate::io; @@ -15,10 +14,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * A non-final component in path is not a directory. +/// * `path` does not point to an existing file or directory. +/// * A non-final component in `path` is not a directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -33,5 +33,5 @@ use crate::task::blocking; /// ``` pub async fn canonicalize>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::canonicalize(path) }).await + blocking::spawn(async move { std::fs::canonicalize(path) }).await } diff --git a/src/fs/copy.rs b/src/fs/copy.rs index 56fd84b..fc3fd3e 100644 --- a/src/fs/copy.rs +++ b/src/fs/copy.rs @@ -1,28 +1,32 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Copies the contents and permissions of one file to another. +/// Copies the contents and permissions of a file to a new location. /// -/// On success, the total number of bytes copied is returned and equals the length of the `from` -/// file. +/// On success, the total number of bytes copied is returned and equals the length of the `to` file +/// after this operation. /// /// The old contents of `to` will be overwritten. If `from` and `to` both point to the same file, -/// then the file will likely get truncated by this operation. +/// then the file will likely get truncated as a result of this operation. +/// +/// If you're working with open [`File`]s and want to copy contents through those types, use the +/// [`io::copy`] function. /// /// This function is an async version of [`std::fs::copy`]. /// +/// [`File`]: struct.File.html +/// [`io::copy`]: ../io/fn.copy.html /// [`std::fs::copy`]: https://doc.rust-lang.org/std/fs/fn.copy.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The `from` path is not a file. -/// * The `from` file does not exist. -/// * The current process lacks permissions to access `from` or write `to`. +/// * `from` does not point to an existing file. +/// * The current process lacks permissions to read `from` or write `to`. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -31,12 +35,12 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// let bytes_copied = fs::copy("a.txt", "b.txt").await?; +/// let num_bytes = fs::copy("a.txt", "b.txt").await?; /// # /// # Ok(()) }) } /// ``` pub async fn copy, Q: AsRef>(from: P, to: Q) -> io::Result { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(async move { fs::copy(&from, &to) }).await + blocking::spawn(async move { std::fs::copy(&from, &to) }).await } diff --git a/src/fs/create_dir.rs b/src/fs/create_dir.rs index 9532eaf..aa2d572 100644 --- a/src/fs/create_dir.rs +++ b/src/fs/create_dir.rs @@ -1,22 +1,26 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Creates a new, empty directory. +/// Creates a new directory. +/// +/// Note that this function will only create the final directory in `path`. If you want to create +/// all of its missing parent directories too, use the [`create_dir_all`] function instead. /// /// This function is an async version of [`std::fs::create_dir`]. /// +/// [`create_dir_all`]: fn.create_dir_all.html /// [`std::fs::create_dir`]: https://doc.rust-lang.org/std/fs/fn.create_dir.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` already exists. -/// * A parent of the given path does not exist. -/// * The current process lacks permissions to create directory at `path`. +/// * `path` already points to an existing file or directory. +/// * A parent directory in `path` does not exist. +/// * The current process lacks permissions to create the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -25,11 +29,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::create_dir("./some/dir").await?; +/// fs::create_dir("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn create_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::create_dir(path) }).await + blocking::spawn(async move { std::fs::create_dir(path) }).await } diff --git a/src/fs/create_dir_all.rs b/src/fs/create_dir_all.rs index b63c362..33176f7 100644 --- a/src/fs/create_dir_all.rs +++ b/src/fs/create_dir_all.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Creates a new, empty directory and all of its parents if they are missing. +/// Creates a new directory and all of its parents if they are missing. /// /// This function is an async version of [`std::fs::create_dir_all`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The parent directories do not exists and couldn't be created. -/// * The current process lacks permissions to create directory at `path`. +/// * `path` already points to an existing file or directory. +/// * The current process lacks permissions to create the directory or its missing parents. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -24,11 +24,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::create_dir_all("./some/dir").await?; +/// fs::create_dir_all("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn create_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::create_dir_all(path) }).await + blocking::spawn(async move { std::fs::create_dir_all(path) }).await } diff --git a/src/fs/dir_builder.rs b/src/fs/dir_builder.rs index 21e2999..0de230d 100644 --- a/src/fs/dir_builder.rs +++ b/src/fs/dir_builder.rs @@ -1,4 +1,3 @@ -use std::fs; use std::path::Path; use cfg_if::cfg_if; @@ -7,21 +6,28 @@ use crate::future::Future; use crate::io; use crate::task::blocking; -/// A builder for creating directories in various manners. +/// A builder for creating directories with configurable options. +/// +/// For Unix-specific options, import the [`os::unix::fs::DirBuilderExt`] trait. /// /// This type is an async version of [`std::fs::DirBuilder`]. /// +/// [`os::unix::fs::DirBuilderExt`]: ../os/unix/fs/trait.DirBuilderExt.html /// [`std::fs::DirBuilder`]: https://doc.rust-lang.org/std/fs/struct.DirBuilder.html #[derive(Debug)] pub struct DirBuilder { + /// Set to `true` if non-existent parent directories should be created. recursive: bool, + /// Unix mode for newly created directories. #[cfg(unix)] mode: Option, } impl DirBuilder { - /// Creates a new builder with [`recursive`] set to `false`. + /// Creates a blank set of options. + /// + /// The [`recursive`] option is initially set to `false`. /// /// [`recursive`]: #method.recursive /// @@ -33,25 +39,24 @@ impl DirBuilder { /// let builder = DirBuilder::new(); /// ``` pub fn new() -> DirBuilder { + #[cfg(not(unix))] + let builder = DirBuilder { recursive: false }; + #[cfg(unix)] let builder = DirBuilder { recursive: false, mode: None, }; - #[cfg(windows)] - let builder = DirBuilder { recursive: false }; - builder } /// Sets the option for recursive mode. /// - /// This option, when `true`, means that all parent directories should be created recursively - /// if they don't exist. Parents are created with the same security settings and permissions as - /// the final directory. + /// When set to `true`, this option means all parent directories should be created recursively + /// if they don't exist. Parents are created with the same permissions as the final directory. /// - /// This option defaults to `false`. + /// This option is initially set to `false`. /// /// # Examples /// @@ -70,6 +75,14 @@ impl DirBuilder { /// /// It is considered an error if the directory already exists unless recursive mode is enabled. /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * `path` already points to an existing file or directory. + /// * The current process lacks permissions to create the directory or its missing parents. + /// * Some other I/O error occurred. + /// /// # Examples /// /// ```no_run @@ -79,13 +92,13 @@ impl DirBuilder { /// /// DirBuilder::new() /// .recursive(true) - /// .create("/tmp/foo/bar/baz") + /// .create("./some/directory") /// .await?; /// # /// # Ok(()) }) } /// ``` pub fn create>(&self, path: P) -> impl Future> { - let mut builder = fs::DirBuilder::new(); + let mut builder = std::fs::DirBuilder::new(); builder.recursive(self.recursive); #[cfg(unix)] diff --git a/src/fs/dir_entry.rs b/src/fs/dir_entry.rs index c7928eb..66b3cb7 100644 --- a/src/fs/dir_entry.rs +++ b/src/fs/dir_entry.rs @@ -1,45 +1,28 @@ use std::ffi::OsString; -use std::fs; +use std::fmt; use std::path::PathBuf; use std::sync::Arc; use cfg_if::cfg_if; +use crate::fs::{FileType, Metadata}; use crate::io; use crate::task::blocking; -/// An entry inside a directory. +/// An entry in a directory. /// -/// An instance of `DirEntry` represents an entry inside a directory on the filesystem. Each entry -/// carriers additional information like the full path or metadata. +/// A stream of entries in a directory is returned by [`read_dir`]. /// /// This type is an async version of [`std::fs::DirEntry`]. /// +/// [`read_dir`]: fn.read_dir.html /// [`std::fs::DirEntry`]: https://doc.rust-lang.org/std/fs/struct.DirEntry.html -#[derive(Debug)] -pub struct DirEntry { - /// The inner synchronous `DirEntry`. - inner: Arc, - - #[cfg(unix)] - ino: u64, -} +pub struct DirEntry(Arc); impl DirEntry { - /// Creates an asynchronous `DirEntry` from a synchronous handle. - pub(crate) fn new(inner: fs::DirEntry) -> DirEntry { - #[cfg(unix)] - let dir_entry = DirEntry { - ino: inner.ino(), - inner: Arc::new(inner), - }; - - #[cfg(windows)] - let dir_entry = DirEntry { - inner: Arc::new(inner), - }; - - dir_entry + /// Creates an asynchronous `DirEntry` from a synchronous one. + pub(crate) fn new(inner: std::fs::DirEntry) -> DirEntry { + DirEntry(Arc::new(inner)) } /// Returns the full path to this entry. @@ -59,20 +42,33 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; + /// while let Some(res) = dir.next().await { + /// let entry = res?; /// println!("{:?}", entry.path()); /// } /// # /// # Ok(()) }) } /// ``` pub fn path(&self) -> PathBuf { - self.inner.path() + self.0.path() } - /// Returns the metadata for this entry. + /// Reads the metadata for this entry. /// - /// This function will not traverse symlinks if this entry points at a symlink. + /// This function will traverse symbolic links to read the metadata. + /// + /// If you want to read metadata without following symbolic links, use [`symlink_metadata`] + /// instead. + /// + /// [`symlink_metadata`]: fn.symlink_metadata.html + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read the metadata. + /// * Some other I/O error occurred. /// /// # Examples /// @@ -84,21 +80,33 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; + /// while let Some(res) = dir.next().await { + /// let entry = res?; /// println!("{:?}", entry.metadata().await?); /// } /// # /// # Ok(()) }) } /// ``` - pub async fn metadata(&self) -> io::Result { - let inner = self.inner.clone(); + pub async fn metadata(&self) -> io::Result { + let inner = self.0.clone(); blocking::spawn(async move { inner.metadata() }).await } - /// Returns the file type for this entry. + /// Reads the file type for this entry. /// - /// This function will not traverse symlinks if this entry points at a symlink. + /// This function will not traverse symbolic links if this entry points at one. + /// + /// If you want to read metadata with following symbolic links, use [`metadata`] instead. + /// + /// [`metadata`]: #method.metadata + /// + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * This entry does not point to an existing file or directory anymore. + /// * The current process lacks permissions to read this entry's metadata. + /// * Some other I/O error occurred. /// /// # Examples /// @@ -110,15 +118,15 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; + /// while let Some(res) = dir.next().await { + /// let entry = res?; /// println!("{:?}", entry.file_type().await?); /// } /// # /// # Ok(()) }) } /// ``` - pub async fn file_type(&self) -> io::Result { - let inner = self.inner.clone(); + pub async fn file_type(&self) -> io::Result { + let inner = self.0.clone(); blocking::spawn(async move { inner.file_type() }).await } @@ -134,15 +142,21 @@ impl DirEntry { /// /// let mut dir = fs::read_dir(".").await?; /// - /// while let Some(entry) = dir.next().await { - /// let entry = entry?; - /// println!("{:?}", entry.file_name()); + /// while let Some(res) = dir.next().await { + /// let entry = res?; + /// println!("{}", entry.file_name().to_string_lossy()); /// } /// # /// # Ok(()) }) } /// ``` pub fn file_name(&self) -> OsString { - self.inner.file_name() + self.0.file_name() + } +} + +impl fmt::Debug for DirEntry { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("DirEntry").field(&self.path()).finish() } } @@ -159,7 +173,7 @@ cfg_if! { if #[cfg(any(unix, feature = "docs"))] { impl DirEntryExt for DirEntry { fn ino(&self) -> u64 { - self.ino + self.0.ino() } } } diff --git a/src/fs/file.rs b/src/fs/file.rs index 3ee4979..33022bc 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1,9 +1,7 @@ -//! Async file implementation. - use std::cell::UnsafeCell; use std::cmp; -use std::fs; -use std::io::{Read as _, Seek, SeekFrom, Write as _}; +use std::fmt; +use std::io::{Read as _, Seek as _, Write as _}; use std::ops::{Deref, DerefMut}; use std::path::Path; use std::pin::Pin; @@ -13,22 +11,22 @@ use std::sync::{Arc, Mutex}; use cfg_if::cfg_if; use futures_io::{AsyncRead, AsyncSeek, AsyncWrite, Initializer}; +use crate::fs::{Metadata, Permissions}; use crate::future; -use crate::io::{self, Write}; +use crate::io::{self, SeekFrom, Write}; use crate::task::{self, blocking, Context, Poll, Waker}; -/// A reference to a file on the filesystem. +/// An open file on the filesystem. /// -/// An instance of a `File` can be read and/or written depending on what options it was opened -/// with. +/// Depending on what options the file was opened with, this type can be used for reading and/or +/// writing. /// -/// Files are automatically closed when they go out of scope. Errors detected on closing are -/// ignored by the implementation of `Drop`. Use the method [`sync_all`] if these errors must be -/// manually handled. +/// Files are automatically closed when they get dropped and any errors detected on closing are +/// ignored. Use the [`sync_all`] method before dropping a file if such errors need to be handled. /// /// This type is an async version of [`std::fs::File`]. /// -/// [`sync_all`]: struct.File.html#method.sync_all +/// [`sync_all`]: #method.sync_all /// [`std::fs::File`]: https://doc.rust-lang.org/std/fs/struct.File.html /// /// # Examples @@ -47,7 +45,7 @@ use crate::task::{self, blocking, Context, Poll, Waker}; /// # Ok(()) }) } /// ``` /// -/// Read the contents of a file into a `Vec`: +/// Read the contents of a file into a vector of bytes: /// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { @@ -61,23 +59,30 @@ use crate::task::{self, blocking, Context, Poll, Waker}; /// # /// # Ok(()) }) } /// ``` -#[derive(Debug)] pub struct File { - file: Arc, + /// A reference to the inner file. + file: Arc, + + /// The state of the file protected by an async lock. lock: Lock, } impl File { /// Opens a file in read-only mode. /// - /// See the [`OpenOptions::open`] method for more details. + /// See the [`OpenOptions::open`] function for more options. /// /// # Errors /// - /// This function will return an error if `path` does not already exist. - /// Other errors may also be returned according to [`OpenOptions::open`]. + /// An error will be returned in the following situations: /// - /// [`OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html + /// * `path` does not point to an existing file. + /// * The current process lacks permissions to read the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open`]. + /// + /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open /// /// # Examples /// @@ -92,7 +97,7 @@ impl File { /// ``` pub async fn open>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { fs::File::open(&path) }).await?; + let file = blocking::spawn(async move { std::fs::File::open(&path) }).await?; Ok(file.into()) } @@ -100,9 +105,19 @@ impl File { /// /// This function will create a file if it does not exist, and will truncate it if it does. /// - /// See the [`OpenOptions::open`] function for more details. + /// See the [`OpenOptions::open`] function for more options. /// - /// [`OpenOptions::open`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html + /// # Errors + /// + /// An error will be returned in the following situations: + /// + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to write to the file. + /// * Some other I/O error occurred. + /// + /// For more details, see the list of errors documented by [`OpenOptions::open`]. + /// + /// [`OpenOptions::open`]: struct.OpenOptions.html#method.open /// /// # Examples /// @@ -117,17 +132,16 @@ impl File { /// ``` pub async fn create>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - let file = blocking::spawn(async move { fs::File::create(&path) }).await?; + let file = blocking::spawn(async move { std::fs::File::create(&path) }).await?; Ok(file.into()) } - /// Attempts to synchronize all OS-internal metadata to disk. + /// Synchronizes OS-internal buffered contents and metadata to disk. /// - /// This function will attempt to ensure that all in-memory data reaches the filesystem before - /// returning. + /// This function will ensure that all in-memory data reaches the filesystem. /// - /// This can be used to handle errors that would otherwise only be caught when the `File` is - /// closed. Dropping a file will ignore errors in synchronizing this in-memory data. + /// This can be used to handle errors that would otherwise only be caught when the file is + /// closed. When a file is dropped, errors in synchronizing this in-memory data are ignored. /// /// # Examples /// @@ -154,14 +168,16 @@ impl File { blocking::spawn(async move { state.file.sync_all() }).await } - /// Similar to [`sync_all`], except that it may not synchronize file metadata. + /// Synchronizes OS-internal buffered contents to disk. /// - /// This is intended for use cases that must synchronize content, but don't need the metadata - /// on disk. The goal of this method is to reduce disk operations. + /// This is similar to [`sync_all`], except that file metadata may not be synchronized. + /// + /// This is intended for use cases that must synchronize the contents of the file, but don't + /// need the file metadata synchronized to disk. /// /// Note that some platforms may simply implement this in terms of [`sync_all`]. /// - /// [`sync_all`]: struct.File.html#method.sync_all + /// [`sync_all`]: #method.sync_all /// /// # Examples /// @@ -188,18 +204,14 @@ impl File { blocking::spawn(async move { state.file.sync_data() }).await } - /// Truncates or extends the underlying file. + /// Truncates or extends the file. /// - /// If the `size` is less than the current file's size, then the file will be truncated. If it - /// is greater than the current file's size, then the file will be extended to `size` and have - /// all of the intermediate data filled in with zeros. + /// If `size` is less than the current file size, then the file will be truncated. If it is + /// greater than the current file size, then the file will be extended to `size` and have all + /// intermediate data filled with zeros. /// - /// The file's cursor isn't changed. In particular, if the cursor was at the end and the file - /// is truncated using this operation, the cursor will now be past the end. - /// - /// # Errors - /// - /// This function will return an error if the file is not opened for writing. + /// The file's cursor stays at the same position, even if the cursor ends up being past the end + /// of the file after this operation. /// /// # Examples /// @@ -225,7 +237,7 @@ impl File { blocking::spawn(async move { state.file.set_len(size) }).await } - /// Queries metadata about the file. + /// Reads the file's metadata. /// /// # Examples /// @@ -239,17 +251,19 @@ impl File { /// # /// # Ok(()) }) } /// ``` - pub async fn metadata(&self) -> io::Result { + pub async fn metadata(&self) -> io::Result { let file = self.file.clone(); blocking::spawn(async move { file.metadata() }).await } - /// Changes the permissions on the underlying file. + /// Changes the permissions on the file. /// /// # Errors /// - /// This function will return an error if the user lacks permission to change attributes on the - /// underlying file, but may also return an error in other OS-specific cases. + /// An error will be returned in the following situations: + /// + /// * The current process lacks permissions to change attributes on the file. + /// * Some other I/O error occurred. /// /// # Examples /// @@ -266,7 +280,7 @@ impl File { /// # /// # Ok(()) }) } /// ``` - pub async fn set_permissions(&self, perm: fs::Permissions) -> io::Result<()> { + pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { let file = self.file.clone(); blocking::spawn(async move { file.set_permissions(perm) }).await } @@ -282,6 +296,12 @@ impl Drop for File { } } +impl fmt::Debug for File { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.file.fmt(f) + } +} + impl AsyncRead for File { fn poll_read( self: Pin<&mut Self>, @@ -374,9 +394,9 @@ impl AsyncSeek for &File { } impl From for File { - /// Converts a `std::fs::File` into its asynchronous equivalent. - fn from(file: fs::File) -> File { + fn from(file: std::fs::File) -> File { let file = Arc::new(file); + File { file: file.clone(), lock: Lock::new(State { @@ -413,7 +433,7 @@ cfg_if! { impl FromRawFd for File { unsafe fn from_raw_fd(fd: RawFd) -> File { - fs::File::from_raw_fd(fd).into() + std::fs::File::from_raw_fd(fd).into() } } @@ -422,7 +442,7 @@ cfg_if! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of file handle after drop") + .expect("cannot acquire ownership of the file handle after drop") .into_raw_fd() } } @@ -440,7 +460,7 @@ cfg_if! { impl FromRawHandle for File { unsafe fn from_raw_handle(handle: RawHandle) -> File { - fs::File::from_raw_handle(handle).into() + std::fs::File::from_raw_handle(handle).into() } } @@ -449,7 +469,7 @@ cfg_if! { let file = self.file.clone(); drop(self); Arc::try_unwrap(file) - .expect("cannot acquire ownership of file handle after drop") + .expect("cannot acquire ownership of the file handle after drop") .into_raw_handle() } } @@ -457,13 +477,11 @@ cfg_if! { } /// An async mutex with non-borrowing lock guards. -#[derive(Debug)] struct Lock(Arc>); unsafe impl Send for Lock {} unsafe impl Sync for Lock {} -#[derive(Debug)] /// The state of a lock. struct LockState { /// Set to `true` when locked. @@ -472,12 +490,12 @@ struct LockState { /// The inner value. value: UnsafeCell, - /// A list of tasks interested in locking. + /// A list of tasks interested in acquiring the lock. wakers: Mutex>, } impl Lock { - /// Creates a new lock with the given value. + /// Creates a new lock initialized with `value`. fn new(value: T) -> Lock { Lock(Arc::new(LockState { locked: AtomicBool::new(false), @@ -511,14 +529,13 @@ impl Lock { /// A lock guard. /// /// When dropped, ownership of the inner value is returned back to the lock. -#[derive(Debug)] struct LockGuard(Arc>); unsafe impl Send for LockGuard {} unsafe impl Sync for LockGuard {} impl LockGuard { - /// Registers a task interested in locking. + /// Registers a task interested in acquiring the lock. /// /// When this lock guard gets dropped, all registered tasks will be woken up. fn register(&self, cx: &Context<'_>) { @@ -532,8 +549,10 @@ impl LockGuard { impl Drop for LockGuard { fn drop(&mut self) { + // Release the lock. self.0.locked.store(false, Ordering::Release); + // Wake up all registered tasks interested in acquiring the lock. for w in self.0.wakers.lock().unwrap().drain(..) { w.wake(); } @@ -557,14 +576,13 @@ impl DerefMut for LockGuard { /// Modes a file can be in. /// /// The file can either be in idle mode, reading mode, or writing mode. -#[derive(Debug)] enum Mode { /// The cache is empty. Idle, /// The cache contains data read from the inner file. /// - /// This `usize` represents how many bytes from the beginning of cache have been consumed. + /// The `usize` represents how many bytes from the beginning of cache have been consumed. Reading(usize), /// The cache contains data that needs to be written to the inner file. @@ -573,14 +591,13 @@ enum Mode { /// The current state of a file. /// -/// The `File` struct puts this state behind a lock. +/// The `File` struct protects this state behind a lock. /// -/// Filesystem operations that get spawned as blocking tasks will take ownership of the state and -/// return it back once the operation completes. -#[derive(Debug)] +/// Filesystem operations that get spawned as blocking tasks will acquire the lock, take ownership +/// of the state and return it back once the operation completes. struct State { /// The inner file. - file: Arc, + file: Arc, /// The current mode (idle, reading, or writing). mode: Mode, @@ -588,10 +605,10 @@ struct State { /// The read/write cache. /// /// If in reading mode, the cache contains a chunk of data that has been read from the file. - /// If in writing mode, the cache contains data that will eventually be written into the file. + /// If in writing mode, the cache contains data that will eventually be written to the file. cache: Vec, - /// `true` if the file is flushed. + /// Set to `true` if the file is flushed. /// /// When a file is flushed, the write cache and the inner file's buffer are empty. is_flushed: bool, @@ -607,17 +624,45 @@ impl LockGuard { /// Seeks to a new position in the file. fn poll_seek(mut self, cx: &mut Context<'_>, pos: SeekFrom) -> Poll> { // If this operation doesn't move the cursor, then poll the current position inside the - // file. This call will hopefully not block. + // file. This call should not block because it doesn't touch the actual file on disk. if pos == SeekFrom::Current(0) { - return Poll::Ready((&*self.file).seek(pos)); + // Poll the internal file cursor. + let internal = (&*self.file).seek(SeekFrom::Current(0))?; + + // Factor in the difference caused by caching. + let actual = match self.mode { + Mode::Idle => internal, + Mode::Reading(start) => internal - self.cache.len() as u64 + start as u64, + Mode::Writing => internal + self.cache.len() as u64, + }; + return Poll::Ready(Ok(actual)); + } + + // If the file is in reading mode and the cache will stay valid after seeking, then adjust + // the current position in the read cache without invaliding it. + if let Mode::Reading(start) = self.mode { + if let SeekFrom::Current(diff) = pos { + if let Some(new) = (start as i64).checked_add(diff) { + if 0 <= new && new <= self.cache.len() as i64 { + // Poll the internal file cursor. + let internal = (&*self.file).seek(SeekFrom::Current(0))?; + + // Adjust the current position in the read cache. + self.mode = Mode::Reading(new as usize); + + // Factor in the difference caused by caching. + return Poll::Ready(Ok(internal - self.cache.len() as u64 + new as u64)); + } + } + } } // Invalidate the read cache and flush the write cache before calling `seek()`. self = futures_core::ready!(self.poll_unread(cx))?; self = futures_core::ready!(self.poll_flush(cx))?; - // Seek to the new position. This call is hopefully not blocking because it should just - // change the internal offset into the file and not touch the actual file. + // Seek to the new position. This call should not block because it only changes the + // internal offset into the file and doesn't touch the actual file on disk. Poll::Ready((&*self.file).seek(pos)) } @@ -702,13 +747,12 @@ impl LockGuard { match self.mode { Mode::Idle | Mode::Writing => Poll::Ready(Ok(self)), Mode::Reading(start) => { - // Number of unconsumed bytes in the read cache. + // The number of unconsumed bytes in the read cache. let n = self.cache.len() - start; if n > 0 { - // Seek `n` bytes backwards. This call is hopefully not blocking because it - // should just change the internal offset into the file and not touch the - // actual file. + // Seek `n` bytes backwards. This call should not block because it only changes + // the internal offset into the file and doesn't touch the actual file on disk. (&*self.file).seek(SeekFrom::Current(-(n as i64)))?; } @@ -731,7 +775,7 @@ impl LockGuard { // If we're in reading mode, invalidate the read buffer. self = futures_core::ready!(self.poll_unread(cx))?; - // Make the cache have as much capacity as `buf`. + // If necessary, grow the cache to have as much capacity as `buf`. if self.cache.capacity() < buf.len() { let diff = buf.len() - self.cache.capacity(); self.cache.reserve(diff); @@ -740,7 +784,7 @@ impl LockGuard { // How many bytes can be written into the cache before filling up. let available = self.cache.capacity() - self.cache.len(); - // If there is available space in the cache or if the buffer is empty, we can write data + // If there is space available in the cache or if the buffer is empty, we can write data // into the cache. if available > 0 || buf.is_empty() { let n = cmp::min(available, buf.len()); diff --git a/src/fs/file_type.rs b/src/fs/file_type.rs new file mode 100644 index 0000000..a162798 --- /dev/null +++ b/src/fs/file_type.rs @@ -0,0 +1,86 @@ +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "docs")] { + /// The type of a file or directory. + /// + /// A file type is returned by [`Metadata::file_type`]. + /// + /// Note that file types are mutually exclusive, i.e. at most one of methods [`is_dir`], + /// [`is_file`], and [`is_symlink`] can return `true`. + /// + /// This type is a re-export of [`std::fs::FileType`]. + /// + /// [`Metadata::file_type`]: struct.Metadata.html#method.file_type + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`is_symlink`]: #method.is_symlink + /// [`std::fs::FileType`]: https://doc.rust-lang.org/std/fs/struct.FileType.html + #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] + pub struct FileType { + _private: (), + } + + impl FileType { + /// Returns `true` if this file type represents a regular directory. + /// + /// If this file type represents a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata(".").await?.file_type(); + /// println!("{:?}", file_type.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() + } + + /// Returns `true` if this file type represents a regular file. + /// + /// If this file type represents a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } + + /// Returns `true` if this file type represents a symbolic link. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let file_type = fs::metadata("a.txt").await?.file_type(); + /// println!("{:?}", file_type.is_symlink()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_symlink(&self) -> bool { + unimplemented!() + } + } + } else { + pub use std::fs::FileType; + } +} diff --git a/src/fs/hard_link.rs b/src/fs/hard_link.rs index 8427e8f..5f950cd 100644 --- a/src/fs/hard_link.rs +++ b/src/fs/hard_link.rs @@ -1,13 +1,12 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Creates a new hard link on the filesystem. +/// Creates a hard link on the filesystem. /// -/// The `dst` path will be a link pointing to the `src` path. Note that systems often require these -/// two paths to both be located on the same filesystem. +/// The `dst` path will be a link pointing to the `src` path. Note that operating systems often +/// require these two paths to be located on the same filesystem. /// /// This function is an async version of [`std::fs::hard_link`]. /// @@ -15,9 +14,10 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The `src` path is not a file or doesn't exist. +/// * `src` does not point to an existing file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -33,5 +33,5 @@ use crate::task::blocking; pub async fn hard_link, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(async move { fs::hard_link(&from, &to) }).await + blocking::spawn(async move { std::fs::hard_link(&from, &to) }).await } diff --git a/src/fs/metadata.rs b/src/fs/metadata.rs index 032fe24..6bb9993 100644 --- a/src/fs/metadata.rs +++ b/src/fs/metadata.rs @@ -1,23 +1,28 @@ -use std::fs::{self, Metadata}; use std::path::Path; +use cfg_if::cfg_if; + use crate::io; use crate::task::blocking; -/// Queries the metadata for a path. +/// Reads metadata for a path. /// -/// This function will traverse symbolic links to query information about the file or directory. +/// This function will traverse symbolic links to read metadata for the target file or directory. +/// If you want to read metadata without following symbolic links, use [`symlink_metadata`] +/// instead. /// /// This function is an async version of [`std::fs::metadata`]. /// +/// [`symlink_metadata`]: fn.symlink_metadata.html /// [`std::fs::metadata`]: https://doc.rust-lang.org/std/fs/fn.metadata.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to query metadata for `path`. +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -32,5 +37,196 @@ use crate::task::blocking; /// ``` pub async fn metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::metadata(path) }).await + blocking::spawn(async move { std::fs::metadata(path) }).await +} + +cfg_if! { + if #[cfg(feature = "docs")] { + use std::time::SystemTime; + + use crate::fs::{FileType, Permissions}; + + /// Metadata for a file or directory. + /// + /// Metadata is returned by [`metadata`] and [`symlink_metadata`]. + /// + /// This type is a re-export of [`std::fs::Metadata`]. + /// + /// [`metadata`]: fn.metadata.html + /// [`symlink_metadata`]: fn.symlink_metadata.html + /// [`is_dir`]: #method.is_dir + /// [`is_file`]: #method.is_file + /// [`std::fs::Metadata`]: https://doc.rust-lang.org/std/fs/struct.Metadata.html + #[derive(Clone, Debug)] + pub struct Metadata { + _private: (), + } + + impl Metadata { + /// Returns the file type from this metadata. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.file_type()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn file_type(&self) -> FileType { + unimplemented!() + } + + /// Returns `true` if this metadata is for a regular directory. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata(".").await?; + /// println!("{:?}", metadata.is_dir()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_dir(&self) -> bool { + unimplemented!() + } + + /// Returns `true` if this metadata is for a regular file. + /// + /// If this metadata is for a symbolic link, this method returns `false`. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.is_file()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn is_file(&self) -> bool { + unimplemented!() + } + + /// Returns the file size in bytes. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{}", metadata.len()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn len(&self) -> u64 { + unimplemented!() + } + + /// Returns the permissions from this metadata. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.permissions()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn permissions(&self) -> Permissions { + unimplemented!() + } + + /// Returns the last modification time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.modified()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn modified(&self) -> io::Result { + unimplemented!() + } + + /// Returns the last access time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.accessed()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn accessed(&self) -> io::Result { + unimplemented!() + } + + /// Returns the creation time. + /// + /// # Errors + /// + /// This data may not be available on all platforms, in which case an error will be + /// returned. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use std::fs; + /// + /// let metadata = fs::metadata("a.txt").await?; + /// println!("{:?}", metadata.created()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn created(&self) -> io::Result { + unimplemented!() + } + } + } else { + pub use std::fs::Metadata; + } } diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 8d98330..5612bb8 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -2,8 +2,13 @@ //! //! This module is an async version of [`std::fs`]. //! +//! [`os::unix::fs`]: ../os/unix/fs/index.html //! [`std::fs`]: https://doc.rust-lang.org/std/fs/index.html //! +//! # Platform-specific extensions +//! +//! * Unix: use the [`os::unix::fs`] module. +//! //! # Examples //! //! Create a new file and write some bytes to it: @@ -23,12 +28,12 @@ pub use dir_builder::DirBuilder; pub use dir_entry::DirEntry; pub use file::File; +pub use file_type::FileType; pub use open_options::OpenOptions; +pub use metadata::Metadata; +pub use permissions::Permissions; pub use read_dir::ReadDir; -#[doc(inline)] -pub use std::fs::{FileType, Metadata, Permissions}; - pub use canonicalize::canonicalize; pub use copy::copy; pub use create_dir::create_dir; @@ -54,9 +59,11 @@ mod create_dir_all; mod dir_builder; mod dir_entry; mod file; +mod file_type; mod hard_link; mod metadata; mod open_options; +mod permissions; mod read; mod read_dir; mod read_link; diff --git a/src/fs/open_options.rs b/src/fs/open_options.rs index 54a6f76..c6cc74a 100644 --- a/src/fs/open_options.rs +++ b/src/fs/open_options.rs @@ -1,35 +1,35 @@ -use std::fs; -use std::io; use std::path::Path; use cfg_if::cfg_if; -use super::File; +use crate::fs::File; use crate::future::Future; +use crate::io; use crate::task::blocking; -/// Options and flags which for configuring how a file is opened. +/// A builder for opening files with configurable options. /// -/// This builder exposes the ability to configure how a [`File`] is opened and what operations are -/// permitted on the open file. The [`File::open`] and [`File::create`] methods are aliases for -/// commonly used options with this builder. +/// Files can be opened in [`read`] and/or [`write`] mode. /// -/// Generally speaking, when using `OpenOptions`, you'll first call [`new`], then chain calls to -/// methods to set each option, then call [`open`], passing the path of the file you're trying to -/// open. This will give you a [`File`] inside that you can further operate on. +/// The [`append`] option opens files in a special writing mode that moves the file cursor to the +/// end of file before every write operation. +/// +/// It is also possible to [`truncate`] the file right after opening, to [`create`] a file if it +/// doesn't exist yet, or to always create a new file with [`create_new`]. /// /// This type is an async version of [`std::fs::OpenOptions`]. /// -/// [`new`]: struct.OpenOptions.html#method.new -/// [`open`]: struct.OpenOptions.html#method.open -/// [`File`]: struct.File.html -/// [`File::open`]: struct.File.html#method.open -/// [`File::create`]: struct.File.html#method.create +/// [`read`]: #method.read +/// [`write`]: #method.write +/// [`append`]: #method.append +/// [`truncate`]: #method.truncate +/// [`create`]: #method.create +/// [`create_new`]: #method.create_new /// [`std::fs::OpenOptions`]: https://doc.rust-lang.org/std/fs/struct.OpenOptions.html /// /// # Examples /// -/// Opening a file for reading: +/// Open a file for reading: /// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { @@ -44,7 +44,7 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` /// -/// Opening a file for both reading and writing, creating it if it doesn't exist: +/// Open a file for both reading and writing, and create it if it doesn't exist yet: /// /// ```no_run /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { @@ -61,10 +61,10 @@ use crate::task::blocking; /// # Ok(()) }) } /// ``` #[derive(Clone, Debug)] -pub struct OpenOptions(fs::OpenOptions); +pub struct OpenOptions(std::fs::OpenOptions); impl OpenOptions { - /// Creates a blank new set of options. + /// Creates a blank set of options. /// /// All options are initially set to `false`. /// @@ -83,12 +83,12 @@ impl OpenOptions { /// # Ok(()) }) } /// ``` pub fn new() -> OpenOptions { - OpenOptions(fs::OpenOptions::new()) + OpenOptions(std::fs::OpenOptions::new()) } - /// Sets the option for read access. + /// Configures the option for read mode. /// - /// This option, when `true`, will indicate that the file should be readable if opened. + /// When set to `true`, this option means the file will be readable after opening. /// /// # Examples /// @@ -109,11 +109,11 @@ impl OpenOptions { self } - /// Sets the option for write access. + /// Configures the option for write mode. /// - /// This option, when `true`, will indicate that the file should be writable if opened. + /// When set to `true`, this option means the file will be writable after opening. /// - /// If the file already exists, any write calls on it will overwrite its contents, without + /// If the file already exists, write calls on it will overwrite the previous contents without /// truncating it. /// /// # Examples @@ -135,31 +135,10 @@ impl OpenOptions { self } - /// Sets the option for append mode. + /// Configures the option for append mode. /// - /// This option, when `true`, means that writes will append to a file instead of overwriting - /// previous contents. Note that setting `.write(true).append(true)` has the same effect as - /// setting only `.append(true)`. - /// - /// For most filesystems, the operating system guarantees that all writes are atomic: no writes - /// get mangled because another process writes at the same time. - /// - /// One maybe obvious note when using append mode: make sure that all data that belongs - /// together is written to the file in one operation. This can be done by concatenating strings - /// before writing them, or using a buffered writer (with a buffer of adequate size), and - /// flushing when the message is complete. - /// - /// If a file is opened with both read and append access, beware that after opening and after - /// every write, the position for reading may be set at the end of the file. So, before - /// writing, save the current position by seeking with a zero offset, and restore it before the - /// next read. - /// - /// ## Note - /// - /// This function doesn't create the file if it doesn't exist. Use the [`create`] method to do - /// so. - /// - /// [`create`]: #method.create + /// When set to `true`, this option means the file will be writable after opening and the file + /// cursor will be moved to the end of file before every write operaiton. /// /// # Examples /// @@ -180,12 +159,14 @@ impl OpenOptions { self } - /// Sets the option for truncating a previous file. + /// Configures the option for truncating the previous file. /// - /// If a file is successfully opened with this option set, it will truncate the file to 0 - /// length if it already exists. + /// When set to `true`, the file will be truncated to the length of 0 bytes. /// - /// The file must be opened with write access for truncation to work. + /// The file must be opened in [`write`] or [`append`] mode for truncation to work. + /// + /// [`write`]: #method.write + /// [`append`]: #method.append /// /// # Examples /// @@ -207,11 +188,11 @@ impl OpenOptions { self } - /// Sets the option for creating a new file. + /// Configures the option for creating a new file if it doesn't exist. /// - /// This option indicates whether a new file will be created if the file does not yet exist. + /// When set to `true`, this option means a new file will be created if it doesn't exist. /// - /// In order for the file to be created, [`write`] or [`append`] access must be used. + /// The file must be opened in [`write`] or [`append`] mode for file creation to work. /// /// [`write`]: #method.write /// [`append`]: #method.append @@ -236,21 +217,15 @@ impl OpenOptions { self } - /// Sets the option to always create a new file. + /// Configures the option for creating a new file or failing if it already exists. /// - /// This option indicates whether a new file will be created. No file is allowed to exist at - /// the target location, also no (dangling) symlink. + /// When set to `true`, this option means a new file will be created, or the open operation + /// will fail if the file already exists. /// - /// This option is useful because it is atomic. Otherwise, between checking whether a file - /// exists and creating a new one, the file may have been created by another process (a TOCTOU - /// race condition / attack). + /// The file must be opened in [`write`] or [`append`] mode for file creation to work. /// - /// If `.create_new(true)` is set, [`.create()`] and [`.truncate()`] are ignored. - /// - /// The file must be opened with write or append access in order to create a new file. - /// - /// [`.create()`]: #method.create - /// [`.truncate()`]: #method.truncate + /// [`write`]: #method.write + /// [`append`]: #method.append /// /// # Examples /// @@ -272,37 +247,27 @@ impl OpenOptions { self } - /// Opens a file at specified path with the configured options. + /// Opens a file with the configured options. /// /// # Errors /// - /// This function will return an error under a number of different circumstances. Some of these - /// error conditions are listed here, together with their [`ErrorKind`]. The mapping to - /// [`ErrorKind`]s is not part of the compatibility contract of the function, especially the - /// `Other` kind might change to more specific kinds in the future. + /// An error will be returned in the following situations: /// - /// * [`NotFound`]: The specified file does not exist and neither `create` or `create_new` is - /// set. - /// * [`NotFound`]: One of the directory components of the file path does not exist. - /// * [`PermissionDenied`]: The user lacks permission to get the specified access rights for - /// the file. - /// * [`PermissionDenied`]: The user lacks permission to open one of the directory components - /// of the specified path. - /// * [`AlreadyExists`]: `create_new` was specified and the file already exists. - /// * [`InvalidInput`]: Invalid combinations of open options (truncate without write access, no - /// access mode set, etc.). - /// * [`Other`]: One of the directory components of the specified file path was not, in fact, a - /// directory. - /// * [`Other`]: Filesystem-level errors: full disk, write permission requested on a read-only - /// file system, exceeded disk quota, too many open files, too long filename, too many - /// symbolic links in the specified path (Unix-like systems only), etc. + /// * The file does not exist and neither [`create`] nor [`create_new`] were set. + /// * The file's parent directory does not exist. + /// * The current process lacks permissions to open the file in the configured mode. + /// * The file already exists and [`create_new`] was set. + /// * Invalid combination of options was used, like [`truncate`] was set but [`write`] wasn't, + /// or none of [`read`], [`write`], and [`append`] modes was set. + /// * An OS-level occurred, like too many files are open or the file name is too long. + /// * Some other I/O error occurred. /// - /// [`ErrorKind`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html - /// [`AlreadyExists`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.AlreadyExists - /// [`InvalidInput`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.InvalidInput - /// [`NotFound`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.NotFound - /// [`Other`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.Other - /// [`PermissionDenied`]: https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.PermissionDenied + /// [`read`]: #method.read + /// [`write`]: #method.write + /// [`append`]: #method.append + /// [`truncate`]: #method.truncate + /// [`create`]: #method.create + /// [`create_new`]: #method.create_new /// /// # Examples /// @@ -311,7 +276,10 @@ impl OpenOptions { /// # /// use async_std::fs::OpenOptions; /// - /// let file = OpenOptions::new().open("a.txt").await?; + /// let file = OpenOptions::new() + /// .read(true) + /// .open("a.txt") + /// .await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/fs/permissions.rs b/src/fs/permissions.rs new file mode 100644 index 0000000..628dd39 --- /dev/null +++ b/src/fs/permissions.rs @@ -0,0 +1,58 @@ +use cfg_if::cfg_if; + +cfg_if! { + if #[cfg(feature = "docs")] { + /// A set of permissions on a file or directory. + /// + /// This type is a re-export of [`std::fs::Permissions`]. + /// + /// [`std::fs::Permissions`]: https://doc.rust-lang.org/std/fs/struct.Permissions.html + #[derive(Clone, PartialEq, Eq, Debug)] + pub struct Permissions { + _private: (), + } + + impl Permissions { + /// Returns the read-only flag. + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let perm = fs::metadata("a.txt").await?.permissions(); + /// println!("{:?}", perm.readonly()); + /// # + /// # Ok(()) }) } + /// ``` + pub fn readonly(&self) -> bool { + unimplemented!() + } + + /// Configures the read-only flag. + /// + /// [`fs::set_permissions`]: fn.set_permissions.html + /// + /// # Examples + /// + /// ```no_run + /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { + /// # + /// use async_std::fs; + /// + /// let mut perm = fs::metadata("a.txt").await?.permissions(); + /// perm.set_readonly(true); + /// fs::set_permissions("a.txt", perm).await?; + /// # + /// # Ok(()) }) } + /// ``` + pub fn set_readonly(&mut self, readonly: bool) { + unimplemented!() + } + } + } else { + pub use std::fs::Permissions; + } +} diff --git a/src/fs/read.rs b/src/fs/read.rs index 105ae79..6b3560d 100644 --- a/src/fs/read.rs +++ b/src/fs/read.rs @@ -1,25 +1,28 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Read the entire contents of a file into a bytes vector. +/// Reads the entire contents of a file as raw bytes. /// /// This is a convenience function for reading entire files. It pre-allocates a buffer based on the -/// file size when available, so it is generally faster than manually opening a file and reading -/// into a `Vec`. +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as a string, use [`read_to_string`] instead. /// /// This function is an async version of [`std::fs::read`]. /// +/// [`read_to_string`]: fn.read_to_string.html /// [`std::fs::read`]: https://doc.rust-lang.org/std/fs/fn.read.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to read `path`. +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -34,5 +37,5 @@ use crate::task::blocking; /// ``` pub async fn read>(path: P) -> io::Result> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read(path) }).await + blocking::spawn(async move { std::fs::read(path) }).await } diff --git a/src/fs/read_dir.rs b/src/fs/read_dir.rs index b377161..ea0caec 100644 --- a/src/fs/read_dir.rs +++ b/src/fs/read_dir.rs @@ -1,30 +1,29 @@ -use std::fs; use std::path::Path; use std::pin::Pin; -use super::DirEntry; +use crate::fs::DirEntry; use crate::future::Future; use crate::io; use crate::task::{blocking, Context, Poll}; -/// Returns a stream over the entries within a directory. +/// Returns a stream of entries in a directory. /// -/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. New errors may be encountered -/// after a stream is initially constructed. +/// The stream yields items of type [`io::Result`]`<`[`DirEntry`]`>`. Note that I/O errors can +/// occur while reading from the stream. /// /// This function is an async version of [`std::fs::read_dir`]. /// -/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html +/// [`io::Result`]: ../io/type.Result.html /// [`DirEntry`]: struct.DirEntry.html /// [`std::fs::read_dir`]: https://doc.rust-lang.org/std/fs/fn.read_dir.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * `path` does not point at a directory. -/// * The current process lacks permissions to view the contents of `path`. +/// * `path` does not point to an existing directory. +/// * The current process lacks permissions to read the contents of the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -34,23 +33,23 @@ use crate::task::{blocking, Context, Poll}; /// use async_std::fs; /// use async_std::prelude::*; /// -/// let mut dir = fs::read_dir(".").await?; +/// let mut entries = fs::read_dir(".").await?; /// -/// while let Some(entry) = dir.next().await { -/// let entry = entry?; -/// println!("{:?}", entry.file_name()); +/// while let Some(res) = entries.next().await { +/// let entry = res?; +/// println!("{}", entry.file_name().to_string_lossy()); /// } /// # /// # Ok(()) }) } /// ``` pub async fn read_dir>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read_dir(path) }) + blocking::spawn(async move { std::fs::read_dir(path) }) .await .map(ReadDir::new) } -/// A stream over entries in a directory. +/// A stream of entries in a directory. /// /// This stream is returned by [`read_dir`] and yields items of type /// [`io::Result`]`<`[`DirEntry`]`>`. Each [`DirEntry`] can then retrieve information like entry's @@ -59,7 +58,7 @@ pub async fn read_dir>(path: P) -> io::Result { /// This type is an async version of [`std::fs::ReadDir`]. /// /// [`read_dir`]: fn.read_dir.html -/// [`io::Result`]: https://doc.rust-lang.org/std/io/type.Result.html +/// [`io::Result`]: ../io/type.Result.html /// [`DirEntry`]: struct.DirEntry.html /// [`std::fs::ReadDir`]: https://doc.rust-lang.org/std/fs/struct.ReadDir.html #[derive(Debug)] @@ -70,13 +69,13 @@ pub struct ReadDir(State); /// The `ReadDir` can be either idle or busy performing an asynchronous operation. #[derive(Debug)] enum State { - Idle(Option), - Busy(blocking::JoinHandle<(fs::ReadDir, Option>)>), + Idle(Option), + Busy(blocking::JoinHandle<(std::fs::ReadDir, Option>)>), } impl ReadDir { /// Creates an asynchronous `ReadDir` from a synchronous handle. - pub(crate) fn new(inner: fs::ReadDir) -> ReadDir { + pub(crate) fn new(inner: std::fs::ReadDir) -> ReadDir { ReadDir(State::Idle(Some(inner))) } } diff --git a/src/fs/read_link.rs b/src/fs/read_link.rs index 9ab87e5..aede99b 100644 --- a/src/fs/read_link.rs +++ b/src/fs/read_link.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::{Path, PathBuf}; use crate::io; use crate::task::blocking; -/// Reads a symbolic link, returning the path it points to. +/// Reads a symbolic link and returns the path it points to. /// /// This function is an async version of [`std::fs::read_link`]. /// @@ -12,10 +11,10 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a symbolic link. -/// * `path` does not exist. +/// * `path` does not point to an existing link. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +29,5 @@ use crate::task::blocking; /// ``` pub async fn read_link>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read_link(path) }).await + blocking::spawn(async move { std::fs::read_link(path) }).await } diff --git a/src/fs/read_to_string.rs b/src/fs/read_to_string.rs index c5ce36b..345f76e 100644 --- a/src/fs/read_to_string.rs +++ b/src/fs/read_to_string.rs @@ -1,21 +1,29 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Read the entire contents of a file into a string. +/// Reads the entire contents of a file as a string. +/// +/// This is a convenience function for reading entire files. It pre-allocates a string based on the +/// file size when available, so it is typically faster than manually opening a file and reading +/// from it. +/// +/// If you want to read the contents as raw bytes, use [`read`] instead. /// /// This function is an async version of [`std::fs::read_to_string`]. /// +/// [`read`]: fn.read.html /// [`std::fs::read_to_string`]: https://doc.rust-lang.org/std/fs/fn.read_to_string.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a file. -/// * The current process lacks permissions to read `path`. +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to read the file. +/// * The contents of the file cannot be read as a UTF-8 string. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +38,5 @@ use crate::task::blocking; /// ``` pub async fn read_to_string>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::read_to_string(path) }).await + blocking::spawn(async move { std::fs::read_to_string(path) }).await } diff --git a/src/fs/remove_dir.rs b/src/fs/remove_dir.rs index 275e799..a176edc 100644 --- a/src/fs/remove_dir.rs +++ b/src/fs/remove_dir.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Removes an existing, empty directory. +/// Removes an empty directory. /// /// This function is an async version of [`std::fs::remove_dir`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not an empty directory. -/// * The current process lacks permissions to remove directory at `path`. +/// * `path` is not an existing and empty directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -24,11 +24,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::remove_dir("./some/dir").await?; +/// fs::remove_dir("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn remove_dir>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::remove_dir(path) }).await + blocking::spawn(async move { std::fs::remove_dir(path) }).await } diff --git a/src/fs/remove_dir_all.rs b/src/fs/remove_dir_all.rs index 0be81df..9db0c31 100644 --- a/src/fs/remove_dir_all.rs +++ b/src/fs/remove_dir_all.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Removes an directory and all of its contents. +/// Removes a directory and all of its contents. /// /// This function is an async version of [`std::fs::remove_dir_all`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a directory. -/// * The current process lacks permissions to remove directory at `path`. +/// * `path` is not an existing and empty directory. +/// * The current process lacks permissions to remove the directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -24,11 +24,11 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::remove_dir_all("./some/dir").await?; +/// fs::remove_dir_all("./some/directory").await?; /// # /// # Ok(()) }) } /// ``` pub async fn remove_dir_all>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::remove_dir_all(path) }).await + blocking::spawn(async move { std::fs::remove_dir_all(path) }).await } diff --git a/src/fs/remove_file.rs b/src/fs/remove_file.rs index 09bd07e..cc0eeb2 100644 --- a/src/fs/remove_file.rs +++ b/src/fs/remove_file.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Removes a file from the filesystem. +/// Removes a file. /// /// This function is an async version of [`std::fs::remove_file`]. /// @@ -12,10 +11,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` is not a file. -/// * The current process lacks permissions to remove file at `path`. +/// * `path` does not point to an existing file. +/// * The current process lacks permissions to remove the file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +30,5 @@ use crate::task::blocking; /// ``` pub async fn remove_file>(path: P) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::remove_file(path) }).await + blocking::spawn(async move { std::fs::remove_file(path) }).await } diff --git a/src/fs/rename.rs b/src/fs/rename.rs index 05f755a..72cd227 100644 --- a/src/fs/rename.rs +++ b/src/fs/rename.rs @@ -1,10 +1,12 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Renames a file or directory to a new name, replacing the original if it already exists. +/// Renames a file or directory to a new location. +/// +/// If a file or directory already exists at the target location, it will be overwritten by this +/// operation. /// /// This function is an async version of [`std::fs::rename`]. /// @@ -12,11 +14,12 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `from` does not exist. +/// * `from` does not point to an existing file or directory. /// * `from` and `to` are on different filesystems. -/// * The current process lacks permissions to rename `from` to `to`. +/// * The current process lacks permissions to do the rename operation. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -32,5 +35,5 @@ use crate::task::blocking; pub async fn rename, Q: AsRef>(from: P, to: Q) -> io::Result<()> { let from = from.as_ref().to_owned(); let to = to.as_ref().to_owned(); - blocking::spawn(async move { fs::rename(&from, &to) }).await + blocking::spawn(async move { std::fs::rename(&from, &to) }).await } diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 05e5bda..41f92b2 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -1,10 +1,10 @@ -use std::fs; use std::path::Path; use crate::io; +use crate::fs::Permissions; use crate::task::blocking; -/// Changes the permissions on a file or directory. +/// Changes the permissions of a file or directory. /// /// This function is an async version of [`std::fs::set_permissions`]. /// @@ -12,10 +12,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to change attributes of `path`. +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to change attributes on the file or directory. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,7 +31,7 @@ use crate::task::blocking; /// # /// # Ok(()) }) } /// ``` -pub async fn set_permissions>(path: P, perm: fs::Permissions) -> io::Result<()> { +pub async fn set_permissions>(path: P, perm: Permissions) -> io::Result<()> { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::set_permissions(path, perm) }).await + blocking::spawn(async move { std::fs::set_permissions(path, perm) }).await } diff --git a/src/fs/symlink_metadata.rs b/src/fs/symlink_metadata.rs index 78c95be..6f1b9d5 100644 --- a/src/fs/symlink_metadata.rs +++ b/src/fs/symlink_metadata.rs @@ -1,21 +1,26 @@ -use std::fs::{self, Metadata}; use std::path::Path; +use crate::fs::Metadata; use crate::io; use crate::task::blocking; -/// Queries the metadata for a path without following symlinks. +/// Reads metadata for a path without following symbolic links. +/// +/// If you want to follow symbolic links before reading metadata of the target file or directory, +/// use [`metadata`] instead. /// /// This function is an async version of [`std::fs::symlink_metadata`]. /// +/// [`metadata`]: fn.metadata.html /// [`std::fs::symlink_metadata`]: https://doc.rust-lang.org/std/fs/fn.symlink_metadata.html /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * `path` does not exist. -/// * The current process lacks permissions to query metadata for `path`. +/// * `path` does not point to an existing file or directory. +/// * The current process lacks permissions to read metadata for the path. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -30,5 +35,5 @@ use crate::task::blocking; /// ``` pub async fn symlink_metadata>(path: P) -> io::Result { let path = path.as_ref().to_owned(); - blocking::spawn(async move { fs::symlink_metadata(path) }).await + blocking::spawn(async move { std::fs::symlink_metadata(path) }).await } diff --git a/src/fs/write.rs b/src/fs/write.rs index ceaf0fc..b0d7abc 100644 --- a/src/fs/write.rs +++ b/src/fs/write.rs @@ -1,10 +1,9 @@ -use std::fs; use std::path::Path; use crate::io; use crate::task::blocking; -/// Writes a slice of bytes as the entire contents of a file. +/// Writes a slice of bytes as the new contents of a file. /// /// This function will create a file if it does not exist, and will entirely replace its contents /// if it does. @@ -15,9 +14,11 @@ use crate::task::blocking; /// /// # Errors /// -/// An error will be returned in the following situations (not an exhaustive list): +/// An error will be returned in the following situations: /// -/// * The current process lacks permissions to write into `path`. +/// * The file's parent directory does not exist. +/// * The current process lacks permissions to write to the file. +/// * Some other I/O error occurred. /// /// # Examples /// @@ -26,12 +27,12 @@ use crate::task::blocking; /// # /// use async_std::fs; /// -/// fs::write("a.txt", b"Lorem ipsum").await?; +/// fs::write("a.txt", b"Hello world!").await?; /// # /// # Ok(()) }) } /// ``` pub async fn write, C: AsRef<[u8]>>(path: P, contents: C) -> io::Result<()> { let path = path.as_ref().to_owned(); let contents = contents.as_ref().to_owned(); - blocking::spawn(async move { fs::write(path, contents) }).await + blocking::spawn(async move { std::fs::write(path, contents) }).await } diff --git a/src/future/timeout.rs b/src/future/timeout.rs index eb7d8ba..e433bf1 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -60,7 +60,7 @@ impl Future for TimeoutFuture { match self.as_mut().future().poll(cx) { Poll::Ready(v) => Poll::Ready(Ok(v)), Poll::Pending => match self.delay().poll(cx) { - Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _priv: () })), + Poll::Ready(_) => Poll::Ready(Err(TimeoutError { _private: () })), Poll::Pending => Poll::Pending, }, } @@ -71,7 +71,7 @@ impl Future for TimeoutFuture { #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { - _priv: (), + _private: (), } impl Error for TimeoutError {} diff --git a/src/io/buf_read/lines.rs b/src/io/buf_read/lines.rs index 17ec447..0536086 100644 --- a/src/io/buf_read/lines.rs +++ b/src/io/buf_read/lines.rs @@ -16,7 +16,7 @@ use crate::task::{Context, Poll}; /// /// [`lines`]: trait.BufRead.html#method.lines /// [`BufRead`]: trait.BufRead.html -/// [`std::io::Lines`]: https://doc.rust-lang.org/nightly/std/io/struct.Lines.html +/// [`std::io::Lines`]: https://doc.rust-lang.org/std/io/struct.Lines.html #[derive(Debug)] pub struct Lines { pub(crate) reader: R, diff --git a/src/io/buf_reader.rs b/src/io/buf_reader.rs index f38307a..6329a1c 100644 --- a/src/io/buf_reader.rs +++ b/src/io/buf_reader.rs @@ -37,10 +37,10 @@ const DEFAULT_CAPACITY: usize = 8 * 1024; /// use async_std::io::BufReader; /// use async_std::prelude::*; /// -/// let mut f = BufReader::new(File::open("a.txt").await?); +/// let mut file = BufReader::new(File::open("a.txt").await?); /// /// let mut line = String::new(); -/// f.read_line(&mut line).await?; +/// file.read_line(&mut line).await?; /// # /// # Ok(()) }) } /// ``` @@ -134,8 +134,8 @@ impl BufReader { /// use async_std::fs::File; /// use async_std::io::BufReader; /// - /// let mut f = BufReader::new(File::open("a.txt").await?); - /// let inner = f.get_mut(); + /// let mut file = BufReader::new(File::open("a.txt").await?); + /// let inner = file.get_mut(); /// # /// # Ok(()) }) } /// ``` diff --git a/src/io/empty.rs b/src/io/empty.rs index 35ed732..2668dcc 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -25,7 +25,7 @@ use crate::task::{Context, Poll}; /// # Ok(()) }) } /// ``` pub fn empty() -> Empty { - Empty { _priv: () } + Empty { _private: () } } /// A reader that contains no data. @@ -34,7 +34,7 @@ pub fn empty() -> Empty { /// /// [`sink`]: fn.sink.html pub struct Empty { - _priv: (), + _private: (), } impl fmt::Debug for Empty { diff --git a/src/io/read/mod.rs b/src/io/read/mod.rs index 819f26e..bc6671c 100644 --- a/src/io/read/mod.rs +++ b/src/io/read/mod.rs @@ -63,10 +63,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = vec![0; 1024]; - /// let n = f.read(&mut buf).await?; + /// let n = file.read(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` @@ -112,10 +112,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = Vec::new(); - /// f.read_to_end(&mut buf).await?; + /// file.read_to_end(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` @@ -149,10 +149,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = String::new(); - /// f.read_to_string(&mut buf).await?; + /// file.read_to_string(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` @@ -201,10 +201,10 @@ pub trait Read { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// /// let mut buf = vec![0; 10]; - /// f.read_exact(&mut buf).await?; + /// file.read_exact(&mut buf).await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/io/seek.rs b/src/io/seek.rs index 61a5d9c..b16da75 100644 --- a/src/io/seek.rs +++ b/src/io/seek.rs @@ -49,9 +49,9 @@ pub trait Seek { /// use async_std::io::SeekFrom; /// use async_std::prelude::*; /// - /// let mut f = File::open("a.txt").await?; + /// let mut file = File::open("a.txt").await?; /// - /// let file_len = f.seek(SeekFrom::End(0)).await?; + /// let file_len = file.seek(SeekFrom::End(0)).await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/io/sink.rs b/src/io/sink.rs index fba5633..071f6ed 100644 --- a/src/io/sink.rs +++ b/src/io/sink.rs @@ -22,7 +22,7 @@ use crate::task::{Context, Poll}; /// # Ok(()) }) } /// ``` pub fn sink() -> Sink { - Sink { _priv: () } + Sink { _private: () } } /// A writer that consumes and drops all data. @@ -31,7 +31,7 @@ pub fn sink() -> Sink { /// /// [`sink`]: fn.sink.html pub struct Sink { - _priv: (), + _private: (), } impl fmt::Debug for Sink { diff --git a/src/io/write/mod.rs b/src/io/write/mod.rs index 04cff74..63a3cd8 100644 --- a/src/io/write/mod.rs +++ b/src/io/write/mod.rs @@ -56,9 +56,9 @@ pub trait Write { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::create("a.txt").await?; + /// let mut file = File::create("a.txt").await?; /// - /// let n = f.write(b"hello world").await?; + /// let n = file.write(b"hello world").await?; /// # /// # Ok(()) }) } /// ``` @@ -76,10 +76,10 @@ pub trait Write { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::create("a.txt").await?; + /// let mut file = File::create("a.txt").await?; /// - /// f.write_all(b"hello world").await?; - /// f.flush().await?; + /// file.write_all(b"hello world").await?; + /// file.flush().await?; /// # /// # Ok(()) }) } /// ``` @@ -113,6 +113,8 @@ pub trait Write { /// an error is returned. This method will not return until the entire buffer has been /// successfully written or such an error occurs. /// + /// [`write`]: #tymethod.write + /// /// # Examples /// /// ```no_run @@ -121,9 +123,9 @@ pub trait Write { /// use async_std::fs::File; /// use async_std::prelude::*; /// - /// let mut f = File::create("a.txt").await?; + /// let mut file = File::create("a.txt").await?; /// - /// f.write_all(b"hello world").await?; + /// file.write_all(b"hello world").await?; /// # /// # Ok(()) }) } /// ``` diff --git a/src/lib.rs b/src/lib.rs index e5615b9..01813e4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,13 +1,16 @@ //! Async version of the Rust standard library. //! -//! This crate is an async version of [`std`]. +//! Modules in this crate are organized in the same way as in the standard library, except blocking +//! functions have been replaced with async functions and threads have been replaced with +//! lightweight tasks. //! -//! Higher-level documentation in the form of the book -//! ["Async programming in Rust with async-std"][book] -//! is available. +//! More information, reading materials, and other resources: //! -//! [`std`]: https://doc.rust-lang.org/std/index.html -//! [book]: https://book.async.rs +//! * [🌐 The async-std website](https://async.rs/) +//! * [📖 The async-std book](https://book.async.rs) +//! * [🐙 GitHub repository](https://github.com/async-rs/async-std) +//! * [📒 List of code examples](https://github.com/async-rs/async-std/tree/master/examples) +//! * [💬 Discord chat](https://discord.gg/JvZeVNe) //! //! # Examples //! @@ -23,8 +26,15 @@ //! } //! ``` //! -//! See [here](https://github.com/async-rs/async-std/tree/master/examples) -//! for more examples. +//! # Features +//! +//! Unstable APIs in this crate are available when the `unstable` Cargo feature is enabled: +//! +//! ```toml +//! [dependencies.async-std] +//! version = "0.99" +//! features = ["unstable"] +//! ``` #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] diff --git a/src/os/unix/fs.rs b/src/os/unix/fs.rs index 16d7cab..be8932c 100644 --- a/src/os/unix/fs.rs +++ b/src/os/unix/fs.rs @@ -69,7 +69,6 @@ cfg_if! { fn custom_flags(&mut self, flags: i32) -> &mut Self; } } else { - #[doc(inline)] pub use std::os::unix::fs::{DirBuilderExt, OpenOptionsExt}; } } diff --git a/src/os/unix/io.rs b/src/os/unix/io.rs index 87e83ea..820d509 100644 --- a/src/os/unix/io.rs +++ b/src/os/unix/io.rs @@ -51,7 +51,6 @@ cfg_if! { fn into_raw_fd(self) -> RawFd; } } else { - #[doc(inline)] pub use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd}; } } diff --git a/src/os/unix/net/mod.rs b/src/os/unix/net/mod.rs index 1597948..465db54 100644 --- a/src/os/unix/net/mod.rs +++ b/src/os/unix/net/mod.rs @@ -94,7 +94,6 @@ cfg_if! { } } } else { - #[doc(inline)] pub use std::os::unix::net::SocketAddr; } } diff --git a/src/os/windows/io.rs b/src/os/windows/io.rs index adf0c0d..20f87d2 100644 --- a/src/os/windows/io.rs +++ b/src/os/windows/io.rs @@ -43,7 +43,6 @@ cfg_if! { fn into_raw_handle(self) -> RawHandle; } } else { - #[doc(inline)] pub use std::os::windows::io::{ AsRawHandle, FromRawHandle, IntoRawHandle, RawHandle, RawSocket, }; diff --git a/src/prelude.rs b/src/prelude.rs index 38956b9..a50e123 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,6 +1,15 @@ //! The async prelude. //! -//! The prelude re-exports the most commonly used traits in this crate. +//! The prelude re-exports most commonly used traits and macros from this crate. +//! +//! # Examples +//! +//! Import the prelude with: +//! +//! ``` +//! # #[allow(unused_imports)] +//! use async_std::prelude::*; +//! ``` #[doc(no_inline)] pub use crate::future::Future; From 7f71af9415d25b45784fcf77546a775ecdad720f Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sat, 14 Sep 2019 09:15:51 +0200 Subject: [PATCH 112/194] cargo fmt --- src/fs/mod.rs | 2 +- src/fs/set_permissions.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/fs/mod.rs b/src/fs/mod.rs index 5612bb8..4598ec8 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -29,8 +29,8 @@ pub use dir_builder::DirBuilder; pub use dir_entry::DirEntry; pub use file::File; pub use file_type::FileType; -pub use open_options::OpenOptions; pub use metadata::Metadata; +pub use open_options::OpenOptions; pub use permissions::Permissions; pub use read_dir::ReadDir; diff --git a/src/fs/set_permissions.rs b/src/fs/set_permissions.rs index 41f92b2..6fa6306 100644 --- a/src/fs/set_permissions.rs +++ b/src/fs/set_permissions.rs @@ -1,7 +1,7 @@ use std::path::Path; -use crate::io; use crate::fs::Permissions; +use crate::io; use crate::task::blocking; /// Changes the permissions of a file or directory. From 50a7db2af47ac00845add30bb7b6b14d1e8e342d Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Sat, 14 Sep 2019 23:13:36 +0900 Subject: [PATCH 113/194] Add Stream::scan --- src/stream/mod.rs | 2 +- src/stream/stream/mod.rs | 45 +++++++++++++++++++++++++++++++++++++++ src/stream/stream/scan.rs | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/stream/stream/scan.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 6081912..3146bd3 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -25,7 +25,7 @@ pub use double_ended_stream::DoubleEndedStream; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Stream, Take}; +pub use stream::{Scan, Stream, Take}; mod double_ended_stream; mod empty; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index fb8b38d..9bcccb6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -29,8 +29,10 @@ mod find_map; mod min_by; mod next; mod nth; +mod scan; mod take; +pub use scan::Scan; pub use take::Take; use all::AllFuture; @@ -501,6 +503,49 @@ pub trait Stream { f, } } + + /// A stream adaptor similar to [`fold`] that holds internal state and produces a new stream. + /// + /// [`fold`]: #method.fold + /// + /// `scan()` takes two arguments: an initial value which seeds the internal state, and a + /// closure with two arguments, the first being a mutable reference to the internal state and + /// the second a stream element. The closure can assign to the internal state to share state + /// between iterations. + /// + /// On iteration, the closure will be applied to each element of the stream and the return + /// value from the closure, an `Option`, is yielded by the stream. + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let mut s = s.scan(1, |state, x| { + /// *state = *state * x; + /// Some(-*state) + /// }); + /// + /// assert_eq!(s.next().await, Some(-1)); + /// assert_eq!(s.next().await, Some(-2)); + /// assert_eq!(s.next().await, Some(-6)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + #[inline] + fn scan(self, initial_state: St, f: F) -> Scan + where + Self: Sized, + St: Unpin, + F: Unpin + FnMut(&mut St, Self::Item) -> Option, + { + Scan::new(self, initial_state, f) + } } impl Stream for T { diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs new file mode 100644 index 0000000..0776a7b --- /dev/null +++ b/src/stream/stream/scan.rs @@ -0,0 +1,41 @@ +use crate::task::{Context, Poll}; + +use std::pin::Pin; + +/// A stream to maintain state while polling another stream. +#[derive(Debug)] +pub struct Scan { + stream: S, + state_f: (St, F), +} + +impl Scan { + pub(crate) fn new(stream: S, initial_state: St, f: F) -> Self { + Self { + stream, + state_f: (initial_state, f), + } + } + + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(state_f: (St, F)); +} + +impl futures_core::stream::Stream for Scan +where + S: futures_core::stream::Stream, + St: Unpin, + F: Unpin + FnMut(&mut St, S::Item) -> Option, +{ + type Item = B; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let poll_result = self.as_mut().stream().poll_next(cx); + poll_result.map(|item| { + item.and_then(|item| { + let (state, f) = self.as_mut().state_f(); + f(state, item) + }) + }) + } +} From 127feb47f9a14fb689193a54b151381c6d5c5ea9 Mon Sep 17 00:00:00 2001 From: Kevin Donahue Date: Sat, 14 Sep 2019 16:35:27 -0400 Subject: [PATCH 114/194] add doc comment for join handle drop behavior --- src/task/task.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/task/task.rs b/src/task/task.rs index c0d52db..8ffe821 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -46,6 +46,9 @@ impl fmt::Debug for Task { /// A handle that awaits the result of a task. /// +/// Dropping a [`JoinHandle`] will detach the task, meaning that there is no longer +/// a handle to the task and no way to `join` on it. +/// /// Created when a task is [spawned]. /// /// [spawned]: fn.spawn.html From 1d862cf60436ef5a5152eeaf871e91f5e2df714b Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Sun, 15 Sep 2019 00:36:09 +0200 Subject: [PATCH 115/194] Remove the Send bound from block_on --- src/task/block_on.rs | 53 ++++++++++++++++---- src/task/log_utils.rs | 32 ++++++++++++ src/task/mod.rs | 1 + src/task/pool.rs | 112 +++++++++++++++--------------------------- 4 files changed, 116 insertions(+), 82 deletions(-) create mode 100644 src/task/log_utils.rs diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 92c4b51..ae0a012 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -6,10 +6,12 @@ use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; use std::thread::{self, Thread}; +use super::log_utils; use super::pool; -use super::Builder; +use super::task; use crate::future::Future; use crate::task::{Context, Poll, Waker}; +use crate::utils::abort_on_panic; /// Spawns a task and blocks the current thread on its result. /// @@ -32,8 +34,7 @@ use crate::task::{Context, Poll, Waker}; /// ``` pub fn block_on(future: F) -> T where - F: Future + Send, - T: Send, + F: Future, { unsafe { // A place on the stack where the result will be stored. @@ -51,17 +52,48 @@ where } }; + // Create a tag for the task. + let tag = task::Tag::new(None); + + // Log this `block_on` operation. + let child_id = tag.task_id().as_u64(); + let parent_id = pool::get_task(|t| t.id().as_u64()).unwrap_or(0); + log_utils::print( + format_args!("block_on"), + log_utils::LogData { + parent_id, + child_id, + }, + ); + + // Wrap the future into one that drops task-local variables on exit. + let future = async move { + let res = future.await; + + // Abort on panic because thread-local variables behave the same way. + abort_on_panic(|| pool::get_task(|task| task.metadata().local_map.clear())); + + log_utils::print( + format_args!("block_on completed"), + log_utils::LogData { + parent_id, + child_id, + }, + ); + res + }; + // Pin the future onto the stack. pin_utils::pin_mut!(future); - // Transmute the future into one that is static and sendable. + // Transmute the future into one that is static. let future = mem::transmute::< - Pin<&mut dyn Future>, - Pin<&'static mut (dyn Future + Send)>, + Pin<&'_ mut dyn Future>, + Pin<&'static mut dyn Future>, >(future); - // Spawn the future and wait for it to complete. - block(pool::spawn_with_builder(Builder::new(), future, "block_on")); + // Block on the future and and wait for it to complete. + pool::set_tag(&tag, || block(future)); // Take out the result. match (*out.get()).take().unwrap() { @@ -87,7 +119,10 @@ impl Future for CatchUnwindFuture { } } -fn block(f: F) -> F::Output { +fn block(f: F) -> T +where + F: Future, +{ thread_local! { static ARC_THREAD: Arc = Arc::new(thread::current()); } diff --git a/src/task/log_utils.rs b/src/task/log_utils.rs new file mode 100644 index 0000000..ad0fe8c --- /dev/null +++ b/src/task/log_utils.rs @@ -0,0 +1,32 @@ +use std::fmt::Arguments; + +/// This struct only exists because kv logging isn't supported from the macros right now. +pub(crate) struct LogData { + pub parent_id: u64, + pub child_id: u64, +} + +impl<'a> log::kv::Source for LogData { + fn visit<'kvs>( + &'kvs self, + visitor: &mut dyn log::kv::Visitor<'kvs>, + ) -> Result<(), log::kv::Error> { + visitor.visit_pair("parent_id".into(), self.parent_id.into())?; + visitor.visit_pair("child_id".into(), self.child_id.into())?; + Ok(()) + } +} + +pub fn print(msg: Arguments<'_>, key_values: impl log::kv::Source) { + log::logger().log( + &log::Record::builder() + .args(msg) + .key_values(&key_values) + .level(log::Level::Trace) + .target(module_path!()) + .module_path(Some(module_path!())) + .file(Some(file!())) + .line(Some(line!())) + .build(), + ); +} diff --git a/src/task/mod.rs b/src/task/mod.rs index eef7284..21b0533 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -32,6 +32,7 @@ pub use task::{JoinHandle, Task, TaskId}; mod block_on; mod local; +mod log_utils; mod pool; mod sleep; mod task; diff --git a/src/task/pool.rs b/src/task/pool.rs index 3640909..c996848 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -1,16 +1,16 @@ use std::cell::Cell; -use std::fmt::Arguments; -use std::mem; use std::ptr; use std::thread; use crossbeam_channel::{unbounded, Sender}; use lazy_static::lazy_static; +use super::log_utils; use super::task; use super::{JoinHandle, Task}; use crate::future::Future; use crate::io; +use crate::utils::abort_on_panic; /// Returns a handle to the current task. /// @@ -64,7 +64,7 @@ where F: Future + Send + 'static, T: Send + 'static, { - spawn_with_builder(Builder::new(), future, "spawn") + spawn_with_builder(Builder::new(), future) } /// Task builder that configures the settings of a new task. @@ -91,15 +91,11 @@ impl Builder { F: Future + Send + 'static, T: Send + 'static, { - Ok(spawn_with_builder(self, future, "spawn")) + Ok(spawn_with_builder(self, future)) } } -pub(crate) fn spawn_with_builder( - builder: Builder, - future: F, - fn_name: &'static str, -) -> JoinHandle +pub(crate) fn spawn_with_builder(builder: Builder, future: F) -> JoinHandle where F: Future + Send + 'static, T: Send + 'static, @@ -117,13 +113,9 @@ where thread::Builder::new() .name("async-task-driver".to_string()) .spawn(|| { - TAG.with(|tag| { - for job in receiver { - tag.set(job.tag()); - abort_on_panic(|| job.run()); - tag.set(ptr::null()); - } - }); + for job in receiver { + set_tag(job.tag(), || abort_on_panic(|| job.run())) + } }) .expect("cannot start a thread driving tasks"); } @@ -135,11 +127,12 @@ where let tag = task::Tag::new(name); let schedule = |job| QUEUE.send(job).unwrap(); + // Log this `spawn` operation. let child_id = tag.task_id().as_u64(); let parent_id = get_task(|t| t.id().as_u64()).unwrap_or(0); - print( - format_args!("{}", fn_name), - LogData { + log_utils::print( + format_args!("spawn"), + log_utils::LogData { parent_id, child_id, }, @@ -152,9 +145,9 @@ where // Abort on panic because thread-local variables behave the same way. abort_on_panic(|| get_task(|task| task.metadata().local_map.clear())); - print( - format_args!("{} completed", fn_name), - LogData { + log_utils::print( + format_args!("spawn completed"), + log_utils::LogData { parent_id, child_id, }, @@ -171,7 +164,30 @@ thread_local! { static TAG: Cell<*const task::Tag> = Cell::new(ptr::null_mut()); } -pub(crate) fn get_task R, R>(f: F) -> Option { +pub(crate) fn set_tag(tag: *const task::Tag, f: F) -> R +where + F: FnOnce() -> R, +{ + struct ResetTag<'a>(&'a Cell<*const task::Tag>); + + impl Drop for ResetTag<'_> { + fn drop(&mut self) { + self.0.set(ptr::null()); + } + } + + TAG.with(|t| { + t.set(tag); + let _guard = ResetTag(t); + + f() + }) +} + +pub(crate) fn get_task(f: F) -> Option +where + F: FnOnce(&Task) -> R, +{ let res = TAG.try_with(|tag| unsafe { tag.get().as_ref().map(task::Tag::task).map(f) }); match res { @@ -179,53 +195,3 @@ pub(crate) fn get_task R, R>(f: F) -> Option { Ok(None) | Err(_) => None, } } - -/// Calls a function and aborts if it panics. -/// -/// This is useful in unsafe code where we can't recover from panics. -#[inline] -fn abort_on_panic(f: impl FnOnce() -> T) -> T { - struct Bomb; - - impl Drop for Bomb { - fn drop(&mut self) { - std::process::abort(); - } - } - - let bomb = Bomb; - let t = f(); - mem::forget(bomb); - t -} - -/// This struct only exists because kv logging isn't supported from the macros right now. -struct LogData { - parent_id: u64, - child_id: u64, -} - -impl<'a> log::kv::Source for LogData { - fn visit<'kvs>( - &'kvs self, - visitor: &mut dyn log::kv::Visitor<'kvs>, - ) -> Result<(), log::kv::Error> { - visitor.visit_pair("parent_id".into(), self.parent_id.into())?; - visitor.visit_pair("child_id".into(), self.child_id.into())?; - Ok(()) - } -} - -fn print(msg: Arguments<'_>, key_values: impl log::kv::Source) { - log::logger().log( - &log::Record::builder() - .args(msg) - .key_values(&key_values) - .level(log::Level::Trace) - .target(module_path!()) - .module_path(Some(module_path!())) - .file(Some(file!())) - .line(Some(line!())) - .build(), - ); -} From 91e61cf6bf6808b8f9380122a0d38b4d79b1fdd1 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Sun, 15 Sep 2019 21:18:02 +0900 Subject: [PATCH 116/194] Remove unnecessary Unpin bounds --- src/stream/stream/scan.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 0776a7b..4846144 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -9,7 +9,7 @@ pub struct Scan { state_f: (St, F), } -impl Scan { +impl Scan { pub(crate) fn new(stream: S, initial_state: St, f: F) -> Self { Self { stream, @@ -21,11 +21,12 @@ impl Scan { pin_utils::unsafe_unpinned!(state_f: (St, F)); } +impl Unpin for Scan {} + impl futures_core::stream::Stream for Scan where S: futures_core::stream::Stream, - St: Unpin, - F: Unpin + FnMut(&mut St, S::Item) -> Option, + F: FnMut(&mut St, S::Item) -> Option, { type Item = B; From e7b0fe2d2e39b7dec27842110fec420d7cd0db5d Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Mon, 16 Sep 2019 11:28:38 +0900 Subject: [PATCH 117/194] Remove Unpin bounds more --- src/stream/stream/mod.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 9bcccb6..24423b6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -541,8 +541,7 @@ pub trait Stream { fn scan(self, initial_state: St, f: F) -> Scan where Self: Sized, - St: Unpin, - F: Unpin + FnMut(&mut St, Self::Item) -> Option, + F: FnMut(&mut St, Self::Item) -> Option, { Scan::new(self, initial_state, f) } From 689b3c65607c3c188fb696a0ac1bc327028f4280 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Mon, 16 Sep 2019 16:47:17 +0900 Subject: [PATCH 118/194] Add io::repeat --- src/io/mod.rs | 2 ++ src/io/repeat.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 src/io/repeat.rs diff --git a/src/io/mod.rs b/src/io/mod.rs index 5152b03..d272fd6 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -30,6 +30,7 @@ pub use buf_reader::BufReader; pub use copy::copy; pub use empty::{empty, Empty}; pub use read::Read; +pub use repeat::{repeat, Repeat}; pub use seek::Seek; pub use sink::{sink, Sink}; pub use stderr::{stderr, Stderr}; @@ -43,6 +44,7 @@ mod buf_reader; mod copy; mod empty; mod read; +mod repeat; mod seek; mod sink; mod stderr; diff --git a/src/io/repeat.rs b/src/io/repeat.rs new file mode 100644 index 0000000..7f8b3a6 --- /dev/null +++ b/src/io/repeat.rs @@ -0,0 +1,64 @@ +use std::fmt; +use std::pin::Pin; + +use futures_io::{AsyncRead, Initializer}; + +use crate::io; +use crate::task::{Context, Poll}; + +/// Creates an instance of a reader that infinitely repeats one byte. +/// +/// All reads from this reader will succeed by filling the specified buffer with the given byte. +/// +/// ## Examples +/// +/// ```rust +/// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { +/// # +/// use async_std::io; +/// use async_std::prelude::*; +/// +/// let mut buffer = [0; 3]; +/// io::repeat(0b101).read_exact(&mut buffer).await?; +/// +/// assert_eq!(buffer, [0b101, 0b101, 0b101]); +/// # +/// # Ok(()) }) } +/// ``` +pub fn repeat(byte: u8) -> Repeat { + Repeat { byte } +} + +/// A reader which yields one byte over and over and over and over and over and... +/// +/// This reader is constructed by the [`repeat`] function. +/// +/// [`repeat`]: fn.repeat.html +pub struct Repeat { + byte: u8, +} + +impl fmt::Debug for Repeat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.pad("Empty { .. }") + } +} + +impl AsyncRead for Repeat { + #[inline] + fn poll_read( + self: Pin<&mut Self>, + _: &mut Context<'_>, + buf: &mut [u8], + ) -> Poll> { + for b in &mut *buf { + *b = self.byte; + } + Poll::Ready(Ok(buf.len())) + } + + #[inline] + unsafe fn initializer(&self) -> Initializer { + Initializer::nop() + } +} From d6ffdbce8dada106315fd2d2bb9e44809904ee44 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Mon, 16 Sep 2019 18:25:46 +0900 Subject: [PATCH 119/194] Remove Unpin bound in `impl Stream for T` --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 24423b6..b0500c6 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -547,7 +547,7 @@ pub trait Stream { } } -impl Stream for T { +impl Stream for T { type Item = ::Item; fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { From b70dfeab15e134452d27c0d5c6ccb9a549f5c5a7 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Mon, 16 Sep 2019 18:35:37 +0900 Subject: [PATCH 120/194] Require S: async_std::stream::Stream in Scan --- src/stream/stream/scan.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/stream/stream/scan.rs b/src/stream/stream/scan.rs index 4846144..222022b 100644 --- a/src/stream/stream/scan.rs +++ b/src/stream/stream/scan.rs @@ -1,7 +1,8 @@ -use crate::task::{Context, Poll}; - use std::pin::Pin; +use crate::stream::Stream; +use crate::task::{Context, Poll}; + /// A stream to maintain state while polling another stream. #[derive(Debug)] pub struct Scan { @@ -25,7 +26,7 @@ impl Unpin for Scan {} impl futures_core::stream::Stream for Scan where - S: futures_core::stream::Stream, + S: Stream, F: FnMut(&mut St, S::Item) -> Option, { type Item = B; From 9c82d5e3f31da095f0bf5690a55ebf5e14a4fdb3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 14:07:06 +0200 Subject: [PATCH 121/194] remove custom log tools in favor of macro crate Signed-off-by: Yoshua Wuyts --- Cargo.toml | 1 + src/task/block_on.rs | 27 ++++++++++++--------------- src/task/log_utils.rs | 32 -------------------------------- src/task/mod.rs | 1 - src/task/pool.rs | 26 +++++++++++--------------- 5 files changed, 24 insertions(+), 63 deletions(-) delete mode 100644 src/task/log_utils.rs diff --git a/Cargo.toml b/Cargo.toml index 2d026fa..939a985 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,6 +38,7 @@ mio-uds = "0.6.7" num_cpus = "1.10.1" pin-utils = "0.1.0-alpha.4" slab = "0.4.2" +kv-log-macro = "1.0.4" [dev-dependencies] femme = "1.2.0" diff --git a/src/task/block_on.rs b/src/task/block_on.rs index ae0a012..0e9a5f4 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -6,13 +6,14 @@ use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; use std::thread::{self, Thread}; -use super::log_utils; use super::pool; use super::task; use crate::future::Future; use crate::task::{Context, Poll, Waker}; use crate::utils::abort_on_panic; +use kv_log_macro::trace; + /// Spawns a task and blocks the current thread on its result. /// /// Calling this function is similar to [spawning] a thread and immediately [joining] it, except an @@ -58,13 +59,11 @@ where // Log this `block_on` operation. let child_id = tag.task_id().as_u64(); let parent_id = pool::get_task(|t| t.id().as_u64()).unwrap_or(0); - log_utils::print( - format_args!("block_on"), - log_utils::LogData { - parent_id, - child_id, - }, - ); + + trace!("block_on", { + parent_id: parent_id, + child_id: child_id, + }); // Wrap the future into one that drops task-local variables on exit. let future = async move { @@ -73,13 +72,11 @@ where // Abort on panic because thread-local variables behave the same way. abort_on_panic(|| pool::get_task(|task| task.metadata().local_map.clear())); - log_utils::print( - format_args!("block_on completed"), - log_utils::LogData { - parent_id, - child_id, - }, - ); + trace!("block_on completed", { + parent_id: parent_id, + child_id: child_id, + }); + res }; diff --git a/src/task/log_utils.rs b/src/task/log_utils.rs deleted file mode 100644 index ad0fe8c..0000000 --- a/src/task/log_utils.rs +++ /dev/null @@ -1,32 +0,0 @@ -use std::fmt::Arguments; - -/// This struct only exists because kv logging isn't supported from the macros right now. -pub(crate) struct LogData { - pub parent_id: u64, - pub child_id: u64, -} - -impl<'a> log::kv::Source for LogData { - fn visit<'kvs>( - &'kvs self, - visitor: &mut dyn log::kv::Visitor<'kvs>, - ) -> Result<(), log::kv::Error> { - visitor.visit_pair("parent_id".into(), self.parent_id.into())?; - visitor.visit_pair("child_id".into(), self.child_id.into())?; - Ok(()) - } -} - -pub fn print(msg: Arguments<'_>, key_values: impl log::kv::Source) { - log::logger().log( - &log::Record::builder() - .args(msg) - .key_values(&key_values) - .level(log::Level::Trace) - .target(module_path!()) - .module_path(Some(module_path!())) - .file(Some(file!())) - .line(Some(line!())) - .build(), - ); -} diff --git a/src/task/mod.rs b/src/task/mod.rs index 21b0533..eef7284 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -32,7 +32,6 @@ pub use task::{JoinHandle, Task, TaskId}; mod block_on; mod local; -mod log_utils; mod pool; mod sleep; mod task; diff --git a/src/task/pool.rs b/src/task/pool.rs index c996848..9050253 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -4,8 +4,8 @@ use std::thread; use crossbeam_channel::{unbounded, Sender}; use lazy_static::lazy_static; +use kv_log_macro::trace; -use super::log_utils; use super::task; use super::{JoinHandle, Task}; use crate::future::Future; @@ -130,13 +130,11 @@ where // Log this `spawn` operation. let child_id = tag.task_id().as_u64(); let parent_id = get_task(|t| t.id().as_u64()).unwrap_or(0); - log_utils::print( - format_args!("spawn"), - log_utils::LogData { - parent_id, - child_id, - }, - ); + + trace!("spawn", { + parent_id: parent_id, + child_id: child_id, + }); // Wrap the future into one that drops task-local variables on exit. let future = async move { @@ -145,13 +143,11 @@ where // Abort on panic because thread-local variables behave the same way. abort_on_panic(|| get_task(|task| task.metadata().local_map.clear())); - log_utils::print( - format_args!("spawn completed"), - log_utils::LogData { - parent_id, - child_id, - }, - ); + trace!("spawn completed", { + parent_id: parent_id, + child_id: child_id, + }); + res }; From ab112e9f39c91c0de591a33f5846abde95d6c679 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 14:22:52 +0200 Subject: [PATCH 122/194] expose IoSlice, IoSliceMut Signed-off-by: Yoshua Wuyts --- src/io/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/io/mod.rs b/src/io/mod.rs index d272fd6..f37ec79 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -23,7 +23,7 @@ pub mod prelude; #[doc(inline)] -pub use std::io::{Error, ErrorKind, Result, SeekFrom}; +pub use std::io::{Error, ErrorKind, IoSlice, IoSliceMut, Result, SeekFrom}; pub use buf_read::{BufRead, Lines}; pub use buf_reader::BufReader; From dc664786c90393b454269722ead55b6fba3d8bc5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 14:46:56 +0200 Subject: [PATCH 123/194] document feature flags Signed-off-by: Yoshua Wuyts --- src/lib.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index e5615b9..ba12b2f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,25 @@ //! //! See [here](https://github.com/async-rs/async-std/tree/master/examples) //! for more examples. +//! +//! # Features +//! +//! `async-std` is strongly commited to following semver. This means your code +//! won't break unless _you_ decide to upgrade. +//! +//! However every now and then we come up with something that we think will +//! work _great_ for `async-std`, and we want to provide a sneak-peek so you +//! can try it out. This is what we call _"unstable"_ features. You can try out +//! the unstable features by enabling the `unstable` feature in you `Cargo.toml` +//! file: +//! +//! ```toml +//! [dependencies] +//! async-std = { version = "0.99.5", features = ["unstable"] } +//! ``` +//! +//! Just be careful when running these features, as they may change between +//! versions. #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] From 6e5f29fa5862e7fed3908ab0182c002244fb2fcb Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 14:54:19 +0200 Subject: [PATCH 124/194] document feature flags Signed-off-by: Yoshua Wuyts --- README.md | 18 ++++++++++++++++++ src/lib.rs | 19 ------------------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index eb0160f..9290757 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,24 @@ fn main() { } ``` +## Features + +`async-std` is strongly commited to following semver. This means your code won't +break unless _you_ decide to upgrade. + +However every now and then we come up with something that we think will work +_great_ for `async-std`, and we want to provide a sneak-peek so you can try it +out. This is what we call _"unstable"_ features. You can try out the unstable +features by enabling the `unstable` feature in you `Cargo.toml` file: + +```toml +[dependencies] +async-std = { version = "0.99.5", features = ["unstable"] } +``` + +Just be careful when running these features, as they may change between +versions. + ## Take a look around Clone the repo: diff --git a/src/lib.rs b/src/lib.rs index ba12b2f..e5615b9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,25 +25,6 @@ //! //! See [here](https://github.com/async-rs/async-std/tree/master/examples) //! for more examples. -//! -//! # Features -//! -//! `async-std` is strongly commited to following semver. This means your code -//! won't break unless _you_ decide to upgrade. -//! -//! However every now and then we come up with something that we think will -//! work _great_ for `async-std`, and we want to provide a sneak-peek so you -//! can try it out. This is what we call _"unstable"_ features. You can try out -//! the unstable features by enabling the `unstable` feature in you `Cargo.toml` -//! file: -//! -//! ```toml -//! [dependencies] -//! async-std = { version = "0.99.5", features = ["unstable"] } -//! ``` -//! -//! Just be careful when running these features, as they may change between -//! versions. #![cfg_attr(feature = "docs", feature(doc_cfg))] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] From 1341fa7addc59f4ad8ba73bf65f2a8dae2c3caa5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 15:34:06 +0200 Subject: [PATCH 125/194] cargo fmt Signed-off-by: Yoshua Wuyts --- src/task/pool.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/pool.rs b/src/task/pool.rs index 9050253..210b862 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -3,8 +3,8 @@ use std::ptr; use std::thread; use crossbeam_channel::{unbounded, Sender}; -use lazy_static::lazy_static; use kv_log_macro::trace; +use lazy_static::lazy_static; use super::task; use super::{JoinHandle, Task}; From e6fe8da058df29bd5204bf5e9a306b20e5a7d353 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 15:58:37 +0200 Subject: [PATCH 126/194] Update README.md Co-Authored-By: Stjepan Glavina --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9290757..6f267c2 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ features by enabling the `unstable` feature in you `Cargo.toml` file: async-std = { version = "0.99.5", features = ["unstable"] } ``` -Just be careful when running these features, as they may change between +Just be careful when using these features, as they may change between versions. ## Take a look around From 7827410c630b80c90e597f7fb4715df088097a28 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 15:59:41 +0200 Subject: [PATCH 127/194] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 6f267c2..9342914 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,9 @@ features by enabling the `unstable` feature in you `Cargo.toml` file: ```toml [dependencies] -async-std = { version = "0.99.5", features = ["unstable"] } +[dependencies.async-std] +version = "0.99" +features = ["unstable"] ``` Just be careful when using these features, as they may change between From 8058d637e71d74f778eb1d6a9e50578d09ef564f Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 15:59:48 +0200 Subject: [PATCH 128/194] Update README.md Co-Authored-By: Stjepan Glavina --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9342914..1ea5724 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ break unless _you_ decide to upgrade. However every now and then we come up with something that we think will work _great_ for `async-std`, and we want to provide a sneak-peek so you can try it out. This is what we call _"unstable"_ features. You can try out the unstable -features by enabling the `unstable` feature in you `Cargo.toml` file: +features by enabling the `unstable` feature in your `Cargo.toml` file: ```toml [dependencies] From 343a6c1039230ebfb2fb42a902dee0966935ce24 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 15:03:34 +0200 Subject: [PATCH 129/194] expose std::pin Signed-off-by: Yoshua Wuyts --- src/lib.rs | 1 + src/pin.rs | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 src/pin.rs diff --git a/src/lib.rs b/src/lib.rs index 01813e4..af94219 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,5 +51,6 @@ pub mod prelude; pub mod stream; pub mod sync; pub mod task; +pub mod pin; pub(crate) mod utils; diff --git a/src/pin.rs b/src/pin.rs new file mode 100644 index 0000000..e0375c0 --- /dev/null +++ b/src/pin.rs @@ -0,0 +1,4 @@ +//! Types that pin data to its location in memory. + +#[doc(inline)] +pub use std::pin::Pin; From 7c73cdff25a7de60d7213c19b37211dcc014a8d2 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 16:01:39 +0200 Subject: [PATCH 130/194] cargo fmt Signed-off-by: Yoshua Wuyts --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index af94219..d20dfe5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,10 +47,10 @@ pub mod future; pub mod io; pub mod net; pub mod os; +pub mod pin; pub mod prelude; pub mod stream; pub mod sync; pub mod task; -pub mod pin; pub(crate) mod utils; From e9de7798638288096b2a8942755e78a652bb008c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 19:59:17 +0200 Subject: [PATCH 131/194] unstable facade around the pin submodule Signed-off-by: Yoshua Wuyts --- src/lib.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d20dfe5..4161d9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -47,10 +47,12 @@ pub mod future; pub mod io; pub mod net; pub mod os; -pub mod pin; pub mod prelude; pub mod stream; pub mod sync; pub mod task; +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub mod pin; + pub(crate) mod utils; From cafcddb0e1ff262b0d7e66d48172e382e8112dbd Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 20:17:27 +0200 Subject: [PATCH 132/194] feature guard pin Signed-off-by: Yoshua Wuyts --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 4161d9f..3bb35c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -53,6 +53,7 @@ pub mod sync; pub mod task; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] pub mod pin; pub(crate) mod utils; From 39a1c2b577c8d9c164c8a88d1938fe9bebca74b5 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Mon, 16 Sep 2019 22:34:11 +0200 Subject: [PATCH 133/194] add link to std pin docs Signed-off-by: Yoshua Wuyts --- src/pin.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pin.rs b/src/pin.rs index e0375c0..b082457 100644 --- a/src/pin.rs +++ b/src/pin.rs @@ -1,4 +1,6 @@ //! Types that pin data to its location in memory. +//! +//! For more documentation see [`std::pin`](https://doc.rust-lang.org/std/pin/index.html). #[doc(inline)] pub use std::pin::Pin; From 73db46c90d0237bf713e3aeb8cc5a9f3e46db84e Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Tue, 17 Sep 2019 12:09:42 +0900 Subject: [PATCH 134/194] Add Stream::zip --- src/stream/mod.rs | 2 +- src/stream/stream/mod.rs | 45 +++++++++++++++++++++++++++++++++ src/stream/stream/zip.rs | 54 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/stream/stream/zip.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 3146bd3..f749ebe 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -25,7 +25,7 @@ pub use double_ended_stream::DoubleEndedStream; pub use empty::{empty, Empty}; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Scan, Stream, Take}; +pub use stream::{Scan, Stream, Take, Zip}; mod double_ended_stream; mod empty; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index b0500c6..dacc3f0 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -31,9 +31,11 @@ mod next; mod nth; mod scan; mod take; +mod zip; pub use scan::Scan; pub use take::Take; +pub use zip::Zip; use all::AllFuture; use any::AnyFuture; @@ -545,6 +547,49 @@ pub trait Stream { { Scan::new(self, initial_state, f) } + + /// 'Zips up' two streams into a single stream of pairs. + /// + /// `zip()` returns a new stream that will iterate over two other streams, returning a tuple + /// where the first element comes from the first stream, and the second element comes from the + /// second stream. + /// + /// In other words, it zips two streams together, into a single one. + /// + /// If either stream returns [`None`], [`poll_next`] from the zipped stream will return + /// [`None`]. If the first stream returns [`None`], `zip` will short-circuit and `poll_next` + /// will not be called on the second stream. + /// + /// [`None`]: https://doc.rust-lang.org/std/option/enum.Option.html#variant.None + /// [`poll_next`]: #tymethod.poll_next + /// + /// ## Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let l: VecDeque = vec![1, 2, 3].into_iter().collect(); + /// let r: VecDeque = vec![4, 5, 6, 7].into_iter().collect(); + /// let mut s = l.zip(r); + /// + /// assert_eq!(s.next().await, Some((1, 4))); + /// assert_eq!(s.next().await, Some((2, 5))); + /// assert_eq!(s.next().await, Some((3, 6))); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + /// ``` + #[inline] + fn zip(self, other: U) -> Zip + where + Self: Sized, + U: Stream, + { + Zip::new(self, other) + } } impl Stream for T { diff --git a/src/stream/stream/zip.rs b/src/stream/stream/zip.rs new file mode 100644 index 0000000..8f7c9ab --- /dev/null +++ b/src/stream/stream/zip.rs @@ -0,0 +1,54 @@ +use std::fmt; +use std::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +/// An iterator that iterates two other iterators simultaneously. +pub struct Zip { + item_slot: Option, + first: A, + second: B, +} + +impl fmt::Debug for Zip { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.debug_struct("Zip") + .field("first", &self.first) + .field("second", &self.second) + .finish() + } +} + +impl Unpin for Zip {} + +impl Zip { + pub(crate) fn new(first: A, second: B) -> Self { + Zip { + item_slot: None, + first, + second, + } + } + + pin_utils::unsafe_unpinned!(item_slot: Option); + pin_utils::unsafe_pinned!(first: A); + pin_utils::unsafe_pinned!(second: B); +} + +impl futures_core::stream::Stream for Zip { + type Item = (A::Item, B::Item); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.as_mut().item_slot().is_none() { + match self.as_mut().first().poll_next(cx) { + Poll::Pending => return Poll::Pending, + Poll::Ready(None) => return Poll::Ready(None), + Poll::Ready(Some(item)) => *self.as_mut().item_slot() = Some(item), + } + } + let second_item = futures_core::ready!(self.as_mut().second().poll_next(cx)); + let first_item = self.as_mut().item_slot().take().unwrap(); + Poll::Ready(second_item.map(|second_item| (first_item, second_item))) + } +} From 0924911ac3978edce34ca71c109c97f07a4c8623 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 16 Sep 2019 16:03:02 +0200 Subject: [PATCH 135/194] Implement simple work stealing --- Cargo.toml | 1 + src/task/block_on.rs | 17 ++-- src/task/builder.rs | 32 +++++++ src/task/local.rs | 18 +++- src/task/mod.rs | 7 +- src/task/pool.rs | 221 ++++++++++++++++--------------------------- src/task/sleepers.rs | 52 ++++++++++ src/task/task.rs | 2 + src/task/worker.rs | 110 +++++++++++++++++++++ 9 files changed, 309 insertions(+), 151 deletions(-) create mode 100644 src/task/builder.rs create mode 100644 src/task/sleepers.rs create mode 100644 src/task/worker.rs diff --git a/Cargo.toml b/Cargo.toml index 907c1eb..a48cab0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ unstable = [] async-task = "1.0.0" cfg-if = "0.1.9" crossbeam-channel = "0.3.9" +crossbeam-deque = "0.7.1" futures-core-preview = "0.3.0-alpha.18" futures-io-preview = "0.3.0-alpha.18" futures-timer = "0.4.0" diff --git a/src/task/block_on.rs b/src/task/block_on.rs index 0e9a5f4..eec530c 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -6,11 +6,11 @@ use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; use std::thread::{self, Thread}; -use super::pool; +use super::local; use super::task; +use super::worker; use crate::future::Future; use crate::task::{Context, Poll, Waker}; -use crate::utils::abort_on_panic; use kv_log_macro::trace; @@ -58,7 +58,7 @@ where // Log this `block_on` operation. let child_id = tag.task_id().as_u64(); - let parent_id = pool::get_task(|t| t.id().as_u64()).unwrap_or(0); + let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0); trace!("block_on", { parent_id: parent_id, @@ -66,31 +66,28 @@ where }); // Wrap the future into one that drops task-local variables on exit. + let future = local::add_finalizer(future); + let future = async move { let res = future.await; - - // Abort on panic because thread-local variables behave the same way. - abort_on_panic(|| pool::get_task(|task| task.metadata().local_map.clear())); - trace!("block_on completed", { parent_id: parent_id, child_id: child_id, }); - res }; // Pin the future onto the stack. pin_utils::pin_mut!(future); - // Transmute the future into one that is static. + // Transmute the future into one that is futurestatic. let future = mem::transmute::< Pin<&'_ mut dyn Future>, Pin<&'static mut dyn Future>, >(future); // Block on the future and and wait for it to complete. - pool::set_tag(&tag, || block(future)); + worker::set_tag(&tag, || block(future)); // Take out the result. match (*out.get()).take().unwrap() { diff --git a/src/task/builder.rs b/src/task/builder.rs new file mode 100644 index 0000000..630876c --- /dev/null +++ b/src/task/builder.rs @@ -0,0 +1,32 @@ +use super::pool; +use super::JoinHandle; +use crate::future::Future; +use crate::io; + +/// Task builder that configures the settings of a new task. +#[derive(Debug)] +pub struct Builder { + pub(crate) name: Option, +} + +impl Builder { + /// Creates a new builder. + pub fn new() -> Builder { + Builder { name: None } + } + + /// Configures the name of the task. + pub fn name(mut self, name: String) -> Builder { + self.name = Some(name); + self + } + + /// Spawns a task with the configured settings. + pub fn spawn(self, future: F) -> io::Result> + where + F: Future + Send + 'static, + T: Send + 'static, + { + Ok(pool::get().spawn(future, self)) + } +} diff --git a/src/task/local.rs b/src/task/local.rs index 8897696..8347e34 100644 --- a/src/task/local.rs +++ b/src/task/local.rs @@ -6,7 +6,9 @@ use std::sync::Mutex; use lazy_static::lazy_static; -use super::pool; +use super::worker; +use crate::future::Future; +use crate::utils::abort_on_panic; /// Declares task-local values. /// @@ -152,7 +154,7 @@ impl LocalKey { where F: FnOnce(&T) -> R, { - pool::get_task(|task| unsafe { + worker::get_task(|task| unsafe { // Prepare the numeric key, initialization function, and the map of task-locals. let key = self.key(); let init = || Box::new((self.__init)()) as Box; @@ -250,3 +252,15 @@ impl Map { entries.clear(); } } + +// Wrap the future into one that drops task-local variables on exit. +pub(crate) unsafe fn add_finalizer(f: impl Future) -> impl Future { + async move { + let res = f.await; + + // Abort on panic because thread-local variables behave the same way. + abort_on_panic(|| worker::get_task(|task| task.metadata().local_map.clear())); + + res + } +} diff --git a/src/task/mod.rs b/src/task/mod.rs index eef7284..66d8b67 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -25,15 +25,20 @@ pub use std::task::{Context, Poll, Waker}; pub use block_on::block_on; +pub use builder::Builder; pub use local::{AccessError, LocalKey}; -pub use pool::{current, spawn, Builder}; +pub use pool::spawn; pub use sleep::sleep; pub use task::{JoinHandle, Task, TaskId}; +pub use worker::current; mod block_on; +mod builder; mod local; mod pool; mod sleep; +mod sleepers; mod task; +mod worker; pub(crate) mod blocking; diff --git a/src/task/pool.rs b/src/task/pool.rs index 210b862..e36aace 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -1,43 +1,18 @@ -use std::cell::Cell; -use std::ptr; +use std::iter; use std::thread; -use crossbeam_channel::{unbounded, Sender}; +use crossbeam_deque::{Injector, Stealer, Worker}; use kv_log_macro::trace; use lazy_static::lazy_static; +use super::local; +use super::sleepers::Sleepers; use super::task; -use super::{JoinHandle, Task}; +use super::worker; +use super::{Builder, JoinHandle}; use crate::future::Future; -use crate::io; use crate::utils::abort_on_panic; -/// Returns a handle to the current task. -/// -/// # Panics -/// -/// This function will panic if not called within the context of a task created by [`block_on`], -/// [`spawn`], or [`Builder::spawn`]. -/// -/// [`block_on`]: fn.block_on.html -/// [`spawn`]: fn.spawn.html -/// [`Builder::spawn`]: struct.Builder.html#method.spawn -/// -/// # Examples -/// -/// ``` -/// # fn main() { async_std::task::block_on(async { -/// # -/// use async_std::task; -/// -/// println!("The name of this task is {:?}", task::current().name()); -/// # -/// # }) } -/// ``` -pub fn current() -> Task { - get_task(|task| task.clone()).expect("`task::current()` called outside the context of a task") -} - /// Spawns a task. /// /// This function is similar to [`std::thread::spawn`], except it spawns an asynchronous task. @@ -64,130 +39,100 @@ where F: Future + Send + 'static, T: Send + 'static, { - spawn_with_builder(Builder::new(), future) + Builder::new().spawn(future).expect("cannot spawn future") } -/// Task builder that configures the settings of a new task. -#[derive(Debug)] -pub struct Builder { - pub(crate) name: Option, +pub(crate) struct Pool { + pub injector: Injector, + pub stealers: Vec>, + pub sleepers: Sleepers, } -impl Builder { - /// Creates a new builder. - pub fn new() -> Builder { - Builder { name: None } - } - - /// Configures the name of the task. - pub fn name(mut self, name: String) -> Builder { - self.name = Some(name); - self - } - - /// Spawns a task with the configured settings. - pub fn spawn(self, future: F) -> io::Result> +impl Pool { + /// Spawn a future onto the pool. + pub fn spawn(&self, future: F, builder: Builder) -> JoinHandle where F: Future + Send + 'static, T: Send + 'static, { - Ok(spawn_with_builder(self, future)) - } -} + let tag = task::Tag::new(builder.name); -pub(crate) fn spawn_with_builder(builder: Builder, future: F) -> JoinHandle -where - F: Future + Send + 'static, - T: Send + 'static, -{ - let Builder { name } = builder; + // Log this `spawn` operation. + let child_id = tag.task_id().as_u64(); + let parent_id = worker::get_task(|t| t.id().as_u64()).unwrap_or(0); - type Job = async_task::Task; - - lazy_static! { - static ref QUEUE: Sender = { - let (sender, receiver) = unbounded::(); - - for _ in 0..num_cpus::get().max(1) { - let receiver = receiver.clone(); - thread::Builder::new() - .name("async-task-driver".to_string()) - .spawn(|| { - for job in receiver { - set_tag(job.tag(), || abort_on_panic(|| job.run())) - } - }) - .expect("cannot start a thread driving tasks"); - } - - sender - }; - } - - let tag = task::Tag::new(name); - let schedule = |job| QUEUE.send(job).unwrap(); - - // Log this `spawn` operation. - let child_id = tag.task_id().as_u64(); - let parent_id = get_task(|t| t.id().as_u64()).unwrap_or(0); - - trace!("spawn", { - parent_id: parent_id, - child_id: child_id, - }); - - // Wrap the future into one that drops task-local variables on exit. - let future = async move { - let res = future.await; - - // Abort on panic because thread-local variables behave the same way. - abort_on_panic(|| get_task(|task| task.metadata().local_map.clear())); - - trace!("spawn completed", { + trace!("spawn", { parent_id: parent_id, child_id: child_id, }); - res - }; + // Wrap the future into one that drops task-local variables on exit. + let future = unsafe { local::add_finalizer(future) }; - let (task, handle) = async_task::spawn(future, schedule, tag); - task.schedule(); - JoinHandle::new(handle) -} + // Wrap the future into one that logs completion on exit. + let future = async move { + let res = future.await; + trace!("spawn completed", { + parent_id: parent_id, + child_id: child_id, + }); + res + }; -thread_local! { - static TAG: Cell<*const task::Tag> = Cell::new(ptr::null_mut()); -} - -pub(crate) fn set_tag(tag: *const task::Tag, f: F) -> R -where - F: FnOnce() -> R, -{ - struct ResetTag<'a>(&'a Cell<*const task::Tag>); - - impl Drop for ResetTag<'_> { - fn drop(&mut self) { - self.0.set(ptr::null()); - } + let (task, handle) = async_task::spawn(future, worker::schedule, tag); + task.schedule(); + JoinHandle::new(handle) } - TAG.with(|t| { - t.set(tag); - let _guard = ResetTag(t); - - f() - }) -} - -pub(crate) fn get_task(f: F) -> Option -where - F: FnOnce(&Task) -> R, -{ - let res = TAG.try_with(|tag| unsafe { tag.get().as_ref().map(task::Tag::task).map(f) }); - - match res { - Ok(Some(val)) => Some(val), - Ok(None) | Err(_) => None, + /// Find the next runnable task to run. + pub fn find_task(&self, local: &Worker) -> Option { + // Pop a task from the local queue, if not empty. + local.pop().or_else(|| { + // Otherwise, we need to look for a task elsewhere. + iter::repeat_with(|| { + // Try stealing a batch of tasks from the injector queue. + self.injector + .steal_batch_and_pop(local) + // Or try stealing a bach of tasks from one of the other threads. + .or_else(|| { + self.stealers + .iter() + .map(|s| s.steal_batch_and_pop(local)) + .collect() + }) + }) + // Loop while no task was stolen and any steal operation needs to be retried. + .find(|s| !s.is_retry()) + // Extract the stolen task, if there is one. + .and_then(|s| s.success()) + }) } } + +#[inline] +pub(crate) fn get() -> &'static Pool { + lazy_static! { + static ref POOL: Pool = { + let num_threads = num_cpus::get().max(1); + let mut stealers = Vec::new(); + + // Spawn worker threads. + for _ in 0..num_threads { + let worker = Worker::new_fifo(); + stealers.push(worker.stealer()); + + thread::Builder::new() + .name("async-task-driver".to_string()) + .spawn(|| abort_on_panic(|| worker::main_loop(worker))) + .expect("cannot start a thread driving tasks"); + } + + Pool { + injector: Injector::new(), + stealers, + sleepers: Sleepers::new(), + } + }; + } + &*POOL +} diff --git a/src/task/sleepers.rs b/src/task/sleepers.rs new file mode 100644 index 0000000..a907175 --- /dev/null +++ b/src/task/sleepers.rs @@ -0,0 +1,52 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Condvar, Mutex}; + +/// The place where worker threads go to sleep. +/// +/// Similar to how thread parking works, if a notification comes up while no threads are sleeping, +/// the next thread that attempts to go to sleep will pick up the notification immediately. +pub struct Sleepers { + /// How many threads are currently a sleep. + sleep: Mutex, + + /// A condvar for notifying sleeping threads. + wake: Condvar, + + /// Set to `true` if a notification came up while nobody was sleeping. + notified: AtomicBool, +} + +impl Sleepers { + /// Creates a new `Sleepers`. + pub fn new() -> Sleepers { + Sleepers { + sleep: Mutex::new(0), + wake: Condvar::new(), + notified: AtomicBool::new(false), + } + } + + /// Puts the current thread to sleep. + pub fn wait(&self) { + let mut sleep = self.sleep.lock().unwrap(); + + if self.notified.swap(false, Ordering::SeqCst) == false { + *sleep += 1; + let _ = self.wake.wait(sleep).unwrap(); + } + } + + /// Notifies one thread. + pub fn notify_one(&self) { + if self.notified.load(Ordering::SeqCst) == false { + let mut sleep = self.sleep.lock().unwrap(); + + if *sleep > 0 { + *sleep -= 1; + self.wake.notify_one(); + } else { + self.notified.store(true, Ordering::SeqCst); + } + } + } +} diff --git a/src/task/task.rs b/src/task/task.rs index 8ffe821..c86e008 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -133,6 +133,8 @@ impl fmt::Display for TaskId { } } +pub(crate) type Runnable = async_task::Task; + pub(crate) struct Metadata { pub task_id: TaskId, pub name: Option, diff --git a/src/task/worker.rs b/src/task/worker.rs new file mode 100644 index 0000000..71b93ea --- /dev/null +++ b/src/task/worker.rs @@ -0,0 +1,110 @@ +use std::cell::Cell; +use std::ptr; + +use crossbeam_deque::Worker; + +use super::pool; +use super::task; +use super::Task; +use crate::utils::abort_on_panic; + +/// Returns a handle to the current task. +/// +/// # Panics +/// +/// This function will panic if not called within the context of a task created by [`block_on`], +/// [`spawn`], or [`Builder::spawn`]. +/// +/// [`block_on`]: fn.block_on.html +/// [`spawn`]: fn.spawn.html +/// [`Builder::spawn`]: struct.Builder.html#method.spawn +/// +/// # Examples +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::task; +/// +/// println!("The name of this task is {:?}", task::current().name()); +/// # +/// # }) } +/// ``` +pub fn current() -> Task { + get_task(|task| task.clone()).expect("`task::current()` called outside the context of a task") +} + +thread_local! { + static TAG: Cell<*const task::Tag> = Cell::new(ptr::null_mut()); +} + +pub(crate) fn set_tag(tag: *const task::Tag, f: F) -> R +where + F: FnOnce() -> R, +{ + struct ResetTag<'a>(&'a Cell<*const task::Tag>); + + impl Drop for ResetTag<'_> { + fn drop(&mut self) { + self.0.set(ptr::null()); + } + } + + TAG.with(|t| { + t.set(tag); + let _guard = ResetTag(t); + + f() + }) +} + +pub(crate) fn get_task(f: F) -> Option +where + F: FnOnce(&Task) -> R, +{ + let res = TAG.try_with(|tag| unsafe { tag.get().as_ref().map(task::Tag::task).map(f) }); + + match res { + Ok(Some(val)) => Some(val), + Ok(None) | Err(_) => None, + } +} + +thread_local! { + static IS_WORKER: Cell = Cell::new(false); + static QUEUE: Cell>> = Cell::new(None); +} + +pub fn is_worker() -> bool { + IS_WORKER.with(|is_worker| is_worker.get()) +} + +fn get_queue) -> T, T>(f: F) -> T { + QUEUE.with(|queue| { + let q = queue.take().unwrap(); + let ret = f(&q); + queue.set(Some(q)); + ret + }) +} + +pub(crate) fn schedule(task: task::Runnable) { + if is_worker() { + get_queue(|q| q.push(task)); + } else { + pool::get().injector.push(task); + } + pool::get().sleepers.notify_one(); +} + +pub(crate) fn main_loop(worker: Worker) { + IS_WORKER.with(|is_worker| is_worker.set(true)); + QUEUE.with(|queue| queue.set(Some(worker))); + + loop { + match get_queue(|q| pool::get().find_task(q)) { + Some(task) => set_tag(task.tag(), || abort_on_panic(|| task.run())), + None => pool::get().sleepers.wait(), + } + } +} From efe351659fb7bb439a12e5c9017836bcddbc1cc3 Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 17 Sep 2019 12:25:02 +0300 Subject: [PATCH 136/194] Fixes review issues --- src/stream/stream/fold.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/stream/stream/fold.rs b/src/stream/stream/fold.rs index 05d4937..18ddcd8 100644 --- a/src/stream/stream/fold.rs +++ b/src/stream/stream/fold.rs @@ -31,23 +31,23 @@ impl FoldFuture { impl Future for FoldFuture where - S: Stream + Unpin + Sized, + S: Stream + Sized, F: FnMut(B, S::Item) -> B, { type Output = B; fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + loop { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); - match next { - Some(v) => { - cx.waker().wake_by_ref(); - let old = self.as_mut().acc().take().unwrap(); - let new = (self.as_mut().f())(old, v); - *self.as_mut().acc() = Some(new); - Poll::Pending + match next { + Some(v) => { + let old = self.as_mut().acc().take().unwrap(); + let new = (self.as_mut().f())(old, v); + *self.as_mut().acc() = Some(new); + } + None => return Poll::Ready(self.as_mut().acc().take().unwrap()), } - None => Poll::Ready(self.as_mut().acc().take().unwrap()), } } } From 9487b73f12da598048a3aff840f9fe35f1987f5c Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Tue, 17 Sep 2019 12:31:24 +0300 Subject: [PATCH 137/194] Update src/stream/stream/enumerate.rs Co-Authored-By: Stjepan Glavina --- src/stream/stream/enumerate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/enumerate.rs b/src/stream/stream/enumerate.rs index 8576da8..a29fc80 100644 --- a/src/stream/stream/enumerate.rs +++ b/src/stream/stream/enumerate.rs @@ -21,7 +21,7 @@ impl Enumerate { impl futures_core::stream::Stream for Enumerate where - S: Stream + Unpin + Sized, + S: Stream, { type Item = (usize, S::Item); From 04dbcbb639eb522201951e200d3cf79cc00253cf Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Tue, 17 Sep 2019 12:28:19 +0100 Subject: [PATCH 138/194] Update src/task/worker.rs Co-Authored-By: Yoshua Wuyts --- src/task/worker.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/task/worker.rs b/src/task/worker.rs index 71b93ea..fc2a6e7 100644 --- a/src/task/worker.rs +++ b/src/task/worker.rs @@ -75,7 +75,7 @@ thread_local! { static QUEUE: Cell>> = Cell::new(None); } -pub fn is_worker() -> bool { +pub(crate) fn is_worker() -> bool { IS_WORKER.with(|is_worker| is_worker.get()) } From 6c4c958abc827c7f9b9d846ecaac86988b5e1249 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 29 Aug 2019 11:32:43 +0200 Subject: [PATCH 139/194] from/into stream Signed-off-by: Yoshua Wuyts update examples Signed-off-by: Yoshua Wuyts impl collect Signed-off-by: Yoshua Wuyts compiles! Signed-off-by: Yoshua Wuyts layout base for collect into vec Signed-off-by: Yoshua Wuyts fmt Signed-off-by: Yoshua Wuyts progress Signed-off-by: Yoshua Wuyts compiles! Signed-off-by: Yoshua Wuyts define failing test Signed-off-by: Yoshua Wuyts cargo fmt Signed-off-by: Yoshua Wuyts stuck again Signed-off-by: Yoshua Wuyts fix trait bounds! Signed-off-by: Yoshua Wuyts cargo fmt Signed-off-by: Yoshua Wuyts hide dyn fut impl Signed-off-by: Yoshua Wuyts dyn ret for vec Signed-off-by: Yoshua Wuyts cargo fmt Signed-off-by: Yoshua Wuyts collect docs Signed-off-by: Yoshua Wuyts remove macro from vec::from_stream Signed-off-by: Yoshua Wuyts shorten collect trait bound Signed-off-by: Yoshua Wuyts Remove some Unpin and Send bounds Signed-off-by: Yoshua Wuyts --- src/lib.rs | 1 + src/stream/from_stream.rs | 28 +++++++++++++++++ src/stream/into_stream.rs | 35 ++++++++++++++++++++++ src/stream/mod.rs | 4 +++ src/stream/stream/mod.rs | 63 +++++++++++++++++++++++++++++++++++++-- src/vec/from_stream.rs | 25 ++++++++++++++++ src/vec/mod.rs | 9 ++++++ 7 files changed, 162 insertions(+), 3 deletions(-) create mode 100644 src/stream/from_stream.rs create mode 100644 src/stream/into_stream.rs create mode 100644 src/vec/from_stream.rs create mode 100644 src/vec/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 3bb35c0..8336517 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,6 +51,7 @@ pub mod prelude; pub mod stream; pub mod sync; pub mod task; +mod vec; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(feature = "unstable")] diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs new file mode 100644 index 0000000..8b9ad26 --- /dev/null +++ b/src/stream/from_stream.rs @@ -0,0 +1,28 @@ +use super::IntoStream; + +use std::pin::Pin; + +/// Conversion from a `Stream`. +/// +/// By implementing `FromStream` for a type, you define how it will be created from a stream. +/// This is common for types which describe a collection of some kind. +/// +/// See also: [`IntoStream`]. +/// +/// [`IntoStream`]: trait.IntoStream.html +pub trait FromStream { + /// Creates a value from a stream. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// // use async_std::stream::FromStream; + /// + /// // let _five_fives = async_std::stream::repeat(5).take(5); + /// ``` + fn from_stream<'a, S: IntoStream + 'a>( + stream: S, + ) -> Pin + 'a>>; +} diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs new file mode 100644 index 0000000..63fb558 --- /dev/null +++ b/src/stream/into_stream.rs @@ -0,0 +1,35 @@ +use futures_core::stream::Stream; + +/// Conversion into a `Stream`. +/// +/// By implementing `IntoIterator` for a type, you define how it will be +/// converted to an iterator. This is common for types which describe a +/// collection of some kind. +/// +/// [`from_stream`]: #tymethod.from_stream +/// [`Stream`]: trait.Stream.html +/// [`collect`]: trait.Stream.html#method.collect +/// +/// See also: [`FromStream`]. +/// +/// [`FromStream`]: trait.FromStream.html +pub trait IntoStream { + /// The type of the elements being iterated over. + type Item; + + /// Which kind of stream are we turning this into? + type IntoStream: Stream; + + /// Creates a stream from a value. + fn into_stream(self) -> Self::IntoStream; +} + +impl IntoStream for I { + type Item = I::Item; + type IntoStream = I; + + #[inline] + fn into_stream(self) -> I { + self + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index f749ebe..a08c827 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -23,12 +23,16 @@ pub use double_ended_stream::DoubleEndedStream; pub use empty::{empty, Empty}; +pub use from_stream::FromStream; +pub use into_stream::IntoStream; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use stream::{Scan, Stream, Take, Zip}; mod double_ended_stream; mod empty; +mod from_stream; +mod into_stream; mod once; mod repeat; mod stream; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 578dd56..88b5452 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -50,14 +50,15 @@ use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; +use super::from_stream::FromStream; +use crate::future::Future; +use crate::task::{Context, Poll}; use std::cmp::Ordering; use std::marker::PhantomData; use std::pin::Pin; use cfg_if::cfg_if; -use crate::task::{Context, Poll}; - cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] @@ -80,6 +81,21 @@ cfg_if! { } } +cfg_if! { + if #[cfg(feature = "docs")] { + #[doc(hidden)] + pub struct DynFuture<'a, T>(std::marker::PhantomData<&'a T>); + + macro_rules! dyn_ret { + ($a:lifetime, $o:ty) => (DynFuture<$a, $o>); + } + } else { + macro_rules! dyn_ret { + ($a:lifetime, $o:ty) => (Pin + 'a>>) + } + } +} + /// An asynchronous stream of values. /// /// This trait is an async version of [`std::iter::Iterator`]. @@ -528,7 +544,6 @@ pub trait Stream { /// /// Basic usage: /// - /// ``` /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; @@ -652,6 +667,48 @@ pub trait Stream { { Zip::new(self, other) } + + /// Transforms a stream into a collection. + /// + /// `collect()` can take anything streamable, and turn it into a relevant + /// collection. This is one of the more powerful methods in the async + /// standard library, used in a variety of contexts. + /// + /// The most basic pattern in which `collect()` is used is to turn one + /// collection into another. You take a collection, call [`stream`] on it, + /// do a bunch of transformations, and then `collect()` at the end. + /// + /// Because `collect()` is so general, it can cause problems with type + /// inference. As such, `collect()` is one of the few times you'll see + /// the syntax affectionately known as the 'turbofish': `::<>`. This + /// helps the inference algorithm understand specifically which collection + /// you're trying to collect into. + /// + /// # Examples + /// + /// ``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let s = stream::repeat(9u8).take(3); + /// let buf: Vec = s.collect().await; + /// + /// assert_eq!(buf, vec![9; 3]); + /// # + /// # }) } + /// ``` + /// + /// [`stream`]: trait.Stream.html#tymethod.next + #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] + fn collect<'a, B>(self) -> dyn_ret!('a, B) + where + Self: futures_core::stream::Stream + Sized + 'a, + B: FromStream<::Item>, + { + FromStream::from_stream(self) + } } impl Stream for T { diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs new file mode 100644 index 0000000..ce2afc5 --- /dev/null +++ b/src/vec/from_stream.rs @@ -0,0 +1,25 @@ +use crate::stream::{FromStream, IntoStream, Stream}; + +use std::pin::Pin; + +impl FromStream for Vec { + #[inline] + fn from_stream<'a, S: IntoStream>( + stream: S, + ) -> Pin + 'a>> + where + ::IntoStream: 'a, + { + let stream = stream.into_stream(); + + Pin::from(Box::new(async move { + pin_utils::pin_mut!(stream); + + let mut out = vec![]; + while let Some(item) = stream.next().await { + out.push(item); + } + out + })) + } +} diff --git a/src/vec/mod.rs b/src/vec/mod.rs new file mode 100644 index 0000000..725fc8f --- /dev/null +++ b/src/vec/mod.rs @@ -0,0 +1,9 @@ +//! The Rust core allocation and collections library +//! +//! This library provides smart pointers and collections for managing +//! heap-allocated values. + +mod from_stream; + +#[doc(inline)] +pub use std::vec::Vec; From 6ee3f6cf9c9fece9f36af390f869b324dfd95663 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Sat, 7 Sep 2019 03:19:47 +0200 Subject: [PATCH 140/194] tests pass again Signed-off-by: Yoshua Wuyts --- src/stream/from_stream.rs | 6 +++--- src/stream/into_stream.rs | 4 ++-- src/stream/stream/mod.rs | 7 ++++--- src/vec/from_stream.rs | 6 +++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 8b9ad26..7a262f7 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -10,7 +10,7 @@ use std::pin::Pin; /// See also: [`IntoStream`]. /// /// [`IntoStream`]: trait.IntoStream.html -pub trait FromStream { +pub trait FromStream { /// Creates a value from a stream. /// /// # Examples @@ -22,7 +22,7 @@ pub trait FromStream { /// /// // let _five_fives = async_std::stream::repeat(5).take(5); /// ``` - fn from_stream<'a, S: IntoStream + 'a>( + fn from_stream<'a, S: IntoStream + Send + 'a>( stream: S, - ) -> Pin + 'a>>; + ) -> Pin + Send + 'a>>; } diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 63fb558..42f9529 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -18,13 +18,13 @@ pub trait IntoStream { type Item; /// Which kind of stream are we turning this into? - type IntoStream: Stream; + type IntoStream: Stream + Send; /// Creates a stream from a value. fn into_stream(self) -> Self::IntoStream; } -impl IntoStream for I { +impl IntoStream for I { type Item = I::Item; type IntoStream = I; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 88b5452..529b7cf 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -91,7 +91,7 @@ cfg_if! { } } else { macro_rules! dyn_ret { - ($a:lifetime, $o:ty) => (Pin + 'a>>) + ($a:lifetime, $o:ty) => (Pin + Send + 'a>>) } } } @@ -544,6 +544,7 @@ pub trait Stream { /// /// Basic usage: /// + /// ``` /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; @@ -551,7 +552,6 @@ pub trait Stream { /// /// let mut s = stream::repeat::(42).take(3); /// assert!(s.any(|x| x == 42).await); - /// /// # /// # }) } /// ``` @@ -704,7 +704,8 @@ pub trait Stream { #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] fn collect<'a, B>(self) -> dyn_ret!('a, B) where - Self: futures_core::stream::Stream + Sized + 'a, + Self: futures_core::stream::Stream + Sized + Send + 'a, + ::Item: Send, B: FromStream<::Item>, { FromStream::from_stream(self) diff --git a/src/vec/from_stream.rs b/src/vec/from_stream.rs index ce2afc5..f603d0d 100644 --- a/src/vec/from_stream.rs +++ b/src/vec/from_stream.rs @@ -2,13 +2,13 @@ use crate::stream::{FromStream, IntoStream, Stream}; use std::pin::Pin; -impl FromStream for Vec { +impl FromStream for Vec { #[inline] fn from_stream<'a, S: IntoStream>( stream: S, - ) -> Pin + 'a>> + ) -> Pin + Send + 'a>> where - ::IntoStream: 'a, + ::IntoStream: Send + 'a, { let stream = stream.into_stream(); From cb7f3dd3767dd2467684b1ff1db60deda828d2c7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 10 Sep 2019 19:38:36 +0200 Subject: [PATCH 141/194] remove unused types Signed-off-by: Yoshua Wuyts --- src/stream/stream/mod.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 529b7cf..29b51d9 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -51,8 +51,6 @@ use next::NextFuture; use nth::NthFuture; use super::from_stream::FromStream; -use crate::future::Future; -use crate::task::{Context, Poll}; use std::cmp::Ordering; use std::marker::PhantomData; use std::pin::Pin; From e6a3160c8b2c86ebfc687feef35ddeb3baedf75c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Fri, 13 Sep 2019 02:49:48 +0200 Subject: [PATCH 142/194] add unstable cfg to FromStream/IntoStream Signed-off-by: Yoshua Wuyts --- src/stream/from_stream.rs | 1 + src/stream/into_stream.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 7a262f7..91d3e24 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -10,6 +10,7 @@ use std::pin::Pin; /// See also: [`IntoStream`]. /// /// [`IntoStream`]: trait.IntoStream.html +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait FromStream { /// Creates a value from a stream. /// diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 42f9529..b291317 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -13,6 +13,7 @@ use futures_core::stream::Stream; /// See also: [`FromStream`]. /// /// [`FromStream`]: trait.FromStream.html +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait IntoStream { /// The type of the elements being iterated over. type Item; From 98927a79a9bf6984680a505bb32522e108e86f33 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 17 Sep 2019 18:00:40 +0200 Subject: [PATCH 143/194] rebase Signed-off-by: Yoshua Wuyts --- src/stream/stream/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 29b51d9..ca83fbb 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -54,6 +54,7 @@ use super::from_stream::FromStream; use std::cmp::Ordering; use std::marker::PhantomData; use std::pin::Pin; +use std::task::{Context, Poll}; use cfg_if::cfg_if; From ad0510110c85fe928d5588f9269870c70b8ba179 Mon Sep 17 00:00:00 2001 From: Sunjay Varma Date: Tue, 17 Sep 2019 16:25:26 -0400 Subject: [PATCH 144/194] Added the ability to collect a stream of results --- src/lib.rs | 1 + src/result/from_stream.rs | 43 +++++++++++++++++++++++++++++++++++++++ src/result/mod.rs | 9 ++++++++ src/stream/stream/mod.rs | 16 +++++++++++++++ 4 files changed, 69 insertions(+) create mode 100644 src/result/from_stream.rs create mode 100644 src/result/mod.rs diff --git a/src/lib.rs b/src/lib.rs index 8336517..76ea9c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ pub mod stream; pub mod sync; pub mod task; mod vec; +mod result; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(feature = "unstable")] diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs new file mode 100644 index 0000000..f74d06d --- /dev/null +++ b/src/result/from_stream.rs @@ -0,0 +1,43 @@ +use crate::stream::{FromStream, IntoStream, Stream}; + +use std::pin::Pin; + +impl FromStream> for Result + where V: FromStream { + /// Takes each element in the stream: if it is an `Err`, no further + /// elements are taken, and the `Err` is returned. Should no `Err` + /// occur, a container with the values of each `Result` is returned. + #[inline] + fn from_stream<'a, S: IntoStream>>( + stream: S, + ) -> Pin + Send + 'a>> + where + ::IntoStream: Send + 'a, + { + let stream = stream.into_stream(); + + Pin::from(Box::new(async move { + pin_utils::pin_mut!(stream); + + // Using `scan` here because it is able to stop the stream early + // if a failure occurs + let mut found_error = None; + let out: V = stream.scan((), |_, elem| { + match elem { + Ok(elem) => Some(elem), + Err(err) => { + found_error = Some(err); + // Stop processing the stream on error + None + }, + } + }).collect().await; + + match found_error { + Some(err) => Err(err), + None => Ok(out), + } + })) + } +} + diff --git a/src/result/mod.rs b/src/result/mod.rs new file mode 100644 index 0000000..908f9c4 --- /dev/null +++ b/src/result/mod.rs @@ -0,0 +1,9 @@ +//! The Rust core error handling type +//! +//! This module provides the `Result` type for returning and +//! propagating errors. + +mod from_stream; + +#[doc(inline)] +pub use std::result::Result; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index ca83fbb..0ad50e7 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -695,6 +695,22 @@ pub trait Stream { /// let buf: Vec = s.collect().await; /// /// assert_eq!(buf, vec![9; 3]); + /// + /// // You can also collect streams of Result values + /// // into any collection that implements FromStream + /// let s = stream::repeat(Ok(9)).take(3); + /// // We are using Vec here, but other collections + /// // are supported as well + /// let buf: Result, ()> = s.collect().await; + /// + /// assert_eq!(buf, Ok(vec![9; 3])); + /// + /// // The stream will stop on the first Err and + /// // return that instead + /// let s = stream::repeat(Err(5)).take(3); + /// let buf: Result, u8> = s.collect().await; + /// + /// assert_eq!(buf, Err(5)); /// # /// # }) } /// ``` From c87dab2d5eb75c95d1d3b81dff264c82e0aff82a Mon Sep 17 00:00:00 2001 From: Sunjay Varma Date: Tue, 17 Sep 2019 16:48:58 -0400 Subject: [PATCH 145/194] rustfmt --- src/lib.rs | 2 +- src/result/from_stream.rs | 28 ++++++++++++++++------------ 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 76ea9c4..dfa9a07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -48,11 +48,11 @@ pub mod io; pub mod net; pub mod os; pub mod prelude; +mod result; pub mod stream; pub mod sync; pub mod task; mod vec; -mod result; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(feature = "unstable")] diff --git a/src/result/from_stream.rs b/src/result/from_stream.rs index f74d06d..71cf61d 100644 --- a/src/result/from_stream.rs +++ b/src/result/from_stream.rs @@ -3,7 +3,9 @@ use crate::stream::{FromStream, IntoStream, Stream}; use std::pin::Pin; impl FromStream> for Result - where V: FromStream { +where + V: FromStream, +{ /// Takes each element in the stream: if it is an `Err`, no further /// elements are taken, and the `Err` is returned. Should no `Err` /// occur, a container with the values of each `Result` is returned. @@ -22,16 +24,19 @@ impl FromStream> for Result // Using `scan` here because it is able to stop the stream early // if a failure occurs let mut found_error = None; - let out: V = stream.scan((), |_, elem| { - match elem { - Ok(elem) => Some(elem), - Err(err) => { - found_error = Some(err); - // Stop processing the stream on error - None - }, - } - }).collect().await; + let out: V = stream + .scan((), |_, elem| { + match elem { + Ok(elem) => Some(elem), + Err(err) => { + found_error = Some(err); + // Stop processing the stream on error + None + } + } + }) + .collect() + .await; match found_error { Some(err) => Err(err), @@ -40,4 +45,3 @@ impl FromStream> for Result })) } } - From 78c49f92b6307d0fbc504dbb6a9414ad87f93818 Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Fri, 16 Aug 2019 15:35:04 +0200 Subject: [PATCH 146/194] Add initial Fuse implementation for Stream Signed-off-by: Yoshua Wuyts --- src/stream/mod.rs | 2 +- src/stream/stream/fuse.rs | 54 +++++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 34 ++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 1 deletion(-) create mode 100644 src/stream/stream/fuse.rs diff --git a/src/stream/mod.rs b/src/stream/mod.rs index a08c827..ec1c23b 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -27,7 +27,7 @@ pub use from_stream::FromStream; pub use into_stream::IntoStream; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; -pub use stream::{Scan, Stream, Take, Zip}; +pub use stream::{Fuse, Scan, Stream, Take, Zip}; mod double_ended_stream; mod empty; diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs new file mode 100644 index 0000000..7cfe43a --- /dev/null +++ b/src/stream/stream/fuse.rs @@ -0,0 +1,54 @@ +use std::pin::Pin; + +use crate::task::{Context, Poll}; + +/// A `Stream` that is permanently closed +/// once a single call to `poll` results in +/// `Poll::Ready(None)`, returning `Poll::Ready(None)` +/// for all future calls to `poll`. +#[derive(Clone, Debug)] +pub struct Fuse { + stream: S, + done: bool, +} + +impl Unpin for Fuse {} + +impl Fuse { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(done: bool); + + /// Returns `true` if the underlying stream is fused. + /// + /// If this `Stream` is fused, all future calls to + /// `poll` will return `Poll::Ready(None)`. + pub fn is_done(&self) -> bool { + self.done + } + + /// Consumes this `Fuse` and returns the inner + /// `Stream`, unfusing it if it had become + /// fused. + pub fn into_inner(self) -> S + where + S: Sized, + { + self.stream + } +} + +impl futures::Stream for Fuse { + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + if self.done { + Poll::Ready(None) + } else { + let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + if next.is_none() { + *self.as_mut().done() = true; + } + Poll::Ready(next) + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index ca83fbb..f72b7bc 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -28,6 +28,7 @@ mod filter_map; mod find; mod find_map; mod fold; +mod fuse; mod min_by; mod next; mod nth; @@ -35,6 +36,7 @@ mod scan; mod take; mod zip; +pub use fuse::Fuse; pub use scan::Scan; pub use take::Take; pub use zip::Zip; @@ -246,6 +248,38 @@ pub trait Stream { Enumerate::new(self) } + /// Transforms this `Stream` into a "fused" `Stream` + /// such that after the first time `poll` returns + /// `Poll::Ready(None)`, all future calls to + /// `poll` will also return `Poll::Ready(None)`. + /// + /// # Examples + /// + /// ``` + /// # #![feature(async_await)] + /// # fn main() { async_std::task::block_on(async { + /// # + /// use async_std::prelude::*; + /// use async_std::stream; + /// + /// let mut s = stream::repeat(9).take(3); + /// + /// while let Some(v) = s.next().await { + /// assert_eq!(v, 9); + /// } + /// # + /// # }) } + /// ``` + fn fuse(self) -> Fuse + where + Self: Sized, + { + Fuse { + stream: self, + done: false, + } + } + /// Both filters and maps a stream. /// /// # Examples From 44b3d3daddc5fbf71a56ad903625883c9a497bc0 Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Fri, 16 Aug 2019 16:06:00 +0200 Subject: [PATCH 147/194] Remove irrelevant example Signed-off-by: Yoshua Wuyts --- src/stream/stream/fuse.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 7cfe43a..4d1a1b9 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -37,6 +37,7 @@ impl Fuse { } } + impl futures::Stream for Fuse { type Item = S::Item; From 7b4bb26c5c702b8d70d53febcefc616f98977184 Mon Sep 17 00:00:00 2001 From: Tyler Neely Date: Fri, 16 Aug 2019 16:12:02 +0200 Subject: [PATCH 148/194] Remove redundant Sized bound Signed-off-by: Yoshua Wuyts --- src/stream/stream/mod.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index f72b7bc..5f7b900 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -270,10 +270,7 @@ pub trait Stream { /// # /// # }) } /// ``` - fn fuse(self) -> Fuse - where - Self: Sized, - { + fn fuse(self) -> Fuse { Fuse { stream: self, done: false, From aa94d450d6109a7a26ec2eeefde230db37ae9e41 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 00:00:30 +0200 Subject: [PATCH 149/194] update stream::fuse Signed-off-by: Yoshua Wuyts --- src/stream/stream/fuse.rs | 38 ++++++++------------------------------ src/stream/stream/mod.rs | 22 +++++++++++----------- 2 files changed, 19 insertions(+), 41 deletions(-) diff --git a/src/stream/stream/fuse.rs b/src/stream/stream/fuse.rs index 4d1a1b9..3541937 100644 --- a/src/stream/stream/fuse.rs +++ b/src/stream/stream/fuse.rs @@ -1,51 +1,29 @@ use std::pin::Pin; +use std::task::{Context, Poll}; -use crate::task::{Context, Poll}; - -/// A `Stream` that is permanently closed -/// once a single call to `poll` results in -/// `Poll::Ready(None)`, returning `Poll::Ready(None)` -/// for all future calls to `poll`. +/// A `Stream` that is permanently closed once a single call to `poll` results in +/// `Poll::Ready(None)`, returning `Poll::Ready(None)` for all future calls to `poll`. #[derive(Clone, Debug)] pub struct Fuse { - stream: S, - done: bool, + pub(crate) stream: S, + pub(crate) done: bool, } impl Unpin for Fuse {} -impl Fuse { +impl Fuse { pin_utils::unsafe_pinned!(stream: S); pin_utils::unsafe_unpinned!(done: bool); - - /// Returns `true` if the underlying stream is fused. - /// - /// If this `Stream` is fused, all future calls to - /// `poll` will return `Poll::Ready(None)`. - pub fn is_done(&self) -> bool { - self.done - } - - /// Consumes this `Fuse` and returns the inner - /// `Stream`, unfusing it if it had become - /// fused. - pub fn into_inner(self) -> S - where - S: Sized, - { - self.stream - } } - -impl futures::Stream for Fuse { +impl futures_core::Stream for Fuse { type Item = S::Item; fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { if self.done { Poll::Ready(None) } else { - let next = futures::ready!(self.as_mut().stream().poll_next(cx)); + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); if next.is_none() { *self.as_mut().done() = true; } diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 5f7b900..e13e2eb 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -248,29 +248,29 @@ pub trait Stream { Enumerate::new(self) } - /// Transforms this `Stream` into a "fused" `Stream` - /// such that after the first time `poll` returns - /// `Poll::Ready(None)`, all future calls to - /// `poll` will also return `Poll::Ready(None)`. + /// Transforms this `Stream` into a "fused" `Stream` such that after the first time `poll` + /// returns `Poll::Ready(None)`, all future calls to `poll` will also return + /// `Poll::Ready(None)`. /// /// # Examples /// /// ``` - /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # /// use async_std::prelude::*; /// use async_std::stream; /// - /// let mut s = stream::repeat(9).take(3); - /// - /// while let Some(v) = s.next().await { - /// assert_eq!(v, 9); - /// } + /// let mut s = stream::once(1).fuse(); + /// assert_eq!(s.next().await, Some(1)); + /// assert_eq!(s.next().await, None); + /// assert_eq!(s.next().await, None); /// # /// # }) } /// ``` - fn fuse(self) -> Fuse { + fn fuse(self) -> Fuse + where + Self: Sized, + { Fuse { stream: self, done: false, From 488c90c0c4cd2bb831b72ef520e69d1bad189dc3 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 01:26:04 +0200 Subject: [PATCH 150/194] add feature guards for unstable features Signed-off-by: Yoshua Wuyts --- src/future/timeout.rs | 2 ++ src/lib.rs | 11 +++++++++-- src/stream/double_ended_stream.rs | 1 + src/stream/from_stream.rs | 1 + src/stream/into_stream.rs | 1 + src/stream/mod.rs | 20 ++++++++++++++------ src/stream/stream/mod.rs | 9 ++++++++- 7 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/future/timeout.rs b/src/future/timeout.rs index e433bf1..bf7a6ff 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -29,6 +29,7 @@ use crate::task::{Context, Poll}; /// # Ok(()) }) } /// ``` #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] pub async fn timeout(dur: Duration, f: F) -> Result where F: Future, @@ -69,6 +70,7 @@ impl Future for TimeoutFuture { /// An error returned when a future times out. #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { _private: (), diff --git a/src/lib.rs b/src/lib.rs index dfa9a07..83647bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,17 +42,24 @@ #![doc(test(attr(allow(unused_extern_crates, unused_variables))))] #![doc(html_logo_url = "https://async.rs/images/logo--hero.svg")] +use cfg_if::cfg_if; + pub mod fs; pub mod future; pub mod io; pub mod net; pub mod os; pub mod prelude; -mod result; pub mod stream; pub mod sync; pub mod task; -mod vec; + +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod vec; + mod result; + } +} #[cfg_attr(feature = "docs", doc(cfg(unstable)))] #[cfg(feature = "unstable")] diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index 2287cc6..bb5df74 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -11,6 +11,7 @@ use std::task::{Context, Poll}; /// /// [`Stream`]: trait.Stream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] pub trait DoubleEndedStream: Stream { /// Removes and returns an element from the end of the stream. /// diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 91d3e24..58b2ad1 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -11,6 +11,7 @@ use std::pin::Pin; /// /// [`IntoStream`]: trait.IntoStream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] pub trait FromStream { /// Creates a value from a stream. /// diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index b291317..0a986a8 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -14,6 +14,7 @@ use futures_core::stream::Stream; /// /// [`FromStream`]: trait.FromStream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] +#[cfg(feature = "unstable")] pub trait IntoStream { /// The type of the elements being iterated over. type Item; diff --git a/src/stream/mod.rs b/src/stream/mod.rs index ec1c23b..36cf3a4 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -21,18 +21,26 @@ //! # }) } //! ``` -pub use double_ended_stream::DoubleEndedStream; +use cfg_if::cfg_if; + pub use empty::{empty, Empty}; -pub use from_stream::FromStream; -pub use into_stream::IntoStream; pub use once::{once, Once}; pub use repeat::{repeat, Repeat}; pub use stream::{Fuse, Scan, Stream, Take, Zip}; -mod double_ended_stream; mod empty; -mod from_stream; -mod into_stream; mod once; mod repeat; mod stream; + +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + mod double_ended_stream; + mod from_stream; + mod into_stream; + + pub use double_ended_stream::DoubleEndedStream; + pub use from_stream::FromStream; + pub use into_stream::IntoStream; + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 62f1b4b..3a02a5c 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -52,7 +52,6 @@ use min_by::MinByFuture; use next::NextFuture; use nth::NthFuture; -use super::from_stream::FromStream; use std::cmp::Ordering; use std::marker::PhantomData; use std::pin::Pin; @@ -60,6 +59,12 @@ use std::task::{Context, Poll}; use cfg_if::cfg_if; +cfg_if! { + if #[cfg(any(feature = "unstable", feature = "docs"))] { + use crate::stream::FromStream; + } +} + cfg_if! { if #[cfg(feature = "docs")] { #[doc(hidden)] @@ -748,6 +753,8 @@ pub trait Stream { /// /// [`stream`]: trait.Stream.html#tymethod.next #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[cfg(feature = "unstable")] fn collect<'a, B>(self) -> dyn_ret!('a, B) where Self: futures_core::stream::Stream + Sized + Send + 'a, From 713ab026c33694a5ca561afe996d47dda77eb132 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 03:47:47 +0200 Subject: [PATCH 151/194] build unstable for docs Signed-off-by: Yoshua Wuyts --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 19b3e58..3eec1d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -50,7 +50,7 @@ matrix: rust: nightly os: linux script: - - cargo doc --features docs + - cargo doc --features docs unstable - name: book rust: nightly From b670600555087d013be35a872d1b019bdb5b029c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 12:42:46 +0200 Subject: [PATCH 152/194] Update src/stream/into_stream.rs Co-Authored-By: Stjepan Glavina --- src/stream/into_stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/into_stream.rs b/src/stream/into_stream.rs index 0a986a8..5e4311e 100644 --- a/src/stream/into_stream.rs +++ b/src/stream/into_stream.rs @@ -14,7 +14,7 @@ use futures_core::stream::Stream; /// /// [`FromStream`]: trait.FromStream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] +#[cfg(any(feature = "unstable", feature = "docs"))] pub trait IntoStream { /// The type of the elements being iterated over. type Item; From bfb16790c389d4246a3a01ef5c881a32eb861411 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 12:42:58 +0200 Subject: [PATCH 153/194] Update src/stream/from_stream.rs Co-Authored-By: Stjepan Glavina --- src/stream/from_stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/from_stream.rs b/src/stream/from_stream.rs index 58b2ad1..d046e3b 100644 --- a/src/stream/from_stream.rs +++ b/src/stream/from_stream.rs @@ -11,7 +11,7 @@ use std::pin::Pin; /// /// [`IntoStream`]: trait.IntoStream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] +#[cfg(any(feature = "unstable", feature = "docs"))] pub trait FromStream { /// Creates a value from a stream. /// From f7ec3f4e2dbb15e3aea188906aa7e939e41679b7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 12:43:06 +0200 Subject: [PATCH 154/194] Update src/stream/stream/mod.rs Co-Authored-By: Stjepan Glavina --- src/stream/stream/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 3a02a5c..6de9a31 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -754,7 +754,7 @@ pub trait Stream { /// [`stream`]: trait.Stream.html#tymethod.next #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead (TODO)"] #[cfg_attr(feature = "docs", doc(cfg(unstable)))] - #[cfg(feature = "unstable")] + #[cfg(any(feature = "unstable", feature = "docs"))] fn collect<'a, B>(self) -> dyn_ret!('a, B) where Self: futures_core::stream::Stream + Sized + Send + 'a, From 9a07196402893605c0594b0c705dffa7f57091c7 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 12:43:14 +0200 Subject: [PATCH 155/194] Update src/stream/double_ended_stream.rs Co-Authored-By: Stjepan Glavina --- src/stream/double_ended_stream.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stream/double_ended_stream.rs b/src/stream/double_ended_stream.rs index bb5df74..6fab77c 100644 --- a/src/stream/double_ended_stream.rs +++ b/src/stream/double_ended_stream.rs @@ -11,7 +11,7 @@ use std::task::{Context, Poll}; /// /// [`Stream`]: trait.Stream.html #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] +#[cfg(any(feature = "unstable", feature = "docs"))] pub trait DoubleEndedStream: Stream { /// Removes and returns an element from the end of the stream. /// From 6b76fb13087432fcee4209092bae113537b457ce Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 12:43:27 +0200 Subject: [PATCH 156/194] Update src/future/timeout.rs Co-Authored-By: Stjepan Glavina --- src/future/timeout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/future/timeout.rs b/src/future/timeout.rs index bf7a6ff..566d27a 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -70,7 +70,7 @@ impl Future for TimeoutFuture { /// An error returned when a future times out. #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] +#[cfg(any(feature = "unstable", feature = "docs"))] #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct TimeoutError { _private: (), From 2964e72b00bacda6687fd7f491226a135a220cbc Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 12:43:34 +0200 Subject: [PATCH 157/194] Update src/future/timeout.rs Co-Authored-By: Stjepan Glavina --- src/future/timeout.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/future/timeout.rs b/src/future/timeout.rs index 566d27a..aa88f64 100644 --- a/src/future/timeout.rs +++ b/src/future/timeout.rs @@ -29,7 +29,7 @@ use crate::task::{Context, Poll}; /// # Ok(()) }) } /// ``` #[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] +#[cfg(any(feature = "unstable", feature = "docs"))] pub async fn timeout(dur: Duration, f: F) -> Result where F: Future, From 4b32749886028aabdf1552ccdb1571bc25182f26 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 18 Sep 2019 13:57:26 +0200 Subject: [PATCH 158/194] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7b670d8..978e1c0 100644 --- a/README.md +++ b/README.md @@ -92,7 +92,6 @@ out. This is what we call _"unstable"_ features. You can try out the unstable features by enabling the `unstable` feature in your `Cargo.toml` file: ```toml -[dependencies] [dependencies.async-std] version = "0.99" features = ["unstable"] From bfd7af8775bcaed4a229e3501a3b52da50954156 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Wed, 18 Sep 2019 13:59:22 +0200 Subject: [PATCH 159/194] Rename local.rs -> task_local.rs --- src/task/block_on.rs | 4 ++-- src/task/mod.rs | 4 ++-- src/task/pool.rs | 4 ++-- src/task/task.rs | 8 ++++---- src/task/{local.rs => task_local.rs} | 0 5 files changed, 10 insertions(+), 10 deletions(-) rename src/task/{local.rs => task_local.rs} (100%) diff --git a/src/task/block_on.rs b/src/task/block_on.rs index eec530c..2d49dca 100644 --- a/src/task/block_on.rs +++ b/src/task/block_on.rs @@ -6,8 +6,8 @@ use std::sync::Arc; use std::task::{RawWaker, RawWakerVTable}; use std::thread::{self, Thread}; -use super::local; use super::task; +use super::task_local; use super::worker; use crate::future::Future; use crate::task::{Context, Poll, Waker}; @@ -66,7 +66,7 @@ where }); // Wrap the future into one that drops task-local variables on exit. - let future = local::add_finalizer(future); + let future = task_local::add_finalizer(future); let future = async move { let res = future.await; diff --git a/src/task/mod.rs b/src/task/mod.rs index 66d8b67..1844cca 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -26,19 +26,19 @@ pub use std::task::{Context, Poll, Waker}; pub use block_on::block_on; pub use builder::Builder; -pub use local::{AccessError, LocalKey}; pub use pool::spawn; pub use sleep::sleep; pub use task::{JoinHandle, Task, TaskId}; +pub use task_local::{AccessError, LocalKey}; pub use worker::current; mod block_on; mod builder; -mod local; mod pool; mod sleep; mod sleepers; mod task; +mod task_local; mod worker; pub(crate) mod blocking; diff --git a/src/task/pool.rs b/src/task/pool.rs index e36aace..49fbfe4 100644 --- a/src/task/pool.rs +++ b/src/task/pool.rs @@ -5,9 +5,9 @@ use crossbeam_deque::{Injector, Stealer, Worker}; use kv_log_macro::trace; use lazy_static::lazy_static; -use super::local; use super::sleepers::Sleepers; use super::task; +use super::task_local; use super::worker; use super::{Builder, JoinHandle}; use crate::future::Future; @@ -67,7 +67,7 @@ impl Pool { }); // Wrap the future into one that drops task-local variables on exit. - let future = unsafe { local::add_finalizer(future) }; + let future = unsafe { task_local::add_finalizer(future) }; // Wrap the future into one that logs completion on exit. let future = async move { diff --git a/src/task/task.rs b/src/task/task.rs index c86e008..8a1adde 100644 --- a/src/task/task.rs +++ b/src/task/task.rs @@ -6,7 +6,7 @@ use std::pin::Pin; use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering}; use std::sync::Arc; -use super::local; +use super::task_local; use crate::future::Future; use crate::task::{Context, Poll}; @@ -138,7 +138,7 @@ pub(crate) type Runnable = async_task::Task; pub(crate) struct Metadata { pub task_id: TaskId, pub name: Option, - pub local_map: local::Map, + pub local_map: task_local::Map, } pub(crate) struct Tag { @@ -154,7 +154,7 @@ impl Tag { Task(Arc::new(Metadata { task_id, name: Some(name), - local_map: local::Map::new(), + local_map: task_local::Map::new(), })) }); @@ -174,7 +174,7 @@ impl Tag { let new = Some(Task(Arc::new(Metadata { task_id: TaskId::new(), name: None, - local_map: local::Map::new(), + local_map: task_local::Map::new(), }))); let new_raw = mem::transmute::, usize>(new); diff --git a/src/task/local.rs b/src/task/task_local.rs similarity index 100% rename from src/task/local.rs rename to src/task/task_local.rs From c533d5f90694fb6cdfdb3544396e5393e56fbd66 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 21:25:58 +0200 Subject: [PATCH 160/194] implement feedback & fix tests Signed-off-by: Yoshua Wuyts --- .travis.yml | 2 +- src/lib.rs | 5 +---- src/stream/stream/mod.rs | 1 + 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 3eec1d5..3a2651b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,4 +64,4 @@ matrix: script: - cargo check --features unstable --all --benches --bins --examples --tests - - cargo test --features unstable --all + - cargo test --all --docs --features unstable diff --git a/src/lib.rs b/src/lib.rs index 83647bb..09d04b2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,13 +56,10 @@ pub mod task; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { + pub mod pin; mod vec; mod result; } } -#[cfg_attr(feature = "docs", doc(cfg(unstable)))] -#[cfg(feature = "unstable")] -pub mod pin; - pub(crate) mod utils; diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 6de9a31..8ddab21 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -96,6 +96,7 @@ cfg_if! { ($a:lifetime, $o:ty) => (DynFuture<$a, $o>); } } else { + #[allow(unused_macros)] macro_rules! dyn_ret { ($a:lifetime, $o:ty) => (Pin + Send + 'a>>) } From bd43490d728bdb877b33044126c56ec4dbf6f061 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Wed, 18 Sep 2019 22:56:13 +0200 Subject: [PATCH 161/194] fix cargo test --doc Signed-off-by: Yoshua Wuyts --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3a2651b..d2862fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,4 +64,4 @@ matrix: script: - cargo check --features unstable --all --benches --bins --examples --tests - - cargo test --all --docs --features unstable + - cargo test --all --doc --features unstable From a5a6dc24c4cfd8ab2025890b29349382519acfb8 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Wed, 18 Sep 2019 12:00:46 +0900 Subject: [PATCH 162/194] Add stream::Extend --- src/stream/extend.rs | 50 ++++++++++++++++++++++++++++++++++++++++++++ src/stream/mod.rs | 2 ++ src/vec/extend.rs | 19 +++++++++++++++++ src/vec/mod.rs | 1 + 4 files changed, 72 insertions(+) create mode 100644 src/stream/extend.rs create mode 100644 src/vec/extend.rs diff --git a/src/stream/extend.rs b/src/stream/extend.rs new file mode 100644 index 0000000..8e1c963 --- /dev/null +++ b/src/stream/extend.rs @@ -0,0 +1,50 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::{IntoStream, Stream}; + +/// Extend a collection with the contents of a stream. +/// +/// Streams produce a series of values asynchronously, and collections can also be thought of as a +/// series of values. The `Extend` trait bridges this gap, allowing you to extend a collection +/// asynchronously by including the contents of that stream. When extending a collection with an +/// already existing key, that entry is updated or, in the case of collections that permit multiple +/// entries with equal keys, that entry is inserted. +/// +/// ## Examples +/// +/// ``` +/// # fn main() { async_std::task::block_on(async { +/// # +/// use async_std::prelude::*; +/// use async_std::stream::{self, Extend}; +/// +/// let mut v: Vec = vec![1, 2]; +/// let s = stream::repeat(3usize).take(3); +/// v.extend_with_stream(s).await; +/// +/// assert_eq!(v, vec![1, 2, 3, 3, 3]); +/// # +/// # }) } +/// ``` +#[cfg_attr(feature = "docs", doc(cfg(unstable)))] +pub trait Extend { + /// Extends a collection with the contents of a stream. + fn extend_with_stream<'a, T: IntoStream + 'a>( + &'a mut self, + stream: T, + ) -> Pin + 'a>>; +} + +impl Extend<()> for () { + fn extend_with_stream<'a, T: IntoStream + 'a>( + &'a mut self, + stream: T, + ) -> Pin + 'a>> { + let stream = stream.into_stream(); + Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(_) = stream.next().await {} + }) + } +} diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 36cf3a4..6393afb 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -36,10 +36,12 @@ mod stream; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { mod double_ended_stream; + mod extend; mod from_stream; mod into_stream; pub use double_ended_stream::DoubleEndedStream; + pub use extend::Extend; pub use from_stream::FromStream; pub use into_stream::IntoStream; } diff --git a/src/vec/extend.rs b/src/vec/extend.rs new file mode 100644 index 0000000..33df5a9 --- /dev/null +++ b/src/vec/extend.rs @@ -0,0 +1,19 @@ +use std::pin::Pin; + +use crate::future::Future; +use crate::stream::{Extend, IntoStream, Stream}; + +impl Extend for Vec { + fn extend_with_stream<'a, S: IntoStream + 'a>( + &'a mut self, + stream: S, + ) -> Pin + 'a>> { + let stream = stream.into_stream(); + Box::pin(async move { + pin_utils::pin_mut!(stream); + while let Some(item) = stream.next().await { + self.push(item); + } + }) + } +} diff --git a/src/vec/mod.rs b/src/vec/mod.rs index 725fc8f..77a0b74 100644 --- a/src/vec/mod.rs +++ b/src/vec/mod.rs @@ -3,6 +3,7 @@ //! This library provides smart pointers and collections for managing //! heap-allocated values. +mod extend; mod from_stream; #[doc(inline)] From 9c00d0b90391da247f138767a2d625f0c5bf99c4 Mon Sep 17 00:00:00 2001 From: Wonwoo Choi Date: Thu, 19 Sep 2019 18:34:31 +0900 Subject: [PATCH 163/194] Rename: extend_with_stream => stream_extend --- src/stream/extend.rs | 6 +++--- src/vec/extend.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/stream/extend.rs b/src/stream/extend.rs index 8e1c963..35c9064 100644 --- a/src/stream/extend.rs +++ b/src/stream/extend.rs @@ -21,7 +21,7 @@ use crate::stream::{IntoStream, Stream}; /// /// let mut v: Vec = vec![1, 2]; /// let s = stream::repeat(3usize).take(3); -/// v.extend_with_stream(s).await; +/// v.stream_extend(s).await; /// /// assert_eq!(v, vec![1, 2, 3, 3, 3]); /// # @@ -30,14 +30,14 @@ use crate::stream::{IntoStream, Stream}; #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub trait Extend { /// Extends a collection with the contents of a stream. - fn extend_with_stream<'a, T: IntoStream + 'a>( + fn stream_extend<'a, T: IntoStream + 'a>( &'a mut self, stream: T, ) -> Pin + 'a>>; } impl Extend<()> for () { - fn extend_with_stream<'a, T: IntoStream + 'a>( + fn stream_extend<'a, T: IntoStream + 'a>( &'a mut self, stream: T, ) -> Pin + 'a>> { diff --git a/src/vec/extend.rs b/src/vec/extend.rs index 33df5a9..2ebcf34 100644 --- a/src/vec/extend.rs +++ b/src/vec/extend.rs @@ -4,7 +4,7 @@ use crate::future::Future; use crate::stream::{Extend, IntoStream, Stream}; impl Extend for Vec { - fn extend_with_stream<'a, S: IntoStream + 'a>( + fn stream_extend<'a, S: IntoStream + 'a>( &'a mut self, stream: S, ) -> Pin + 'a>> { From 7fe6c8a42c034398c69252d47afb8602b60845de Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Tue, 17 Sep 2019 23:12:09 +0200 Subject: [PATCH 164/194] add stream::join Signed-off-by: Yoshua Wuyts --- Cargo.toml | 1 + src/stream/mod.rs | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index a48cab0..1c12bcd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,7 @@ docs = [] unstable = [] [dependencies] +async-macros = "1.0.0" async-task = "1.0.0" cfg-if = "0.1.9" crossbeam-channel = "0.3.9" diff --git a/src/stream/mod.rs b/src/stream/mod.rs index 36cf3a4..5e17aa5 100644 --- a/src/stream/mod.rs +++ b/src/stream/mod.rs @@ -42,5 +42,9 @@ cfg_if! { pub use double_ended_stream::DoubleEndedStream; pub use from_stream::FromStream; pub use into_stream::IntoStream; + + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] + #[doc(inline)] + pub use async_macros::{join_stream as join, JoinStream as Join}; } } From 8d31aa69cf783c0493847aea58af38c83174d860 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 19 Sep 2019 14:24:41 +0200 Subject: [PATCH 165/194] prepare v0.99.6 Signed-off-by: Yoshua Wuyts --- CHANGELOG.md | 33 ++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 930e3f6..79bbf34 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,36 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview ## [Unreleased] +# [0.99.6] - 2019-09-19 + +## Added + +- Added `stream::Stream::collect` as "unstable" +- Added `stream::Stream::enumerate` +- Added `stream::Stream::fuse` +- Added `stream::Stream::fold` +- Added `stream::Stream::scan` +- Added `stream::Stream::zip` +- Added `stream::join` macro as "unstable" +- Added `stream::DoubleEndedStream` as "unstable" +- Added `stream::FromStream` trait as "unstable" +- Added `stream::IntoStream` trait as "unstable" +- Added `io::Cursor` as "unstable" +- Added `io::BufRead::consume` method +- Added `io::repeat` +- Added `io::Slice` and `io::SliceMut` +- Added documentation for feature flags +- Added `pin` submodule as "unstable" +- Added the ability to `collect` a stream of `Result`s into a + `Result, E>` + +## Changed + +- Refactored the scheduling algorithm of our executor to use work stealing +- Refactored the network driver, removing 400 lines of code +- Removed the `Send` bound from `task::block_on` +- Removed `Unpin` bound from `impl Stream for T` + # [0.99.5] - 2019-09-12 ## Added @@ -52,7 +82,8 @@ and this project adheres to [Semantic Versioning](https://book.async.rs/overview - Initial beta release -[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.5...HEAD +[Unreleased]: https://github.com/async-rs/async-std/compare/v0.99.6...HEAD +[0.99.6]: https://github.com/async-rs/async-std/compare/v0.99.5...0.99.6 [0.99.5]: https://github.com/async-rs/async-std/compare/v0.99.4...v0.99.5 [0.99.4]: https://github.com/async-rs/async-std/compare/v0.99.3...v0.99.4 [0.99.3]: https://github.com/async-rs/async-std/tree/v0.99.3 diff --git a/Cargo.toml b/Cargo.toml index 1c12bcd..f768f81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "async-std" -version = "0.99.5" +version = "0.99.6" authors = [ "Stjepan Glavina ", "Yoshua Wuyts ", From 42fac2676114570cd978e8c5d6c5911702c81a3c Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 19 Sep 2019 16:02:28 +0200 Subject: [PATCH 166/194] fix unstable display for pin docs Signed-off-by: Yoshua Wuyts --- src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lib.rs b/src/lib.rs index 09d04b2..d4ed7c2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,6 +56,7 @@ pub mod task; cfg_if! { if #[cfg(any(feature = "unstable", feature = "docs"))] { + #[cfg_attr(feature = "docs", doc(cfg(unstable)))] pub mod pin; mod vec; mod result; From fa31c6347e0b11826ef6d592edff9a874878fc71 Mon Sep 17 00:00:00 2001 From: Yoshua Wuyts Date: Thu, 19 Sep 2019 18:28:17 +0200 Subject: [PATCH 167/194] expose sync::{Arc,Weak} Signed-off-by: Yoshua Wuyts --- src/sync/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sync/mod.rs b/src/sync/mod.rs index 42dcb60..1a8e255 100644 --- a/src/sync/mod.rs +++ b/src/sync/mod.rs @@ -29,6 +29,9 @@ //! # }) } //! ``` +#[doc(inline)] +pub use std::sync::{Arc, Weak}; + pub use mutex::{Mutex, MutexGuard}; pub use rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; From 55ea367415da07971984439a450ff371e5e32526 Mon Sep 17 00:00:00 2001 From: Oleksii Kachaiev Date: Thu, 19 Sep 2019 23:54:48 -0700 Subject: [PATCH 168/194] Rename server functions to follow *_loop convention (#139) Rename: server -> accept_loop, client -> connection_loop, client_writer -> connection_writer_loop --- docs/src/tutorial/accept_loop.md | 9 ++++--- docs/src/tutorial/all_together.md | 20 +++++++------- docs/src/tutorial/clean_shutdown.md | 20 +++++++------- .../connecting_readers_and_writers.md | 8 +++--- docs/src/tutorial/handling_disconnection.md | 26 +++++++++---------- docs/src/tutorial/receiving_messages.md | 14 +++++----- docs/src/tutorial/sending_messages.md | 10 +++---- 7 files changed, 54 insertions(+), 53 deletions(-) diff --git a/docs/src/tutorial/accept_loop.md b/docs/src/tutorial/accept_loop.md index 96c15ba..058663f 100644 --- a/docs/src/tutorial/accept_loop.md +++ b/docs/src/tutorial/accept_loop.md @@ -34,7 +34,8 @@ Now we can write the server's accept loop: # # type Result = std::result::Result>; # -async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1 +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { // 1 + let listener = TcpListener::bind(addr).await?; // 2 let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { // 3 @@ -44,7 +45,7 @@ async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1 } ``` -1. We mark the `server` function as `async`, which allows us to use `.await` syntax inside. +1. We mark the `accept_loop` function as `async`, which allows us to use `.await` syntax inside. 2. `TcpListener::bind` call returns a future, which we `.await` to extract the `Result`, and then `?` to get a `TcpListener`. Note how `.await` and `?` work nicely together. This is exactly how `std::net::TcpListener` works, but with `.await` added. @@ -72,7 +73,7 @@ Finally, let's add main: # # type Result = std::result::Result>; # -# async fn server(addr: impl ToSocketAddrs) -> Result<()> { // 1 +# async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { // 1 # let listener = TcpListener::bind(addr).await?; // 2 # let mut incoming = listener.incoming(); # while let Some(stream) = incoming.next().await { // 3 @@ -83,7 +84,7 @@ Finally, let's add main: # // main fn run() -> Result<()> { - let fut = server("127.0.0.1:8080"); + let fut = accept_loop("127.0.0.1:8080"); task::block_on(fut) } ``` diff --git a/docs/src/tutorial/all_together.md b/docs/src/tutorial/all_together.md index 641c7da..dcc0661 100644 --- a/docs/src/tutorial/all_together.md +++ b/docs/src/tutorial/all_together.md @@ -25,7 +25,7 @@ type Receiver = mpsc::UnboundedReceiver; // main fn run() -> Result<()> { - task::block_on(server("127.0.0.1:8080")) + task::block_on(accept_loop("127.0.0.1:8080")) } fn spawn_and_log_error(fut: F) -> task::JoinHandle<()> @@ -39,21 +39,21 @@ where }) } -async fn server(addr: impl ToSocketAddrs) -> Result<()> { +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let (broker_sender, broker_receiver) = mpsc::unbounded(); // 1 - let _broker_handle = task::spawn(broker(broker_receiver)); + let _broker_handle = task::spawn(broker_loop(broker_receiver)); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let stream = stream?; println!("Accepting from: {}", stream.peer_addr()?); - spawn_and_log_error(client(broker_sender.clone(), stream)); + spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } Ok(()) } -async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { +async fn connection_loop(mut broker: Sender, stream: TcpStream) -> Result<()> { let stream = Arc::new(stream); // 2 let reader = BufReader::new(&*stream); let mut lines = reader.lines(); @@ -83,7 +83,7 @@ async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { Ok(()) } -async fn client_writer( +async fn connection_writer_loop( mut messages: Receiver, stream: Arc, ) -> Result<()> { @@ -107,7 +107,7 @@ enum Event { }, } -async fn broker(mut events: Receiver) -> Result<()> { +async fn broker_loop(mut events: Receiver) -> Result<()> { let mut peers: HashMap> = HashMap::new(); while let Some(event) = events.next().await { @@ -126,7 +126,7 @@ async fn broker(mut events: Receiver) -> Result<()> { Entry::Vacant(entry) => { let (client_sender, client_receiver) = mpsc::unbounded(); entry.insert(client_sender); // 4 - spawn_and_log_error(client_writer(client_receiver, stream)); // 5 + spawn_and_log_error(connection_writer_loop(client_receiver, stream)); // 5 } } } @@ -136,8 +136,8 @@ async fn broker(mut events: Receiver) -> Result<()> { } ``` -1. Inside the `server`, we create the broker's channel and `task`. -2. Inside `client`, we need to wrap `TcpStream` into an `Arc`, to be able to share it with the `client_writer`. +1. Inside the `accept_loop`, we create the broker's channel and `task`. +2. Inside `connection_loop`, we need to wrap `TcpStream` into an `Arc`, to be able to share it with the `connection_writer_loop`. 3. On login, we notify the broker. Note that we `.unwrap` on send: broker should outlive all the clients and if that's not the case the broker probably panicked, so we can escalate the panic as well. 4. Similarly, we forward parsed messages to the broker, assuming that it is alive. diff --git a/docs/src/tutorial/clean_shutdown.md b/docs/src/tutorial/clean_shutdown.md index 992a35d..234067a 100644 --- a/docs/src/tutorial/clean_shutdown.md +++ b/docs/src/tutorial/clean_shutdown.md @@ -53,7 +53,7 @@ Let's add waiting to the server: # } # # -# async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { +# async fn connection_loop(mut broker: Sender, stream: TcpStream) -> Result<()> { # let stream = Arc::new(stream); // 2 # let reader = BufReader::new(&*stream); # let mut lines = reader.lines(); @@ -83,7 +83,7 @@ Let's add waiting to the server: # Ok(()) # } # -# async fn client_writer( +# async fn connection_writer_loop( # mut messages: Receiver, # stream: Arc, # ) -> Result<()> { @@ -107,7 +107,7 @@ Let's add waiting to the server: # }, # } # -# async fn broker(mut events: Receiver) -> Result<()> { +# async fn broker_loop(mut events: Receiver) -> Result<()> { # let mut peers: HashMap> = HashMap::new(); # # while let Some(event) = events.next().await { @@ -126,7 +126,7 @@ Let's add waiting to the server: # Entry::Vacant(entry) => { # let (client_sender, client_receiver) = mpsc::unbounded(); # entry.insert(client_sender); // 4 -# spawn_and_log_error(client_writer(client_receiver, stream)); // 5 +# spawn_and_log_error(connection_writer_loop(client_receiver, stream)); // 5 # } # } # } @@ -135,16 +135,16 @@ Let's add waiting to the server: # Ok(()) # } # -async fn server(addr: impl ToSocketAddrs) -> Result<()> { +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let (broker_sender, broker_receiver) = mpsc::unbounded(); - let broker_handle = task::spawn(broker(broker_receiver)); + let broker_handle = task::spawn(broker_loop(broker_receiver)); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let stream = stream?; println!("Accepting from: {}", stream.peer_addr()?); - spawn_and_log_error(client(broker_sender.clone(), stream)); + spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } drop(broker_sender); // 1 broker_handle.await?; // 5 @@ -175,7 +175,7 @@ And to the broker: # type Sender = mpsc::UnboundedSender; # type Receiver = mpsc::UnboundedReceiver; # -# async fn client_writer( +# async fn connection_writer_loop( # mut messages: Receiver, # stream: Arc, # ) -> Result<()> { @@ -210,7 +210,7 @@ And to the broker: # }, # } # -async fn broker(mut events: Receiver) -> Result<()> { +async fn broker_loop(mut events: Receiver) -> Result<()> { let mut writers = Vec::new(); let mut peers: HashMap> = HashMap::new(); while let Some(event) = events.next().await { // 2 @@ -229,7 +229,7 @@ async fn broker(mut events: Receiver) -> Result<()> { Entry::Vacant(entry) => { let (client_sender, client_receiver) = mpsc::unbounded(); entry.insert(client_sender); - let handle = spawn_and_log_error(client_writer(client_receiver, stream)); + let handle = spawn_and_log_error(connection_writer_loop(client_receiver, stream)); writers.push(handle); // 4 } } diff --git a/docs/src/tutorial/connecting_readers_and_writers.md b/docs/src/tutorial/connecting_readers_and_writers.md index d5da471..51520f5 100644 --- a/docs/src/tutorial/connecting_readers_and_writers.md +++ b/docs/src/tutorial/connecting_readers_and_writers.md @@ -1,7 +1,7 @@ ## Connecting Readers and Writers -So how do we make sure that messages read in `client` flow into the relevant `client_writer`? +So how do we make sure that messages read in `connection_loop` flow into the relevant `connection_writer_loop`? We should somehow maintain an `peers: HashMap>` map which allows a client to find destination channels. However, this map would be a bit of shared mutable state, so we'll have to wrap an `RwLock` over it and answer tough questions of what should happen if the client joins at the same moment as it receives a message. @@ -28,7 +28,7 @@ The order of events "Bob sends message to Alice" and "Alice joins" is determined # type Sender = mpsc::UnboundedSender; # type Receiver = mpsc::UnboundedReceiver; # -# async fn client_writer( +# async fn connection_writer_loop( # mut messages: Receiver, # stream: Arc, # ) -> Result<()> { @@ -65,7 +65,7 @@ enum Event { // 1 }, } -async fn broker(mut events: Receiver) -> Result<()> { +async fn broker_loop(mut events: Receiver) -> Result<()> { let mut peers: HashMap> = HashMap::new(); // 2 while let Some(event) = events.next().await { @@ -84,7 +84,7 @@ async fn broker(mut events: Receiver) -> Result<()> { Entry::Vacant(entry) => { let (client_sender, client_receiver) = mpsc::unbounded(); entry.insert(client_sender); // 4 - spawn_and_log_error(client_writer(client_receiver, stream)); // 5 + spawn_and_log_error(connection_writer_loop(client_receiver, stream)); // 5 } } } diff --git a/docs/src/tutorial/handling_disconnection.md b/docs/src/tutorial/handling_disconnection.md index 27c5052..c941969 100644 --- a/docs/src/tutorial/handling_disconnection.md +++ b/docs/src/tutorial/handling_disconnection.md @@ -15,7 +15,7 @@ There's a more minimal solution however, which makes clever use of RAII. Closing a channel is a synchronization event, so we don't need to send a shutdown message, we can just drop the sender. This way, we statically guarantee that we issue shutdown exactly once, even if we early return via `?` or panic. -First, let's add a shutdown channel to the `client`: +First, let's add a shutdown channel to the `connection_loop`: ```rust,edition2018 # extern crate async_std; @@ -47,7 +47,7 @@ enum Event { }, } -async fn client(mut broker: Sender, stream: Arc) -> Result<()> { +async fn connection_loop(mut broker: Sender, stream: Arc) -> Result<()> { // ... # let name: String = unimplemented!(); let (_shutdown_sender, shutdown_receiver) = mpsc::unbounded::(); // 3 @@ -65,7 +65,7 @@ async fn client(mut broker: Sender, stream: Arc) -> Result<()> 2. We pass the shutdown channel to the writer task 3. In the reader, we create a `_shutdown_sender` whose only purpose is to get dropped. -In the `client_writer`, we now need to choose between shutdown and message channels. +In the `connection_writer_loop`, we now need to choose between shutdown and message channels. We use the `select` macro for this purpose: ```rust,edition2018 @@ -84,7 +84,7 @@ use futures_util::{select, FutureExt, StreamExt}; # #[derive(Debug)] # enum Void {} // 1 -async fn client_writer( +async fn connection_writer_loop( messages: &mut Receiver, stream: Arc, shutdown: Receiver, // 1 @@ -112,7 +112,7 @@ async fn client_writer( 2. Because of `select`, we can't use a `while let` loop, so we desugar it further into a `loop`. 3. In the shutdown case we use `match void {}` as a statically-checked `unreachable!()`. -Another problem is that between the moment we detect disconnection in `client_writer` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel. +Another problem is that between the moment we detect disconnection in `connection_writer_loop` and the moment when we actually remove the peer from the `peers` map, new messages might be pushed into the peer's channel. To not lose these messages completely, we'll return the messages channel back to the broker. This also allows us to establish a useful invariant that the message channel strictly outlives the peer in the `peers` map, and makes the broker itself infailable. @@ -146,25 +146,25 @@ enum Void {} // main fn run() -> Result<()> { - task::block_on(server("127.0.0.1:8080")) + task::block_on(accept_loop("127.0.0.1:8080")) } -async fn server(addr: impl ToSocketAddrs) -> Result<()> { +async fn accept_loop(addr: impl ToSocketAddrs) -> Result<()> { let listener = TcpListener::bind(addr).await?; let (broker_sender, broker_receiver) = mpsc::unbounded(); - let broker_handle = task::spawn(broker(broker_receiver)); + let broker_handle = task::spawn(broker_loop(broker_receiver)); let mut incoming = listener.incoming(); while let Some(stream) = incoming.next().await { let stream = stream?; println!("Accepting from: {}", stream.peer_addr()?); - spawn_and_log_error(client(broker_sender.clone(), stream)); + spawn_and_log_error(connection_loop(broker_sender.clone(), stream)); } drop(broker_sender); broker_handle.await; Ok(()) } -async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { +async fn connection_loop(mut broker: Sender, stream: TcpStream) -> Result<()> { let stream = Arc::new(stream); let reader = BufReader::new(&*stream); let mut lines = reader.lines(); @@ -199,7 +199,7 @@ async fn client(mut broker: Sender, stream: TcpStream) -> Result<()> { Ok(()) } -async fn client_writer( +async fn connection_writer_loop( messages: &mut Receiver, stream: Arc, shutdown: Receiver, @@ -236,7 +236,7 @@ enum Event { }, } -async fn broker(events: Receiver) { +async fn broker_loop(events: Receiver) { let (disconnect_sender, mut disconnect_receiver) = // 1 mpsc::unbounded::<(String, Receiver)>(); let mut peers: HashMap> = HashMap::new(); @@ -271,7 +271,7 @@ async fn broker(events: Receiver) { entry.insert(client_sender); let mut disconnect_sender = disconnect_sender.clone(); spawn_and_log_error(async move { - let res = client_writer(&mut client_receiver, stream, shutdown).await; + let res = connection_writer_loop(&mut client_receiver, stream, shutdown).await; disconnect_sender.send((name, client_receiver)).await // 4 .unwrap(); res diff --git a/docs/src/tutorial/receiving_messages.md b/docs/src/tutorial/receiving_messages.md index 667cf1c..8b4428c 100644 --- a/docs/src/tutorial/receiving_messages.md +++ b/docs/src/tutorial/receiving_messages.md @@ -18,18 +18,18 @@ We need to: # # type Result = std::result::Result>; # -async fn server(addr: impl ToSocketAddrs) -> Result<()> { +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 { let stream = stream?; println!("Accepting from: {}", stream.peer_addr()?); - let _handle = task::spawn(client(stream)); // 1 + let _handle = task::spawn(connection_loop(stream)); // 1 } Ok(()) } -async fn client(stream: TcpStream) -> Result<()> { +async fn connection_loop(stream: TcpStream) -> Result<()> { let reader = BufReader::new(&stream); // 2 let mut lines = reader.lines(); @@ -53,7 +53,7 @@ async fn client(stream: TcpStream) -> Result<()> { ``` 1. We use `task::spawn` function to spawn an independent task for working with each client. - That is, after accepting the client the `server` loop immediately starts waiting for the next one. + That is, after accepting the client the `accept_loop` immediately starts waiting for the next one. This is the core benefit of event-driven architecture: we serve many clients concurrently, without spending many hardware threads. 2. Luckily, the "split byte stream into lines" functionality is already implemented. @@ -67,7 +67,7 @@ async fn client(stream: TcpStream) -> Result<()> { ## Managing Errors -One serious problem in the above solution is that, while we correctly propagate errors in the `client`, we just drop the error on the floor afterwards! +One serious problem in the above solution is that, while we correctly propagate errors in the `connection_loop`, we just drop the error on the floor afterwards! That is, `task::spawn` does not return an error immediately (it can't, it needs to run the future to completion first), only after it is joined. We can "fix" it by waiting for the task to be joined, like this: @@ -83,7 +83,7 @@ We can "fix" it by waiting for the task to be joined, like this: # # type Result = std::result::Result>; # -# async fn client(stream: TcpStream) -> Result<()> { +# async fn connection_loop(stream: TcpStream) -> Result<()> { # let reader = BufReader::new(&stream); // 2 # let mut lines = reader.lines(); # @@ -106,7 +106,7 @@ We can "fix" it by waiting for the task to be joined, like this: # } # # async move |stream| { -let handle = task::spawn(client(stream)); +let handle = task::spawn(connection_loop(stream)); handle.await # }; ``` diff --git a/docs/src/tutorial/sending_messages.md b/docs/src/tutorial/sending_messages.md index 6fec8c9..f453baf 100644 --- a/docs/src/tutorial/sending_messages.md +++ b/docs/src/tutorial/sending_messages.md @@ -1,13 +1,13 @@ ## Sending Messages Now it's time to implement the other half -- sending messages. -A most obvious way to implement sending is to give each `client` access to the write half of `TcpStream` of each other clients. +A most obvious way to implement sending is to give each `connection_loop` access to the write half of `TcpStream` of each other clients. That way, a client can directly `.write_all` a message to recipients. However, this would be wrong: if Alice sends `bob: foo`, and Charley sends `bob: bar`, Bob might actually receive `fobaor`. Sending a message over a socket might require several syscalls, so two concurrent `.write_all`'s might interfere with each other! As a rule of thumb, only a single task should write to each `TcpStream`. -So let's create a `client_writer` task which receives messages over a channel and writes them to the socket. +So let's create a `connection_writer_loop` task which receives messages over a channel and writes them to the socket. This task would be the point of serialization of messages. if Alice and Charley send two messages to Bob at the same time, Bob will see the messages in the same order as they arrive in the channel. @@ -28,7 +28,7 @@ use std::sync::Arc; type Sender = mpsc::UnboundedSender; // 2 type Receiver = mpsc::UnboundedReceiver; -async fn client_writer( +async fn connection_writer_loop( mut messages: Receiver, stream: Arc, // 3 ) -> Result<()> { @@ -42,5 +42,5 @@ async fn client_writer( 1. We will use channels from the `futures` crate. 2. For simplicity, we will use `unbounded` channels, and won't be discussing backpressure in this tutorial. -3. As `client` and `client_writer` share the same `TcpStream`, we need to put it into an `Arc`. - Note that because `client` only reads from the stream and `client_writer` only writes to the stream, we don't get a race here. +3. As `connection_loop` and `connection_writer_loop` share the same `TcpStream`, we need to put it into an `Arc`. + Note that because `client` only reads from the stream and `connection_writer_loop` only writes to the stream, we don't get a race here. From e7ae10ebee69258c144d10793e829f786f2b1f2f Mon Sep 17 00:00:00 2001 From: Fedor Sakharov Date: Wed, 11 Sep 2019 14:44:42 +0300 Subject: [PATCH 169/194] adds stream::filter combinator --- src/stream/stream/filter.rs | 49 +++++++++++++++++++++++++++++++++++++ src/stream/stream/mod.rs | 30 +++++++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 src/stream/stream/filter.rs diff --git a/src/stream/stream/filter.rs b/src/stream/stream/filter.rs new file mode 100644 index 0000000..135d75d --- /dev/null +++ b/src/stream/stream/filter.rs @@ -0,0 +1,49 @@ +use std::marker::PhantomData; +use std::pin::Pin; + +use crate::stream::Stream; +use crate::task::{Context, Poll}; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct Filter { + stream: S, + predicate: P, + __t: PhantomData, +} + +impl Filter { + pin_utils::unsafe_pinned!(stream: S); + pin_utils::unsafe_unpinned!(predicate: P); + + pub(super) fn new(stream: S, predicate: P) -> Self { + Filter { + stream, + predicate, + __t: PhantomData, + } + } +} + +impl futures_core::stream::Stream for Filter +where + S: Stream, + P: FnMut(&S::Item) -> bool, +{ + type Item = S::Item; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { + let next = futures_core::ready!(self.as_mut().stream().poll_next(cx)); + + match next { + Some(v) => match (self.as_mut().predicate())(&v) { + true => Poll::Ready(Some(v)), + false => { + cx.waker().wake_by_ref(); + Poll::Pending + } + }, + None => Poll::Ready(None), + } + } +} diff --git a/src/stream/stream/mod.rs b/src/stream/stream/mod.rs index 8ddab21..195f2eb 100644 --- a/src/stream/stream/mod.rs +++ b/src/stream/stream/mod.rs @@ -24,6 +24,7 @@ mod all; mod any; mod enumerate; +mod filter; mod filter_map; mod find; mod find_map; @@ -44,6 +45,7 @@ pub use zip::Zip; use all::AllFuture; use any::AnyFuture; use enumerate::Enumerate; +use filter::Filter; use filter_map::FilterMap; use find::FindFuture; use find_map::FindMapFuture; @@ -282,6 +284,34 @@ pub trait Stream { done: false, } } + /// Creates a stream that uses a predicate to determine if an element + /// should be yeilded. + /// + /// # Examples + /// + /// Basic usage: + /// + ///``` + /// # fn main() { async_std::task::block_on(async { + /// # + /// use std::collections::VecDeque; + /// use async_std::stream::Stream; + /// + /// let s: VecDeque = vec![1, 2, 3, 4].into_iter().collect(); + /// let mut s = s.filter(|i| i % 2 == 0); + /// + /// assert_eq!(s.next().await, Some(2)); + /// assert_eq!(s.next().await, Some(4)); + /// assert_eq!(s.next().await, None); + /// # + /// # }) } + fn filter