Skip to content

Commit 1b988a8

Browse files
authored
Merge pull request #67 from ghimiresdp/feature/arc-mutex
Added Arc Mutex example
2 parents 52617c8 + b16113e commit 1b988a8

3 files changed

Lines changed: 178 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ cargo test --bin huffman
181181
- [Box Pointers](advanced/smart-pointers/src/box.rs) `cargo run --bin box`
182182
- [Reference Counters (`Rc`)](advanced/smart-pointers/src/rc.rs) `cargo run --bin rc`
183183
- [`RefCell`](advanced/smart-pointers/src/refcell.rs)`cargo run --bin refcell`
184+
- [`Arc Mutex`](advanced/smart-pointers/src/arc-mutex.rs)`cargo run --bin arc-mutex`
184185

185186
### 5.9. Specialized topics
186187
- Writing a custom allocator

advanced/smart-pointers/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ path = 'src/rc.rs'
1919
[[bin]]
2020
name = 'refcell'
2121
path = 'src/refcell.rs'
22+
23+
[[bin]]
24+
name = 'arc-mutex'
25+
path = 'src/arc-mutex.rs'
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use std::sync::{Arc, Mutex};
2+
use std::thread;
3+
use std::time::Duration;
4+
/**
5+
* # Mutex
6+
* Mutex ( MUTual EXclusion ) is a synchronization primitive that allows multiple
7+
* threads to access a shared resource safely.
8+
*
9+
* Think mutex as a rented bike which can be used by multiple users, however,
10+
* one should first reserve before being able to ride. The second user will be
11+
* able to ride the bike only once the first user returns it to the owner.
12+
*
13+
* In case of mutex, a thread must lock the resource to be able to mutate it.
14+
* after its operation is completed, the thread releases the lock and the next
15+
* thread in the queue will be able to use it.
16+
*
17+
* when we lock a mutex, it returns a smart pointer to the resource called a
18+
* MutexGuard which is a smart pointer that releases the lock when it goes out
19+
* of scope.
20+
*/
21+
22+
struct Rent {
23+
user: String,
24+
time: usize,
25+
}
26+
27+
struct Bike {
28+
ride_time: usize,
29+
}
30+
31+
impl Bike {
32+
fn new() -> Self {
33+
Self { ride_time: 0 }
34+
}
35+
36+
fn ride(&mut self, time: usize) {
37+
println!("A {} hours bike ride has been started", time);
38+
thread::sleep(Duration::from_secs(time as u64));
39+
self.ride_time += time;
40+
println!("ride complete!!");
41+
}
42+
}
43+
44+
impl Rent {
45+
fn new(user: &str, time: usize) -> Self {
46+
Self {
47+
user: user.to_owned(),
48+
time,
49+
}
50+
}
51+
}
52+
53+
fn main() {
54+
// in this example, a bike will be rented by 4 different people, however
55+
// it is not possible to ride a bike by all at once, so mutex guard will
56+
// make sure that the bike is mutated by only one user.
57+
//
58+
println!("\n{}", "=".repeat(50));
59+
60+
let bike = Arc::new(Mutex::new(Bike::new()));
61+
let rentals = vec![
62+
Rent::new("Alice", 5),
63+
Rent::new("Bob", 2),
64+
Rent::new("Charlie", 4),
65+
Rent::new("Daniel", 3),
66+
];
67+
68+
// we keep track of thread handles here
69+
let mut handles = vec![];
70+
71+
// iterate through rentals
72+
for rent in rentals {
73+
// clone for each thread
74+
let bike = Arc::clone(&bike);
75+
76+
// create a thread handle
77+
// here we need to have the ownership of the bike to be able to mutate
78+
// so we need to use move along with closure
79+
let handle = thread::spawn(move || {
80+
// since the process is threaded, waiting order might
81+
// differ on each execution.
82+
println!("[{:^20}] waiting!!", &rent.user.clone());
83+
84+
let mut rental = bike.lock().unwrap();
85+
println!("{}", "-".repeat(50));
86+
println!(
87+
"The bike is now rented by {} for {} hours.",
88+
&rent.user.clone(),
89+
&rent.time
90+
);
91+
92+
// the ride() method emulated bike ride and makes the thread sleep
93+
// for the given time in seconds. Until the thread is completed,
94+
// another thread will not be able to lock the bike so it will wait
95+
// until previous thread is released.
96+
rental.ride(rent.time);
97+
98+
println!("The bike ran {} hours.", rental.ride_time);
99+
println!("[ The bike has been returned ]")
100+
});
101+
handles.push(handle);
102+
}
103+
104+
for handle in handles {
105+
handle.join().unwrap();
106+
}
107+
println!("{}", "=".repeat(50));
108+
println!(
109+
"The bike had a total ride of {} hours",
110+
bike.as_ref().lock().unwrap().ride_time
111+
);
112+
}
113+
// Output (order might differ) due to threading
114+
// ==================================================
115+
// [ Alice ] waiting!!
116+
// --------------------------------------------------
117+
// The bike is now rented by Alice for 5 hours.
118+
// [ Charlie ] waiting!!
119+
// [ Daniel ] waiting!!
120+
// A 5 hours bike ride has been started
121+
// [ Bob ] waiting!!
122+
// ride complete!!
123+
// The bike ran 5 hours.
124+
// [ The bike has been returned ]
125+
// --------------------------------------------------
126+
// The bike is now rented by Charlie for 4 hours.
127+
// A 4 hours bike ride has been started
128+
// ride complete!!
129+
// The bike ran 9 hours.
130+
// [ The bike has been returned ]
131+
// --------------------------------------------------
132+
// The bike is now rented by Daniel for 3 hours.
133+
// A 3 hours bike ride has been started
134+
// ride complete!!
135+
// The bike ran 12 hours.
136+
// [ The bike has been returned ]
137+
// --------------------------------------------------
138+
// The bike is now rented by Bob for 2 hours.
139+
// A 2 hours bike ride has been started
140+
// ride complete!!
141+
// The bike ran 14 hours.
142+
// [ The bike has been returned ]
143+
// ==================================================
144+
// The bike had a total ride of 14 hours
145+
146+
#[cfg(test)]
147+
mod test {
148+
use std::sync::{Arc, Mutex};
149+
150+
use crate::Bike;
151+
152+
#[test]
153+
fn test_lock_unlock() {
154+
let bike = Arc::new(Mutex::new(Bike::new()));
155+
156+
// Lock and mutate
157+
{
158+
let mut guard = bike.lock().unwrap();
159+
guard.ride(5);
160+
assert_eq!(guard.ride_time, 5);
161+
// guard goes out of scope here, lock is released
162+
}
163+
// remember, the first lock is released here as the guard goes out of
164+
// scope, so we can lock it again otherwise it will result in a deadlock
165+
// and the program will hang forever.
166+
// Lock again and mutate
167+
{
168+
let mut guard = bike.lock().unwrap();
169+
guard.ride(3); // total ride time is now 8
170+
assert_eq!(guard.ride_time, 8);
171+
}
172+
}
173+
}

0 commit comments

Comments
 (0)