Skip to content

Commit c5f3bba

Browse files
committed
Initial commit.
0 parents  commit c5f3bba

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2839
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target/
2+
/Cargo.lock

Cargo.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "rust-atomics-and-locks"
3+
version = "1.0.0"
4+
edition = "2021"
5+
rust-version = "1.66.0"
6+
7+
[dependencies]
8+
atomic-wait = "1.0.1"
9+
10+
[target.'cfg(target_os = "linux")'.dependencies]
11+
libc = "0.2.138"

LICENSE

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
You may use all code in this repository for any purpose.
2+
3+
Attribution is appreciated, but not required.
4+
An attrubution usually includes the book title, author,
5+
publisher, and ISBN. For example: "Rust Atomics and
6+
Locks by Mara Bos (O’Reilly). Copyright 2023 Mara Bos,
7+
978-1-098-11944-7."

README.md

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
This repository contains the code examples, data structures, and links from
2+
[Rust Atomics and Locks](https://marabos.nl/atomics/).
3+
4+
The examples from chapters 1, 2, 3, and 8 can be found in [examples/](examples/).
5+
The data structures from chapters 4, 5, 6, and 9 can be found in [src/](src/).
6+
7+
### Chapter 1 — Basics of Rust Concurrency
8+
9+
- [examples/ch1-01-hello.rs](examples/ch1-01-hello.rs)
10+
- [examples/ch1-02-hello-join.rs](examples/ch1-02-hello-join.rs)
11+
- [examples/ch1-03-spawn-closure.rs](examples/ch1-03-spawn-closure.rs)
12+
- [examples/ch1-04-scoped-threads.rs](examples/ch1-04-scoped-threads.rs)
13+
- [examples/ch1-05-rc.rs](examples/ch1-05-rc.rs)
14+
- [examples/ch1-06-cell.rs](examples/ch1-06-cell.rs)
15+
- [examples/ch1-07-refcell.rs](examples/ch1-07-refcell.rs)
16+
- [examples/ch1-08-mutex.rs](examples/ch1-08-mutex.rs)
17+
- [examples/ch1-09-sleep-before-unlock.rs](examples/ch1-09-sleep-before-unlock.rs)
18+
- [examples/ch1-10-unlock-before-sleep.rs](examples/ch1-10-unlock-before-sleep.rs)
19+
- [examples/ch1-11-thread-parking.rs](examples/ch1-11-thread-parking.rs)
20+
- [examples/ch1-12-condvar.rs](examples/ch1-12-condvar.rs)
21+
22+
### Chapter 2 — Atomics
23+
24+
- [examples/ch2-01-stop-flag.rs](examples/ch2-01-stop-flag.rs)
25+
- [examples/ch2-02-progress-reporting.rs](examples/ch2-02-progress-reporting.rs)
26+
- [examples/ch2-03-progress-reporting-unpark.rs](examples/ch2-03-progress-reporting-unpark.rs)
27+
- [examples/ch2-04-lazy-init.rs](examples/ch2-04-lazy-init.rs)
28+
- [examples/ch2-05-fetch-add.rs](examples/ch2-05-fetch-add.rs)
29+
- [examples/ch2-06-progress-reporting-multiple-threads.rs](examples/ch2-06-progress-reporting-multiple-threads.rs)
30+
- [examples/ch2-07-statistics.rs](examples/ch2-07-statistics.rs)
31+
- [examples/ch2-08-id-allocation.rs](examples/ch2-08-id-allocation.rs)
32+
- [examples/ch2-09-id-allocation-panic.rs](examples/ch2-09-id-allocation-panic.rs)
33+
- [examples/ch2-10-id-allocation-subtract-before-panic.rs](examples/ch2-10-id-allocation-subtract-before-panic.rs)
34+
- [examples/ch2-11-increment-with-compare-exchange.rs](examples/ch2-11-increment-with-compare-exchange.rs)
35+
- [examples/ch2-12-id-allocation-without-overflow.rs](examples/ch2-12-id-allocation-without-overflow.rs)
36+
- [examples/ch2-13-lazy-one-time-init.rs](examples/ch2-13-lazy-one-time-init.rs)
37+
38+
### Chapter 3 — Memory Ordering
39+
40+
- [examples/ch3-01-relaxed.rs](examples/ch3-01-relaxed.rs)
41+
- [examples/ch3-02-spawn-join.rs](examples/ch3-02-spawn-join.rs)
42+
- [examples/ch3-03-total-modification-order.rs](examples/ch3-03-total-modification-order.rs)
43+
- [examples/ch3-04-total-modification-order-2.rs](examples/ch3-04-total-modification-order-2.rs)
44+
- [examples/ch3-05-out-of-thin-air.rs](examples/ch3-05-out-of-thin-air.rs)
45+
- [examples/ch3-06-release-acquire.rs](examples/ch3-06-release-acquire.rs)
46+
- [examples/ch3-07-release-acquire-unsafe.rs](examples/ch3-07-release-acquire-unsafe.rs)
47+
- [examples/ch3-08-lock.rs](examples/ch3-08-lock.rs)
48+
- [examples/ch3-09-lazy-init-box.rs](examples/ch3-09-lazy-init-box.rs)
49+
- [examples/ch3-10-seqcst.rs](examples/ch3-10-seqcst.rs)
50+
- [examples/ch3-11-fence.rs](examples/ch3-11-fence.rs)
51+
52+
### Chapter 4 — Building Our Own Spin Lock
53+
54+
- [src/ch4_spin_lock/s1_minimal.rs](src/ch4_spin_lock/s1_minimal.rs)
55+
- [src/ch4_spin_lock/s2_unsafe.rs](src/ch4_spin_lock/s2_unsafe.rs)
56+
- [src/ch4_spin_lock/s3_guard.rs](src/ch4_spin_lock/s3_guard.rs)
57+
58+
### Chapter 5 — Building Our Own Channels
59+
60+
- [src/ch5_channels/s1_simple.rs](src/ch5_channels/s1_simple.rs)
61+
- [src/ch5_channels/s2_unsafe.rs](src/ch5_channels/s2_unsafe.rs)
62+
- [src/ch5_channels/s3_checks.rs](src/ch5_channels/s3_checks.rs)
63+
- [src/ch5_channels/s3_single_atomic.rs](src/ch5_channels/s3_single_atomic.rs)
64+
- [src/ch5_channels/s4_types.rs](src/ch5_channels/s4_types.rs)
65+
- [src/ch5_channels/s5_borrowing.rs](src/ch5_channels/s5_borrowing.rs)
66+
- [src/ch5_channels/s6_blocking.rs](src/ch5_channels/s6_blocking.rs)
67+
68+
### Chapter 6 — Building Our Own “Arc”
69+
70+
- [src/ch6_arc/s1_basic.rs](src/ch6_arc/s1_basic.rs)
71+
- [src/ch6_arc/s2_weak.rs](src/ch6_arc/s2_weak.rs)
72+
- [src/ch6_arc/s3_optimized.rs](src/ch6_arc/s3_optimized.rs)
73+
74+
### Chapter 7 — Understanding the Processor
75+
76+
- https://godbolt.org/
77+
78+
### Chapter 8 — Operating System Primitives
79+
80+
- [examples/ch8-01-futex.rs](examples/ch8-01-futex.rs)
81+
82+
### Chapter 9 — Building Our Own Locks
83+
84+
- [src/ch9_locks/mutex_1.rs](src/ch9_locks/mutex_1.rs)
85+
- [src/ch9_locks/mutex_2.rs](src/ch9_locks/mutex_2.rs)
86+
- [src/ch9_locks/mutex_3.rs](src/ch9_locks/mutex_3.rs)
87+
- [src/ch9_locks/condvar_1.rs](src/ch9_locks/condvar_1.rs)
88+
- [src/ch9_locks/condvar_2.rs](src/ch9_locks/condvar_2.rs)
89+
- [src/ch9_locks/rwlock_1.rs](src/ch9_locks/rwlock_1.rs)
90+
- [src/ch9_locks/rwlock_2.rs](src/ch9_locks/rwlock_2.rs)
91+
- [src/ch9_locks/rwlock_3.rs](src/ch9_locks/rwlock_3.rs)
92+
93+
### Chapter 10 — Ideas and Inspiration
94+
95+
- [Wikipedia article on semaphores](https://en.wikipedia.org/wiki/Semaphore_(programming))
96+
- [Stanford University course notes on semaphores](https://see.stanford.edu/materials/icsppcs107/23-Concurrency-Examples.pdf)
97+
- [Wikipedia article on the read-copy-update pattern](https://en.wikipedia.org/wiki/Read-copy-update)
98+
- [LWN article "What is RCU, Fundamentally?"](https://lwn.net/Articles/262464/)
99+
- [Wikipedia article on non-blocking linked lists](https://en.wikipedia.org/wiki/Non-blocking_linked_list)
100+
- [LWN article "Using RCU for Linked Lists—A Case Study"](https://lwn.net/Articles/610972/)
101+
- [Notes on the implementation of Windows SRW locks](https://github.com/rust-lang/rust/issues/93740#issuecomment-1064139337)
102+
- [A Rust implementation of queue-based locks](https://github.com/kprotty/usync)
103+
- [WebKit blog post, "Locking in WebKit"](https://webkit.org/blog/6161/locking-in-webkit/)
104+
- [Documentation of the `parking_lot` crate](https://docs.rs/parking_lot)
105+
- [Wikipedia article on Linux's Seqlock](https://en.wikipedia.org/wiki/Seqlock)
106+
- [Rust RFC 3301, `AtomicPerByte`](https://rust.tf/rfc3301)
107+
- [Documentation of the `seqlock` crate](https://docs.rs/seqlock)
108+
109+
### License
110+
111+
You may use all code in this repository for any purpose.
112+
113+
Attribution is appreciated, but not required.
114+
An attrubution usually includes the book title, author, publisher, and ISBN.
115+
For example: "_Rust Atomics and Locks_ by Mara Bos (O’Reilly). Copyright 2023 Mara Bos, 978-1-098-11944-7."

examples/ch1-01-hello.rs

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
use std::thread;
2+
3+
fn main() {
4+
thread::spawn(f);
5+
thread::spawn(f);
6+
7+
println!("Hello from the main thread.");
8+
}
9+
10+
fn f() {
11+
println!("Hello from another thread!");
12+
13+
let id = thread::current().id();
14+
println!("This is my thread id: {id:?}");
15+
}

examples/ch1-02-hello-join.rs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
use std::thread;
2+
3+
fn main() {
4+
let t1 = thread::spawn(f);
5+
let t2 = thread::spawn(f);
6+
7+
println!("Hello from the main thread.");
8+
9+
t1.join().unwrap();
10+
t2.join().unwrap();
11+
}
12+
13+
fn f() {
14+
println!("Hello from another thread!");
15+
16+
let id = thread::current().id();
17+
println!("This is my thread id: {id:?}");
18+
}

examples/ch1-03-spawn-closure.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use std::thread;
2+
3+
fn main() {
4+
let numbers = vec![1, 2, 3];
5+
6+
thread::spawn(move || {
7+
for n in numbers {
8+
println!("{n}");
9+
}
10+
}).join().unwrap();
11+
}

examples/ch1-04-scoped-threads.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
use std::thread;
2+
3+
fn main() {
4+
let numbers = vec![1, 2, 3];
5+
6+
thread::scope(|s| {
7+
s.spawn(|| {
8+
println!("length: {}", numbers.len());
9+
});
10+
s.spawn(|| {
11+
for n in &numbers {
12+
println!("{n}");
13+
}
14+
});
15+
});
16+
}

examples/ch1-05-rc.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
use std::rc::Rc;
2+
3+
fn main() {
4+
let a = Rc::new([1, 2, 3]);
5+
let b = a.clone();
6+
7+
assert_eq!(a.as_ptr(), b.as_ptr()); // Same allocation!
8+
}

examples/ch1-06-cell.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::cell::Cell;
2+
3+
fn f(v: &Cell<Vec<i32>>) {
4+
let mut v2 = v.take(); // Replaces the contents of the Cell with an empty Vec
5+
v2.push(1);
6+
v.set(v2); // Put the modified Vec back
7+
}
8+
9+
fn main() {
10+
let v = Cell::new(vec![1, 2, 3]);
11+
f(&v);
12+
assert_eq!(v.into_inner(), vec![1, 2, 3, 1]);
13+
}

examples/ch1-07-refcell.rs

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
use std::cell::RefCell;
2+
3+
fn f(v: &RefCell<Vec<i32>>) {
4+
v.borrow_mut().push(1); // We can modify the `Vec` directly.
5+
}
6+
7+
fn main() {
8+
let v = RefCell::new(vec![1, 2, 3]);
9+
f(&v);
10+
assert_eq!(v.into_inner(), vec![1, 2, 3, 1]);
11+
}

examples/ch1-08-mutex.rs

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use std::sync::Mutex;
2+
use std::thread;
3+
4+
fn main() {
5+
let n = Mutex::new(0);
6+
thread::scope(|s| {
7+
for _ in 0..10 {
8+
s.spawn(|| {
9+
let mut guard = n.lock().unwrap();
10+
for _ in 0..100 {
11+
*guard += 1;
12+
}
13+
});
14+
}
15+
});
16+
assert_eq!(n.into_inner().unwrap(), 1000);
17+
}
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use std::sync::Mutex;
2+
use std::thread;
3+
use std::time::Duration;
4+
5+
fn main() {
6+
let n = Mutex::new(0);
7+
thread::scope(|s| {
8+
for _ in 0..10 {
9+
s.spawn(|| {
10+
let mut guard = n.lock().unwrap();
11+
for _ in 0..100 {
12+
*guard += 1;
13+
}
14+
thread::sleep(Duration::from_secs(1)); // New!
15+
});
16+
}
17+
});
18+
assert_eq!(n.into_inner().unwrap(), 1000);
19+
}
+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use std::sync::Mutex;
2+
use std::thread;
3+
use std::time::Duration;
4+
5+
fn main() {
6+
let n = Mutex::new(0);
7+
thread::scope(|s| {
8+
for _ in 0..10 {
9+
s.spawn(|| {
10+
let mut guard = n.lock().unwrap();
11+
for _ in 0..100 {
12+
*guard += 1;
13+
}
14+
drop(guard); // New: drop the guard before sleeping!
15+
thread::sleep(Duration::from_secs(1));
16+
});
17+
}
18+
});
19+
assert_eq!(n.into_inner().unwrap(), 1000);
20+
}

examples/ch1-11-thread-parking.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use std::collections::VecDeque;
2+
use std::sync::Mutex;
3+
use std::thread;
4+
use std::time::Duration;
5+
6+
fn main() {
7+
let queue = Mutex::new(VecDeque::new());
8+
9+
thread::scope(|s| {
10+
// Consuming thread
11+
let t = s.spawn(|| loop {
12+
let item = queue.lock().unwrap().pop_front();
13+
if let Some(item) = item {
14+
dbg!(item);
15+
} else {
16+
thread::park();
17+
}
18+
});
19+
20+
// Producing thread
21+
for i in 0.. {
22+
queue.lock().unwrap().push_back(i);
23+
t.thread().unpark();
24+
thread::sleep(Duration::from_secs(1));
25+
}
26+
});
27+
}

examples/ch1-12-condvar.rs

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use std::collections::VecDeque;
2+
use std::sync::Condvar;
3+
use std::sync::Mutex;
4+
use std::thread;
5+
use std::time::Duration;
6+
7+
fn main() {
8+
let queue = Mutex::new(VecDeque::new());
9+
let not_empty = Condvar::new();
10+
11+
thread::scope(|s| {
12+
s.spawn(|| {
13+
loop {
14+
let mut q = queue.lock().unwrap();
15+
let item = loop {
16+
if let Some(item) = q.pop_front() {
17+
break item;
18+
} else {
19+
q = not_empty.wait(q).unwrap();
20+
}
21+
};
22+
drop(q);
23+
dbg!(item);
24+
}
25+
});
26+
27+
for i in 0.. {
28+
queue.lock().unwrap().push_back(i);
29+
not_empty.notify_one();
30+
thread::sleep(Duration::from_secs(1));
31+
}
32+
});
33+
}

examples/ch2-01-stop-flag.rs

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
use std::sync::atomic::AtomicBool;
2+
use std::sync::atomic::Ordering::Relaxed;
3+
use std::thread;
4+
use std::time::Duration;
5+
6+
fn main() {
7+
static STOP: AtomicBool = AtomicBool::new(false);
8+
9+
// Spawn a thread to do the work.
10+
let background_thread = thread::spawn(|| {
11+
while !STOP.load(Relaxed) {
12+
some_work();
13+
}
14+
});
15+
16+
// Use the main thread to listen for user input.
17+
for line in std::io::stdin().lines() {
18+
match line.unwrap().as_str() {
19+
"help" => println!("commands: help, stop"),
20+
"stop" => break,
21+
cmd => println!("unknown command: {cmd:?}"),
22+
}
23+
}
24+
25+
// Inform the background thread it needs to stop.
26+
STOP.store(true, Relaxed);
27+
28+
// Wait until the background thread finishes.
29+
background_thread.join().unwrap();
30+
}
31+
32+
fn some_work() {
33+
thread::sleep(Duration::from_millis(100));
34+
}

0 commit comments

Comments
 (0)