1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-15 23:26:48 +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:
Dustin J. Mitchell 2024-04-23 09:26:41 -04:00 committed by GitHub
parent a03b7e68e5
commit face5af783
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
58 changed files with 385 additions and 246 deletions

20
Cargo.lock generated
View File

@ -322,16 +322,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "comprehensive-rust"
version = "0.1.0"
dependencies = [
"reqwest",
"scraper",
"tempfile",
"thiserror",
]
[[package]] [[package]]
name = "control-flow-basics" name = "control-flow-basics"
version = "0.1.0" version = "0.1.0"
@ -2412,6 +2402,16 @@ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]]
name = "sync-exercises"
version = "0.1.0"
dependencies = [
"reqwest",
"scraper",
"tempfile",
"thiserror",
]
[[package]] [[package]]
name = "sync_wrapper" name = "sync_wrapper"
version = "0.1.2" version = "0.1.2"

View File

@ -9,8 +9,8 @@ members = [
"src/borrowing", "src/borrowing",
"src/control-flow-basics", "src/control-flow-basics",
"src/error-handling", "src/error-handling",
"src/exercises", "src/concurrency/sync-exercises",
"src/exercises/concurrency/chat-async", "src/concurrency/async-exercises/chat-async",
"src/generics", "src/generics",
"src/iterators", "src/iterators",
"src/lifetimes", "src/lifetimes",

View File

@ -76,13 +76,26 @@ use-boolean-and = true
# #
# Please keep the table sorted and avoid multi-step redirects. # Please keep the table sorted and avoid multi-step redirects.
[output.html.redirect] [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/concurrency/channels.html" = "../channels.html"
"async/pitfall/async-traits.html" = "../pitfalls/async-traits.html" "async/control-flow/join.html" = "../concurrency/async-control-flow/join.html"
"basic-syntax.html" = "control-flow-basics.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/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.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/methods.html" = "../control-flow-basics/functions.html"
"basic-syntax/references-dangling.html" = "../references/shared.html" "basic-syntax/references-dangling.html" = "../references/shared.html"
"basic-syntax/references.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/string-slices.html" = "../references/strings.html"
"basic-syntax/type-inference.html" = "../types-and-values/inference.html" "basic-syntax/type-inference.html" = "../types-and-values/inference.html"
"basic-syntax/variables.html" = "../types-and-values/variables.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-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/blocks.html" = "../control-flow-basics/blocks-and-scopes.html"
"control-flow/break-continue.html" = "../control-flow-basics/break-continue.html" "control-flow/break-continue.html" = "../control-flow-basics/break-continue.html"
"control-flow/for-expressions.html" = "../control-flow-basics/loops.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-expressions.html" = "../control-flow-basics/conditionals.html"
"control-flow/if-let-expressions.html" = "../pattern-matching/let-control-flow.html" "control-flow/if-let-expressions.html" = "../pattern-matching/let-control-flow.html"
"control-flow/loop-expressions.html" = "../control-flow-basics/loops.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/panic-unwind.html" = "../error-handling/panics.html"
"error-handling/result.html" = "../std-types/result.html" "error-handling/result.html" = "../std-types/result.html"
"error-handling/try-operator.html" = "../error-handling/try.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/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/afternoon.html" = "../../control-flow-basics/exercise.html"
"exercises/day-1/book-library.html" = "../day-2/book-library.html" "exercises/day-1/book-library.html" = "../day-2/book-library.html"
"exercises/day-1/for-loops.html" = "../../tuples-and-arrays/exercise.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/manual.html" = "../memory-management/approaches.html"
"memory-management/rust.html" = "../memory-management/ownership.html" "memory-management/rust.html" = "../memory-management/ownership.html"
"memory-management/scope-based.html" = "../memory-management/approaches.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.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-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/example.html" = "../methods-and-traits/methods.html"
"methods.html" = "methods-and-traits/methods.html"
"methods/receiver.html" = "../methods-and-traits/methods.html" "methods/receiver.html" = "../methods-and-traits/methods.html"
"outros-recursos.html" = "other-resources.html" "outros-recursos.html" = "other-resources.html"
"ownership.html" = "memory-management/ownership.html"
"ownership/borrowing.html" = "../borrowing/shared.html" "ownership/borrowing.html" = "../borrowing/shared.html"
"ownership/copy-clone.html" = "../memory-management/copy-types.html" "ownership/copy-clone.html" = "../memory-management/copy-types.html"
"ownership/double-free-modern-cpp.html" = "../memory-management/move.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-data-structures.html" = "../lifetimes/struct-lifetimes.html"
"ownership/lifetimes-function-calls.html" = "../lifetimes/lifetime-elision.html" "ownership/lifetimes-function-calls.html" = "../lifetimes/lifetime-elision.html"
"ownership/lifetimes.html" = "../lifetimes/lifetime-annotations.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/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/moves-function-calls.html" = "../memory-management/move.html"
"ownership/shared-unique-borrows.html" = "../borrowing/shared.html" "ownership/shared-unique-borrows.html" = "../borrowing/shared.html"
"pattern-matching/destructuring-arrays.html" = "../tuples-and-arrays/destructuring.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" "pattern-matching/match-guards.html" = "../tuples-and-arrays/match.html"
"running-the-course/day-4.html" = "course-structure.html" "running-the-course/day-4.html" = "course-structure.html"
"sintaxe-básica/funções-interlude.html" = "../basic-syntax/functions-interlude.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/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-annotations.html" = "../lifetimes/lifetime-annotations.html"
"slices-and-lifetimes/lifetime-elision.html" = "../lifetimes/lifetime-elision.html" "slices-and-lifetimes/lifetime-elision.html" = "../lifetimes/lifetime-elision.html"
"slices-and-lifetimes/slices.html" = "../references/slices.html" "slices-and-lifetimes/slices.html" = "../references/slices.html"
"slices-and-lifetimes/solution.html" = "../lifetimes/solution.html" "slices-and-lifetimes/solution.html" = "../lifetimes/solution.html"
"slices-and-lifetimes/str.html" = "../references/strings.html" "slices-and-lifetimes/str.html" = "../references/strings.html"
"slices-and-lifetimes/struct-lifetimes.html" = "../lifetimes/struct-lifetimes.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-niche.html" = "../smart-pointers/box.html"
"std/box-recursive.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/cell.html" = "../borrowing/interior-mutability.html"
"std/hashmap.html" = "../std-types/hashmap.html" "std/hashmap.html" = "../std-types/hashmap.html"
"std.html" = "std-types/std.html"
"std/option-result.html" = "../std-types/option.html" "std/option-result.html" = "../std-types/option.html"
"std/rc.html" = "../smart-pointers/rc.html" "std/rc.html" = "../smart-pointers/rc.html"
"std/string.html" = "../std-types/string.html" "std/string.html" = "../std-types/string.html"
"std/vec.html" = "../std-types/vec.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/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" "structs/tuple-structs.html" = "../user-defined-types/tuple-structs.html"
"structure.html" = "running-the-course/course-structure.html" "structure.html" = "running-the-course/course-structure.html"
"testing/doc-tests.html" = "../testing/other.html" "testing/doc-tests.html" = "../testing/other.html"
@ -212,14 +239,14 @@ use-boolean-and = true
"testing/integration-tests.html" = "../testing/other.html" "testing/integration-tests.html" = "../testing/other.html"
"testing/mockall.html" = "../android/testing/mockall.html" "testing/mockall.html" = "../android/testing/mockall.html"
"testing/useful-crates.html" = "../testing.html" "testing/useful-crates.html" = "../testing.html"
"traits.html" = "methods-and-traits/traits.html"
"traits/closures.html" = "../std-traits/closures.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.html" = "../std-traits/default.html"
"traits/default-methods.html" = "../methods-and-traits/traits.html"
"traits/deriving-traits.html" = "../methods-and-traits/deriving.html" "traits/deriving-traits.html" = "../methods-and-traits/deriving.html"
"traits/drop.html" = "../memory-management/drop.html" "traits/drop.html" = "../memory-management/drop.html"
"traits/from-into.html" = "../std-traits/from-and-into.html" "traits/from-into.html" = "../std-traits/from-and-into.html"
"traits/from-iterator.html" = "../iterators/fromiterator.html" "traits/from-iterator.html" = "../iterators/fromiterator.html"
"traits.html" = "methods-and-traits/traits.html"
"traits/impl-trait.html" = "../generics/impl-trait.html" "traits/impl-trait.html" = "../generics/impl-trait.html"
"traits/important-traits.html" = "../std-traits/comparisons.html" "traits/important-traits.html" = "../std-traits/comparisons.html"
"traits/iterator.html" = "../iterators/iterators.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-bounds.html" = "../generics/trait-bounds.html"
"traits/trait-objects.html" = "../smart-pointers/trait-objects.html" "traits/trait-objects.html" = "../smart-pointers/trait-objects.html"
"tuples-and-arrays/match.html" = "../pattern-matching/match.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" "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/calling-unsafe-functions.html" = "../unsafe-rust/unsafe-functions.html"
"unsafe/extern-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.html" = "../unsafe-rust/mutable-static.html"
"unsafe/mutable-static-variables.md" = "mutable-static-variables.html" "unsafe/mutable-static-variables.md" = "mutable-static-variables.html"
"unsafe/raw-pointers.html" = "../unsafe-rust/dereferencing.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-bare-metal.html" = "bare-metal.html"
"welcome-day-1/what-is-rust.html" = "../hello-world/what-is-rust.html" "welcome-day-1/what-is-rust.html" = "../hello-world/what-is-rust.html"
"welcome.html" = "./" "welcome.html" = "./"
"why-rust.html" = "hello-world/benefits.html"
"why-rust/an-example-in-c.html" = "../hello-world/example.html" "why-rust/an-example-in-c.html" = "../hello-world/example.html"
"why-rust/compile-time.html" = "../hello-world/benefits.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/modern.html" = "../hello-world/benefits.html"
"why-rust/runtime.html" = "../hello-world/benefits.html" "why-rust/runtime.html" = "../hello-world/benefits.html"

View File

@ -35,7 +35,7 @@ pub fn replace(
return; return;
}; };
chapter.content = DIRECTIVE chapter.content = DIRECTIVE
.replace(&chapter.content, |captures: &regex::Captures| { .replace_all(&chapter.content, |captures: &regex::Captures| {
let directive_str = captures[1].trim(); let directive_str = captures[1].trim();
let directive: Vec<_> = directive_str.split_whitespace().collect(); let directive: Vec<_> = directive_str.split_whitespace().collect();
match directive.as_slice() { match directive.as_slice() {
@ -50,7 +50,7 @@ pub fn replace(
} }
["course", "outline", course_name] => { ["course", "outline", course_name] => {
let Some(course) = courses.find_course(course_name) else { let Some(course) = courses.find_course(course_name) else {
return captures[0].to_string(); return format!("not found - {}", captures[0].to_string());
}; };
course.schedule() course.schedule()
} }

View File

@ -353,46 +353,50 @@
--- ---
- [Welcome](concurrency.md) - [Welcome](concurrency/welcome.md)
- [Threads](concurrency/threads.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) - [Channels](concurrency/channels.md)
- [Senders and Reveivers](concurrency/channels/senders-receivers.md)
- [Unbounded Channels](concurrency/channels/unbounded.md) - [Unbounded Channels](concurrency/channels/unbounded.md)
- [Bounded Channels](concurrency/channels/bounded.md) - [Bounded Channels](concurrency/channels/bounded.md)
- [`Send` and `Sync`](concurrency/send-sync.md) - [`Send` and `Sync`](concurrency/send-sync.md)
- [Marker Traits](concurrency/send-sync/marker-traits.md)
- [`Send`](concurrency/send-sync/send.md) - [`Send`](concurrency/send-sync/send.md)
- [`Sync`](concurrency/send-sync/sync.md) - [`Sync`](concurrency/send-sync/sync.md)
- [Examples](concurrency/send-sync/examples.md) - [Examples](concurrency/send-sync/examples.md)
- [Shared State](concurrency/shared_state.md) - [Shared State](concurrency/shared-state.md)
- [`Arc`](concurrency/shared_state/arc.md) - [`Arc`](concurrency/shared-state/arc.md)
- [`Mutex`](concurrency/shared_state/mutex.md) - [`Mutex`](concurrency/shared-state/mutex.md)
- [Example](concurrency/shared_state/example.md) - [Example](concurrency/shared-state/example.md)
- [Exercises](exercises/concurrency/morning.md) - [Exercises](concurrency/sync-exercises.md)
- [Dining Philosophers](exercises/concurrency/dining-philosophers.md) - [Dining Philosophers](concurrency/sync-exercises/dining-philosophers.md)
- [Multi-threaded Link Checker](exercises/concurrency/link-checker.md) - [Multi-threaded Link Checker](concurrency/sync-exercises/link-checker.md)
- [Solutions](exercises/concurrency/solutions-morning.md) - [Solutions](concurrency/sync-exercises/solutions.md)
# Concurrency: Afternoon # Concurrency: Afternoon
- [Async Basics](async.md) - [Welcome](concurrency/welcome-async.md)
- [`async`/`await`](async/async-await.md) - [Async Basics](concurrency/async.md)
- [Futures](async/futures.md) - [`async`/`await`](concurrency/async/async-await.md)
- [Runtimes](async/runtimes.md) - [Futures](concurrency/async/futures.md)
- [Tokio](async/runtimes/tokio.md) - [Runtimes](concurrency/async/runtimes.md)
- [Tasks](async/tasks.md) - [Tokio](concurrency/async/runtimes/tokio.md)
- [Async Channels](async/channels.md) - [Tasks](concurrency/async/tasks.md)
- [Control Flow](async/control-flow.md) - [Channels and Control Flow](concurrency/async-control-flow.md)
- [Join](async/control-flow/join.md) - [Async Channels](concurrency/async-control-flow/channels.md)
- [Select](async/control-flow/select.md) - [Join](concurrency/async-control-flow/join.md)
- [Pitfalls](async/pitfalls.md) - [Select](concurrency/async-control-flow/select.md)
- [Blocking the Executor](async/pitfalls/blocking-executor.md) - [Pitfalls](concurrency/async-pitfalls.md)
- [`Pin`](async/pitfalls/pin.md) - [Blocking the Executor](concurrency/async-pitfalls/blocking-executor.md)
- [Async Traits](async/pitfalls/async-traits.md) - [`Pin`](concurrency/async-pitfalls/pin.md)
- [Cancellation](async/pitfalls/cancellation.md) - [Async Traits](concurrency/async-pitfalls/async-traits.md)
- [Exercises](exercises/concurrency/afternoon.md) - [Cancellation](concurrency/async-pitfalls/cancellation.md)
- [Dining Philosophers](exercises/concurrency/dining-philosophers-async.md) - [Exercises](concurrency/async-exercises.md)
- [Broadcast Chat Application](exercises/concurrency/chat-app.md) - [Dining Philosophers](concurrency/async-exercises/dining-philosophers.md)
- [Solutions](exercises/concurrency/solutions-afternoon.md) - [Broadcast Chat Application](concurrency/async-exercises/chat-app.md)
- [Solutions](concurrency/async-exercises/solutions.md)
# Final Words # Final Words

View File

@ -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)

View File

@ -0,0 +1,3 @@
# Channels and Control Flow
{{%segment outline}}

View File

@ -1,3 +1,7 @@
---
minutes: 8
---
# Async Channels # Async Channels
Several crates have support for asynchronous channels. For instance `tokio`: Several crates have support for asynchronous channels. For instance `tokio`:

View File

@ -1,3 +1,7 @@
---
minutes: 4
---
# Join # Join
A join operation waits until all of a set of futures are ready, and returns a A join operation waits until all of a set of futures are ready, and returns a

View File

@ -1,3 +1,7 @@
---
minutes: 5
---
# Select # Select
A select operation waits until any of a set of futures is ready, and responds to A select operation waits until any of a set of futures is ready, and responds to

View File

@ -0,0 +1,3 @@
# Exercises
{{%segment outline}}

View File

@ -0,0 +1 @@
# Exercises

View File

@ -1,3 +1,7 @@
---
minutes: 30
---
# Broadcast Chat Application # Broadcast Chat Application
In this exercise, we want to use our new knowledge to implement a broadcast chat In this exercise, we want to use our new knowledge to implement a broadcast chat

View File

@ -1,3 +1,7 @@
---
minutes: 20
---
# Dining Philosophers --- Async # Dining Philosophers --- Async
See [dining philosophers](dining-philosophers.md) for a description of the 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 --> <!-- File src/main.rs -->
```rust,compile_fail ```rust,compile_fail
{{#include dining-philosophers-async.rs:Philosopher}} {{#include dining-philosophers.rs:Philosopher}}
// left_fork: ... // left_fork: ...
// right_fork: ... // right_fork: ...
// thoughts: ... // thoughts: ...
} }
{{#include dining-philosophers-async.rs:Philosopher-think}} {{#include dining-philosophers.rs:Philosopher-think}}
{{#include dining-philosophers-async.rs:Philosopher-eat}} {{#include dining-philosophers.rs:Philosopher-eat}}
{{#include dining-philosophers-async.rs:Philosopher-eat-body}} {{#include dining-philosophers.rs:Philosopher-eat-body}}
{{#include dining-philosophers-async.rs:Philosopher-eat-end}} {{#include dining-philosophers.rs:Philosopher-eat-end}}
// Create forks // Create forks
// Create philosophers // Create philosophers

View File

@ -1,17 +1,17 @@
# Concurrency Afternoon Exercise ---
minutes: 20
---
# Solutions
## Dining Philosophers --- Async ## Dining Philosophers --- Async
([back to exercise](dining-philosophers-async.md))
```rust,compile_fail ```rust,compile_fail
{{#include dining-philosophers-async.rs:solution}} {{#include dining-philosophers.rs:solution}}
``` ```
## Broadcast Chat Application ## Broadcast Chat Application
([back to exercise](chat-app.md))
_src/bin/server.rs_: _src/bin/server.rs_:
```rust,compile_fail ```rust,compile_fail

View File

@ -1,10 +1,7 @@
# Pitfalls of async/await # Pitfalls
Async / await provides convenient and efficient abstraction for concurrent Async / await provides convenient and efficient abstraction for concurrent
asynchronous programming. However, the async/await model in Rust also comes with 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) {{%segment outline}}
- [Pin](pitfalls/pin.md)
- [Async Traits](pitfalls/async-traits.md)
- [Cancellation](pitfalls/cancellation.md)

View File

@ -1,3 +1,7 @@
---
minutes: 5
---
# Async Traits # Async Traits
Async methods in traits are were stabilized only recently, in the 1.75 release. Async methods in traits are were stabilized only recently, in the 1.75 release.

View File

@ -1,3 +1,7 @@
---
minutes: 10
---
# Blocking the executor # Blocking the executor
Most async runtimes only allow IO tasks to run concurrently. This means that CPU Most async runtimes only allow IO tasks to run concurrently. This means that CPU

View File

@ -1,3 +1,7 @@
---
minutes: 18
---
# Cancellation # Cancellation
Dropping a future implies it can never be polled again. This is called Dropping a future implies it can never be polled again. This is called

View File

@ -1,3 +1,7 @@
---
minutes: 20
---
# `Pin` # `Pin`
Async blocks and functions return types implementing the `Future` trait. The Async blocks and functions return types implementing the `Future` trait. The

3
src/concurrency/async.md Normal file
View File

@ -0,0 +1,3 @@
# Async Basics
{{%segment outline}}

View File

@ -1,3 +1,7 @@
---
minutes: 6
---
# `async`/`await` # `async`/`await`
At a high level, async Rust code looks very much like "normal" sequential code: At a high level, async Rust code looks very much like "normal" sequential code:

View File

@ -1,3 +1,7 @@
---
minutes: 4
---
# Futures # Futures
[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) is a trait, [`Future`](https://doc.rust-lang.org/std/future/trait.Future.html) is a trait,

View File

@ -1,3 +1,7 @@
---
minutes: 10
---
# Runtimes # Runtimes
A _runtime_ provides support for performing operations asynchronously (a A _runtime_ provides support for performing operations asynchronously (a

View File

@ -1,3 +1,7 @@
---
minutes: 6
---
# Tasks # Tasks
Rust has a task system, which is a form of lightweight threading. Rust has a task system, which is a form of lightweight threading.

View File

@ -1,32 +1,3 @@
# Channels # Channels
Rust channels have two parts: a `Sender<T>` and a `Receiver<T>`. The two parts {{%segment outline}}
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>

View File

@ -1,3 +1,7 @@
---
minutes: 8
---
# Bounded Channels # Bounded Channels
With bounded (synchronous) channels, `send` can block the current thread: With bounded (synchronous) channels, `send` can block the current thread:

View 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>

View File

@ -1,3 +1,7 @@
---
minutes: 2
---
# Unbounded Channels # Unbounded Channels
You get an unbounded and asynchronous channel with `mpsc::channel()`: You get an unbounded and asynchronous channel with `mpsc::channel()`:

View File

@ -1,25 +1,3 @@
# `Send` and `Sync` # `Send` and `Sync`
How does Rust know to forbid shared access across threads? The answer is in two {{%segment outline}}
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>

View File

@ -1,3 +1,7 @@
---
minutes: 6
---
# Examples # Examples
## `Send + Sync` ## `Send + Sync`

View 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>

View File

@ -1,3 +1,7 @@
---
minutes: 2
---
# `Send` # `Send`
> A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread. > A type `T` is [`Send`][1] if it is safe to move a `T` value to another thread.

View File

@ -1,3 +1,7 @@
---
minutes: 2
---
# `Sync` # `Sync`
> A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple > A type `T` is [`Sync`][1] if it is safe to access a `T` value from multiple

View File

@ -0,0 +1,3 @@
# Shared State
{{%segment outline}}

View File

@ -1,3 +1,7 @@
---
minutes: 5
---
# `Arc` # `Arc`
[`Arc<T>`][1] allows shared read-only access via `Arc::clone`: [`Arc<T>`][1] allows shared read-only access via `Arc::clone`:

View File

@ -1,3 +1,7 @@
---
minutes: 8
---
# Example # Example
Let us see `Arc` and `Mutex` in action: Let us see `Arc` and `Mutex` in action:

View File

@ -1,3 +1,7 @@
---
minutes: 14
---
# `Mutex` # `Mutex`
[`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T` [`Mutex<T>`][1] ensures mutual exclusion _and_ allows mutable access to `T`

View File

@ -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

View File

@ -0,0 +1,3 @@
# Exercises
{{%segment outline}}

View File

@ -1,16 +1,16 @@
[package] [package]
name = "comprehensive-rust" name = "sync-exercises"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
publish = false publish = false
[[bin]] [[bin]]
name = "dining-philosophers" name = "dining-philosophers"
path = "concurrency/dining-philosophers.rs" path = "dining-philosophers.rs"
[[bin]] [[bin]]
name = "link-checker" name = "link-checker"
path = "concurrency/link-checker.rs" path = "link-checker.rs"
[dependencies] [dependencies]
reqwest = { version = "0.12.4", features = ["blocking"] } reqwest = { version = "0.12.4", features = ["blocking"] }

View File

@ -1,3 +1,7 @@
---
minutes: 20
---
# Dining Philosophers # Dining Philosophers
The dining philosophers problem is a classic problem in concurrency: The dining philosophers problem is a classic problem in concurrency:

View File

@ -1,3 +1,7 @@
---
minutes: 20
---
# Multi-threaded Link Checker # Multi-threaded Link Checker
Let us use our new knowledge to create a multi-threaded link checker. It should Let us use our new knowledge to create a multi-threaded link checker. It should

View File

@ -1,17 +1,17 @@
# Concurrency Morning Exercise ---
minutes: 30
---
# Solutions
## Dining Philosophers ## Dining Philosophers
([back to exercise](dining-philosophers.md))
```rust ```rust
{{#include dining-philosophers.rs:solution}} {{#include dining-philosophers.rs:solution}}
``` ```
## Link Checker ## Link Checker
([back to exercise](link-checker.md))
```rust,compile_fail ```rust,compile_fail
{{#include link-checker.rs:solution}} {{#include link-checker.rs:solution}}
``` ```

View File

@ -1,74 +1,3 @@
# Threads # Threads
Rust threads work similarly to threads in other languages: {{%segment outline}}
```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>

View 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>

View File

@ -1,3 +1,7 @@
---
minutes: 13
---
# Scoped Threads # Scoped Threads
Normal threads cannot borrow from their environment: Normal threads cannot borrow from their environment:

View File

@ -1,8 +1,9 @@
--- ---
session: Afternoon session: Afternoon
target_minutes: 180
--- ---
# Async Rust # Welcome
"Async" is a concurrency model where multiple tasks are executed concurrently by "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 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 - JavaScript's `Promise` is similar, but again callback-based. The language
runtime implements the event loop, so many of the details of Promise runtime implements the event loop, so many of the details of Promise
resolution are hidden. resolution are hidden.
## Schedule
{{%session outline}}

View File

@ -1,6 +1,7 @@
--- ---
course: Concurrency course: Concurrency
session: Morning session: Morning
target_minutes: 180
--- ---
# Welcome to Concurrency in Rust # 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 compile time bugs. This is often referred to as _fearless concurrency_ since you
can rely on the compiler to ensure correctness at runtime. can rely on the compiler to ensure correctness at runtime.
## Schedule
{{%session outline}}
<details> <details>
- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc. - Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc.

View File

@ -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>

View File

@ -70,6 +70,8 @@ cargo add tokio --features full
cargo run cargo run
``` ```
{{%course outline Concurrency}}
## Format ## Format
The course is meant to be very interactive and we recommend letting the The course is meant to be very interactive and we recommend letting the