// 987: Read-Write Lock Pattern
// Rust: RwLock<T> โ many readers OR one writer, never both
use std::sync::{Arc, RwLock};
use std::thread;
// --- Approach 1: Multiple readers in parallel ---
fn concurrent_readers() -> Vec<i32> {
let data = Arc::new(RwLock::new(42i32));
let handles: Vec<_> = (0..5).map(|_| {
let data = Arc::clone(&data);
thread::spawn(move || {
let guard = data.read().unwrap(); // shared read lock
*guard // all 5 can hold read lock simultaneously
})
}).collect();
handles.into_iter().map(|h| h.join().unwrap()).collect()
}
// --- Approach 2: Writer excludes readers ---
fn write_then_read() -> i32 {
let data = Arc::new(RwLock::new(0i32));
{
let mut guard = data.write().unwrap(); // exclusive write lock
*guard = 100;
// guard drops here โ write lock released
}
let guard = data.read().unwrap();
*guard
}
// --- Approach 3: Shared config pattern (read-heavy) ---
#[derive(Clone, Debug)]
struct Config {
threshold: i32,
name: String,
}
fn config_pattern() -> (String, i32) {
let config = Arc::new(RwLock::new(Config {
threshold: 10,
name: "default".to_string(),
}));
// Many readers
let readers: Vec<_> = (0..4).map(|_| {
let config = Arc::clone(&config);
thread::spawn(move || {
let c = config.read().unwrap();
(c.name.clone(), c.threshold)
})
}).collect();
// One writer updates the config
{
let cfg = Arc::clone(&config);
let writer = thread::spawn(move || {
let mut c = cfg.write().unwrap();
c.threshold = 99;
c.name = "updated".to_string();
});
writer.join().unwrap();
}
for h in readers { h.join().unwrap(); } // let readers finish
let c = config.read().unwrap();
(c.name.clone(), c.threshold)
}
fn main() {
let reads = concurrent_readers();
println!("concurrent readers: {:?}", reads);
let v = write_then_read();
println!("write then read: {}", v);
let (name, threshold) = config_pattern();
println!("config after update: name={} threshold={}", name, threshold);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_concurrent_readers_all_see_same() {
let reads = concurrent_readers();
assert_eq!(reads.len(), 5);
assert!(reads.iter().all(|&v| v == 42));
}
#[test]
fn test_write_then_read() {
assert_eq!(write_then_read(), 100);
}
#[test]
fn test_config_pattern() {
let (name, threshold) = config_pattern();
assert_eq!(name, "updated");
assert_eq!(threshold, 99);
}
#[test]
fn test_try_read_write() {
let rw = RwLock::new(0i32);
let _r1 = rw.read().unwrap();
let _r2 = rw.read().unwrap(); // multiple reads OK
// rw.try_write() would fail here (readers active)
assert!(rw.try_write().is_err());
}
#[test]
fn test_rwlock_write_exclusive() {
let rw = Arc::new(RwLock::new(vec![1, 2, 3]));
{
let mut w = rw.write().unwrap();
w.push(4);
}
assert_eq!(*rw.read().unwrap(), vec![1, 2, 3, 4]);
}
}
(* 987: Read-Write Lock Pattern *)
(* OCaml: Simulated RwLock using reader count + writer mutex *)
(* --- RwLock simulation: multiple readers OR one writer --- *)
type 'a rwlock = {
mutable data: 'a;
mutable readers: int;
m: Mutex.t;
can_write: Condition.t;
can_read: Condition.t;
mutable writer_waiting: bool;
}
let make_rwlock v = {
data = v;
readers = 0;
m = Mutex.create ();
can_write = Condition.create ();
can_read = Condition.create ();
writer_waiting = false;
}
let read_lock rw =
Mutex.lock rw.m;
while rw.writer_waiting do
Condition.wait rw.can_read rw.m
done;
rw.readers <- rw.readers + 1;
Mutex.unlock rw.m
let read_unlock rw =
Mutex.lock rw.m;
rw.readers <- rw.readers - 1;
if rw.readers = 0 then Condition.signal rw.can_write;
Mutex.unlock rw.m
let write_lock rw =
Mutex.lock rw.m;
rw.writer_waiting <- true;
while rw.readers > 0 do
Condition.wait rw.can_write rw.m
done
let write_unlock rw =
rw.writer_waiting <- false;
Condition.broadcast rw.can_read;
Mutex.unlock rw.m
let with_read rw f =
read_lock rw;
let result = (try f rw.data with e -> read_unlock rw; raise e) in
read_unlock rw;
result
let with_write rw f =
write_lock rw;
(try f rw with e -> write_unlock rw; raise e);
write_unlock rw
(* --- Approach 1: Multiple readers, no conflict --- *)
let () =
let rw = make_rwlock 42 in
(* Multiple concurrent readers *)
let threads = List.init 5 (fun _ ->
Thread.create (fun () ->
let v = with_read rw (fun x -> x) in
assert (v = 42)
) ()
) in
List.iter Thread.join threads;
Printf.printf "Approach 1 (multiple readers): all read %d\n" 42
(* --- Approach 2: Writer updates, readers see new value --- *)
let () =
let rw = make_rwlock 0 in
let writer = Thread.create (fun () ->
with_write rw (fun rw -> rw.data <- 100)
) () in
Thread.join writer;
let v = with_read rw (fun x -> x) in
assert (v = 100);
Printf.printf "Approach 2 (writer then reader): %d\n" v
let () = Printf.printf "โ All tests passed\n"