) -> Result<(), io::Error> {
// ...
for line in ... {
log.push(line.len());
// ...
}
Ok(())
}
fn main() -> Result<(), io::Error> {
let mut log = vec![];
for stream in listener.incoming() {
// ...
thread::spawn(|| {
let _ = handle_client(stream, &mut log);
});
}
Ok(())
}
```
---
## Errors
error[E0373]: closure may outlive the current function, but it borrows `log`,
which is owned by the current function
--> src/main.rs:26:23
|
26 | thread::spawn(|| {
| ^^ may outlive borrowed value `log`
27 | let _ = handle_client(stream, &mut log);
| --- `log` is borrowed here
|
note: function requires argument type to outlive `'static`
---
## Lifetime problem
Problem:
* local data may be cleaned up prematurely
Solution:
* move the decision when to clean the data from *compile-time* to *run-time*
* use reference-counting
---
## Attempt 1: `Rc`
* `let mut log = Rc::new(vec![]);`
* `let mut thread_log = log.clone()` now doesn't clone the data, but simply increases the reference count
* both variables now have *owned* type, and satisfy `F: 'static` requirement
```text
error[E0277]: `Rc>` cannot be sent between threads safely
```
---
## `Rc` in Rust Standard Library
* uses `usize` for reference counting
* explicitly marked as `!Send`
```rust ignore
pub struct Rc {
ptr: NonNull>,
}
impl !Send for Rc {}
struct RcBox {
strong: Cell,
weak: Cell,
value: T,
}
```
---
## `Arc` in Rust Standard Library
* uses `AtomicUsize` for reference counting
* explicitly marked as `Send`
```rust ignore
pub struct Arc {
ptr: NonNull>,
}
impl Send for Arc {}
struct ArcInner {
strong: atomic::AtomicUsize,
weak: atomic::AtomicUsize,
data: T,
}
```
---
## `Rc` vs `Arc`
* `Arc` uses `AtomicUsize` for reference counting
* slower
* safe to increment / decrement from multiple threads
* With the help of marker trait `Send` and trait bounds on `thread::spawn`, the compiler *forces* you to use the correct type
---
## `Arc` / `Rc` "transparency"
```rust ignore
let mut log = Arc::new(Vec::new());
// how does this code work?
log.len();
// and why doesn't this work?
log.push(1);
```
---
## `Deref` and `DerefMut` traits
```rust ignore
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
pub trait DerefMut: Deref {
fn deref_mut(&mut self) -> &mut Self::Target;
}
```
---
## `Deref` coercions
* `Deref` can convert a `&self` reference to a reference of another type
* conversion function call can be inserted by the compiler for you automatically
* in most cases the conversion is a no-op or a fixed pointer offset
* deref functions can be inlined
* `Target` is an associated type
* can't `deref()` into multiple different types
* `DerefMut: Deref` allows the `DerefMut` trait to reuse the same `Target` type
* read-only and read-write references coerce to the references of the same type
---
## `Arc` / `Rc` "transparency" with `Deref`
```rust ignore
let mut log = Arc::new(Vec::new());
// Arc implements `Deref` from `&Arc into `&T`
log.len();
// the same as
Vec::len( as Deref>::deref(&log));
// Arc DOES NOT implement `DerefMut`
// log.push(1);
// the line above would have expanded to:
// Vec::push( as DerefMut>::deref_mut(&mut log), 1);
```
---
## `Arc` and mutability
* lack of `impl DerefMut for Arc` prevents accidental creation of multiple `&mut` to underlying data
* the solution is to move mutability decision to runtime
```rust ignore
let log = Arc::new(Mutex::new(Vec::new()));
```
 
* `Arc` guarantees *availability* of data in memory
* prevents memory form being cleaned up prematurely
* `Mutex` guarantees *exclusivity of mutable access*
* provides *only one* `&mut` to underlying data simultaneously
---
## `Mutex` in Action
* `log` is passed as `&` and is `deref`-ed from `Arc` by the compiler
* `mut`ability is localized to a local `guard` variable
* `Mutex::lock` method takes `&self`
* `MutexGuard` implements `Deref` *and* `DerefMut`!
* `'_` lifetime annotation is needed only because guard struct has a `&Mutex` inside
```rust ignore
fn handle_client(..., log: &Mutex>) -> ... {
for line in ... {
let mut guard: MutexGuard<'_, Vec> = log.lock().unwrap();
guard.push(line.len());
// line above expands to:
// Vec::push( as DerefMut>::deref_mut(&mut guard), line.len());
writeln!(writer, "{}", line)?;
writer.flush()?;
}
}
```
---
## `Mutex` locking and unlocking
* we `lock` the mutex for exclusive access to underlying data at runtime
* old C APIs used a pair of functions to lock and unlock the mutex
* `MutexGuard` does unlocking automatically when is dropped
* time between guard creation and drop is called *critical section*
---
## Lock Poisoning
* `MutexGuard` in its `Drop` implementation checks if it is being dropped normally or during a `panic` unwind
* in later case sets a poison flag on the mutex
* calling `lock().unwrap()` on a poisoned Mutex causes `panic`
* if the mutex is *"popular"* poisoning can cause many application threads to panic, too.
* `PoisonError` doesn't provide information about the panic that caused the poisoning
---
## Critical Section "Hygiene"
* keep it short to reduce the window when mutex is locked
* avoid calling functions that can panic
* using a named variable for Mutex guard helps avoiding unexpected temporary lifetime behavior
---
## Critical Section Example
```rust ignore
fn handle_client(..., log: &Mutex>) -> ... {
for line in ... {
{
let mut guard: MutexGuard<'_, Vec> = log.lock().unwrap();
guard.push(line.len());
} // critical section ends here, before all the IO
writeln!(writer, "{}", line)?;
writer.flush()?;
}
}
```
 
* `drop(guard)` also works, but extra block nicely highlights the critical section
---
## Lessons Learned
* careful use of traits and trait boundaries lets the compiler detect problematic multi-threading code at compile time
* `Arc` and `Mutex` let the program ensure data availability and exclusive mutability at runtime where the compiler can't predict the behavior of the program
* `Deref` coercions make concurrency primitives virtually invisible and transparent to use
* **Make invalid state unrepresentable**
---
## Full Example
```rust ignore
use std::{
io::{self, BufRead as _, Write as _},
net,
sync::{Arc, Mutex},
thread,
};
fn handle_client(stream: net::TcpStream, log: &Mutex>) -> Result<(), io::Error> {
let mut writer = io::BufWriter::new(&stream);
let reader = io::BufReader::new(&stream);
for line in reader.lines() {
let line = line?;
{
let mut guard = log.lock().unwrap();
guard.push(line.len());
}
writeln!(writer, "{}", line)?;
writer.flush()?;
}
Ok(())
}
fn main() -> Result<(), io::Error> {
let log = Arc::new(Mutex::new(vec![]));
let listener = net::TcpListener::bind("0.0.0.0:7878")?;
for stream in listener.incoming() {
let stream = stream?;
let thread_log = log.clone();
thread::spawn(move || {
let _ = handle_client(stream, &thread_log);
});
}
Ok(())
}
```