Theory and Design of PL (CS 538)
April 27, 2020
# Generator producing 0, 1, ..., n-1 one at a time
def firstn(n):
num = 0
while num < n:
yield num # return num to caller, suspend execution
num += 1 # resume here next time generator called
gen = firstn(100); # initialize generator
res0 = next(gen); # 0
res1 = next(gen); # 1
res2 = next(gen); # 2
res3 = next(gen); # 3
Can we just write “normal” code instead?
let my_async_block = async { 42 }; // you write this
// Compiler generates (something like) this:
enum AsyncState42 { Start, Done };
struct AsyncBlock42 { state: AsyncState42 };
impl Future for AsyncBlock42 {
type Output = i32;
fn poll(&mut self) -> Poll<i32> {
if self.state == Start {
*self.state = Done; Ready(42)
} else {
panic!("Already returned 42")
} } }
let my_async_block = AsyncBlock42 { state: Start };
let my_fut = async {
let my_str = my_async_fn(vec![1, 2, 3]);
// ... type of my_str isn't String ...
}
my_fut
is polled, it doesn’t do anything:
.await
after a Future
await
in async context!fut
is a Future, fut.await
means:
fut
is Ready (use poll()
)?
after a Resultres
is a Result, res?
means:
res
is Ok(…)let my_fut = async {
let my_str = my_async_fn(vec![1, 2, 3]).await;
// ... do stuff with my_str ...
}
my_async_fn
my_str
async fn get_food_order() -> Food { /* ... */ }
async fn get_drink_order() -> Drink { /* ... */ }
async fn make_food(the_food: Food) -> () {
if the_food = Burger {
make_burger.await;
} else {
make_pizza.await;
}
}
async fn make_drink(the_drink: Drink) -> () { /* ... */ }
async fn wash_dishes() -> () { /* ... */ }
std::sync::Mutex::lock
(all of std::sync)std::fs::read
(all of std::fs, std::net)std::thread::sleep
(all of std::thread)Never use blocking calls in async code!!!
pub trait Future {
type Output;
fn poll(
self: Pin<&mut Self>, // ignore Pin for now
cx: &mut Context
) -> Poll<Self::Output>;
}
Context
holds a Waker, argument to poll
poll
threads the Waker through
cx
alongcx
with ReactorWe’ll talk about tokio, though the Rust async ecosystem is evolving rapidly
block_on
task::spawn
spawn_blocking
for tasks that may blockpub trait Stream {
type Item;
fn poll_next(
self: Pin<&mut Self>,
cx: &mut Context
) -> Poll<Option<Self::Item>>;
}
Poll<Option<Item>>
NotReady
: next item not readyReady(Some(item))
: next item readyReady(None)
: stream finished