diff --git a/Cargo.toml b/Cargo.toml index 35e82d1..c09b591 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,58 +12,47 @@ keywords = ["async", "await", "future", "io", "networking"] categories = ["asynchronous", "concurrency", "network-programming"] readme = "README.md" -[features] -default = [] -tokio02 = ["tokio"] # Optional feature for compatibility with tokio-based libraries. - [dependencies] -async-channel = "1.1.1" -async-executor = "0.1.1" -async-io = "0.1.5" -blocking = "0.5.0" -cfg-if = "0.1.10" -easy-parallel = "3.1.0" -futures-lite = "0.1.9" -num_cpus = "1.13.0" - -[dependencies.tokio] -version = "0.2.22" -default-features = false -features = ["rt-threaded"] -optional = true +async-channel = "1.4.1" +async-executor = "0.2.0" +async-fs = "1.1.1" +async-io = "0.2.1" +async-lock = "2.0.1" +async-net = "1.0.0" +async-process = "0.1.3" +blocking = "0.6.0" +futures-lite = "1.0.0" +once_cell = "1.4.1" [dev-dependencies] -anyhow = "1.0.31" -async-channel = "1.1.1" +anyhow = "1.0.32" +async-channel = "1.4.1" async-dup = "1.2.1" -async-h1 = "2.1.0" +async-h1 = "2.1.2" async-native-tls = "0.3.3" -async-net = "0.1.1" -async-std = "1.6.2" -async-tungstenite = { version = "0.7.1", features = ["async-native-tls"] } +async-std = "1.6.3" +async-tungstenite = { version = "0.8.0", features = ["async-native-tls"] } base64 = "0.12.3" -ctrlc = "3.1.5" +ctrlc = "3.1.6" doc-comment = "0.3" futures = "0.3.5" http = "0.2.1" -http-types = "2.3.0" +http-types = "2.4.0" hyper = { version = "0.13.7", default-features = false, features = ["stream"] } native-tls = "0.2.4" num_cpus = "1.13.0" -reqwest = "0.10.6" scraper = "0.12.0" signal-hook = "0.1.16" -smol = { path = ".", features = ["tokio02"] } surf = { version = "2.0.0-alpha.4", default-features = false, features = ["h1-client"] } tempfile = "3.1.0" -tide = "0.12.0" -tungstenite = "0.11.0" +tide = "0.13.0" +tokio = { version = "0.2.22", default-features = false, features = ["rt-threaded"] } +tungstenite = "0.11.1" url = "2.1.1" -warp = "0.2.4" [target.'cfg(target_os = "linux")'.dev-dependencies] inotify = { version = "0.8.3", default-features = false } -nix = "0.17.0" +nix = "0.18.0" timerfd = "1.1.1" [target.'cfg(windows)'.dev-dependencies] diff --git a/examples/async-h1-client.rs b/examples/async-h1-client.rs index fe04b4e..23d4d6f 100644 --- a/examples/async-h1-client.rs +++ b/examples/async-h1-client.rs @@ -25,7 +25,8 @@ async fn fetch(req: Request) -> Result { // Connect to the host. let socket_addr = { let host = host.clone(); - smol::unblock!((host.as_str(), port).to_socket_addrs())? + smol::unblock(move || (host.as_str(), port).to_socket_addrs()) + .await? .next() .context("cannot resolve address")? }; @@ -45,7 +46,7 @@ async fn fetch(req: Request) -> Result { } fn main() -> Result<()> { - smol::run(async { + smol::block_on(async { // Create a request. let addr = "https://www.rust-lang.org"; let req = Request::new(Method::Get, Url::parse(addr)?); diff --git a/examples/async-h1-server.rs b/examples/async-h1-server.rs index 8ab13a2..907b455 100644 --- a/examples/async-h1-server.rs +++ b/examples/async-h1-server.rs @@ -18,7 +18,7 @@ use std::net::TcpListener; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor}; use http_types::{Request, Response, StatusCode}; -use smol::{future, Async, Task}; +use smol::{future, Async}; /// Serves a request and returns a response. async fn serve(req: Request) -> http_types::Result { @@ -47,7 +47,7 @@ async fn listen(listener: Async, tls: Option) -> Resul let task = match &tls { None => { let stream = async_dup::Arc::new(stream); - Task::spawn(async move { + smol::spawn(async move { if let Err(err) = async_h1::accept(stream, serve).await { println!("Connection error: {:#?}", err); } @@ -58,7 +58,7 @@ async fn listen(listener: Async, tls: Option) -> Resul match tls.accept(stream).await { Ok(stream) => { let stream = async_dup::Arc::new(async_dup::Mutex::new(stream)); - Task::spawn(async move { + smol::spawn(async move { if let Err(err) = async_h1::accept(stream, serve).await { println!("Connection error: {:#?}", err); } @@ -83,13 +83,13 @@ fn main() -> Result<()> { let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start HTTP and HTTPS servers. - smol::run(async { + smol::block_on(async { let http = listen(Async::::bind(([127, 0, 0, 1], 8000))?, None); let https = listen( Async::::bind(([127, 0, 0, 1], 8001))?, Some(tls), ); - future::try_join(http, https).await?; + future::try_zip(http, https).await?; Ok(()) }) } diff --git a/examples/chat-client.rs b/examples/chat-client.rs index a0dbab9..0753428 100644 --- a/examples/chat-client.rs +++ b/examples/chat-client.rs @@ -17,7 +17,7 @@ use std::net::TcpStream; use smol::{future, io, Async, Unblock}; fn main() -> io::Result<()> { - smol::run(async { + smol::block_on(async { // Connect to the server and create async stdin and stdout. let stream = Async::::connect(([127, 0, 0, 1], 6000)).await?; let stdin = Unblock::new(std::io::stdin()); diff --git a/examples/chat-server.rs b/examples/chat-server.rs index e671f28..99a8048 100644 --- a/examples/chat-server.rs +++ b/examples/chat-server.rs @@ -17,7 +17,7 @@ use std::net::{SocketAddr, TcpListener, TcpStream}; use async_channel::{bounded, Receiver, Sender}; use async_dup::Arc; -use smol::{io, prelude::*, Async, Task}; +use smol::{io, prelude::*, Async}; /// An event on the chat server. enum Event { @@ -76,7 +76,7 @@ async fn read_messages(sender: Sender, client: Arc>) -> } fn main() -> io::Result<()> { - smol::run(async { + smol::block_on(async { // Create a listener for incoming client connections. let listener = Async::::bind(([127, 0, 0, 1], 6000))?; @@ -86,7 +86,7 @@ fn main() -> io::Result<()> { // Spawn a background task that dispatches events to clients. let (sender, receiver) = bounded(100); - Task::spawn(dispatch(receiver)).detach(); + smol::spawn(dispatch(receiver)).detach(); loop { // Accept the next connection. @@ -95,7 +95,7 @@ fn main() -> io::Result<()> { let sender = sender.clone(); // Spawn a background task reading messages from the client. - Task::spawn(async move { + smol::spawn(async move { // Client starts with a `Join` event. let _ = sender.send(Event::Join(addr, client.clone())).await; diff --git a/examples/ctrl-c.rs b/examples/ctrl-c.rs index 0c190d0..ecc412a 100644 --- a/examples/ctrl-c.rs +++ b/examples/ctrl-c.rs @@ -6,8 +6,6 @@ //! cargo run --example ctrl-c //! ``` -use smol::future; - fn main() { // Set a handler that sends a message through a channel. let (s, ctrl_c) = async_channel::bounded(100); @@ -16,7 +14,7 @@ fn main() { }; ctrlc::set_handler(handle).unwrap(); - smol::run(async { + smol::block_on(async { println!("Waiting for Ctrl-C..."); // Receive a message that indicates the Ctrl-C signal occurred. diff --git a/examples/get-request.rs b/examples/get-request.rs index ff4e28c..14e38f8 100644 --- a/examples/get-request.rs +++ b/examples/get-request.rs @@ -10,9 +10,9 @@ use smol::{io, prelude::*, Async, Unblock}; use std::net::{TcpStream, ToSocketAddrs}; fn main() -> io::Result<()> { - smol::run(async { + smol::block_on(async { // Connect to http://example.com - let mut addrs = smol::unblock!(("example.com", 80).to_socket_addrs())?; + let mut addrs = smol::unblock(move || ("example.com", 80).to_socket_addrs()).await?; let addr = addrs.next().unwrap(); let mut stream = Async::::connect(addr).await?; diff --git a/examples/hyper-client.rs b/examples/hyper-client.rs index 7263e61..ca70df2 100644 --- a/examples/hyper-client.rs +++ b/examples/hyper-client.rs @@ -15,7 +15,7 @@ use anyhow::{bail, Context as _, Error, Result}; use async_native_tls::TlsStream; use http::Uri; use hyper::{Body, Client, Request, Response}; -use smol::{io, prelude::*, Async, Task}; +use smol::{io, prelude::*, Async}; /// Sends a request and fetches the response. async fn fetch(req: Request) -> Result> { @@ -27,7 +27,7 @@ async fn fetch(req: Request) -> Result> { } fn main() -> Result<()> { - smol::run(async { + smol::block_on(async { // Create a request. let req = Request::get("https://www.rust-lang.org").body(Body::empty())?; @@ -55,7 +55,7 @@ struct SmolExecutor; impl hyper::rt::Executor for SmolExecutor { fn execute(&self, fut: F) { - Task::spawn(async { drop(fut.await) }).detach(); + smol::spawn(async { drop(fut.await) }).detach(); } } @@ -81,7 +81,8 @@ impl hyper::service::Service for SmolConnector { let socket_addr = { let host = host.to_string(); let port = uri.port_u16().unwrap_or(80); - smol::unblock!((host.as_str(), port).to_socket_addrs())? + smol::unblock(move || (host.as_str(), port).to_socket_addrs()) + .await? .next() .context("cannot resolve address")? }; @@ -93,7 +94,8 @@ impl hyper::service::Service for SmolConnector { let socket_addr = { let host = host.to_string(); let port = uri.port_u16().unwrap_or(443); - smol::unblock!((host.as_str(), port).to_socket_addrs())? + smol::unblock(move || (host.as_str(), port).to_socket_addrs()) + .await? .next() .context("cannot resolve address")? }; diff --git a/examples/hyper-server.rs b/examples/hyper-server.rs index cf09aca..d411085 100644 --- a/examples/hyper-server.rs +++ b/examples/hyper-server.rs @@ -21,7 +21,7 @@ use anyhow::{Error, Result}; use async_native_tls::{Identity, TlsAcceptor, TlsStream}; use hyper::service::{make_service_fn, service_fn}; use hyper::{Body, Request, Response, Server}; -use smol::{future, io, prelude::*, Async, Task}; +use smol::{future, io, prelude::*, Async}; /// Serves a request and returns a response. async fn serve(req: Request, host: String) -> Result> { @@ -56,13 +56,13 @@ fn main() -> Result<()> { let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start HTTP and HTTPS servers. - smol::run(async { + smol::block_on(async { let http = listen(Async::::bind(([127, 0, 0, 1], 8000))?, None); let https = listen( Async::::bind(([127, 0, 0, 1], 8001))?, Some(tls), ); - future::try_join(http, https).await?; + future::try_zip(http, https).await?; Ok(()) }) } @@ -73,7 +73,7 @@ struct SmolExecutor; impl hyper::rt::Executor for SmolExecutor { fn execute(&self, fut: F) { - Task::spawn(async { drop(fut.await) }).detach(); + smol::spawn(async { drop(fut.await) }).detach(); } } diff --git a/examples/linux-inotify.rs b/examples/linux-inotify.rs index cc36109..10c73e3 100644 --- a/examples/linux-inotify.rs +++ b/examples/linux-inotify.rs @@ -32,7 +32,7 @@ fn main() -> std::io::Result<()> { } } - smol::run(async { + smol::block_on(async { // Watch events in the current directory. let mut inotify = Async::new(Inotify::init()?)?; inotify.get_mut().add_watch(".", WatchMask::ALL_EVENTS)?; diff --git a/examples/linux-timerfd.rs b/examples/linux-timerfd.rs index cb76204..f53335f 100644 --- a/examples/linux-timerfd.rs +++ b/examples/linux-timerfd.rs @@ -35,7 +35,7 @@ fn main() -> std::io::Result<()> { Ok(()) } - smol::run(async { + smol::block_on(async { let start = Instant::now(); println!("Sleeping..."); diff --git a/examples/other-runtimes.rs b/examples/other-runtimes.rs deleted file mode 100644 index 499c050..0000000 --- a/examples/other-runtimes.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! Demonstrates how to use `async-std`, `tokio`, `surf`, and `request`. -//! -//! For compatibility with tokio-based libraries, enable the `tokio02` feature flag: -//! -//! ```toml -//! [dependencies] -//! smol = { version = "0.3", features = ["tokio02"] } -//! ``` -//! -//! Run with: -//! -//! ``` -//! cargo run --example other-runtimes -//! ``` - -use std::time::{Duration, Instant}; - -use anyhow::{Error, Result}; - -fn main() -> Result<()> { - smol::run(async { - // Sleep using async-std. - let start = Instant::now(); - println!("Sleeping using async-std..."); - async_std::task::sleep(Duration::from_secs(1)).await; - println!("Woke up after {:?}", start.elapsed()); - - // Sleep using tokio (the `tokio02` feature must be enabled). - let start = Instant::now(); - println!("Sleeping using tokio..."); - tokio::time::delay_for(Duration::from_secs(1)).await; - println!("Woke up after {:?}", start.elapsed()); - - // Make a GET request using surf. - let body = surf::get("https://www.rust-lang.org") - .recv_string() - .await - .map_err(Error::msg)?; - println!("Body from surf: {:?}", body); - - // Make a GET request using reqwest (the `tokio02` feature must be enabled). - let resp = reqwest::get("https://www.rust-lang.org").await?; - let body = resp.text().await?; - println!("Body from reqwest: {:?}", body); - - Ok(()) - }) -} diff --git a/examples/simple-client.rs b/examples/simple-client.rs index fdfc167..24404ac 100644 --- a/examples/simple-client.rs +++ b/examples/simple-client.rs @@ -33,7 +33,8 @@ async fn fetch(addr: &str) -> Result> { // Connect to the host. let socket_addr = { let host = host.clone(); - smol::unblock!((host.as_str(), port).to_socket_addrs())? + smol::unblock(move || (host.as_str(), port).to_socket_addrs()) + .await? .next() .context("cannot resolve address")? }; @@ -59,7 +60,7 @@ async fn fetch(addr: &str) -> Result> { } fn main() -> Result<()> { - smol::run(async { + smol::block_on(async { let addr = "https://www.rust-lang.org"; let resp = fetch(addr).await?; println!("{}", String::from_utf8_lossy(&resp)); diff --git a/examples/simple-server.rs b/examples/simple-server.rs index 0ec477f..f943bb7 100644 --- a/examples/simple-server.rs +++ b/examples/simple-server.rs @@ -17,7 +17,7 @@ use std::net::{TcpListener, TcpStream}; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor}; -use smol::{future, prelude::*, Async, Task}; +use smol::{future, prelude::*, Async}; const RESPONSE: &[u8] = br#" HTTP/1.1 200 OK @@ -65,7 +65,7 @@ async fn listen(listener: Async, tls: Option) -> Resul let tls = tls.clone(); // Spawn a background task serving this connection. - Task::spawn(async move { + smol::spawn(async move { if let Err(err) = serve(stream, tls).await { println!("Connection error: {:#?}", err); } @@ -80,13 +80,13 @@ fn main() -> Result<()> { let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start HTTP and HTTPS servers. - smol::run(async { + smol::block_on(async { let http = listen(Async::::bind(([127, 0, 0, 1], 8000))?, None); let https = listen( Async::::bind(([127, 0, 0, 1], 8001))?, Some(tls), ); - future::try_join(http, https).await?; + future::try_zip(http, https).await?; Ok(()) }) } diff --git a/examples/tcp-client.rs b/examples/tcp-client.rs index d98d532..f7a92bc 100644 --- a/examples/tcp-client.rs +++ b/examples/tcp-client.rs @@ -17,7 +17,7 @@ use std::net::TcpStream; use smol::{future, io, Async, Unblock}; fn main() -> io::Result<()> { - smol::run(async { + smol::block_on(async { // Create async stdin and stdout handles. let stdin = Unblock::new(std::io::stdin()); let mut stdout = Unblock::new(std::io::stdout()); @@ -28,7 +28,7 @@ fn main() -> io::Result<()> { println!("Type a message and hit enter!\n"); // Pipe messages from stdin to the server and pipe messages from the server to stdout. - future::try_join( + future::try_zip( io::copy(stdin, &mut &stream), io::copy(&stream, &mut stdout), ) diff --git a/examples/tcp-server.rs b/examples/tcp-server.rs index 5f37fa7..b71cbb3 100644 --- a/examples/tcp-server.rs +++ b/examples/tcp-server.rs @@ -14,7 +14,7 @@ use std::net::{TcpListener, TcpStream}; -use smol::{io, Async, Task}; +use smol::{io, Async}; /// Echoes messages from the client back to it. async fn echo(stream: Async) -> io::Result<()> { @@ -23,7 +23,7 @@ async fn echo(stream: Async) -> io::Result<()> { } fn main() -> io::Result<()> { - smol::run(async { + smol::block_on(async { // Create a listener. let listener = Async::::bind(([127, 0, 0, 1], 7000))?; println!("Listening on {}", listener.get_ref().local_addr()?); @@ -35,7 +35,7 @@ fn main() -> io::Result<()> { println!("Accepted client: {}", peer_addr); // Spawn a task that echoes messages from the client back to it. - Task::spawn(echo(stream)).detach(); + smol::spawn(echo(stream)).detach(); } }) } diff --git a/examples/tls-client.rs b/examples/tls-client.rs index 6c4d956..c8ac36d 100644 --- a/examples/tls-client.rs +++ b/examples/tls-client.rs @@ -24,7 +24,7 @@ fn main() -> Result<()> { builder.add_root_certificate(Certificate::from_pem(include_bytes!("certificate.pem"))?); let tls = TlsConnector::from(builder); - smol::run(async { + smol::block_on(async { // Create async stdin and stdout handles. let stdin = Unblock::new(std::io::stdin()); let mut stdout = Unblock::new(std::io::stdout()); @@ -37,7 +37,7 @@ fn main() -> Result<()> { // Pipe messages from stdin to the server and pipe messages from the server to stdout. let stream = async_dup::Mutex::new(stream); - future::try_join( + future::try_zip( io::copy(stdin, &mut &stream), io::copy(&stream, &mut stdout), ) diff --git a/examples/tls-server.rs b/examples/tls-server.rs index 20431db..3f3fa77 100644 --- a/examples/tls-server.rs +++ b/examples/tls-server.rs @@ -16,7 +16,7 @@ use std::net::{TcpListener, TcpStream}; use anyhow::Result; use async_native_tls::{Identity, TlsAcceptor, TlsStream}; -use smol::{io, Async, Task}; +use smol::{io, Async}; /// Echoes messages from the client back to it. async fn echo(stream: TlsStream>) -> Result<()> { @@ -30,7 +30,7 @@ fn main() -> Result<()> { let identity = Identity::from_pkcs12(include_bytes!("identity.pfx"), "password")?; let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); - smol::run(async { + smol::block_on(async { // Create a listener. let listener = Async::::bind(([127, 0, 0, 1], 7001))?; println!("Listening on {}", listener.get_ref().local_addr()?); @@ -46,7 +46,7 @@ fn main() -> Result<()> { ); // Spawn a task that echoes messages from the client back to it. - Task::spawn(echo(stream)).detach(); + smol::spawn(echo(stream)).detach(); } }) } diff --git a/examples/unix-signal.rs b/examples/unix-signal.rs index 2483c43..3c4b451 100644 --- a/examples/unix-signal.rs +++ b/examples/unix-signal.rs @@ -12,7 +12,7 @@ fn main() -> std::io::Result<()> { use smol::{prelude::*, Async}; - smol::run(async { + smol::block_on(async { // Create a Unix stream that receives a byte on each signal occurrence. let (a, mut b) = Async::::pair()?; signal_hook::pipe::register(signal_hook::SIGINT, a)?; diff --git a/examples/web-crawler.rs b/examples/web-crawler.rs index 93b4649..8aa5adb 100644 --- a/examples/web-crawler.rs +++ b/examples/web-crawler.rs @@ -11,7 +11,6 @@ use std::collections::{HashSet, VecDeque}; use anyhow::Result; use async_channel::{bounded, Sender}; use scraper::{Html, Selector}; -use smol::Task; const ROOT: &str = "https://www.rust-lang.org"; @@ -34,7 +33,7 @@ fn links(body: String) -> Vec { } fn main() -> Result<()> { - smol::run(async { + smol::block_on(async { let mut seen = HashSet::new(); let mut queue = VecDeque::new(); seen.insert(ROOT.to_string()); @@ -53,7 +52,7 @@ fn main() -> Result<()> { Some(url) => { println!("{}", url); tasks += 1; - Task::spawn(fetch(url, s.clone())).detach(); + smol::spawn(fetch(url, s.clone())).detach(); } } } diff --git a/examples/websocket-client.rs b/examples/websocket-client.rs index 8ddbc77..c1fa11b 100644 --- a/examples/websocket-client.rs +++ b/examples/websocket-client.rs @@ -35,7 +35,8 @@ async fn connect(addr: &str, tls: TlsConnector) -> Result<(WsStream, Response)> // Resolve the address. let socket_addr = { let host = host.clone(); - smol::unblock!((host.as_str(), port).to_socket_addrs())? + smol::unblock(move || (host.as_str(), port).to_socket_addrs()) + .await? .next() .context("cannot resolve address")? }; @@ -64,7 +65,7 @@ fn main() -> Result<()> { builder.add_root_certificate(Certificate::from_pem(include_bytes!("certificate.pem"))?); let tls = TlsConnector::from(builder); - smol::run(async { + smol::block_on(async { // Connect to the server. let (mut stream, resp) = connect("wss://127.0.0.1:9001", tls).await?; dbg!(resp); diff --git a/examples/websocket-server.rs b/examples/websocket-server.rs index 36e7de7..d374728 100644 --- a/examples/websocket-server.rs +++ b/examples/websocket-server.rs @@ -20,7 +20,7 @@ use anyhow::{Context as _, Result}; use async_native_tls::{Identity, TlsAcceptor, TlsStream}; use async_tungstenite::WebSocketStream; use futures::sink::{Sink, SinkExt}; -use smol::{future, prelude::*, Async, Task}; +use smol::{future, prelude::*, Async}; use tungstenite::Message; /// Echoes messages from the client back to it. @@ -46,13 +46,13 @@ async fn listen(listener: Async, tls: Option) -> Resul match &tls { None => { let stream = WsStream::Plain(async_tungstenite::accept_async(stream).await?); - Task::spawn(echo(stream)).detach(); + smol::spawn(echo(stream)).detach(); } Some(tls) => { // In case of WSS, establish a secure TLS connection first. let stream = tls.accept(stream).await?; let stream = WsStream::Tls(async_tungstenite::accept_async(stream).await?); - Task::spawn(echo(stream)).detach(); + smol::spawn(echo(stream)).detach(); } } } @@ -64,13 +64,13 @@ fn main() -> Result<()> { let tls = TlsAcceptor::from(native_tls::TlsAcceptor::new(identity)?); // Start WS and WSS servers. - smol::run(async { + smol::block_on(async { let ws = listen(Async::::bind(([127, 0, 0, 1], 9000))?, None); let wss = listen( Async::::bind(([127, 0, 0, 1], 9001))?, Some(tls), ); - future::try_join(ws, wss).await?; + future::try_zip(ws, wss).await?; Ok(()) }) } diff --git a/examples/windows-uds.rs b/examples/windows-uds.rs index 5079552..b24726a 100644 --- a/examples/windows-uds.rs +++ b/examples/windows-uds.rs @@ -10,7 +10,7 @@ fn main() -> std::io::Result<()> { use std::path::PathBuf; - use smol::{io, prelude::*, Async, Task, Unblock}; + use smol::{io, prelude::*, Async, Unblock}; use tempfile::tempdir; use uds_windows::{UnixListener, UnixStream}; @@ -28,13 +28,13 @@ fn main() -> std::io::Result<()> { let dir = tempdir()?; let path = dir.path().join("socket"); - smol::run(async { + smol::block_on(async { // Create a listener. let listener = Async::new(UnixListener::bind(&path)?)?; println!("Listening on {:?}", listener.get_ref().local_addr()?); // Spawn a client task. - let task = Task::spawn(client(path)); + let task = smol::spawn(client(path)); // Accept the client. let (stream, _) = listener.read_with(|l| l.accept()).await?; diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..3d1411f --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +version = 'Two' diff --git a/src/lib.rs b/src/lib.rs index 24f4384..7a392d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,12 +5,11 @@ //! Connect to an HTTP website, make a GET request, and pipe the response to the standard output: //! //! ``` -//! use async_net::TcpStream; -//! use smol::{io, prelude::*, Unblock}; +//! use smol::{io, net, prelude::*, Unblock}; //! //! fn main() -> io::Result<()> { -//! smol::run(async { -//! let mut stream = TcpStream::connect("example.com:80").await?; +//! smol::block_on(async { +//! let mut stream = net::TcpStream::connect("example.com:80").await?; //! let req = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n"; //! stream.write_all(req).await?; //! @@ -21,32 +20,14 @@ //! } //! ``` //! -//! This example uses [`async-net`] for networking, but you can also use the primitive [`Async`] -//! type. See the [full code][get-request]. +//! This example uses the [`net`] module for networking, but you can also use the primitive +//! [`Async`] type. See the [full code][get-request]. //! //! Look inside the [examples] directory for more. //! //! [`async-net`]: https://docs.rs/async-net //! [examples]: https://github.com/stjepang/smol/tree/master/examples //! [get-request]: https://github.com/stjepang/smol/blob/master/examples/get-request.rs -//! -//! # Compatibility -//! -//! All async libraries work with smol out of the box. -//! -//! The only exception is [tokio], which is traditionally incompatible with [futures] and crashes -//! when called from other executors. Fortunately, there are ways around it. -//! -//! Enable the `tokio02` feature flag and [`smol::run()`][`crate::run()`] will create a minimal -//! tokio runtime for its libraries: -//! -//! ```toml -//! [dependencies] -//! smol = { version = "0.3", features = ["tokio02"] } -//! ``` -//! -//! [tokio]: https://docs.rs/tokio -//! [futures]: https://docs.rs/futures #![forbid(unsafe_code)] #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] @@ -54,31 +35,38 @@ #[cfg(doctest)] doc_comment::doctest!("../README.md"); -use std::env; use std::future::Future; +use std::panic::catch_unwind; +use std::thread; -use async_executor::{Executor, LocalExecutor}; -use cfg_if::cfg_if; -use easy_parallel::Parallel; +use once_cell::sync::Lazy; #[doc(inline)] pub use { - async_executor::Task, - async_io::Async, - async_io::Timer, + async_executor::{Executor, LocalExecutor, Task}, + async_io::{block_on, Async, Timer}, blocking::{unblock, Unblock}, futures_lite::{future, io, stream}, futures_lite::{pin, ready}, }; -/// Async traits and their extensions. -/// -/// # Examples -/// -/// ``` -/// use smol::prelude::*; -/// ``` +// TODO: Mutex::lock_arc() -> ArcMutexGuard, also Mutex::try_lock_arc() +#[doc(inline)] +pub use { + async_channel as channel, async_fs as fs, async_lock as lock, async_net as net, + async_process as process, +}; + pub mod prelude { + //! Traits [`Future`], [`AsyncBufRead`], [`AsyncRead`], [`AsyncSeek`], [`AsyncWrite`], and + //! their extensions. + //! + //! # Examples + //! + //! ``` + //! use smol::prelude::*; + //! ``` + #[doc(no_inline)] pub use futures_lite::{ future::{Future, FutureExt}, @@ -90,111 +78,35 @@ pub mod prelude { }; } -/// Starts a thread-local executor and then runs the future. +/// Spawns a task onto the single-threaded global executor. +/// +/// There is a single-threaded global executor that gets lazily initialized on first use. It is +/// advisable to use it in tests or small programs, but it is otherwise a better idea to define +/// your own [`Executor`]s. /// /// # Examples /// /// ``` -/// use smol::Task; +/// let task = smol::spawn(async { +/// 1 + 2 +/// }); /// /// smol::block_on(async { -/// let task = Task::local(async { -/// println!("Hello world"); -/// }); -/// task.await; -/// }) +/// assert_eq!(task.await, 3); +/// }); /// ``` -pub fn block_on(future: impl Future) -> T { - let local_ex = LocalExecutor::new(); - - cfg_if! { - if #[cfg(not(feature = "tokio02"))] { - local_ex.run(future) - } else { - // A minimal tokio runtime to support libraries depending on it. - let mut rt = tokio::runtime::Builder::new() - .enable_all() - .basic_scheduler() - .build() - .expect("cannot start tokio runtime"); - let handle = rt.handle().clone(); - - // A channel that coordinates shutdown when the main future completes. - let (trigger, shutdown) = async_channel::unbounded::<()>(); - let future = async move { - let _trigger = trigger; // Dropped at the end of this async block. - future.await - }; - - Parallel::new() - .add(|| rt.block_on(shutdown.recv())) - .finish(|| handle.enter(|| local_ex.run(future))) - .1 - } - } -} - -/// Starts a thread-local and a multi-threaded executor and then runs the future. -/// -/// This function runs two executors at the same time: -/// -/// 1. The current thread runs a [`LocalExecutor`] and the main `future` on it. -/// 2. A thread pool runs an [`Executor`] until the main `future` completes. -/// -/// The number of spawned threads matches the number of logical CPU cores on the system, but it can -/// be overriden by setting the `SMOL_THREADS` environment variable. -/// -/// # Examples -/// -/// ``` -/// use smol::Task; -/// -/// smol::run(async { -/// let task = Task::spawn(async { -/// println!("Hello world"); -/// }); -/// task.await; -/// }) -/// ``` -pub fn run(future: impl Future) -> T { - // A channel that coordinates shutdown when the main future completes. - let (trigger, shutdown) = async_channel::unbounded::<()>(); - let future = async move { - let _trigger = trigger; // Dropped at the end of this async block. - future.await - }; - - let num_threads = { - // Parse SMOL_THREADS or use the number of CPU cores on the system. - env::var("SMOL_THREADS") - .ok() - .and_then(|s| s.parse().ok()) - .unwrap_or_else(|| num_cpus::get()) - }; - - let ex = Executor::new(); - let local_ex = LocalExecutor::new(); - - cfg_if! { - if #[cfg(not(feature = "tokio02"))] { - Parallel::new() - .each(0..num_threads, |_| ex.run(shutdown.recv())) - .finish(|| ex.enter(|| local_ex.run(future))) - .1 - } else { - // A minimal tokio runtime to support libraries depending on it. - let mut rt = tokio::runtime::Builder::new() - .enable_all() - .basic_scheduler() - .build() - .expect("cannot start tokio runtime"); - let handle = rt.handle().clone(); - - Parallel::new() - .add(|| ex.enter(|| rt.block_on(shutdown.recv()))) - .each(0..num_threads, |_| handle.enter(|| ex.run(shutdown.recv()))) - .finish(|| handle.enter(|| ex.enter(|| local_ex.run(future)))) - .1 - } - } +pub fn spawn(future: impl Future + Send + 'static) -> Task { + static GLOBAL: Lazy = Lazy::new(|| { + thread::Builder::new() + .name("smol".to_string()) + .spawn(|| { + loop { + let _ = + catch_unwind(|| async_io::block_on(GLOBAL.run(future::pending::<()>()))); + } + }) + .unwrap(); + Executor::new() + }); + GLOBAL.spawn(future) }