More comments

This commit is contained in:
Stjepan Glavina 2020-04-21 22:04:47 +02:00
parent 72b5b2deac
commit 78a867e455
8 changed files with 122 additions and 58 deletions

View File

@ -16,12 +16,12 @@ A small and fast async runtime for Rust.
This runtime extends [the standard library][std] and
[futures] with async combinators.
It has a small API and is only 1500 lines of well-documented code.
It has a very small API and consists of only 1500 lines of well-documented code.
[std]: https://docs.rs/std
[futures]: https://docs.rs/futures
Reading [docs] and [examples] is a good place to start learning async Rust.
Reading the [docs] and [examples] is a good place to start learning async Rust.
[docs]: https://docs.rs/smol
[examples]: ./examples
@ -79,7 +79,7 @@ My personal crate recommendation list:
* HTTP clients: [surf], [isahc], [reqwest]
* HTTP servers: [async-h1], [hyper]
* WebSockets: [tungstenite]
* TLS authentication: [async-native-tls] and [native-tls]
* TLS authentication: [async-native-tls]
* Signals: [ctrlc], [signal-hook]
[piper]: https://docs.rs/piper

View File

@ -1,17 +0,0 @@
// TODO: document
use std::time::{Duration, Instant};
use smol::Timer;
async fn sleep(dur: Duration) {
Timer::after(dur).await;
}
fn main() {
smol::run(async {
let start = Instant::now();
println!("Sleeping...");
sleep(Duration::from_secs(1)).await;
println!("Woke up after {:?}", start.elapsed());
})
}

View File

@ -1,29 +0,0 @@
// TODO: document
use std::io;
use std::time::Duration;
use anyhow::Result;
use futures::io::BufReader;
use futures::prelude::*;
use smol::Timer;
async fn timeout<T>(dur: Duration, f: impl Future<Output = io::Result<T>>) -> io::Result<T> {
futures::select! {
out = f.fuse() => out,
_ = Timer::after(dur).fuse() => {
Err(io::Error::from(io::ErrorKind::TimedOut))
}
}
}
fn main() -> Result<()> {
smol::run(async {
let mut line = String::new();
let mut stdin = BufReader::new(smol::reader(std::io::stdin()));
timeout(Duration::from_secs(5), stdin.read_line(&mut line)).await?;
println!("Line: {}", line);
Ok(())
})
}

View File

