// Example 125: Send and Sync Marker Traits
//
// Send: type can be transferred to another thread
// Sync: type can be shared (via &T) between threads
// These are auto-implemented and enforced at compile time.
use std::sync::{Arc, Mutex, mpsc};
use std::thread;
// Approach 1: Send โ transferring ownership to threads
fn approach1() {
// Vec<i32> is Send โ can be moved to another thread
let data = vec![1, 2, 3, 4, 5];
let handle = thread::spawn(move || {
data.iter().sum::<i32>()
});
let sum = handle.join().unwrap();
assert_eq!(sum, 15);
println!("Sum: {} (sent Vec to thread)", sum);
// String is Send
let msg = String::from("hello from main");
let handle = thread::spawn(move || {
println!("Thread received: {}", msg);
msg.len()
});
assert_eq!(handle.join().unwrap(), 15);
}
// Approach 2: Sync โ sharing references between threads
fn approach2() {
// Arc<Mutex<T>> is both Send and Sync
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
handles.push(thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
}));
}
for h in handles {
h.join().unwrap();
}
assert_eq!(*counter.lock().unwrap(), 10);
println!("Counter: {} (Mutex + Arc = thread-safe)", counter.lock().unwrap());
}
// Approach 3: Channel-based message passing (Send required)
fn approach3() {
let (tx, rx) = mpsc::channel();
// Spawn producers
for i in 1..=5 {
let tx = tx.clone();
thread::spawn(move || {
tx.send(i).unwrap(); // i32 is Send
});
}
drop(tx); // close the sender
let sum: i32 = rx.iter().sum();
assert_eq!(sum, 15);
println!("Channel sum: {}", sum);
}
// Types that are NOT Send/Sync
fn _not_send_sync_examples() {
use std::rc::Rc;
use std::cell::Cell;
let _rc = Rc::new(42);
// thread::spawn(move || *_rc); // ERROR: Rc is not Send
let _cell = Cell::new(42);
// Can't share &Cell between threads: Cell is not Sync
}
fn main() {
println!("=== Approach 1: Send (Transfer) ===");
approach1();
println!("\n=== Approach 2: Sync (Share) ===");
approach2();
println!("\n=== Approach 3: Channels ===");
approach3();
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_send_to_thread() {
let v = vec![1, 2, 3];
let h = thread::spawn(move || v.len());
assert_eq!(h.join().unwrap(), 3);
}
#[test]
fn test_arc_is_sync() {
let a = Arc::new(42);
let a2 = Arc::clone(&a);
let h = thread::spawn(move || *a2);
assert_eq!(h.join().unwrap(), 42);
assert_eq!(*a, 42);
}
#[test]
fn test_mutex_shared_mutation() {
let m = Arc::new(Mutex::new(Vec::new()));
let m2 = Arc::clone(&m);
let h = thread::spawn(move || {
m2.lock().unwrap().push(1);
});
h.join().unwrap();
m.lock().unwrap().push(2);
assert_eq!(m.lock().unwrap().len(), 2);
}
#[test]
fn test_channel_communication() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || tx.send(42).unwrap());
assert_eq!(rx.recv().unwrap(), 42);
}
fn _assert_send<T: Send>() {}
fn _assert_sync<T: Sync>() {}
#[test]
fn test_marker_traits() {
_assert_send::<String>();
_assert_send::<Vec<i32>>();
_assert_send::<Arc<i32>>();
_assert_sync::<Arc<i32>>();
_assert_sync::<Mutex<i32>>();
// Rc is NOT Send: _assert_send::<Rc<i32>>(); // won't compile
}
}
(* Example 125: Send and Sync โ Thread Safety Guarantees *)
(* OCaml 5 has domains for parallelism. Before that, the GIL
prevented true parallelism. Send/Sync concepts don't exist. *)
(* Approach 1: Thread-safe immutable data *)
let approach1 () =
let data = [1; 2; 3; 4; 5] in
(* In OCaml 5 with domains, immutable data is inherently safe *)
let sum = List.fold_left ( + ) 0 data in
assert (sum = 15);
Printf.printf "Sum: %d (thread-safe: immutable)\n" sum
(* Approach 2: Mutex for shared mutable state *)
let approach2 () =
let counter = Mutex.create () in
let count = ref 0 in
(* Simulating thread-safe mutation *)
for _ = 1 to 10 do
Mutex.lock counter;
incr count;
Mutex.unlock counter
done;
assert (!count = 10);
Printf.printf "Counter: %d (mutex-protected)\n" !count
(* Approach 3: Message passing via channels *)
let approach3 () =
(* OCaml doesn't have std channels like Rust, but we can simulate *)
let queue = Queue.create () in
for i = 1 to 5 do
Queue.add i queue
done;
let sum = ref 0 in
while not (Queue.is_empty queue) do
sum := !sum + Queue.pop queue
done;
assert (!sum = 15);
Printf.printf "Channel sum: %d\n" !sum
let () =
approach1 ();
approach2 ();
approach3 ();
Printf.printf "โ All tests passed\n"