1
0
mirror of https://github.com/google/comprehensive-rust.git synced 2025-05-15 07:06:52 +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"
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"

View File

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

View File

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

View File

@ -35,7 +35,7 @@ pub fn replace(
return;
};
chapter.content = DIRECTIVE
.replace(&chapter.content, |captures: &regex::Captures| {
.replace_all(&chapter.content, |captures: &regex::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()
}

View File

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

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
Several crates have support for asynchronous channels. For instance `tokio`:

View File

@ -1,3 +1,7 @@
---
minutes: 4
---
# Join
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
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
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
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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
---
minutes: 5
---
# Async Traits
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
Most async runtimes only allow IO tasks to run concurrently. This means that CPU

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,3 +1,7 @@
---
minutes: 8
---
# Bounded Channels
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
You get an unbounded and asynchronous channel with `mpsc::channel()`:

View File

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

View File

@ -1,3 +1,7 @@
---
minutes: 6
---
# Examples
## `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`
> 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`
> 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<T>`][1] allows shared read-only access via `Arc::clone`:

View File

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

View File

@ -1,3 +1,7 @@
---
minutes: 14
---
# `Mutex`
[`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]
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"] }

View File

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

View File

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

View File

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

View File

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

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
Normal threads cannot borrow from their environment:

View File

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

View File

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

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
```
{{%course outline Concurrency}}
## Format
The course is meant to be very interactive and we recommend letting the