Skip to content

Commit 1db16a1

Browse files
authored
Missing lessons (#52)
* Missing lessons * Fix
1 parent 0d2ec7d commit 1db16a1

File tree

4 files changed

+235
-1
lines changed

4 files changed

+235
-1
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
+++
2+
title = "Fearless concurrency"
3+
date = 2022-11-28
4+
weight = 1
5+
[extra]
6+
lesson_date = 2022-12-12
7+
+++
8+
9+
## Parallelism vs Concurrency
10+
11+
Concurrency is when tasks **can make** progress **independently** of each other.
12+
13+
Parallelism is when multiple tasks **make** progress **at the same time**.
14+
15+
## Concurrency models in Rust
16+
17+
### Threads
18+
19+
Nothing unusual here.
20+
21+
Threads can be created with the `thread::spawn` function [docs - please read them!](https://doc.rust-lang.org/std/thread/fn.spawn.html).
22+
23+
This method returns a `JoinHandle<T>` which can be used to wait for the thread to finish. `T` is the type of the thread's return value.
24+
25+
#### Propagating panics
26+
27+
In Rust a panic of one thread doesn't affect the other threads (similar to how Java handles exceptions in threads).
28+
29+
#### Closures
30+
31+
Closures which are used to create threads must take ownership of any values they use. It can be forced with the `move` keyword.
32+
33+
```rust
34+
use std::thread;
35+
36+
fn main() {
37+
let v = vec![1, 2, 3];
38+
39+
let handle = thread::spawn(move || {
40+
println!("Here's a vector: {:?}", v);
41+
});
42+
43+
handle.join().unwrap();
44+
}
45+
```
46+
47+
Normal ownership rules still apply. It means that we cannot mutate the vector in the spawned thread from the main thread!
48+
49+
But what if we need to share some state?
50+
51+
### Message passing
52+
53+
One possible way is to use message passing. We can use a blocking queue (called `mpsc` - ["multi producer single consumer FIFO queue"](https://doc.rust-lang.org/std/sync/mpsc/index.html)) to do it.
54+
We talked about blocking queues in the Concurrent programming class. In Rust, they are strongly-typed. Sending and receiving ends have different types.
55+
56+
### Mutexes
57+
58+
In Rust, a mutex _wraps_ a value and makes it thread-safe.
59+
Because it becomes a part of the type, it's impossible to access the underlying value in an unsynchronized manner. It is conceptually similar to the `RefCell` type.
60+
61+
`Arc` is a smart pointer like `Rc` but it can be shared between threads.
62+
63+
Please read more about them in [the book](https://doc.rust-lang.org/stable/book/ch16-03-shared-state.html).
64+
65+
[The docs](https://doc.rust-lang.org/std/sync/struct.Mutex.html) also mention `poisoning`.
66+
67+
### RwLocks
68+
69+
[RwLocks](https://doc.rust-lang.org/std/sync/struct.RwLock.html) are similar to mutexes, but they distinguish between read and write locks.
70+
71+
## Send and Sync
72+
73+
They are marker traits used to indicate that a type or a reference to it can be sent across threads. See the [nomicon](https://doc.rust-lang.org/nomicon/send-and-sync.html) for more information.
74+
75+
## Atomic types
76+
77+
Atomic types are described in [the docs](https://doc.rust-lang.org/std/sync/atomic/).
78+
79+
```rust
80+
use std::sync::Arc;
81+
use std::sync::atomic::{AtomicUsize, Ordering};
82+
use std::{hint, thread};
83+
84+
fn main() {
85+
let spinlock = Arc::new(AtomicUsize::new(1));
86+
87+
let spinlock_clone = Arc::clone(&spinlock);
88+
let thread = thread::spawn(move|| {
89+
spinlock_clone.store(0, Ordering::SeqCst);
90+
});
91+
92+
// Wait for the other thread to release the lock
93+
while spinlock.load(Ordering::SeqCst) != 0 {
94+
hint::spin_loop();
95+
}
96+
97+
if let Err(panic) = thread.join() {
98+
println!("Thread had an error: {:?}", panic);
99+
}
100+
}
101+
```
102+
103+
Note that `atomic` values don't have to be wrapped in a mutex when shared across threads.
104+
105+
### Wait...
106+
107+
If most types are `Sync + Send`, then what stops us from using a standard, non-atomic integer in the example above?
108+
109+
```rust
110+
let spinlock = Arc::new(1);
111+
112+
let spinlock_clone = Arc::clone(&spinlock);
113+
let thread = thread::spawn(move|| {
114+
*spinlock_clone += 1;
115+
});
116+
117+
while *spinlock != 0 {
118+
hint::spin_loop();
119+
}
120+
```
121+
122+
```
123+
error[E0594]: cannot assign to data in an `Arc`
124+
--> src/main.rs:9:9
125+
|
126+
9 | *spinlock_clone += 1;
127+
| ^^^^^^^^^^^^^^^^^^^^ cannot assign
128+
|
129+
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<i32>`
130+
```
131+
132+
...so we would have to use a `RefCell` to be able to modify the value through a shared reference...
133+
134+
```rust
135+
let spinlock = Arc::new(RefCell::new(1));
136+
137+
let spinlock_clone = Arc::clone(&spinlock);
138+
let thread = thread::spawn(move|| {
139+
*spinlock_clone.borrow_mut() += 1;
140+
});
141+
142+
// Wait for the other thread to release the lock
143+
while *spinlock.borrow() != 0 {
144+
hint::spin_loop();
145+
}
146+
```
147+
148+
...but `RefCell` isn't `Sync`:
149+
150+
```
151+
error[E0277]: `RefCell<i32>` cannot be shared between threads safely
152+
--> src/main.rs:9:18
153+
|
154+
9 | let thread = thread::spawn(move|| {
155+
| ^^^^^^^^^^^^^ `RefCell<i32>` cannot be shared between threads safely
156+
|
157+
= help: the trait `Sync` is not implemented for `RefCell<i32>`
158+
= note: required because of the requirements on the impl of `Send` for `Arc<RefCell<i32>>`
159+
= note: required because it appears within the type `[closure@src/main.rs:9:32: 11:6]`
160+
note: required by a bound in `spawn`
161+
```
162+
163+
And that bound mentioned in the last line looks like this:
164+
165+
```rust
166+
pub fn spawn<F, T>(f: F) -> JoinHandle<T> where
167+
F: FnOnce() -> T,
168+
F: Send + 'static,
169+
T: Send + 'static,
170+
```
171+
172+
#### Exercise for the reader
173+
174+
Why is it impossible to share a reference to a `Mutex` between threads?
175+
176+
## Data parallelism with Rayon
177+
178+
[Rayon](https://docs.rs/rayon/latest/rayon/) is a library for parallelization of data processing.
179+
It can be used to parallelize the execution of functions over a collection of data by switching the standard `Iterator` to a `ParallelIterator`.
180+
It works very similar to [Java's parallel streams](https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html#executing_streams_in_parallel).
181+
182+
Why do that? Because thread synchronization is hard! [Rust prevents data races](https://doc.rust-lang.org/nomicon/races.html), but [logical races and deadlocks are impossible to prevent!](https://users.rust-lang.org/t/deadlock-is-it-a-bug-or-is-it-intentional/1544)!
183+
184+
[Rayon's FAQ](https://github.com/rayon-rs/rayon/blob/master/FAQ.md) is worth reading.
185+
186+
## Reading
187+
188+
- [The Book](https://doc.rust-lang.org/book/ch16-00-concurrency.html)
189+
- [Safely writing code that isn't thread-safe](http://archive.today/WFlZV)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
+++
2+
title = "Design patterns"
3+
date = 2022-12-05
4+
weight = 1
5+
[extra]
6+
lesson_date = 2022-12-05
7+
+++
8+
9+
## Object-oriented programming and Rust
10+
11+
The book has [a chapter dedicated to it](https://doc.rust-lang.org/stable/book/ch17-01-what-is-oo.html).
12+
Especially the ["typestate"](https://doc.rust-lang.org/stable/book/ch17-03-oo-design-patterns.html#encoding-states-and-behavior-as-types) pattern is very interesting.
13+
You can read more about it [here](http://cliffle.com/blog/rust-typestate/).
14+
15+
## How to build a good library
16+
17+
[These guidelines](https://rust-lang.github.io/api-guidelines/about.html) have been created by the Rust library team.
18+
19+
## How to handle errors
20+
21+
[This post](https://nick.groenen.me/posts/rust-error-handling/) is from 2020, but the libraries it mentions (`anyhow` and `thiserror`) are still the most popular.
22+
23+
## Serde
24+
25+
[Serde](https://serde.rs/) is the most popular serialization library for Rust.
26+
27+
## Assignment
28+
29+
This week's assignment is to write a "distributed" calculator.
30+
You should base your solution on the [final project from the book](https://doc.rust-lang.org/stable/book/ch20-00-final-project-a-web-server.html).

content/lessons/11_async_1/index.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
+++
2+
title = "Async: Part 1"
3+
date = 2022-12-12
4+
weight = 1
5+
[extra]
6+
lesson_date = 2022-12-12
7+
+++
8+
9+
## Tokio
10+
11+
We'll use the [Tokio tutorial](https://tokio.rs/tokio/tutorial) (chapters `Overview`-`Channels`).
12+
13+
## Common Rust Lifetime Misconceptions
14+
15+
Please read [this blogpost](https://github.com/pretzelhammer/rust-blog/blob/master/posts/common-rust-lifetime-misconceptions.md).

zola_version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
zola 0.15.3
1+
zola 0.16.1

0 commit comments

Comments
 (0)