diff --git a/module2/data_race/Cargo.toml b/module2/data_race/Cargo.toml new file mode 100644 index 0000000..57962bc --- /dev/null +++ b/module2/data_race/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "data_race" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/module2/data_race/Makefile b/module2/data_race/Makefile new file mode 100644 index 0000000..4daa6f8 --- /dev/null +++ b/module2/data_race/Makefile @@ -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 \ No newline at end of file diff --git a/module2/data_race/src/main.rs b/module2/data_race/src/main.rs new file mode 100644 index 0000000..ee66e9b --- /dev/null +++ b/module2/data_race/src/main.rs @@ -0,0 +1,113 @@ +// Mutex that protects the data vector, and then we spawn three threads +//that each acquire a lock on the mutex and modify an element of the vector. +// https://doc.rust-lang.org/std/sync/struct.Mutex.html + +use std::sync::{Arc, Condvar, Mutex, RwLock}; +use std::thread; + +fn using_mutex() { + println!("Mutex Example"); + let data = Arc::new(Mutex::new(vec![1, 2, 3])); + + let handles: Vec<_> = (0..3) + .map(|i| { + let data = Arc::clone(&data); + thread::spawn(move || { + let mut data = data.lock().unwrap(); + data[i] += 1; + println!("{:?}", data); + }) + }) + .collect(); + + for handle in handles { + handle.join().unwrap(); + } + + let data = data.lock().unwrap(); + println!("Final data: {:?}", *data); +} + +fn using_rwlock() { + println!("RwLock Example"); + // Create an RwLock protecting a vector + let data = Arc::new(RwLock::new(vec![1, 2, 3])); + + // Spawn three threads that each acquire a write lock on the RwLock and modify an element of the vector + let handles: Vec<_> = (0..3) + .map(|i| { + let data = Arc::clone(&data); + thread::spawn(move || { + let mut data = data.write().unwrap(); + data[i] += 1; + println!("Thread {}: {:?}", i, *data); + }) + }) + .collect(); + + // Wait for all threads to finish + for handle in handles { + handle.join().unwrap(); + } + + // Acquire a read lock to print the final state of the vector + let data = data.read().unwrap(); + + println!("Final data: {:?}", *data); +} + +fn using_condvar() { + println!("Condvar Example"); + let data = Arc::new((Mutex::new(vec![1, 2, 3]), Condvar::new())); + let mut handles = vec![]; + + for i in 0..3 { + let data = Arc::clone(&data); + let handle = thread::spawn(move || { + let (lock, cvar) = &*data; + let mut data = lock.lock().unwrap(); + data[i] += 1; + println!("Thread {}: {:?}", i, *data); + cvar.notify_one(); + }); + handles.push(handle); + } + + for handle in handles { + handle.join().unwrap(); + } + + let (lock, cvar) = &*data; + let mut data = lock.lock().unwrap(); + while data.iter().any(|&x| x == 1) { + data = cvar.wait(data).unwrap(); + } + + println!("Final data: {:?}", *data); +} + +fn main() { + using_mutex(); + using_rwlock(); + using_condvar(); +} + +/* + +// This code will not compile because the borrow checker will prevent it. +use std::thread; + +fn main() { + let mut data = vec![1, 2, 3]; + + for i in 0..3 { + // Try to capture a mutable reference in multiple threads + // This will fail to compile! + thread::spawn(move || { + data[i] += 1; + }); + } + + // No data race can occur, this will not compile. +} +*/