Rust concurrency tutorial 2015 12-02

58
1 Hack without fear! Nicholas Matsakis Mozilla Research

Transcript of Rust concurrency tutorial 2015 12-02

1

Hack without fear!Nicholas Matsakis!Mozilla Research

2

Systems programming without the hasslecrashes!heisenbugs!fear

Parallel!

The Plan

3

1. Sequential search

2. Spawn threads

3. Channels

4. Shared data

0. Hello, world!

5. Mutex

Hello, world!

4

smallcultfollowing.com/20151202/ !

Example “Hello World”

fn main() { println!(“Hello, world!”); }

Ownership!!n. The act, state, or right of possessing something.

5

Borrow!!v. To receive something with the promise of returning it.

Ownership/Borrowing

Memory safety

Data-race freedom

No need for a runtime

GCC++

6

Ownership7

fn main() { let name = format!(“…”); helper(name); helper(name); }

fn helper(name: String) { println!(..); } !!!

Ownership

Take ownership of a String

8

Error: use of moved value: `name`

void main() { Vector name = …; helper(name); helper(name); }

void helper(Vector name) { … } !!!

“Ownership” in Java

Take reference to Vector

9

new Thread(…);

Borrowing10

fn main() { let name = format!(“…”); helper(&name); helper(&name); }

Shared borrow

Take a reference to a String

11

Lend the string

fn helper(name: &String) { println!(..); } !!!

Borrowing Exercise

12

Example “Borrowing”

13

fn main() { let name = format!(“…”); helper(&name); helper(&name); }

fn helper(name: &String) { thread::spawn(…); }

http://is.gd/cEeyzx

However: see crossbeam, simple_parallel, etc on

crates.io

Clone

14

fn main() { let name = format!(“…”); helper(name.clone()); helper(name); }

fn helper(name: String) { println!(..); } !!!Copy the String

Copy (auto-Clone)

15

fn main() { let name = 22; helper(name); helper(name); }

fn helper(name: i32) { println!(..); } !!!

i32 is a Copy type

16

Default: Type cannot be copied. Values move from place to place. Example: File descriptor. !

Clone: Type is expensive to copy, so make it explicit by calling clone(). Examples: Vector, hashtable.!!

Copy: Type is implicitly copied whenever it is referenced. Examples: u32, i32, (f32, i32).

The Plan

17

1. Sequential search

2. Spawn threads

0. Hello, world!

3. Channels

4. Shared data

5. Mutex

18Shop till you drop!

By QuentinUK (Own work), via Wikimedia Commons

19

$5.0 $25.5 $81.5 ——— $112.0

$5.0 $20.5 $81.5 ——— $107.0

$5.0 $23.5 $81.5 ——— $110.0

Declaring a structure

20

use std::collections::HashMap; !struct Store { name: String, prices: HashMap<String, f32>, }

Store

name

prices

String

HashMap

String f32

String f32

Standard traits

21

#[derive(Clone, Debug)] struct Store { name: String, prices: HashMap<String, f32>, }

Clone create explicit copies by writing `foo.clone()`Copy create implicit copies (requires Clone)Debug debug printing with `println!(“{:?}”, foo)`PartialEq equality comparisons (`foo == bar`)PartialOrd inequality comparisons (`foo > bar` etc)

…Hash hashing for a hashmap

Methods

22

struct Store { .. } !impl Store { fn add_item(&mut self, name: String, price: f32) { self.prices.insert(name, price); } ! fn price(&self, item_name: &str) -> f32 { self.prices[item_name] } }

store.add_item(…); // must be let mut store.price(…); // let OR let mut

itself an &mut method

Methods

23

struct Store { .. } !impl Store { fn new(name: String) -> Store { Store { name: name, prices: HashMap::new(), } } }

Store::new(some_name)

24

