diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..744ced8c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +# Version 0.99.3 + +- Initial beta release diff --git a/Cargo.toml b/Cargo.toml index 45149ad7..d80ab391 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,10 @@ [package] name = "async-std" -version = "0.1.0" -authors = ["Stjepan Glavina "] +version = "0.99.3" +authors = [ + "Stjepan Glavina ", + "The async-std Project Developers", +] edition = "2018" license = "Apache-2.0/MIT" repository = "https://github.com/async-rs/async-std" @@ -13,13 +16,13 @@ categories = ["asynchronous", "concurrency", "network-programming"] [package.metadata.docs.rs] features = ["docs"] -rustdoc-args = ["--features docs"] +rustdoc-args = ["--cfg", "feature=\"docs\""] [features] docs = [] [dependencies] -async-task = { git = "ssh://git@github.com/async-rs/async-task.git" } +async-task = "1.0.0" cfg-if = "0.1.9" crossbeam-channel = "0.3.9" futures-preview = "0.3.0-alpha.17" diff --git a/README.md b/README.md index 4901f8da..d4393ff8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,13 @@ # Async version of Rust's standard library -[![Build Status](https://travis-ci.com/async-rs/async-std.svg?branch=master)](https://travis-ci.org/async-rs/async-std) -[![License](https://img.shields.io/badge/license-MIT%2FApache--2.0-blue.svg)]( -https://github.com/async-rs/async-std) +[![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) [![Cargo](https://img.shields.io/crates/v/async-std.svg)](https://crates.io/crates/async-std) [![Documentation](https://docs.rs/async-std/badge.svg)](https://docs.rs/async-std) [![chat](https://img.shields.io/discord/598880689856970762.svg?logo=discord)](https://discord.gg/JvZeVNe) -This crate provides an async version of [`std`]. It provides all the interfaces you are used to, but in an async version and ready for Rust's `async/await`-syntax. +This crate provides an async version of [`std`]. It provides all the interfaces you +are used to, but in an async version and ready for Rust's `async`/`await` syntax. [`std`]: https://doc.rust-lang.org/std/index.html @@ -49,15 +49,51 @@ fn main() { } ``` +## Low-Friction Sockets with Built-In Timeouts + +```rust +#![feature(async_await)] + +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) + }) +} + +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); + }); +} +``` + ## Take a look around Clone the repo: ``` -git clone git@github.com:stjepang/async-std.git && cd async-std +git clone git@github.com:async-rs/async-std.git && cd async-std ``` -Read the docs: +Generate docs: ``` cargo doc --features docs.rs --open diff --git a/docs/src/concepts/tasks.md b/docs/src/concepts/tasks.md index a56cff50..a4238ddc 100644 --- a/docs/src/concepts/tasks.md +++ b/docs/src/concepts/tasks.md @@ -60,21 +60,68 @@ For now, it is enough to know that once you `spawn`ed a task, it will continue r Tasks in `async_std` are one of the core abstractions. Much like Rust’s `thread`s, they provide some practical functionality over the raw concept. `Tasks` have a relationship to the runtime, but they are in themselves separate. `async_std` tasks have a number of desirable properties: -- They are single-allocated +- They are allocated in one single allocation - All tasks have a *backchannel*, which allows them to propagate results and errors to the spawning task through the `JoinHandle` - The carry desirable metadata for debugging - They support task local storage -`async_std` s task api handles setup and teardown of a backing runtime for you and doesn’t rely on a runtime being started. +`async_std`s task api handles setup and teardown of a backing runtime for you and doesn’t rely on a runtime being started. ## Blocking -TODO: fill me in +`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 Rusts stdlib 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 they concurrent execution model of `async-std`. Essentially, never do this: + +```rust +fn main() { + task::block_on(async { + // this is std::fs, which blocks + std::fs::read_to_string("test_file"); + }) +} +``` + +If you want to mix operation kinds, consider putting such operations on a `thread`. ## Errors and panics -TODO: fill me in +`Task`s report errors through normal channels: If they are fallible, their `Output` should be of kind `Result`. +In case of `panic`, behaviour differs depending on if there's a reasonable part that addresses the `panic`. If not, the program _aborts_. + +In practice, that means that `block_on` propagates panics to the blocking component: + +```rust +fn main() { + task::block_on(async { + panic!("test"); + }); +} +``` + +``` +thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. +``` + +While panicing a spawned tasks will abort: + +```rust +task::spawn(async { + panic!("test"); +}); + +task::block_on(async { + task::sleep(Duration::from_millis(10000)).await; +}) +``` + +``` +thread 'async-task-driver' panicked at 'test', examples/panic.rs:8:9 +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace. +Aborted (core dumped) +``` + +That might seem odd at first, but the other option would be to silently ignore panics in spawned tasks. The current behaviour can be changed by catching panics in the spawned task and reacting with custom behaviour. This gives users the choice of panic handling strategy. ## Conclusion diff --git a/docs/src/security/policy.md b/docs/src/security/policy.md index d6285d0e..35d43c7c 100644 --- a/docs/src/security/policy.md +++ b/docs/src/security/policy.md @@ -2,14 +2,12 @@ Safety is one of the core principles of what we do, and to that end, we would like to ensure that async-std has a secure implementation. Thank you for taking the time to responsibly disclose any issues you find. -All security bugs in async-std distribution should be reported by email to security@ferrous-systems.com. This list is delivered to a small security team. Your email will be acknowledged within 24 hours, and you’ll receive a more detailed response to your email within 48 hours indicating the next steps in handling your report. If you would like, you can encrypt your report using our public key. This key is also On MIT’s keyserver and reproduced below. +All security bugs in async-std distribution should be reported by email to florian.gilcher@ferrous-systems.com. This list is delivered to a small security team. Your email will be acknowledged within 24 hours, and you’ll receive a more detailed response to your email within 48 hours indicating the next steps in handling your report. If you would like, you can encrypt your report using our public key. This key is also On MIT’s keyserver and reproduced below. Be sure to use a descriptive subject line to avoid having your report be missed. After the initial reply to your report, the security team will endeavor to keep you informed of the progress being made towards a fix and full announcement. As recommended by [RFPolicy][rf-policy], these updates will be sent at least every five days. In reality, this is more likely to be every 24-48 hours. If you have not received a reply to your email within 48 hours, or have not heard from the security team for the past five days, there are a few steps you can take (in order): -* Contact the current security coordinator TODO directly. -* Contact the back-up contact TODO directly. * Post on our Community forums Please note that the discussion forums are public areas. When escalating in these venues, please do not discuss your issue. Simply say that you’re trying to get a hold of someone from the security team. @@ -34,4 +32,35 @@ This policy is adapted from the [Rust project](https://www.rust-lang.org/policie ## PGP Key -TODO \ No newline at end of file +``` +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBF1Wu/ABCADJaGt4HwSlqKB9BGHWYKZj/6mTMbmc29vsEOcCSQKo6myCf9zc +sasWAttep4FAUDX+MJhVbBTSq9M1YVxp33Qh5AF0t9SnJZnbI+BZuGawcHDL01xE +bE+8bcA2+szeTTUZCeWwsaoTd/2qmQKvpUCBQp7uBs/ITO/I2q7+xCGXaOHZwUKc +H8SUBLd35nYFtjXAeejoZVkqG2qEjrc9bkZAwxFXi7Fw94QdkNLaCjNfKxZON/qP +A3WOpyWPr3ERk5C5prjEAvrW8kdqpTRjdmzQjsr8UEXb5GGEOo93N4OLZVQ2mXt9 +dfn++GOnOk7sTxvfiDH8Ru5o4zCtKgO+r5/LABEBAAG0UkZsb3JpYW4gR2lsY2hl +ciAoU2VjdXJpdHkgY29udGFjdCBhc3luYy1zdGQpIDxmbG9yaWFuLmdpbGNoZXJA +ZmVycm91cy1zeXN0ZW1zLmNvbT6JATgEEwECACIFAl1Wu/ACGwMGCwkIBwMCBhUI +AgkKCwQWAgMBAh4BAheAAAoJEACXY97PwLtSc0AH/18yvrElVOkG0ADWX7l+JKHH +nMQtYj0Auop8d6TuKBbpwtYhwELrQoITDMV7f2XEnchNsvYxAyBZhIISmXeJboE1 +KzZD1O+4QPXRcXhj+QNsKQ680mrgZXgAI2Y4ptIW9Vyw3jiHu/ZVopvDAt4li+up +3fRJGPAvGu+tclpJmA+Xam23cDj89M7/wHHgKIyT59WgFwyCgibL+NHKwg2Unzou +9uyZQnq6hf62sQTWEZIAr9BQpKmluplNIJHDeECWzZoE9ucE2ZXsq5pq9qojsAMK +yRdaFdpBcD/AxtrTKFeXGS7X7LqaljY/IFBEdJOqVNWpqSLjGWqjSLIEsc1AB0K5 +AQ0EXVa78AEIAJMxBOEEW+2c3CcjFuUfcRsoBsFH3Vk+GwCbjIpNHq/eAvS1yy2L +u10U5CcT5Xb6be3AeCYv00ZHVbEi6VwoauVCSX8qDjhVzQxvNLgQ1SduobjyF6t8 +3M/wTija6NvMKszyw1l2oHepxSMLej1m49DyCDFNiZm5rjQcYnFT4J71syxViqHF +v2fWCheTrHP3wfBAt5zyDet7IZd/EhYAK6xXEwr9nBPjfbaVexm2B8K6hOPNj0Bp +OKm4rcOj7JYlcxrwhMvNnwEue7MqH1oXAsoaC1BW+qs4acp/hHpesweL6Rcg1pED +OJUQd3UvRsqRK0EsorDu0oj5wt6Qp3ZEbPMAEQEAAYkBHwQYAQIACQUCXVa78AIb +DAAKCRAAl2Pez8C7Uv8bB/9scRm2wvzHLbFtcEHaHvlKO1yYfSVqKqJzIKHc7pM2 ++szM8JVRTxAbzK5Xih9SB5xlekixxO2UCJI5DkJ/ir/RCcg+/CAQ8iLm2UcYAgJD +TocKiR5gjNAvUDI4tMrDLLdF+7+RCQGc7HBSxFiNBJVGAztGVh1+cQ0zaCX6Tt33 +1EQtyRcPID0m6+ip5tCJN0dILC0YcwzXGrSgjB03JqItIyJEucdQz6UB84TIAGku +JJl4tktgD9T7Rb5uzRhHCSbLy89DQVvCcKD4B94ffuDW3HO8n8utDusOiZuG4BUf +WdFy6/gTLNiFbTzkq1BBJQMN1nBwGs1sn63RRgjumZ1N +=dIcF +-----END PGP PUBLIC KEY BLOCK----- +``` \ No newline at end of file diff --git a/examples/stdin-timeout.rs b/examples/stdin-timeout.rs index dce1809b..7cc05bf2 100644 --- a/examples/stdin-timeout.rs +++ b/examples/stdin-timeout.rs @@ -8,13 +8,15 @@ use async_std::io; use async_std::task; fn main() -> io::Result<()> { + // This async scope times out after 5 seconds. task::block_on(io::timeout(Duration::from_secs(5), async { let stdin = io::stdin(); + // Read a line from the standard input and display it. let mut line = String::new(); stdin.read_line(&mut line).await?; + dbg!(line); - print!("Got line: {}", line); Ok(()) })) } diff --git a/src/future/pending.rs b/src/future/pending.rs index f45199f2..57a40ea0 100644 --- a/src/future/pending.rs +++ b/src/future/pending.rs @@ -8,10 +8,13 @@ /// use std::time::Duration; /// /// use async_std::future::pending; -/// use async_std::prelude::*; +/// use async_std::io; /// /// let dur = Duration::from_secs(1); -/// assert!(pending::<()>().timeout(dur).await.is_err()); +/// let fut = pending(); +/// +/// let res: io::Result<()> = io::timeout(dur, fut).await; +/// assert!(res.is_err()); /// # /// # }) } /// ``` diff --git a/src/future/ready.rs b/src/future/ready.rs index 7535d504..6438c60e 100644 --- a/src/future/ready.rs +++ b/src/future/ready.rs @@ -10,9 +10,9 @@ /// # #![feature(async_await)] /// # fn main() { async_std::task::block_on(async { /// # -/// use async_std::future::ready; +/// use async_std::future; /// -/// assert_eq!(ready(10).await, 10); +/// assert_eq!(future::ready(10).await, 10); /// # /// # }) } /// ``` diff --git a/src/io/empty.rs b/src/io/empty.rs index 41978bcc..4e5236aa 100644 --- a/src/io/empty.rs +++ b/src/io/empty.rs @@ -62,7 +62,10 @@ impl AsyncRead for Empty { impl AsyncBufRead for Empty { #[inline] - fn poll_fill_buf(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll> { + fn poll_fill_buf<'a>( + self: Pin<&'a mut Self>, + _: &mut Context<'_>, + ) -> Poll> { Poll::Ready(Ok(&[])) } diff --git a/src/io/timeout.rs b/src/io/timeout.rs index b35a8030..51979819 100644 --- a/src/io/timeout.rs +++ b/src/io/timeout.rs @@ -24,6 +24,7 @@ use crate::task::{Context, Poll}; /// let stdin = io::stdin(); /// let mut line = String::new(); /// let n = stdin.read_line(&mut line).await?; +/// Ok(()) /// }) /// .await?; /// # diff --git a/src/lib.rs b/src/lib.rs index 42216a29..a9a0685a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ //! Async version of the Rust standard library. //! //! This crate is an async version of [`std`]. +//! //! Higher-level documentation in the form of the book //! ["Async programming in Rust with async-std"][book] //! is available. @@ -22,6 +23,9 @@ //! }) //! } //! ``` +//! +//! 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))] @@ -39,6 +43,5 @@ pub mod prelude; pub mod stream; pub mod sync; pub mod task; -pub mod time; pub(crate) mod utils; diff --git a/src/prelude.rs b/src/prelude.rs index eabd92f8..38956b93 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,30 +1,6 @@ //! The async prelude. //! -//! The prelude re-exports the most commonly used traits in async programming. -//! -//! # Examples -//! -//! Import the prelude to use the [`timeout`] combinator: -//! -//! ```no_run -//! # #![feature(async_await)] -//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -//! # -//! use std::time::Duration; -//! -//! use async_std::io; -//! use async_std::prelude::*; -//! -//! let stdin = io::stdin(); -//! let mut line = String::new(); -//! let dur = Duration::from_secs(5); -//! -//! stdin.read_line(&mut line).timeout(dur).await??; -//! # -//! # Ok(()) }) } -//! ``` -//! -//! [`timeout`]: ../time/trait.Timeout.html#method.timeout +//! The prelude re-exports the most commonly used traits in this crate. #[doc(no_inline)] pub use crate::future::Future; @@ -40,5 +16,3 @@ pub use crate::io::Write as _; pub use crate::stream::Stream; #[doc(no_inline)] pub use crate::task_local; -#[doc(no_inline)] -pub use crate::time::Timeout as _; diff --git a/src/task/sleep.rs b/src/task/sleep.rs index a0392be6..fa8804eb 100644 --- a/src/task/sleep.rs +++ b/src/task/sleep.rs @@ -1,8 +1,7 @@ use std::time::Duration; -use futures::future; - -use crate::time::Timeout; +use crate::future; +use crate::io; /// Sleeps for the specified amount of time. /// @@ -27,5 +26,5 @@ use crate::time::Timeout; /// # }) } /// ``` pub async fn sleep(dur: Duration) { - let _ = future::pending::<()>().timeout(dur).await; + let _: io::Result<()> = io::timeout(dur, future::pending()).await; } diff --git a/src/time/mod.rs b/src/time/mod.rs deleted file mode 100644 index f8f389cd..00000000 --- a/src/time/mod.rs +++ /dev/null @@ -1,33 +0,0 @@ -//! Timeouts for async operations. -//! -//! This module is an async extension of [`std::time`]. -//! -//! [`std::time`]: https://doc.rust-lang.org/std/time/index.html -//! -//! # Examples -//! -//! Read a line from stdin with a timeout of 5 seconds. -//! -//! ```no_run -//! # #![feature(async_await)] -//! # fn main() -> std::io::Result<()> { async_std::task::block_on(async { -//! # -//! use std::time::Duration; -//! -//! use async_std::io; -//! use async_std::prelude::*; -//! -//! let stdin = io::stdin(); -//! let mut line = String::new(); -//! -//! let n = stdin -//! .read_line(&mut line) -//! .timeout(Duration::from_secs(5)) -//! .await??; -//! # -//! # Ok(()) }) } -//! ``` - -pub use timeout::{Timeout, TimeoutError}; - -mod timeout; diff --git a/src/time/timeout.rs b/src/time/timeout.rs deleted file mode 100644 index 9f21bcc4..00000000 --- a/src/time/timeout.rs +++ /dev/null @@ -1,107 +0,0 @@ -use std::error::Error; -use std::fmt; -use std::pin::Pin; -use std::time::Duration; - -use cfg_if::cfg_if; -use futures_timer::Delay; -use pin_utils::unsafe_pinned; - -use crate::future::Future; -use crate::io; -use crate::task::{Context, Poll}; - -cfg_if! { - if #[cfg(feature = "docs")] { - #[doc(hidden)] - pub struct ImplFuture(std::marker::PhantomData); - - macro_rules! ret { - ($f:tt, $o:ty) => (ImplFuture<$o>); - } - } else { - macro_rules! ret { - ($f:tt, $o:ty) => ($f); - } - } -} - -/// An error returned when a future times out. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct TimeoutError; - -impl Error for TimeoutError {} - -impl fmt::Display for TimeoutError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - "future has timed out".fmt(f) - } -} - -impl From for io::Error { - fn from(_: TimeoutError) -> io::Error { - io::Error::new(io::ErrorKind::TimedOut, "future has timed out") - } -} - -/// An extension trait that configures timeouts for futures. -pub trait Timeout: Future + Sized { - /// Awaits a future to completion or times out after a duration of time. - /// - /// # Examples - /// - /// ```no_run - /// # #![feature(async_await)] - /// # fn main() -> std::io::Result<()> { async_std::task::block_on(async { - /// # - /// use std::time::Duration; - /// - /// use async_std::io; - /// use async_std::prelude::*; - /// - /// let stdin = io::stdin(); - /// let mut line = String::new(); - /// - /// let n = stdin - /// .read_line(&mut line) - /// .timeout(Duration::from_secs(5)) - /// .await??; - /// # - /// # Ok(()) }) } - /// ``` - fn timeout(self, dur: Duration) -> ret!(TimeoutFuture, Result) { - TimeoutFuture { - future: self, - delay: Delay::new(dur), - } - } -} - -/// A future that times out after a duration of time. -#[doc(hidden)] -#[allow(missing_debug_implementations)] -pub struct TimeoutFuture { - future: F, - delay: Delay, -} - -impl TimeoutFuture { - unsafe_pinned!(future: F); - 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)), - Poll::Pending => Poll::Pending, - }, - } - } -} - -impl Timeout for F {}