@ -36,8 +36,10 @@ pub(crate) struct BlockingExecutor {
struct State {
/// Number of sleeping threads in the pool.
idle_count: usize,
/// Total number of thread in the pool.
thread_count: usize,
/// Runnable blocking tasks.
queue: VecDeque<Runnable>,
}

View File

@ -17,16 +17,75 @@ use crate::work_stealing::WorkStealingExecutor;
/// calling [`run()`] in order for futures waiting on I/O and timers to get notified.
///
/// # Examples
/// TODO a thread-pool example with num_cpus::get().max(1)
/// TODO a stoppable thread-pool with channels
///
/// Single-threaded executor:
///
/// ```
/// smol::run(async {
/// println!("Hello from the smol executor!");
/// });
/// ```
///
/// Multi-threaded executor:
///
/// ```no_run
/// use futures::future;
/// use smol::Task;
/// use std::thread;
///
/// for _ in 0..num_cpus::get().max(1) {
/// // Same number of threads as there are CPU cores.
/// let num_threads = num_cpus::get().max(1);
///
/// // Create an executor thread pool.
/// for _ in 0..num_threads {
/// // A pending future is one that simply yields forever.
/// thread::spawn(|| smol::run(future::pending::<()>()));
/// }
///
/// // No need to `run()`, now we can just block on the main future.
/// smol::block_on(async {
/// Task::spawn(async {
/// println!("Hello from an executor thread!");
/// })
/// .await;
/// });
/// ```
///
/// Stoppable multi-threaded executor:
///
/// ```
/// use smol::Task;
/// use std::thread;
///
/// // Same number of threads as there are CPU cores.
/// let num_threads = num_cpus::get().max(1);
///
/// // A channel that sends the shutdown signal.
/// let (s, r) = piper::chan::<()>(0);
/// let mut threads = Vec::new();
///
/// // Create an executor thread pool.
/// for _ in 0..num_cpus::get().max(1) {
/// // Spawn an executor thread that waits for the shutdown signal.
/// let r = r.clone();
/// threads.push(thread::spawn(move || smol::run(r.recv())));
/// }
///
/// // No need to `run()`, now we can just block on the main future.
/// smol::block_on(async {
/// Task::spawn(async {
/// println!("Hello from an executor thread!");
/// })
/// .await;
/// });
///
/// // Send a shutdown signal.
/// drop(s);
///
/// // Wait for threads to finish.
/// for t in threads {
/// t.join().unwrap();
/// }
/// ```
pub fn run<T>(future: impl Future<Output = T>) -> T {
// Create a thread-local executor and a worker in the work-stealing executor.

View File

@ -44,7 +44,7 @@ impl ThreadLocalExecutor {
}
}
/// TODO
/// Enters the context of this executor.
pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
if EXECUTOR.is_set() {
panic!("cannot run an executor inside another executor");
@ -52,7 +52,7 @@ impl ThreadLocalExecutor {
EXECUTOR.set(self, f)
}
/// TODO
/// Returns the event indicating there is a scheduled task.
pub fn event(&self) -> &IoEvent {
&self.event
}

View File

@ -8,6 +8,8 @@ use crate::reactor::Reactor;
/// Fires at the chosen point in time.
///
/// Timers are futures that output the [`Instant`] at which they fired.
///
/// # Examples
///
/// Sleep for 1 second:
@ -24,6 +26,33 @@ use crate::reactor::Reactor;
/// sleep(Duration::from_secs(1)).await;
/// # });
/// ```
///
/// Set a timeout on an I/O operation:
///
/// ```
/// use futures::prelude::*;
/// use futures::io::{self, BufReader};
/// use smol::Timer;
/// use std::time::Duration;
///
/// async fn timeout<T>(
/// dur: Duration,
/// f: impl Future<Output = io::Result<T>>,
/// ) -> io::Result<T> {
/// futures::select! {
/// t = f.fuse() => t,
/// _ = Timer::after(dur).fuse() => Err(io::Error::from(io::ErrorKind::TimedOut)),
/// }
/// }
///
/// # smol::run(async {
/// // Create a buffered stdin reader.
/// let mut stdin = BufReader::new(smol::reader(std::io::stdin()));
///
/// // Read a line within 5 seconds.
/// let mut line = String::new();
/// timeout(Duration::from_secs(5), stdin.read_line(&mut line)).await?;
/// # io::Result::Ok(()) });
#[derive(Debug)]
pub struct Timer {
/// A unique ID for this timer.
@ -38,14 +67,34 @@ pub struct Timer {
impl Timer {
/// Fires after the specified duration of time.
///
/// TODO
/// # Examples
///
/// ```
/// use smol::Timer;
/// use std::time::Duration;
///
/// # smol::run(async {
/// Timer::after(Duration::from_secs(1)).await;
/// # });
/// ```
pub fn after(dur: Duration) -> Timer {
Timer::at(Instant::now() + dur)
}
/// Fires at the specified instant in time.
///
/// TODO
/// # Examples
///
/// ```
/// use smol::Timer;
/// use std::time::{duration, Instant};
///
/// # smol::run(async {
/// let now = Instant::now();
/// let when = now + Duration::from_secs(1);
/// Timer::after(when).await;
/// # });
/// ```
pub fn at(when: Instant) -> Timer {
let id = None;
Timer { id, when }

View File

@ -68,7 +68,7 @@ impl WorkStealingExecutor {
&EXECUTOR
}
/// TODO
/// Returns the event indicating there is a scheduled task.
pub fn event(&self) -> &IoEvent {
&self.event
}
@ -140,7 +140,7 @@ pub(crate) struct Worker<'a> {
}
impl Worker<'_> {
/// TODO
/// Enters the context of this executor.
pub fn enter<T>(&self, f: impl FnOnce() -> T) -> T {
if WORKER.is_set() {
panic!("cannot run an executor inside another executor");