SlideShare a Scribd company logo
1
Hack without fear!
Nicholas Matsakis!
Mozilla Research
2
Systems programming without the hassle
crashes!
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
Ownership
7
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(…);
Borrowing
10
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
18
Shop 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 listening!

More Related Content

Rust concurrency tutorial 2015 12-02

  • 1. 1 Hack without fear! Nicholas Matsakis! Mozilla Research
  • 2. 2 Systems programming without the hassle crashes! heisenbugs! fear Parallel!
  • 3. The Plan 3 1. Sequential search 2. Spawn threads 3. Channels 4. Shared data 0. Hello, world! 5. Mutex
  • 4. Hello, world! 4 smallcultfollowing.com/20151202/ ! Example “Hello World” fn main() { println!(“Hello, world!”); }
  • 5. Ownership! ! n. The act, state, or right of possessing something. 5 Borrow! ! v. To receive something with the promise of returning it.
  • 8. 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`
  • 9. void main() { Vector name = …; helper(name); helper(name); } void helper(Vector name) { … } ! ! ! “Ownership” in Java Take reference to Vector 9 new Thread(…);
  • 11. 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!(..); } ! ! !
  • 13. 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
  • 14. Clone 14 fn main() { let name = format!(“…”); helper(name.clone()); helper(name); } fn helper(name: String) { println!(..); } ! ! ! Copy the String
  • 15. Copy (auto-Clone) 15 fn main() { let name = 22; helper(name); helper(name); } fn helper(name: i32) { println!(..); } ! ! ! i32 is a Copy type
  • 16. 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).
  • 17. The Plan 17 1. Sequential search 2. Spawn threads 0. Hello, world! 3. Channels 4. Shared data 5. Mutex
  • 18. 18 Shop till you drop! By QuentinUK (Own work), via Wikimedia Commons
  • 20. 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
  • 21. 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
  • 22. 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
  • 23. Methods 23 struct Store { .. } ! impl Store { fn new(name: String) -> Store { Store { name: name, prices: HashMap::new(), } } } Store::new(some_name)
  • 24. 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 }
  • 25. 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.”);
  • 26. 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
  • 27. 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
  • 28. Iterators 28 for s in v.iter() .filter(|s| s.len() > 2) { // only strings greater than length 2 println!(“{:?}”, s); } (IntoIterator trait)
  • 29. 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
  • 30. 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;
  • 31. The Plan 31 1. Sequential search 2. Spawn threads 0. Hello, world! 3. Channels 4. Shared data 5. Mutex
  • 32. 32 for store in stores { let sum = compute_sum(&store, shopping_list); if sum < best_price { best = Some(store.name); best_price = sum; } }
  • 33. for store in stores { let sum = compute_sum(&store, shopping_list); if sum < best_price { best = Some(store.name); best_price = sum; } } 33
  • 34. 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. 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. 36 use std::thread; … ! for store in stores { let handle = thread::spawn(…); let sum = handle.join().unwrap(); … } R-Mart Bullseye Woolmart 22 44
  • 37. 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;
  • 38. The Plan 38 1. Sequential search 2. Spawn threads 0. Hello, world! 3. Channels 4. Shared data 5. Mutex
  • 39. 39 Joining a thread allows thread to send one result. What if we wanted multiple results? Or if we wanted a response?
  • 40. 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. 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. 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. 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();
  • 44. 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;
  • 45. The Plan 45 1. Sequential search 2. Spawn threads 0. Hello, world! 3. Channels 4. Shared data 5. Mutex
  • 46. 46
  • 47. 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
  • 48. 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. 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!
  • 50. Exercise: Shared Memory 50 use std::sync::Arc; doc.rust-lang.org/std/ let arc1 = Arc::new(…); let arc2 = arc1.clone();
  • 51. The Plan 51 1. Sequential search 2. Spawn threads 0. Hello, world! 3. Channels 4. Shared data 5. Mutex
  • 54. Data races Sharing Mutation No ordering Data race Job of the API 54 The default path.
  • 55. 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. 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
  • 57. 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;