Skip to content

Commit

Permalink
concurrency: Add detailed teaching notes for welcome and threads slides
Browse files Browse the repository at this point in the history
  • Loading branch information
fw-immunant committed Mar 6, 2024
1 parent 9b57c48 commit 424839c
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 7 deletions.
10 changes: 10 additions & 0 deletions src/concurrency.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,13 @@ channels.
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.

<details>

- Rust lets us access OS concurrency toolkit: threads, sync. primitives, etc.
- The type system gives us safety for concurrency without any special features.
- The same tools that help with "concurrent" access in a single thread (e.g., a
called function that might mutate an argument or save references to it to read
later) save us from multi-threading issues.

</details>
43 changes: 36 additions & 7 deletions src/concurrency/threads.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,48 @@ fn main() {

<details>

Key points:
- Rust thread APIs look not too different from e.g. C++ ones.

- Notice that the thread is stopped before it reaches 10 --- the main thread is
not waiting.
- 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.
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`].

- Trigger a panic in the thread, notice how this doesn't affect `main`.
- 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.

- Use the `Result` return value from `handle.join()` to get access to the panic
payload. This is a good time to talk about [`Any`].
- 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>

0 comments on commit 424839c

Please sign in to comment.