Async/Await
The Problem With Processes
Processes are a great concurrency / parallelism mechanism
- Fantastic isolation of control flow and data
- Simple semantics
- OS deals with them well
But processes are inefficient
- Inter-process communication is inefficient
- Memory usage tends toward inefficiency
So threads
The Problem With Threads
Threads are a decent concurrency / parallelism mechanism
- Control flow isolation
- Manageable semantics
- OS deals with them OK
- Way more efficient than processes
But threads are still inefficient
- Context switch costs
- Stack per thread
- Inter-thread locking
Task Pools
Idea: Small number of threads executing large number of short tasks
- Minimize context switch costs
- Still stack per thread, but not many of those
- Synchronization can be done with task rather than locks
Mechanism: Task Pool — a collection of ready-to-run tasks
- Worker fetches task from pool and executes it until
- Task finishes
- Task is going to block — put it aside after arranging for it to be put back in the pool when it can proceed (not easy)
- Worker fetches task from pool and executes it until
Futures
Old idea: a closure that will "continue running" a computation when called
Futures eventually deliver a value that is the result of computation
Compare with delay/force, promises, generators, continuations, etc
Represent computation to be performed in task pool as a future
std::future::Future
Odd Rust implementation of future idea
New trait
pub trait Future { type Output; fn poll( self: Pin<&mut Self>, cx: &mut Context, ) -> Poll<Self::Output>; }
Ignore the
Pin
andContext
When
poll()
is called on aFuture
, it will return eitherPoll::Pending
orPoll::Ready(val)
If
poll()
returnsPoll::Pending
, it will also arrange for the future to wake up via callback when it can proceed with the computation
async/await
Can declare a function or block
async
If the result type of the function or block was
T
it is nowimpl Future<Output=T>
The compiler builds a custom implementation of
Future
that "does the right thing" in this situationIf you evaluate such a function or block with
.await
, it will poll until it gets a ready value and then it will return that value
Rust's async/await Ecosystem
Need a few things:
Implementations of potentially-blocking primitives that are
async
Task pool implementation that provides machinery for spawning and supplying worker threads
Currently provided by either the
tokio
ecosystem or theasync-std
ecosystemIdea of
std::future::Future
is that you can use the primitives with either ecosystem (or build your own machinery, or whatever)
Pick Your Ecosystem
Tokio
- Older: lots more experience with
- Firefox thing
- Lots of backward compatibility adventures
async-std
- Simpler, cleaner, newer interface
- Easier to get started with
async/await Maturity
All of this is quite immature (after many years)
- Docs aren't done
- Things are changing fairly rapidly
- Big chunks are missing
The Colored Function Problem is real