fn build_stores() -> Vec<Store> { let mut stores = vec![]; ! let mut store = Store::new(format!("R-mart")); store.add_item(format!("chocolate"), 5.0); store.add_item(format!("doll"), 22.0); store.add_item(format!("bike"), 150.0); stores.push(store); ! … ! stores // or `return stores`, as you prefer }

Basic Loops

25

let mut i = 0; while i < 3 { println!(“{:?}”, i); i += 1; }

println!(“Once you enter…”); loop { println!(“…you can never leave”); !}

!!! break; // continue works too !println!(“…oh, I guess that works. nm.”);

For Loops

26

fn main() { let v = vec![format!(“Alpha”), format!(“Beta”), format!(“Gamma”)]; for s in v { println!(“{:?}”, s); } }

http://is.gd/6kJc0O

“Alpha”

“Beta”

“Gamma”

v: s:

String

For Loops

27

fn main() { let v = vec![format!(“Alpha”), format!(“Beta”), format!(“Gamma”)]; ! for s in &v { println!(“{:?}”, s); } ! for s in &v { println!(“{:?}”, s); } }

“Alpha”

“Beta”

“Gamma”

v:

s:

&String

Iterators

28

for s in v.iter() .filter(|s| s.len() > 2) { // only strings greater than length 2 println!(“{:?}”, s); }

(IntoIterator trait)

Options and Enums

29

enum Option<T> { Some(T), None }

fn main() { let v: Option<i32> = Some(22); match v { Some(x) => println!(“v = {}”, x), None => println!(“v = None”), } println!(“v = {}”, v.unwrap()); // risky }

http://is.gd/Gfum32

Exercise: Sequential Search

30

for s in v { … } for s in &v { … }

enum Option<T> { Some(T), None }

x.unwrap() is: match x { Some(v) => v, None => panic!() }

std::f32::INFINITY

doc.rust-lang.org/std/

if x != 0 { … }

while x != 0 { … }

println!(“{:?}”, x)

let x = Some(22); let x = None;

The Plan

31

1. Sequential search

2. Spawn threads

0. Hello, world!

3. Channels

4. Shared data

5. Mutex

32

for store in stores { let sum = compute_sum(&store, shopping_list); if sum < best_price { best = Some(store.name); best_price = sum; } }

for store in stores { let sum = compute_sum(&store, shopping_list); if sum < best_price { best = Some(store.name); best_price = sum; } }

33

34

use std::thread; … !for store in stores { let handle = thread::spawn( move || compute_sum(&store, shopping_list)); … }

Closure takes ownership

of variables it uses.

Variables used by this closure.

35

use std::thread; … !for store in stores { let handle = thread::spawn( move || compute_sum(&store, shopping_list)); let sum = handle.join().unwrap(); … }

Handle to the thread we spawned.

Wait for thread to finish and

get return value.

Thread may have panicked. Propagate.

Result<f32, Error>

36

use std::thread; … !for store in stores { let handle = thread::spawn(…); let sum = handle.join().unwrap(); … }

R-Mart

Bullseye

Woolmart

22

44

Exercise: Parallel Search

37

let mut v = vec![]; v.push(…); for item in v { }

enum Option<T> { Some(T), None }

x.unwrap() is: match x { Some(v) => v, None => panic!() }

doc.rust-lang.org/std/

if x != 0 { … }

while x != 0 { … }

println!(“{:?}”, x)

let x = Some(22); let x = None;

The Plan

38

1. Sequential search

2. Spawn threads

0. Hello, world!

3. Channels

4. Shared data

5. Mutex

39

Joining a thread allows thread to send one result.

What if we wanted multiple results?

Or if we wanted a response?

MessageMessage

rx

tx

tx

m

fn parent() { let (tx, rx) = channel(); spawn(move || {…}); let m = rx.recv().unwrap(); }

40

move || { let m = Message::new(); … tx.send(m).unwrap(); }

41

rx0

tx0

let (tx0, rx0) = channel(); let tx1 = tx0.clone(); spawn(move || /* omitted */); let tx2 = tx0.clone(); spawn(move || /* omitted */); mem::drop(tx0);

tx1

tx2

42

for value in rx { use(value); }

loop { let value = match rx.recv() { Ok(v) => v, Err(mpsc::RecvError) => break, }; use(value); }

while let Ok(value) = rx.recv() { use(value); }

43

let (tx1, rx1) = channel(); let message = …; tx0.send((m, tx1)).unwrap(); let response = rx0.recv.unwrap();

rx0

tx0 rx1

message

tx1

response

let (tx0, rx0) = channel(); spawn(move || /* see lower-right corner below */); let (message, tx1) = rx0.recv().unwrap(); tx1.send(response).unwrap();

Exercise: Channels

44

use std::mpsc::channel;

doc.rust-lang.org/std/

let (tx, rx) = channel();

tx.send(m).unwrap();

let m = rx.recv().unwrap();

let tx1 = tx.clone();

for m in rx { … }

use std::mem::drop;

The Plan

45

1. Sequential search

2. Spawn threads

0. Hello, world!

3. Channels

4. Shared data

5. Mutex

46

47

use std::sync::Arc; let shopping_list: Vec<ShoppingList> = …; let arc1 = Arc::new(shopping_list); let arc2 = arc1.clone(); let data = &arc1[0];

arc1

arc2

data

Arc => Immutable

48

use std::sync::Arc; let shopping_list: Vec<ShoppingList> = …; let arc1 = Arc::new(shopping_list); let data = &mut arc1[0];

<anon>:6:21: 6:24 error: cannot borrow immutable borrowed content as mutable <anon>:6 let data = &mut arc[0]; ^~~

http://is.gd/nP3Pvb

49

It’s a dangling pointer!

No, it’s a data race!

No, it’s a double free!

Simultaneous Sharing and

Mutation

No, it’s iterator

invalidation!

Exercise: Shared Memory

50

use std::sync::Arc;

doc.rust-lang.org/std/

let arc1 = Arc::new(…);

let arc2 = arc1.clone();

The Plan

51

1. Sequential search

2. Spawn threads

0. Hello, world!

3. Channels

4. Shared data

5. Mutex

52

Start threads

Compute sums

Compare sums

53https://www.flickr.com/photos/mabi/38871148

Data races

Sharing

Mutation

No ordering

Data race

Job of the API

54

The default path.

0

55

let counter = Mutex::new(0); { let mut data = counter.lock().unwrap(); *data += 1; }

counter

data

https://commons.wikimedia.org/wiki/File:No-DRM_lock.svg

1

56

let counter = Mutex::new(0); let arc1 = Arc::new(counter); let arc2 = arc1.clone(); let mut data = arc1.lock(); *data += 1;

0

https://commons.wikimedia.org/wiki/File:No-DRM_lock.svg

arc1

arc2

data

Exercise: Mutex

57

doc.rust-lang.org/std/

let counter = Mutex::new(0); let arc1 = Arc::new(counter); let arc2 = arc1.clone(); let mut data = arc1.lock(); *data += 1;

58

Thanks for

listenin

g!