From 70e5c035438777355bb665a8ae05bc3493d573cd Mon Sep 17 00:00:00 2001 From: Nick Cameron Date: Tue, 5 Nov 2024 14:22:55 +1300 Subject: [PATCH] Planning Signed-off-by: Nick Cameron --- src/SUMMARY.md | 27 +++++++++- src/part-guide/adv-async-await.md | 65 ++++++++++++++++++++++++ src/part-guide/concurrency-primitives.md | 25 +++++++++ src/part-guide/dtors.md | 42 +++++++++++++++ src/part-guide/futures.md | 44 ++++++++++++++++ src/part-guide/io.md | 59 +++++++++++++++++++++ src/part-guide/runtimes.md | 29 +++++++++++ src/part-guide/streams.md | 43 ++++++++++++++++ src/part-guide/sync.md | 41 +++++++++++++++ src/part-guide/timers-signals.md | 15 ++++++ src/part-guide/tools.md | 28 ++++++++++ 11 files changed, 417 insertions(+), 1 deletion(-) create mode 100644 src/part-guide/adv-async-await.md create mode 100644 src/part-guide/concurrency-primitives.md create mode 100644 src/part-guide/dtors.md create mode 100644 src/part-guide/futures.md create mode 100644 src/part-guide/io.md create mode 100644 src/part-guide/runtimes.md create mode 100644 src/part-guide/streams.md create mode 100644 src/part-guide/sync.md create mode 100644 src/part-guide/timers-signals.md create mode 100644 src/part-guide/tools.md diff --git a/src/SUMMARY.md b/src/SUMMARY.md index c60ce942..ffc06ec0 100644 --- a/src/SUMMARY.md +++ b/src/SUMMARY.md @@ -11,10 +11,35 @@ - [Introduction](part-guide/intro.md) - [Concurrent programming](part-guide/concurrency.md) -- [Async and Await](part-guide/async-await.md) +- [Async and await](part-guide/async-await.md) +- [Advanced async/await topics](part-guide/adv-async-await.md) +- [IO and issues with blocking](part-guide/io.md) +- [Concurrency primitives](part-guide/concurrency-primitives.md) +- [Channels, locking, and synchronization](part-guide/sync.md) +- [Tools for async programming](part-guide/tools.md) +- [Destruction and clean-up](part-guide/dtors.md) +- [Futures](part-guide/futures.md) +- [Runtimes](part-guide/runtimes.md) +- [Timers and signal handling](part-guide/times-signals.md) +- [Async iterators (streams)](part-guide/streams.md) # Part 2: reference +- [Implementing futures and streams]() +- [Alternate runtimes]() +- [Implementing your own runtime]() +- [async in sync, sync in async]() +- [Async IO: readiness vs completion, and io_uring]() +- [Design patterns]() +- [Cancellation]() (cancellation safety) +- [Starvation]() +- [Pinning]() +- [Async and FFI]() +- [Comparing async programming in Rust to other languages]() +- [The implementation of async/await in rustc]() +- structured concurrency? + + # Old chapters - [Getting Started](01_getting_started/01_chapter.md) diff --git a/src/part-guide/adv-async-await.md b/src/part-guide/adv-async-await.md new file mode 100644 index 00000000..4054eb53 --- /dev/null +++ b/src/part-guide/adv-async-await.md @@ -0,0 +1,65 @@ +# More async/await topics + +## Unit tests + +## Blocking and cancellation + +- Two important concepts to be aware of early, we'll revisit in more detail as we go along +- Cancellation + - How to do it + - drop a future + - cancellation token + - abort functions + - Why it matters, cancellation safety (forward ref) +- Blocking + - IO and computation can block + - why it's bad + - how to deal is a forward ref to io chapter + +## `Send + 'static` bounds on futures + +- Why they're there, multi-threaded runtimes +- spawn local to avoid them +- What makes an async fn `Send + 'static` and how to fix bugs with it + +## Async traits + +- syntax + - The `Send + 'static` issue and working around it + - trait_variant + - explicit future + - return type notation (https://blog.rust-lang.org/inside-rust/2024/09/26/rtn-call-for-testing.html) +- overriding + - future vs async notation for methods +- object safety +- capture rules (https://blog.rust-lang.org/2024/09/05/impl-trait-capture-rules.html) +- history and async-trait crate + + +## Async blocks and closures + +- async block syntax + - what it means +- using an async block in a function returning a future + - subtype of async method +- closures + - coming soon (https://github.com/rust-lang/rust/pull/132706, https://blog.rust-lang.org/inside-rust/2024/08/09/async-closures-call-for-testing.html) + - async blocks in closures vs async closures +- errors in async blocks + - https://rust-lang.github.io/async-book/07_workarounds/02_err_in_async_blocks.html + +## Recursion + +- Allowed (relatively new), but requires some explicit boxing + - forward reference to futures, pinning + - https://rust-lang.github.io/async-book/07_workarounds/04_recursion.html + - https://blog.rust-lang.org/2024/03/21/Rust-1.77.0.html#support-for-recursion-in-async-fn + - async-recursion macro (https://docs.rs/async-recursion/latest/async_recursion/) + + +## Lifetimes and borrowing + +- Mentioned the static lifetime above +- Lifetime bounds on futures (`Future + '_`, etc.) +- Borrowing across await points +- I don't know, I'm sure there are more lifetime issues with async functions ... diff --git a/src/part-guide/concurrency-primitives.md b/src/part-guide/concurrency-primitives.md new file mode 100644 index 00000000..aa874393 --- /dev/null +++ b/src/part-guide/concurrency-primitives.md @@ -0,0 +1,25 @@ +# Concurrency primitives + +- concurrent composition of futures + - c.f., sequential composition with await, composition of tasks with spawn + - concurrent/task behaviour + - behaviour on error +- streams as alternative, forward ref +- different versions in different runtimes/other crates + - focus on the Tokio versions + +## Join + +- Tokio/futures-rs join macro +- c.f., joining tasks +- join in futures-concurrency +- FuturesUnordered + - like a dynamic version of join + - forward ref to stream + +## Race/select + +- Tokio select macro +- cancellation issues +- different behaviour of futures-rs version +- race in futures-concurrency diff --git a/src/part-guide/dtors.md b/src/part-guide/dtors.md new file mode 100644 index 00000000..b1d46900 --- /dev/null +++ b/src/part-guide/dtors.md @@ -0,0 +1,42 @@ +# Destruction and clean-up + +- Object destruction and recap of Drop +- General clean up requirements in software +- Async issues + - Might want to do stuff async during clean up, e.g., send a final message + - Might need to clean up stuff which is still being used async-ly + - Might want to clean up when an async task completes or cancels and there is no way to catch that + - State of the runtime during clean-up phase (esp if we're panicking or whatever) + - No async Drop + - WIP + - forward ref to completion io topic + +## Cancellation + +- How it happens (recap of adv-async-await.md) + - drop a future + - cancellation token + - abort functions +- What we can do about 'catching' cancellation + - logging or monitoring cancellation +- How cancellation affects other futures tasks (forward ref to cancellation safety chapter, this should just be a heads-up) + +## Panicking and async + +- Propagation of panics across tasks (spawn result) +- Panics leaving data inconsistent (tokio mutexes) +- Calling async code when panicking (make sure you don't) + +## Patterns for clean-up + +- Avoid needing clean up (abort/restart) +- Don't use async for cleanup and don't worry too much +- async clean up method + dtor bomb (i.e., separate clean-up from destruction) +- centralise/out-source clean-up in a separate task or thread or supervisor object/process + +## Why no async Drop (yet) + +- Note this is advanced section and not necessary to read +- Why async Drop is hard +- Possible solutions and there issues +- Current status diff --git a/src/part-guide/futures.md b/src/part-guide/futures.md new file mode 100644 index 00000000..cf14fcae --- /dev/null +++ b/src/part-guide/futures.md @@ -0,0 +1,44 @@ +# Futures + +We've talked a lot about futures in the preceding chapters; they're a key part of Rust's async programming story! In this chapter we're going to get into some of the details of what futures are and how they work, and some libraries for working directly with futures. + +## The `Future` and `IntoFuture` traits + +- Future + - Output assoc type + - No real detail here, polling is in the next section, reference adv sections on Pin, executors/wakers +- IntoFuture + - Usage - general, in await, async builder pattern (pros and cons in using) +- Boxing futures, `Box` and how it used to be common and necessary but mostly isn't now, except for recursion, etc. + +## Polling + +- what it is and who does it, Poll type + - ready is final state +- how it connects with await +- drop = cancel + - for futures and thus tasks + - implications for async programming in general + - reference to chapter on cancellation safety + +### Fusing + +## futures-rs crate + +- History and purpose + - see streams chapter + - helpers for writing executors or other low-level futures stuff + - pinning and boxing + - executor as a partial runtime (see alternate runtimes in reference) +- TryFuture +- convenience futures: pending, ready, ok/err, etc. +- combinator functions on FutureExt +- alternative to Tokio stuff + - functions + - IO traits + +## futures-concurrency crate + +https://docs.rs/futures-concurrency/latest/futures_concurrency/ + + diff --git a/src/part-guide/io.md b/src/part-guide/io.md new file mode 100644 index 00000000..ed5e5aab --- /dev/null +++ b/src/part-guide/io.md @@ -0,0 +1,59 @@ +# IO and issues with blocking + +## Blocking and non-blocking IO + +- High level view +- How async IO fits with async concurrency +- Why blocking IO is bad +- forward ref to streams for streams/sinks + +## Read and Write + +- async Read and Write traits + - part of the runtime +- how to use +- specific implementations + - network vs disk + - tcp, udp + - file system is not really async, but io_uring (ref to that chapter) + - practical examples + - stdout, etc. + - pipe, fd, etc. + + +## Memory management + +- Issues with buffer management and async IO +- Different solutions and pros and cons + - zero-copy approach + - shared buffer approach +- Utility crates to help with this, Bytes, etc. + +## Advanced topics on IO + +- buf read/write +- Read + Write, split, join +- copy +- simplex and duplex +- cancelation + +## The OS view of IO + +- Different kinds of IO and mechanisms, completion IO, reference to completion IO chapter in adv section + - different runtimes can faciliate this + - mio for low-level interface + + +## Other blocking operations + +- Why this is bad +- Long running CPU work + - Using Tokio for just CPU work: https://thenewstack.io/using-rustlangs-async-tokio-runtime-for-cpu-bound-tasks/ +- Solutions + - spawn blocking + - thread pool + - etc. +- yielding to the runtime + - not the same as Rust's yield keyword + - await doesn't yield + - implicit yields in Tokio diff --git a/src/part-guide/runtimes.md b/src/part-guide/runtimes.md new file mode 100644 index 00000000..5d9f5db6 --- /dev/null +++ b/src/part-guide/runtimes.md @@ -0,0 +1,29 @@ +# Runtimes and runtime issues + +## Running async code + +- Explicit startup vs async main +- tokio context concept +- block_on +- runtime as reflected in the code (Runtime, Handle) +- runtime shutdown + +## Threads and tasks + +- default work stealing, multi-threaded + - revisit Send + 'static bounds +- yield +- spawn-local +- spawn-blocking (recap), block-in-place +- tokio-specific stuff on yielding to other threads, local vs global queues, etc + +## Configuration options + +- thread pool size +- single threaded, thread per core etc. + +## Alternate runtimes + +- Why you'd want to use a different runtime or implement your own +- What kind of variations exist in the high-level design +- Forward ref to adv chapters diff --git a/src/part-guide/streams.md b/src/part-guide/streams.md new file mode 100644 index 00000000..aee675cb --- /dev/null +++ b/src/part-guide/streams.md @@ -0,0 +1,43 @@ +# Async iterators (FKA streams) + +- Stream as an async iterator or as many futures +- WIP + - current status + - futures and Tokio Stream traits + - nightly trait +- lazy like sync iterators +- pinning and streams (forward ref to pinning chapter) +- fused streams + +## Consuming an async iterator + +- while let with async next +- for_each, for_each_concurrent +- collect +- into_future, buffered + +## Stream combinators + +- Taking a future instead of a closure +- Some example combinators +- unordered variations + +## Implementing an async iterator + +- Implementing the trait +- Practicalities and util functions +- async_iter stream macro + +## Sinks + +- https://docs.rs/futures/latest/futures/sink/index.html + +## Future work + +- current status + - https://rust-lang.github.io/rfcs/2996-async-iterator.html +- async next vs poll +- async iteration syntax +- (async) generators +- lending iterators + diff --git a/src/part-guide/sync.md b/src/part-guide/sync.md new file mode 100644 index 00000000..0d579231 --- /dev/null +++ b/src/part-guide/sync.md @@ -0,0 +1,41 @@ +# Channels, locking, and synchronization + +note on runtime specificness of sync primitves + +Why we need async primitives rather than use the sync ones + +## Channels + +- basically same as the std ones, but await + - communicate between tasks (same thread or different) +- one shot +- mpsc +- other channels +- bounded and unbounded channels + +## Locks + +- async Mutex + - c.f., std::Mutex - can be held across await points (borrowing the mutex in the guard, guard is Send, scheduler-aware? or just because lock is async?), lock is async (will not block the thread waiting for lock to be available) + - even a clippy lint for holding the guard across await (https://rust-lang.github.io/rust-clippy/master/index.html#await_holding_lock) + - more expensive because it can be held across await + - use std::Mutex if you can + - can use try_lock or mutex is expected to not be under contention + - lock is not magically dropped when yield (that's kind of the point of a lock!) + - deadlock by holding mutex over await + - tasks deadlocked, but other tasks can make progress so might not look like a deadlock in process stats/tools/OS + - usual advice - limit scope, minimise locks, order locks, prefer alternatives + - no mutex poisoning + - lock_owned + - blocking_lock + - cannot use in async + - applies to other locks (should the above be moved before discussion of mutex specifically? Probably yes) +- RWLock +- Semaphore +- yielding + +## Other synchronization primitives + +- notify, barrier +- OnceCell +- atomics diff --git a/src/part-guide/timers-signals.md b/src/part-guide/timers-signals.md new file mode 100644 index 00000000..c638e0be --- /dev/null +++ b/src/part-guide/timers-signals.md @@ -0,0 +1,15 @@ +# Timers and Signal handling + +## Time and Timers + +- runtime integration, don't use thread::sleep, etc. +- std Instant and Duration +- sleep +- interval +- timeout + +## Signal handling + +- what is signal handling and why is it an async issue? +- very OS specific +- see Tokio docs diff --git a/src/part-guide/tools.md b/src/part-guide/tools.md new file mode 100644 index 00000000..304cfe15 --- /dev/null +++ b/src/part-guide/tools.md @@ -0,0 +1,28 @@ +# Tools for async programming + +- Why we need specialist tools for async +- Are there other tools to cover + - loom + +## Monitoring + +- [Tokio console](https://github.com/tokio-rs/console) + +## Tracing and logging + +- issues with async tracing +- tracing crate (https://github.com/tokio-rs/tracing) + +## Debugging + +- Understanding async backtraces (RUST_BACKTRACE and in a debugger) +- Techniques for debugging async code +- Using Tokio console for debugging +- Debugger support (WinDbg?) + +## Profiling + +- How async messes up flamegraphs +- How to profile async IO +- Getting insight into the runtime + - Tokio metrics