generated from JadeCara/rust_setup
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #9 from JadeCara/jade/threading_philosophers
threaded dining
- Loading branch information
Showing
3 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[package] | ||
name = "dining_philosophers" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
SHELL := /bin/bash | ||
.PHONY: help | ||
|
||
help: | ||
@grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' | ||
|
||
clean: ## Clean the project using cargo | ||
cargo clean | ||
|
||
build: ## Build the project using cargo | ||
cargo build | ||
|
||
run: ## Run the project using cargo | ||
cargo run | ||
|
||
test: ## Run the tests using cargo | ||
cargo test | ||
|
||
lint: ## Run the linter using cargo | ||
@rustup component add clippy 2> /dev/null | ||
cargo clippy | ||
|
||
format: ## Format the code using cargo | ||
@rustup component add rustfmt 2> /dev/null | ||
cargo fmt | ||
|
||
release: | ||
cargo build --release | ||
|
||
all: format lint test run | ||
|
||
bump: ## Bump the version of the project | ||
@echo "Current version is $(shell cargo pkgid | cut -d# -f2)" | ||
@read -p "Enter the new version: " version; \ | ||
updated_version=$$(cargo pkgid | cut -d# -f2 | sed "s/$(shell cargo pkgid | cut -d# -f2)/$$version/"); \ | ||
sed -i -E "s/^version = .*/version = \"$$updated_version\"/" Cargo.toml | ||
@echo "Version bumped to $$(cargo pkgid | cut -d# -f2)" | ||
rm Cargo.toml-e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* The dining philosophers problem involves multiple threads needing | ||
* synchronized access to shared resources, risking deadlock. | ||
* | ||
* This code models philosophers as threads and forks as shared Mutex<()> | ||
* wrapped in Arc for thread-safe reference counting. | ||
* | ||
* To prevent deadlock from a "deadly embrace" of waiting for neighboring | ||
* forks, philosophers acquire lower numbered forks first. This breaks | ||
* symmetry and avoids circular waiting. | ||
* | ||
* The Mutexes provide exclusive fork access. The Arc allows sharing forks | ||
* between philosophers. | ||
* | ||
* The simulation prints start time, eating duration, and total time for | ||
* all philosophers. Total time approximately equals philosophers divided | ||
* by forks, as that number can eat concurrently. | ||
* | ||
* Key techniques: | ||
* - Used Mutex<()> to represent exclusive fork access | ||
* - Wrapped in Arc to share Mutexes between threads | ||
* - Numbered philosophers and acquire lower fork first | ||
* - Prints timing metrics for simulation | ||
* | ||
* There is diminishing returns with concurrency timing is very important to | ||
* establish if threading is worth the overhead. | ||
*/ | ||
|
||
use std::sync::{Arc, Mutex}; | ||
use std::thread; | ||
use std::time::{Duration, Instant}; | ||
|
||
struct Fork { | ||
id: u32, | ||
mutex: Mutex<()>, | ||
} | ||
|
||
struct Philosopher { | ||
id: u32, | ||
name: String, | ||
left_fork: Arc<Fork>, | ||
right_fork: Arc<Fork>, | ||
} | ||
|
||
impl Philosopher { | ||
fn new(id: u32, name: &str, left_fork: Arc<Fork>, right_fork: Arc<Fork>) -> Philosopher { | ||
Philosopher { | ||
id, | ||
name: name.to_string(), | ||
left_fork, | ||
right_fork, | ||
} | ||
} | ||
|
||
fn eat(&self) { | ||
let (first_fork, second_fork) = if self.id % 2 == 0 { | ||
(&self.left_fork, &self.right_fork) | ||
} else { | ||
(&self.right_fork, &self.left_fork) | ||
}; | ||
|
||
let _first_guard = first_fork.mutex.lock().unwrap(); | ||
println!("{} picked up fork {}.", self.name, first_fork.id); | ||
let _second_guard = second_fork.mutex.lock().unwrap(); | ||
println!("{} picked up fork {}.", self.name, second_fork.id); | ||
|
||
println!("{} is eating.", self.name); | ||
thread::sleep(Duration::from_secs(1)); | ||
println!("{} finished eating.", self.name); | ||
|
||
println!("{} put down fork {}.", self.name, first_fork.id); | ||
println!("{} put down fork {}.", self.name, second_fork.id); | ||
} | ||
} | ||
|
||
fn main() { | ||
println!("Dining Philosophers Problem: 15 Philosophers, 4 Forks...Yikes!!"); | ||
|
||
//we only have 4 forks at the table | ||
let forks = (0..4) | ||
.map(|id| { | ||
Arc::new(Fork { | ||
id, | ||
mutex: Mutex::new(()), | ||
}) | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
let philosophers = vec![ | ||
("Jürgen Habermas", 0, 1), | ||
("Friedrich Engels", 1, 2), | ||
("Karl Marx", 2, 3), | ||
("Thomas Piketty", 3, 0), | ||
("Michel Foucault", 0, 1), | ||
("Socrates", 1, 2), | ||
("Plato", 2, 3), | ||
("Aristotle", 3, 0), | ||
("Pythagoras", 0, 1), | ||
("Heraclitus", 1, 2), | ||
("Democritus", 2, 3), | ||
("Diogenes", 3, 0), | ||
("Epicurus", 0, 1), | ||
("Zeno of Citium", 1, 2), | ||
("Thales of Miletus", 2, 3), | ||
] | ||
.into_iter() | ||
.enumerate() | ||
.map(|(id, (name, left, right))| { | ||
Philosopher::new( | ||
id as u32, | ||
name, | ||
Arc::clone(&forks[left]), | ||
Arc::clone(&forks[right]), | ||
) | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
let start = Instant::now(); | ||
|
||
let handles = philosophers | ||
.into_iter() | ||
.map(|philosopher| { | ||
thread::spawn(move || { | ||
philosopher.eat(); | ||
}) | ||
}) | ||
.collect::<Vec<_>>(); | ||
|
||
for handle in handles { | ||
handle.join().unwrap(); | ||
} | ||
|
||
println!("Total time: {:?}", start.elapsed()); | ||
} |