mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-05-15 15:16:51 +02:00
Update Concurrency course with times (#2007)
As I mentioned in #1536: * Break into segments at approximately the places @fw-immunant put breaks * Move all of the files into `src/concurrency` * Add timings and segment/session metadata so course outlines appear There's room for more work here, including some additional feedback from @fw-immunant after the session I observed, but let's do one step at a time :)
This commit is contained in:
parent
a03b7e68e5
commit
face5af783
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -322,16 +322,6 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "comprehensive-rust"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"reqwest",
|
||||
"scraper",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "control-flow-basics"
|
||||
version = "0.1.0"
|
||||
@ -2412,6 +2402,16 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync-exercises"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"reqwest",
|
||||
"scraper",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sync_wrapper"
|
||||
version = "0.1.2"
|
||||
|
@ -9,8 +9,8 @@ members = [
|
||||
"src/borrowing",
|
||||
"src/control-flow-basics",
|
||||
"src/error-handling",
|
||||
"src/exercises",
|
||||
"src/exercises/concurrency/chat-async",
|
||||
"src/concurrency/sync-exercises",
|
||||
"src/concurrency/async-exercises/chat-async",
|
||||
"src/generics",
|
||||
"src/iterators",
|
||||
"src/lifetimes",
|
||||
|
62
book.toml
62
book.toml
@ -76,13 +76,26 @@ use-boolean-and = true
|
||||
#
|
||||
# Please keep the table sorted and avoid multi-step redirects.
|
||||
[output.html.redirect]
|
||||
"tuples-and-arrays/tuples-and-arrays.html" = "tuples.html"
|
||||
"async/async-await.html" = "../concurrency/async/async-await.html"
|
||||
"async/channels.html" = "../concurrency/async-control-flow/channels.html"
|
||||
"async/concurrency/channels.html" = "../channels.html"
|
||||
"async/pitfall/async-traits.html" = "../pitfalls/async-traits.html"
|
||||
"basic-syntax.html" = "control-flow-basics.html"
|
||||
"async/control-flow/join.html" = "../concurrency/async-control-flow/join.html"
|
||||
"async/control-flow/select.html" = "../concurrency/async-control-flow/select.html"
|
||||
"async/futures.html" = "../concurrency/async/futures.html"
|
||||
"async.html" = "concurrency/welcome-async.html"
|
||||
"async/pitfall/async-traits.html" = "../async-pitfalls/async-traits.html"
|
||||
"async/pitfalls/async-traits.html" = "../concurrency/async-pitfalls/async-traits.html"
|
||||
"async/pitfalls/blocking-executor.html" = "../concurrency/async-pitfalls/blocking-executor.html"
|
||||
"async/pitfalls/cancellation.html" = "../concurrency/async-pitfalls/cancellation.html"
|
||||
"async/pitfalls.html" = "../concurrency/async-pitfalls.html"
|
||||
"async/pitfalls/pin.html" = "../concurrency/async-pitfalls/pin.html"
|
||||
"async/runtimes.html" = "../concurrency/async/runtimes.html"
|
||||
"async/runtimes/tokio.html" = "../concurrency/async/runtimes/tokio.html"
|
||||
"async/tasks.html" = "../concurrency/async/tasks.html"
|
||||
"basic-syntax/compound-types.html" = "../tuples-and-arrays/tuples-and-arrays.html"
|
||||
"basic-syntax/functions-interlude.html" = "../control-flow-basics/functions.html"
|
||||
"basic-syntax/functions.html" = "../control-flow-basics/functions.html"
|
||||
"basic-syntax/functions-interlude.html" = "../control-flow-basics/functions.html"
|
||||
"basic-syntax.html" = "control-flow-basics.html"
|
||||
"basic-syntax/methods.html" = "../control-flow-basics/functions.html"
|
||||
"basic-syntax/references-dangling.html" = "../references/shared.html"
|
||||
"basic-syntax/references.html" = "../references/shared.html"
|
||||
@ -94,11 +107,17 @@ use-boolean-and = true
|
||||
"basic-syntax/string-slices.html" = "../references/strings.html"
|
||||
"basic-syntax/type-inference.html" = "../types-and-values/inference.html"
|
||||
"basic-syntax/variables.html" = "../types-and-values/variables.html"
|
||||
"concurrency.html" = "concurrency/welcome.html"
|
||||
"concurrency/scoped-threads.html" = "threads/scoped.html"
|
||||
"concurrency/shared_state/arc.html" = "../shared-state/arc.html"
|
||||
"concurrency/shared_state/example.html" = "../shared-state/example.html"
|
||||
"concurrency/shared_state.html" = "shared-state.html"
|
||||
"concurrency/shared_state/mutex.html" = "../shared-state/mutex.html"
|
||||
"control-flow-basics/conditionals.html" = "if.html"
|
||||
"control-flow.html" = "control-flow-basics.html"
|
||||
"control-flow/blocks.html" = "../control-flow-basics/blocks-and-scopes.html"
|
||||
"control-flow/break-continue.html" = "../control-flow-basics/break-continue.html"
|
||||
"control-flow/for-expressions.html" = "../control-flow-basics/loops.html"
|
||||
"control-flow.html" = "control-flow-basics.html"
|
||||
"control-flow/if-expressions.html" = "../control-flow-basics/conditionals.html"
|
||||
"control-flow/if-let-expressions.html" = "../pattern-matching/let-control-flow.html"
|
||||
"control-flow/loop-expressions.html" = "../control-flow-basics/loops.html"
|
||||
@ -118,7 +137,15 @@ use-boolean-and = true
|
||||
"error-handling/panic-unwind.html" = "../error-handling/panics.html"
|
||||
"error-handling/result.html" = "../std-types/result.html"
|
||||
"error-handling/try-operator.html" = "../error-handling/try.html"
|
||||
"exercises/concurrency/afternoon.html" = "../../concurrency/async-exercises.html"
|
||||
"exercises/concurrency/chat-app.html" = "../../concurrency/async-exercises/chat-app.html"
|
||||
"exercises/concurrency/dining-philosophers-async.html" = "../../concurrency/async-exercises/dining-philosophers.html"
|
||||
"exercises/concurrency/dining-philosophers.html" = "../../concurrency/sync-exercises/dining-philosophers.html"
|
||||
"exercises/concurrency/elevator.html" = "chat-app.html"
|
||||
"exercises/concurrency/link-checker.html" = "../../concurrency/sync-exercises/link-checker.html"
|
||||
"exercises/concurrency/morning.html" = "../../concurrency/sync-exercises.html"
|
||||
"exercises/concurrency/solutions-afternoon.html" = "../../concurrency/async-exercises/solutions.html"
|
||||
"exercises/concurrency/solutions-morning.html" = "../../concurrency/sync-exercises/solutions.html"
|
||||
"exercises/day-1/afternoon.html" = "../../control-flow-basics/exercise.html"
|
||||
"exercises/day-1/book-library.html" = "../day-2/book-library.html"
|
||||
"exercises/day-1/for-loops.html" = "../../tuples-and-arrays/exercise.html"
|
||||
@ -161,22 +188,22 @@ use-boolean-and = true
|
||||
"memory-management/manual.html" = "../memory-management/approaches.html"
|
||||
"memory-management/rust.html" = "../memory-management/ownership.html"
|
||||
"memory-management/scope-based.html" = "../memory-management/approaches.html"
|
||||
"memory-management/stack-vs-heap.html" = "../memory-management/review.html"
|
||||
"memory-management/stack.html" = "../memory-management/review.html"
|
||||
"memory-management/stack-vs-heap.html" = "../memory-management/review.html"
|
||||
"methods-and-traits/trait-objects.html" = "../smart-pointers/trait-objects.html"
|
||||
"methods.html" = "methods-and-traits/methods.html"
|
||||
"methods/example.html" = "../methods-and-traits/methods.html"
|
||||
"methods.html" = "methods-and-traits/methods.html"
|
||||
"methods/receiver.html" = "../methods-and-traits/methods.html"
|
||||
"outros-recursos.html" = "other-resources.html"
|
||||
"ownership.html" = "memory-management/ownership.html"
|
||||
"ownership/borrowing.html" = "../borrowing/shared.html"
|
||||
"ownership/copy-clone.html" = "../memory-management/copy-types.html"
|
||||
"ownership/double-free-modern-cpp.html" = "../memory-management/move.html"
|
||||
"ownership.html" = "memory-management/ownership.html"
|
||||
"ownership/lifetimes-data-structures.html" = "../lifetimes/struct-lifetimes.html"
|
||||
"ownership/lifetimes-function-calls.html" = "../lifetimes/lifetime-elision.html"
|
||||
"ownership/lifetimes.html" = "../lifetimes/lifetime-annotations.html"
|
||||
"ownership/move-semantics.html" = "../memory-management/move.html"
|
||||
"ownership/moved-strings-rust.html" = "../memory-management/move.html"
|
||||
"ownership/move-semantics.html" = "../memory-management/move.html"
|
||||
"ownership/moves-function-calls.html" = "../memory-management/move.html"
|
||||
"ownership/shared-unique-borrows.html" = "../borrowing/shared.html"
|
||||
"pattern-matching/destructuring-arrays.html" = "../tuples-and-arrays/destructuring.html"
|
||||
@ -185,26 +212,26 @@ use-boolean-and = true
|
||||
"pattern-matching/match-guards.html" = "../tuples-and-arrays/match.html"
|
||||
"running-the-course/day-4.html" = "course-structure.html"
|
||||
"sintaxe-básica/funções-interlude.html" = "../basic-syntax/functions-interlude.html"
|
||||
"slices-and-lifetimes.html" = "lifetimes.html"
|
||||
"slices-and-lifetimes/exercise.html" = "../lifetimes/exercise.html"
|
||||
"slices-and-lifetimes.html" = "lifetimes.html"
|
||||
"slices-and-lifetimes/lifetime-annotations.html" = "../lifetimes/lifetime-annotations.html"
|
||||
"slices-and-lifetimes/lifetime-elision.html" = "../lifetimes/lifetime-elision.html"
|
||||
"slices-and-lifetimes/slices.html" = "../references/slices.html"
|
||||
"slices-and-lifetimes/solution.html" = "../lifetimes/solution.html"
|
||||
"slices-and-lifetimes/str.html" = "../references/strings.html"
|
||||
"slices-and-lifetimes/struct-lifetimes.html" = "../lifetimes/struct-lifetimes.html"
|
||||
"std.html" = "std-types/std.html"
|
||||
"std/box.html" = "../smart-pointers/box.html"
|
||||
"std/box-niche.html" = "../smart-pointers/box.html"
|
||||
"std/box-recursive.html" = "../smart-pointers/box.html"
|
||||
"std/box.html" = "../smart-pointers/box.html"
|
||||
"std/cell.html" = "../borrowing/interior-mutability.html"
|
||||
"std/hashmap.html" = "../std-types/hashmap.html"
|
||||
"std.html" = "std-types/std.html"
|
||||
"std/option-result.html" = "../std-types/option.html"
|
||||
"std/rc.html" = "../smart-pointers/rc.html"
|
||||
"std/string.html" = "../std-types/string.html"
|
||||
"std/vec.html" = "../std-types/vec.html"
|
||||
"structs.html" = "user-defined-types/named-structs.html"
|
||||
"structs/field-shorthand.html" = "../user-defined-types/named-structs.html"
|
||||
"structs.html" = "user-defined-types/named-structs.html"
|
||||
"structs/tuple-structs.html" = "../user-defined-types/tuple-structs.html"
|
||||
"structure.html" = "running-the-course/course-structure.html"
|
||||
"testing/doc-tests.html" = "../testing/other.html"
|
||||
@ -212,14 +239,14 @@ use-boolean-and = true
|
||||
"testing/integration-tests.html" = "../testing/other.html"
|
||||
"testing/mockall.html" = "../android/testing/mockall.html"
|
||||
"testing/useful-crates.html" = "../testing.html"
|
||||
"traits.html" = "methods-and-traits/traits.html"
|
||||
"traits/closures.html" = "../std-traits/closures.html"
|
||||
"traits/default-methods.html" = "../methods-and-traits/traits.html"
|
||||
"traits/default.html" = "../std-traits/default.html"
|
||||
"traits/default-methods.html" = "../methods-and-traits/traits.html"
|
||||
"traits/deriving-traits.html" = "../methods-and-traits/deriving.html"
|
||||
"traits/drop.html" = "../memory-management/drop.html"
|
||||
"traits/from-into.html" = "../std-traits/from-and-into.html"
|
||||
"traits/from-iterator.html" = "../iterators/fromiterator.html"
|
||||
"traits.html" = "methods-and-traits/traits.html"
|
||||
"traits/impl-trait.html" = "../generics/impl-trait.html"
|
||||
"traits/important-traits.html" = "../std-traits/comparisons.html"
|
||||
"traits/iterator.html" = "../iterators/iterators.html"
|
||||
@ -228,10 +255,11 @@ use-boolean-and = true
|
||||
"traits/trait-bounds.html" = "../generics/trait-bounds.html"
|
||||
"traits/trait-objects.html" = "../smart-pointers/trait-objects.html"
|
||||
"tuples-and-arrays/match.html" = "../pattern-matching/match.html"
|
||||
"tuples-and-arrays/tuples-and-arrays.html" = "tuples.html"
|
||||
"types-and-values/strings.html" = "../references/strings.html"
|
||||
"unsafe.html" = "unsafe-rust/unsafe.html"
|
||||
"unsafe/calling-unsafe-functions.html" = "../unsafe-rust/unsafe-functions.html"
|
||||
"unsafe/extern-functions.html" = "../unsafe-rust/unsafe-functions.html"
|
||||
"unsafe.html" = "unsafe-rust/unsafe.html"
|
||||
"unsafe/mutable-static-variables.html" = "../unsafe-rust/mutable-static.html"
|
||||
"unsafe/mutable-static-variables.md" = "mutable-static-variables.html"
|
||||
"unsafe/raw-pointers.html" = "../unsafe-rust/dereferencing.html"
|
||||
@ -243,9 +271,9 @@ use-boolean-and = true
|
||||
"welcome-bare-metal.html" = "bare-metal.html"
|
||||
"welcome-day-1/what-is-rust.html" = "../hello-world/what-is-rust.html"
|
||||
"welcome.html" = "./"
|
||||
"why-rust.html" = "hello-world/benefits.html"
|
||||
"why-rust/an-example-in-c.html" = "../hello-world/example.html"
|
||||
"why-rust/compile-time.html" = "../hello-world/benefits.html"
|
||||
"why-rust.html" = "hello-world/benefits.html"
|
||||
"why-rust/modern.html" = "../hello-world/benefits.html"
|
||||
"why-rust/runtime.html" = "../hello-world/benefits.html"
|
||||
|
||||
|
@ -35,7 +35,7 @@ pub fn replace(
|
||||
return;
|
||||
};
|
||||
chapter.content = DIRECTIVE
|
||||
.replace(&chapter.content, |captures: ®ex::Captures| {
|
||||
.replace_all(&chapter.content, |captures: ®ex::Captures| {
|
||||
let directive_str = captures[1].trim();
|
||||
let directive: Vec<_> = directive_str.split_whitespace().collect();
|
||||
match directive.as_slice() {
|
||||
@ -50,7 +50,7 @@ pub fn replace(
|
||||
}
|
||||
["course", "outline", course_name] => {
|
||||
let Some(course) = courses.find_course(course_name) else {
|
||||
return captures[0].to_string();
|
||||
return format!("not found - {}", captures[0].to_string());
|
||||
};
|
||||
course.schedule()
|
||||
}
|
||||
|
@ -353,46 +353,50 @@
|
||||
|
||||
---
|
||||
|
||||
- [Welcome](concurrency.md)
|
||||
- [Welcome](concurrency/welcome.md)
|
||||
- [Threads](concurrency/threads.md)
|
||||
- [Scoped Threads](concurrency/scoped-threads.md)
|
||||
- [Plain Threads](concurrency/threads/plain.md)
|
||||
- [Scoped Threads](concurrency/threads/scoped.md)
|
||||
- [Channels](concurrency/channels.md)
|
||||
- [Senders and Reveivers](concurrency/channels/senders-receivers.md)
|
||||
- [Unbounded Channels](concurrency/channels/unbounded.md)
|
||||
- [Bounded Channels](concurrency/channels/bounded.md)
|
||||
- [`Send` and `Sync`](concurrency/send-sync.md)
|
||||
- [Marker Traits](concurrency/send-sync/marker-traits.md)
|
||||
- [`Send`](concurrency/send-sync/send.md)
|
||||
- [`Sync`](concurrency/send-sync/sync.md)
|
||||
- [Examples](concurrency/send-sync/examples.md)
|
||||
- [Shared State](concurrency/shared_state.md)
|
||||
- [`Arc`](concurrency/shared_state/arc.md)
|
||||
- [`Mutex`](concurrency/shared_state/mutex.md)
|
||||
- [Example](concurrency/shared_state/example.md)
|
||||
- [Exercises](exercises/concurrency/morning.md)
|
||||
- [Dining Philosophers](exercises/concurrency/dining-philosophers.md)
|
||||
- [Multi-threaded Link Checker](exercises/concurrency/link-checker.md)
|
||||
- [Solutions](exercises/concurrency/solutions-morning.md)
|
||||
- [Shared State](concurrency/shared-state.md)
|
||||
- [`Arc`](concurrency/shared-state/arc.md)
|
||||
- [`Mutex`](concurrency/shared-state/mutex.md)
|
||||
- [Example](concurrency/shared-state/example.md)
|
||||
- [Exercises](concurrency/sync-exercises.md)
|
||||
- [Dining Philosophers](concurrency/sync-exercises/dining-philosophers.md)
|
||||
- [Multi-threaded Link Checker](concurrency/sync-exercises/link-checker.md)
|
||||
- [Solutions](concurrency/sync-exercises/solutions.md)
|
||||
|
||||
# Concurrency: Afternoon
|
||||
|
||||
- [Async Basics](async.md)
|
||||
- [`async`/`await`](async/async-await.md)
|
||||
- [Futures](async/futures.md)
|
||||
- [Runtimes](async/runtimes.md)
|
||||
- [Tokio](async/runtimes/tokio.md)
|
||||
- [Tasks](async/tasks.md)
|
||||
- [Async Channels](async/channels.md)
|
||||
- [Control Flow](async/control-flow.md)
|
||||
- [Join](async/control-flow/join.md)
|
||||
- [Select](async/control-flow/select.md)
|
||||
- [Pitfalls](async/pitfalls.md)
|
||||
- [Blocking the Executor](async/pitfalls/blocking-executor.md)
|
||||
- [`Pin`](async/pitfalls/pin.md)
|
||||
- [Async Traits](async/pitfalls/async-traits.md)
|
||||
- [Cancellation](async/pitfalls/cancellation.md)
|
||||
- [Exercises](exercises/concurrency/afternoon.md)
|
||||
- [Dining Philosophers](exercises/concurrency/dining-philosophers-async.md)
|
||||
- [Broadcast Chat Application](exercises/concurrency/chat-app.md)
|
||||
- [Solutions](exercises/concurrency/solutions-afternoon.md)
|
||||
- [Welcome](concurrency/welcome-async.md)
|
||||
- [Async Basics](concurrency/async.md)
|
||||
- [`async`/`await`](concurrency/async/async-await.md)
|
||||
- [Futures](concurrency/async/futures.md)
|
||||
- [Runtimes](concurrency/async/runtimes.md)
|
||||
- [Tokio](concurrency/async/runtimes/tokio.md)
|
||||
- [Tasks](concurrency/async/tasks.md)
|
||||
- [Channels and Control Flow](concurrency/async-control-flow.md)
|
||||
- [Async Channels](concurrency/async-control-flow/channels.md)
|
||||
- [Join](concurrency/async-control-flow/join.md)
|
||||
- [Select](concurrency/async-control-flow/select.md)
|
||||
- [Pitfalls](concurrency/async-pitfalls.md)
|
||||
- [Blocking the Executor](concurrency/async-pitfalls/blocking-executor.md)
|
||||
- [`Pin`](concurrency/async-pitfalls/pin.md)
|
||||
- [Async Traits](concurrency/async-pitfalls/async-traits.md)
|
||||
- [Cancellation](concurrency/async-pitfalls/cancellation.md)
|
||||
- [Exercises](concurrency/async-exercises.md)
|
||||
- [Dining Philosophers](concurrency/async-exercises/dining-philosophers.md)
|
||||
- [Broadcast Chat Application](concurrency/async-exercises/chat-app.md)
|
||||
- [Solutions](concurrency/async-exercises/solutions.md)
|
||||
|
||||
# Final Words
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
# Futures Control Flow
|
||||
|
||||
Futures can be combined together to produce concurrent compute flow graphs. We
|
||||
have already seen tasks, that function as independent threads of execution.
|
||||
|
||||
- [Join](control-flow/join.md)
|
||||
- [Select](control-flow/select.md)
|
3
src/concurrency/async-control-flow.md
Normal file
3
src/concurrency/async-control-flow.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Channels and Control Flow
|
||||
|
||||
{{%segment outline}}
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 8
|
||||
---
|
||||
|
||||
# Async Channels
|
||||
|
||||
Several crates have support for asynchronous channels. For instance `tokio`:
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 4
|
||||
---
|
||||
|
||||
# Join
|
||||
|
||||
A join operation waits until all of a set of futures are ready, and returns a
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# Select
|
||||
|
||||
A select operation waits until any of a set of futures is ready, and responds to
|
3
src/concurrency/async-exercises.md
Normal file
3
src/concurrency/async-exercises.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Exercises
|
||||
|
||||
{{%segment outline}}
|
1
src/concurrency/async-exercises/afternoon.md
Normal file
1
src/concurrency/async-exercises/afternoon.md
Normal file
@ -0,0 +1 @@
|
||||
# Exercises
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 30
|
||||
---
|
||||
|
||||
# Broadcast Chat Application
|
||||
|
||||
In this exercise, we want to use our new knowledge to implement a broadcast chat
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Dining Philosophers --- Async
|
||||
|
||||
See [dining philosophers](dining-philosophers.md) for a description of the
|
||||
@ -11,17 +15,17 @@ code below to a file called `src/main.rs`, fill out the blanks, and test that
|
||||
<!-- File src/main.rs -->
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include dining-philosophers-async.rs:Philosopher}}
|
||||
{{#include dining-philosophers.rs:Philosopher}}
|
||||
// left_fork: ...
|
||||
// right_fork: ...
|
||||
// thoughts: ...
|
||||
}
|
||||
|
||||
{{#include dining-philosophers-async.rs:Philosopher-think}}
|
||||
{{#include dining-philosophers.rs:Philosopher-think}}
|
||||
|
||||
{{#include dining-philosophers-async.rs:Philosopher-eat}}
|
||||
{{#include dining-philosophers-async.rs:Philosopher-eat-body}}
|
||||
{{#include dining-philosophers-async.rs:Philosopher-eat-end}}
|
||||
{{#include dining-philosophers.rs:Philosopher-eat}}
|
||||
{{#include dining-philosophers.rs:Philosopher-eat-body}}
|
||||
{{#include dining-philosophers.rs:Philosopher-eat-end}}
|
||||
// Create forks
|
||||
|
||||
// Create philosophers
|
@ -1,17 +1,17 @@
|
||||
# Concurrency Afternoon Exercise
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Solutions
|
||||
|
||||
## Dining Philosophers --- Async
|
||||
|
||||
([back to exercise](dining-philosophers-async.md))
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include dining-philosophers-async.rs:solution}}
|
||||
{{#include dining-philosophers.rs:solution}}
|
||||
```
|
||||
|
||||
## Broadcast Chat Application
|
||||
|
||||
([back to exercise](chat-app.md))
|
||||
|
||||
_src/bin/server.rs_:
|
||||
|
||||
```rust,compile_fail
|
@ -1,10 +1,7 @@
|
||||
# Pitfalls of async/await
|
||||
# Pitfalls
|
||||
|
||||
Async / await provides convenient and efficient abstraction for concurrent
|
||||
asynchronous programming. However, the async/await model in Rust also comes with
|
||||
its share of pitfalls and footguns. We illustrate some of them in this chapter:
|
||||
its share of pitfalls and footguns. We illustrate some of them in this chapter.
|
||||
|
||||
- [Blocking the Executor](pitfalls/blocking-executor.md)
|
||||
- [Pin](pitfalls/pin.md)
|
||||
- [Async Traits](pitfalls/async-traits.md)
|
||||
- [Cancellation](pitfalls/cancellation.md)
|
||||
{{%segment outline}}
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# Async Traits
|
||||
|
||||
Async methods in traits are were stabilized only recently, in the 1.75 release.
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 10
|
||||
---
|
||||
|
||||
# Blocking the executor
|
||||
|
||||
Most async runtimes only allow IO tasks to run concurrently. This means that CPU
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 18
|
||||
---
|
||||
|
||||
# Cancellation
|
||||
|
||||
Dropping a future implies it can never be polled again. This is called
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# `Pin`
|
||||
|
||||
Async blocks and functions return types implementing the `Future` trait. The
|
3
src/concurrency/async.md
Normal file
3
src/concurrency/async.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Async Basics
|
||||
|
||||
{{%segment outline}}
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 6
|
||||
---
|
||||
|
||||
# `async`/`await`
|
||||
|
||||
At a high level, async Rust code looks very much like "normal" sequential code:
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 4
|
||||
---
|
||||
|
||||
# Futures
|
||||
|
||||
[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) is a trait,
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 10
|
||||
---
|
||||
|
||||
# Runtimes
|
||||
|
||||
A _runtime_ provides support for performing operations asynchronously (a
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 6
|
||||
---
|
||||
|
||||
# Tasks
|
||||
|
||||
Rust has a task system, which is a form of lightweight threading.
|
@ -1,32 +1,3 @@
|
||||
# Channels
|
||||
|
||||
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts
|
||||
are connected via the channel, but you only see the end-points.
|
||||
|
||||
```rust,editable
|
||||
use std::sync::mpsc;
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tx.send(10).unwrap();
|
||||
tx.send(20).unwrap();
|
||||
|
||||
println!("Received: {:?}", rx.recv());
|
||||
println!("Received: {:?}", rx.recv());
|
||||
|
||||
let tx2 = tx.clone();
|
||||
tx2.send(30).unwrap();
|
||||
println!("Received: {:?}", rx.recv());
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- `mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender`
|
||||
implement `Clone` (so you can make multiple producers) but `Receiver` does
|
||||
not.
|
||||
- `send()` and `recv()` return `Result`. If they return `Err`, it means the
|
||||
counterpart `Sender` or `Receiver` is dropped and the channel is closed.
|
||||
|
||||
</details>
|
||||
{{%segment outline}}
|
||||
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 8
|
||||
---
|
||||
|
||||
# Bounded Channels
|
||||
|
||||
With bounded (synchronous) channels, `send` can block the current thread:
|
||||
|
36
src/concurrency/channels/senders-receivers.md
Normal file
36
src/concurrency/channels/senders-receivers.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
minutes: 9
|
||||
---
|
||||
|
||||
# Senders and Receivers
|
||||
|
||||
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts
|
||||
are connected via the channel, but you only see the end-points.
|
||||
|
||||
```rust,editable
|
||||
use std::sync::mpsc;
|
||||
|
||||
fn main() {
|
||||
let (tx, rx) = mpsc::channel();
|
||||
|
||||
tx.send(10).unwrap();
|
||||
tx.send(20).unwrap();
|
||||
|
||||
println!("Received: {:?}", rx.recv());
|
||||
println!("Received: {:?}", rx.recv());
|
||||
|
||||
let tx2 = tx.clone();
|
||||
tx2.send(30).unwrap();
|
||||
println!("Received: {:?}", rx.recv());
|
||||
}
|
||||
```
|
||||
|
||||
<details>
|
||||
|
||||
- `mpsc` stands for Multi-Producer, Single-Consumer. `Sender` and `SyncSender`
|
||||
implement `Clone` (so you can make multiple producers) but `Receiver` does
|
||||
not.
|
||||
- `send()` and `recv()` return `Result`. If they return `Err`, it means the
|
||||
counterpart `Sender` or `Receiver` is dropped and the channel is closed.
|
||||
|
||||
</details>
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# Unbounded Channels
|
||||
|
||||
You get an unbounded and asynchronous channel with `mpsc::channel()`:
|
||||
|
@ -1,25 +1,3 @@
|
||||
# `Send` and `Sync`
|
||||
|
||||
How does Rust know to forbid shared access across threads? The answer is in two
|
||||
traits:
|
||||
|
||||
- [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread
|
||||
boundary.
|
||||
- [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread
|
||||
boundary.
|
||||
|
||||
`Send` and `Sync` are [unsafe traits][3]. The compiler will automatically derive
|
||||
them for your types as long as they only contain `Send` and `Sync` types. You
|
||||
can also implement them manually when you know it is valid.
|
||||
|
||||
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[2]: https://doc.rust-lang.org/std/marker/trait.Sync.html
|
||||
[3]: ../unsafe/unsafe-traits.md
|
||||
|
||||
<details>
|
||||
|
||||
- One can think of these traits as markers that the type has certain
|
||||
thread-safety properties.
|
||||
- They can be used in the generic constraints as normal traits.
|
||||
|
||||
</details>
|
||||
{{%segment outline}}
|
||||
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 6
|
||||
---
|
||||
|
||||
# Examples
|
||||
|
||||
## `Send + Sync`
|
||||
|
29
src/concurrency/send-sync/marker-traits.md
Normal file
29
src/concurrency/send-sync/marker-traits.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# Marker Traits
|
||||
|
||||
How does Rust know to forbid shared access across threads? The answer is in two
|
||||
traits:
|
||||
|
||||
- [`Send`][1]: a type `T` is `Send` if it is safe to move a `T` across a thread
|
||||
boundary.
|
||||
- [`Sync`][2]: a type `T` is `Sync` if it is safe to move a `&T` across a thread
|
||||
boundary.
|
||||
|
||||
`Send` and `Sync` are [unsafe traits][3]. The compiler will automatically derive
|
||||
them for your types as long as they only contain `Send` and `Sync` types. You
|
||||
can also implement them manually when you know it is valid.
|
||||
|
||||
[1]: https://doc.rust-lang.org/std/marker/trait.Send.html
|
||||
[2]: https://doc.rust-lang.org/std/marker/trait.Sync.html
|
||||
[3]: ../unsafe/unsafe-traits.md
|
||||
|
||||
<details>
|
||||
|
||||
- One can think of these traits as markers that the type has certain
|
||||
thread-safety properties.
|
||||
- They can be used in the generic constraints as normal traits.
|
||||
|
||||
</details>
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# `Send`
|
||||
|
||||
> A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread.
|
||||
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 2
|
||||
---
|
||||
|
||||
# `Sync`
|
||||
|
||||
> A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple
|
||||
|
3
src/concurrency/shared-state.md
Normal file
3
src/concurrency/shared-state.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Shared State
|
||||
|
||||
{{%segment outline}}
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 5
|
||||
---
|
||||
|
||||
# `Arc`
|
||||
|
||||
[`Arc<T>`][1] allows shared read-only access via `Arc::clone`:
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 8
|
||||
---
|
||||
|
||||
# Example
|
||||
|
||||
Let us see `Arc` and `Mutex` in action:
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 14
|
||||
---
|
||||
|
||||
# `Mutex`
|
||||
|
||||
[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`
|
@ -1,11 +0,0 @@
|
||||
# Shared State
|
||||
|
||||
Rust uses the type system to enforce synchronization of shared data. This is
|
||||
primarily done via two types:
|
||||
|
||||
- [`Arc<T>`][1], atomic reference counted `T`: handles sharing between threads
|
||||
and takes care to deallocate `T` when the last reference is dropped,
|
||||
- [`Mutex<T>`][2]: ensures mutually exclusive access to the `T` value.
|
||||
|
||||
[1]: https://doc.rust-lang.org/std/sync/struct.Arc.html
|
||||
[2]: https://doc.rust-lang.org/std/sync/struct.Mutex.html
|
3
src/concurrency/sync-exercises.md
Normal file
3
src/concurrency/sync-exercises.md
Normal file
@ -0,0 +1,3 @@
|
||||
# Exercises
|
||||
|
||||
{{%segment outline}}
|
@ -1,16 +1,16 @@
|
||||
[package]
|
||||
name = "comprehensive-rust"
|
||||
name = "sync-exercises"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "dining-philosophers"
|
||||
path = "concurrency/dining-philosophers.rs"
|
||||
path = "dining-philosophers.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "link-checker"
|
||||
path = "concurrency/link-checker.rs"
|
||||
path = "link-checker.rs"
|
||||
|
||||
[dependencies]
|
||||
reqwest = { version = "0.12.4", features = ["blocking"] }
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Dining Philosophers
|
||||
|
||||
The dining philosophers problem is a classic problem in concurrency:
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 20
|
||||
---
|
||||
|
||||
# Multi-threaded Link Checker
|
||||
|
||||
Let us use our new knowledge to create a multi-threaded link checker. It should
|
@ -1,17 +1,17 @@
|
||||
# Concurrency Morning Exercise
|
||||
---
|
||||
minutes: 30
|
||||
---
|
||||
|
||||
# Solutions
|
||||
|
||||
## Dining Philosophers
|
||||
|
||||
([back to exercise](dining-philosophers.md))
|
||||
|
||||
```rust
|
||||
{{#include dining-philosophers.rs:solution}}
|
||||
```
|
||||
|
||||
## Link Checker
|
||||
|
||||
([back to exercise](link-checker.md))
|
||||
|
||||
```rust,compile_fail
|
||||
{{#include link-checker.rs:solution}}
|
||||
```
|
@ -1,74 +1,3 @@
|
||||
# Threads
|
||||
|
||||
Rust threads work similarly to threads in other languages:
|
||||
|
||||
```rust,editable
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
for i in 1..10 {
|
||||
println!("Count in thread: {i}!");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
});
|
||||
|
||||
for i in 1..5 {
|
||||
println!("Main thread: {i}");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Threads are all daemon threads, the main thread does not wait for them.
|
||||
- Thread panics are independent of each other.
|
||||
- Panics can carry a payload, which can be unpacked with `downcast_ref`.
|
||||
|
||||
<details>
|
||||
|
||||
- Rust thread APIs look not too different from e.g. C++ ones.
|
||||
|
||||
- Run the example.
|
||||
- 5ms timing is loose enough that main and spawned threads stay mostly in
|
||||
lockstep.
|
||||
- Notice that the program ends before the spawned thread reaches 10!
|
||||
- This is because main ends the program and spawned threads do not make it
|
||||
persist.
|
||||
- Compare to pthreads/C++ std::thread/boost::thread if desired.
|
||||
|
||||
- How do we wait around for the spawned thread to complete?
|
||||
- [`thread::spawn`] returns a `JoinHandle`. Look at the docs.
|
||||
- `JoinHandle` has a [`.join()`] method that blocks.
|
||||
|
||||
- Use `let handle = thread::spawn(...)` and later `handle.join()` to wait for
|
||||
the thread to finish and have the program count all the way to 10.
|
||||
|
||||
- Now what if we want to return a value?
|
||||
- Look at docs again:
|
||||
- [`thread::spawn`]'s closure returns `T`
|
||||
- `JoinHandle` [`.join()`] returns `thread::Result<T>`
|
||||
|
||||
- Use the `Result` return value from `handle.join()` to get access to the
|
||||
returned value.
|
||||
|
||||
- Ok, what about the other case?
|
||||
- Trigger a panic in the thread. Note that this doesn't panic `main`.
|
||||
- Access the panic payload. This is a good time to talk about [`Any`].
|
||||
|
||||
- Now we can return values from threads! What about taking inputs?
|
||||
- Capture something by reference in the thread closure.
|
||||
- An error message indicates we must move it.
|
||||
- Move it in, see we can compute and then return a derived value.
|
||||
|
||||
- If we want to borrow?
|
||||
- Main kills child threads when it returns, but another function would just
|
||||
return and leave them running.
|
||||
- That would be stack use-after-return, which violates memory safety!
|
||||
- How do we avoid this? see next slide.
|
||||
|
||||
[`Any`]: https://doc.rust-lang.org/std/any/index.html
|
||||
[`thread::spawn`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
|
||||
[`.join()`]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join
|
||||
|
||||
</details>
|
||||
{{%segment outline}}
|
||||
|
78
src/concurrency/threads/plain.md
Normal file
78
src/concurrency/threads/plain.md
Normal file
@ -0,0 +1,78 @@
|
||||
---
|
||||
minutes: 15
|
||||
---
|
||||
|
||||
# Plain Threads
|
||||
|
||||
Rust threads work similarly to threads in other languages:
|
||||
|
||||
```rust,editable
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
fn main() {
|
||||
thread::spawn(|| {
|
||||
for i in 1..10 {
|
||||
println!("Count in thread: {i}!");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
});
|
||||
|
||||
for i in 1..5 {
|
||||
println!("Main thread: {i}");
|
||||
thread::sleep(Duration::from_millis(5));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Threads are all daemon threads, the main thread does not wait for them.
|
||||
- Thread panics are independent of each other.
|
||||
- Panics can carry a payload, which can be unpacked with `downcast_ref`.
|
||||
|
||||
<details>
|
||||
|
||||
- Rust thread APIs look not too different from e.g. C++ ones.
|
||||
|
||||
- Run the example.
|
||||
- 5ms timing is loose enough that main and spawned threads stay mostly in
|
||||
lockstep.
|
||||
- Notice that the program ends before the spawned thread reaches 10!
|
||||
- This is because main ends the program and spawned threads do not make it
|
||||
persist.
|
||||
- Compare to pthreads/C++ std::thread/boost::thread if desired.
|
||||
|
||||
- How do we wait around for the spawned thread to complete?
|
||||
- [`thread::spawn`] returns a `JoinHandle`. Look at the docs.
|
||||
- `JoinHandle` has a [`.join()`] method that blocks.
|
||||
|
||||
- Use `let handle = thread::spawn(...)` and later `handle.join()` to wait for
|
||||
the thread to finish and have the program count all the way to 10.
|
||||
|
||||
- Now what if we want to return a value?
|
||||
- Look at docs again:
|
||||
- [`thread::spawn`]'s closure returns `T`
|
||||
- `JoinHandle` [`.join()`] returns `thread::Result<T>`
|
||||
|
||||
- Use the `Result` return value from `handle.join()` to get access to the
|
||||
returned value.
|
||||
|
||||
- Ok, what about the other case?
|
||||
- Trigger a panic in the thread. Note that this doesn't panic `main`.
|
||||
- Access the panic payload. This is a good time to talk about [`Any`].
|
||||
|
||||
- Now we can return values from threads! What about taking inputs?
|
||||
- Capture something by reference in the thread closure.
|
||||
- An error message indicates we must move it.
|
||||
- Move it in, see we can compute and then return a derived value.
|
||||
|
||||
- If we want to borrow?
|
||||
- Main kills child threads when it returns, but another function would just
|
||||
return and leave them running.
|
||||
- That would be stack use-after-return, which violates memory safety!
|
||||
- How do we avoid this? see next slide.
|
||||
|
||||
[`Any`]: https://doc.rust-lang.org/std/any/index.html
|
||||
[`thread::spawn`]: https://doc.rust-lang.org/std/thread/fn.spawn.html
|
||||
[`.join()`]: https://doc.rust-lang.org/std/thread/struct.JoinHandle.html#method.join
|
||||
|
||||
</details>
|
@ -1,3 +1,7 @@
|
||||
---
|
||||
minutes: 13
|
||||
---
|
||||
|
||||
# Scoped Threads
|
||||
|
||||
Normal threads cannot borrow from their environment:
|
@ -1,8 +1,9 @@
|
||||
---
|
||||
session: Afternoon
|
||||
target_minutes: 180
|
||||
---
|
||||
|
||||
# Async Rust
|
||||
# Welcome
|
||||
|
||||
"Async" is a concurrency model where multiple tasks are executed concurrently by
|
||||
executing each task until it would block, then switching to another task that is
|
||||
@ -27,3 +28,7 @@ available.
|
||||
- JavaScript's `Promise` is similar, but again callback-based. The language
|
||||
runtime implements the event loop, so many of the details of Promise
|
||||
resolution are hidden.
|
||||
|
||||
## Schedule
|
||||
|
||||
{{%session outline}}
|
@ -1,6 +1,7 @@
|
||||
---
|
||||
course: Concurrency
|
||||
session: Morning
|
||||
target_minutes: 180
|
||||
---
|
||||
|
||||
# Welcome to Concurrency in Rust
|
||||
@ -12,6 +13,10 @@ The Rust type system plays an important role in making many concurrency bugs
|
||||
compile time bugs. This is often referred to as _fearless concurrency_ since you
|
||||
can rely on the compiler to ensure correctness at runtime.
|
||||
|
||||
## Schedule
|
||||
|
||||
{{%session outline}}
|
||||
|
||||
<details>
|
||||
|
||||
- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc.
|
@ -1,17 +0,0 @@
|
||||
# Exercises
|
||||
|
||||
To practice your Async Rust skills, we have again two exercises for you:
|
||||
|
||||
- Dining philosophers: we already saw this problem in the morning. This time you
|
||||
are going to implement it with Async Rust.
|
||||
|
||||
- A Broadcast Chat Application: this is a larger project that allows you
|
||||
experiment with more advanced Async Rust features.
|
||||
|
||||
<details>
|
||||
|
||||
After looking at the exercises, you can look at the [solutions] provided.
|
||||
|
||||
[solutions]: solutions-afternoon.md
|
||||
|
||||
</details>
|
@ -70,6 +70,8 @@ cargo add tokio --features full
|
||||
cargo run
|
||||
```
|
||||
|
||||
{{%course outline Concurrency}}
|
||||
|
||||
## Format
|
||||
|
||||
The course is meant to be very interactive and we recommend letting the
|
||||
|
Loading…
x
Reference in New Issue
Block a user