### Hello World in Rust Threads Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A basic example of creating and running a new thread in Rust. ```rust fn main() { std::thread::spawn(|| { println!("Hello from a new thread!"); }); println!("Hello from the main thread!"); } ``` -------------------------------- ### Total Modification Order Example 2 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Another example illustrating total modification order, potentially showing different interleavings. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; fn main() { let x = AtomicUsize::new(0); let y = AtomicUsize::new(0); let mut handles = vec![]; handles.push(thread::spawn(move || { x.store(1, Ordering::SeqCst); y.store(1, Ordering::SeqCst); })); handles.push(thread::spawn(move || { y.store(2, Ordering::SeqCst); x.store(2, Ordering::SeqCst); })); for handle in handles { handle.join().unwrap(); } // With SeqCst, the final values of x and y are consistent across all threads. // Possible outcomes depend on the interleaving, but the order within each thread is preserved. println!("Final x: {}, Final y: {}", x.load(Ordering::SeqCst), y.load(Ordering::SeqCst)); } ``` -------------------------------- ### Futex Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates the use of Futex (Fast Userspace Mutex) for efficient synchronization, often used by the OS. ```rust // Futex operations are typically exposed via system calls and are not directly // available as safe Rust primitives in the standard library. This example is conceptual // and would require FFI (Foreign Function Interface) to interact with the OS. // Example conceptual usage: /* use std::os::raw::c_int; extern "C" { // Hypothetical futex syscall signature (simplified) fn syscall(call: c_long, addr: *mut c_int, op: c_int, val: c_int, ...) -> c_long; } const FUTEX_WAIT: c_int = 0; const FUTEX_WAKE: c_int = 1; fn futex_wait(addr: *mut c_int, expected_val: c_int) { unsafe { syscall(98, addr as _, FUTEX_WAIT, expected_val); } // 98 is __NR_futex on x86_64 } fn futex_wake(addr: *mut c_int, num_wake: c_int) { unsafe { syscall(98, addr as _, FUTEX_WAKE, num_wake); } } // In a real scenario, you'd use this with a Mutex or Condvar implementation. */ ``` -------------------------------- ### Sequential Consistency (SeqCst) Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates `Ordering::SeqCst`, the strongest memory ordering, ensuring a global total order of all atomic operations. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; fn main() { let x = AtomicUsize::new(0); let y = AtomicUsize::new(0); let mut handles = vec![]; // Thread 1 writes to x then y handles.push(thread::spawn(move || { x.store(1, Ordering::SeqCst); y.store(1, Ordering::SeqCst); })); // Thread 2 writes to y then x handles.push(thread::spawn(move || { y.store(2, Ordering::SeqCst); x.store(2, Ordering::SeqCst); })); for handle in handles { handle.join().unwrap(); } // With SeqCst, the final values are consistent across all threads. // The order of operations is globally visible and unambiguous. println!("Final x: {}, Final y: {}", x.load(Ordering::SeqCst), y.load(Ordering::SeqCst)); } ``` -------------------------------- ### Atomic Spawn and Join Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates basic thread spawning and joining, often used in conjunction with atomic operations. ```rust use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { println!("Worker thread started."); thread::sleep(Duration::from_secs(1)); println!("Worker thread finished."); }); println!("Main thread waiting for worker..."); handle.join().unwrap(); println!("Main thread finished."); } ``` -------------------------------- ### Atomic Fence Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates the use of `Ordering::SeqCst` fence for creating synchronization points without necessarily involving specific atomic variables. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; fn main() { let data = Arc::new(AtomicUsize::new(0)); let ready = Arc::new(AtomicUsize::new(0)); let mut handles = vec![]; // Thread 1: Writes data, then uses a fence to ensure visibility handles.push(thread::spawn({ let data = Arc::clone(&data); let ready = Arc::clone(&ready); move || { data.store(42, Ordering::Relaxed); // Ensure the write to 'data' is visible before the 'ready' flag is set. std::sync::atomic::fence(Ordering::Release); ready.store(1, Ordering::Relaxed); println!("Writer: Data written and visibility ensured."); } })); // Thread 2: Waits for the fence synchronization point handles.push(thread::spawn({ let data = Arc::clone(&data); let ready = Arc::clone(&ready); move || { while ready.load(Ordering::Relaxed) == 0 { thread::yield_now(); } // Ensure we see the write to 'data' that happened before the fence. std::sync::atomic::fence(Ordering::Acquire); let value = data.load(Ordering::Relaxed); println!("Reader: Synchronization point reached. Data value: {}", value); } })); for handle in handles { handle.join().unwrap(); } } ``` -------------------------------- ### Release-Acquire Unsafe Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md An unsafe version demonstrating Release-Acquire ordering, often used when dealing with raw pointers or C interop. ```rust use std::sync::atomic::{AtomicPtr, Ordering}; use std::ptr; use std::thread; static mut PTR: AtomicPtr = AtomicPtr::new(ptr::null_mut()); static mut FLAG: AtomicUsize = AtomicUsize::new(0); fn main() { let mut handles = vec![]; // Thread 1: Allocates memory, stores value, sets flag handles.push(thread::spawn(|| { let mut val = Box::new(42); let ptr = Box::into_raw(val); unsafe { // Release ensures the allocation and write are visible before FLAG is set. PTR.store(ptr, Ordering::Release); FLAG.store(1, Ordering::Release); println!("Writer: Pointer set, flag raised."); } })); // Thread 2: Waits for flag, reads pointer, dereferences handles.push(thread::spawn(|| { while unsafe { FLAG.load(Ordering::Acquire) } == 0 { thread::yield_now(); } unsafe { let ptr = PTR.load(Ordering::Acquire); if !ptr.is_null() { // Acquire ensures we see the write to the memory pointed to by PTR. let value = *ptr; println!("Reader: Flag detected. Value read: {}", value); // IMPORTANT: Must deallocate the memory later if it was allocated with Box::into_raw // let _ = Box::from_raw(ptr); } } })); for handle in handles { handle.join().unwrap(); } // Cleanup the allocated memory (simplified) unsafe { let ptr = PTR.load(Ordering::Acquire); if !ptr.is_null() { let _ = Box::from_raw(ptr); } } } ``` -------------------------------- ### Total Modification Order Example 1 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates a scenario related to total modification order, often involving multiple atomic variables. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; fn main() { let v = AtomicUsize::new(0); let mut handles = vec![]; handles.push(thread::spawn(move || { v.store(1, Ordering::SeqCst); })); handles.push(thread::spawn(move || { v.store(2, Ordering::SeqCst); })); for handle in handles { handle.join().unwrap(); } // With SeqCst, the final value is guaranteed to be either 1 or 2, // and the order of writes is globally consistent. println!("Final value (SeqCst): {}", v.load(Ordering::SeqCst)); } ``` -------------------------------- ### Using Mutex for Thread-Safe Mutability Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Shows a basic example of using `Mutex` to protect shared data across threads. ```rust use std::sync::{Mutex, Arc}; use std::thread; fn main() { let counter = Arc::new(Mutex::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { let mut num = counter.lock().unwrap(); *num += 1; }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", *counter.lock().unwrap()); } ``` -------------------------------- ### Atomic Lock Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A basic implementation of a lock using atomic operations, likely a spinlock. ```rust use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use std::thread; struct SpinLock { locked: AtomicBool, } impl SpinLock { fn new() -> Self { SpinLock { locked: AtomicBool::new(false) } } fn lock(&self) { // Spin until the lock is acquired while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { // Spin hint can be used here if available (e.g., `std::hint::spin_loop()`) std::hint::spin_loop(); } } fn unlock(&self) { self.locked.store(false, Ordering::Release); } } fn main() { let lock = Arc::new(SpinLock::new()); let mut handles = vec![]; for _ in 0..5 { let lock = Arc::clone(&lock); let handle = thread::spawn(move || { lock.lock(); println!("Thread acquired lock."); // Simulate work thread::sleep(std::time::Duration::from_millis(100)); println!("Thread releasing lock."); lock.unlock(); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } } ``` -------------------------------- ### Out of Thin Air Example Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates a scenario where a value might appear to be initialized without an explicit write, often due to compiler optimizations or specific hardware behaviors. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; // This example is highly dependent on compiler optimizations and CPU architecture. // It aims to show a potential race condition where a value might be read // before it's explicitly written by another thread, especially without proper memory ordering. static mut SHARED_VALUE: usize = 0; static mut READY: bool = false; fn main() { let mut handles = vec![]; handles.push(thread::spawn(|| { // Simulate some work before writing thread::sleep(std::time::Duration::from_millis(10)); unsafe { SHARED_VALUE = 42; READY = true; // Signal that the value is ready } })); handles.push(thread::spawn(|| { // This thread might read READY as true before SHARED_VALUE is actually written // if memory ordering is not enforced correctly. while !unsafe { READY } { // Spin wait or sleep briefly thread::yield_now(); } // Without proper ordering (like Acquire on read, Release on write), // this read might happen before the write is visible. let val = unsafe { SHARED_VALUE }; println!("Reader thread saw value: {}", val); })); for handle in handles { handle.join().unwrap(); } } ``` -------------------------------- ### Joining a Rust Thread Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates how to create a thread and wait for its completion using `join()`. ```rust fn main() { let handle = std::thread::spawn(|| { println!("Hello from a new thread!"); }); handle.join().unwrap(); println!("Hello from the main thread!"); } ``` -------------------------------- ### Release-Acquire Memory Ordering Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates `Ordering::Release` on a store and `Ordering::Acquire` on a load to establish a synchronization point between threads. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; fn main() { let data = AtomicUsize::new(0); let ready = AtomicUsize::new(0); let mut handles = vec![]; // Thread 1: Writes data and then signals readiness handles.push(thread::spawn(move || { data.store(10, Ordering::Relaxed); // Release ensures that the store to 'data' is visible before 'ready' is set. ready.store(1, Ordering::Release); println!("Writer: Data written, readiness signaled."); })); // Thread 2: Waits for readiness and then reads data handles.push(thread::spawn(move || { // Acquire ensures that we see the write to 'data' after 'ready' becomes 1. while ready.load(Ordering::Acquire) == 0 { thread::yield_now(); // Spin wait } let value = data.load(Ordering::Acquire); println!("Reader: Readiness detected. Data value: {}", value); })); for handle in handles { handle.join().unwrap(); } } ``` -------------------------------- ### Thread Parking and Unparking Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates using `thread::park()` and `thread::unpark()` for thread synchronization. ```rust use std::thread; use std::time::Duration; fn main() { let handle = thread::spawn(|| { println!("Thread: Parking myself."); thread::park(); println!("Thread: Woken up!"); }); thread::sleep(Duration::from_secs(1)); println!("Main: Unparking thread."); handle.thread().unpark(); handle.join().unwrap(); } ``` -------------------------------- ### Spawning a Closure in a Rust Thread Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates spawning a thread with a closure that captures its environment. ```rust fn main() { let mut data = vec![1, 2, 3]; std::thread::spawn(move || { data.push(4); println!("New thread: {:?}", data); }); println!("Main thread: {:?}", data); } ``` -------------------------------- ### Lazy Initialization with Box Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates lazy initialization of a heap-allocated value using `Box` and atomic operations. ```rust use std::sync::atomic::{AtomicPtr, Ordering}; use std::ptr; static mut LAZY_BOX: AtomicPtr = AtomicPtr::new(ptr::null_mut()); fn get_lazy_box() -> &'static mut u32 { unsafe { let mut ptr = LAZY_BOX.load(Ordering::Acquire); if ptr.is_null() { println!("Initializing the Box..."); let boxed_val = Box::new(123); ptr = Box::into_raw(boxed_val); // Use Release to ensure the write is visible to other threads. LAZY_BOX.store(ptr, Ordering::Release); } &mut *ptr } } fn main() { let val1 = get_lazy_box(); println!("Value 1: {}", *val1); let val2 = get_lazy_box(); println!("Value 2: {}", *val2); // Remember to deallocate the Box when it's no longer needed. // This requires careful management, e.g., using compare_exchange to // ensure only one thread deallocates. // unsafe { let _ = Box::from_raw(LAZY_BOX.load(Ordering::Acquire)); } } ``` -------------------------------- ### Atomic Lazy Initialization Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Uses `AtomicPtr` and `Ordering::Acquire`/`Ordering::Release` for lazy initialization of a resource. ```rust use std::sync::atomic::{AtomicPtr, Ordering}; use std::ptr; static mut SHARED_DATA: AtomicPtr = AtomicPtr::new(ptr::null_mut()); fn get_shared_data() -> &'static mut u32 { unsafe { let mut data_ptr = SHARED_DATA.load(Ordering::Acquire); if data_ptr.is_null() { let new_data = Box::new(42); data_ptr = Box::into_raw(new_data); // Use Release to ensure visibility of the allocation to other threads // Use Compare-and-swap or similar for robust concurrent initialization // This example is simplified for illustration. SHARED_DATA.store(data_ptr, Ordering::Release); } &mut *data_ptr } } fn main() { let data = get_shared_data(); println!("Data: {}", *data); // In a real scenario, you'd need a mechanism to deallocate the memory // when no longer needed, e.g., using Atomic::compare_exchange to // detect the last user and deallocate. } ``` -------------------------------- ### Atomic Progress Reporting with Unpark Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Combines atomic progress reporting with thread parking/unparking for more efficient waiting. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; use std::time::Duration; fn main() { let progress = Arc::new(AtomicUsize::new(0)); let progress_clone = Arc::clone(&progress); let handle = thread::spawn(move || { for i in 0..10 { thread::sleep(Duration::from_millis(200)); progress_clone.store(i + 1, Ordering::Relaxed); if i == 5 { println!("Worker: Reached step 5, unparking main thread."); thread::current().thread().unpark(); } } }); while progress.load(Ordering::Relaxed) < 10 { println!("Main: Current progress: {}", progress.load(Ordering::Relaxed)); if progress.load(Ordering::Relaxed) == 5 { println!("Main: Progress is 5, parking myself."); thread::park(); // Wait for worker to unpark println!("Main: Woken up!"); } thread::sleep(Duration::from_millis(100)); } handle.join().unwrap(); println!("Main: Task completed."); } ``` -------------------------------- ### Simple Channel Implementation Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A basic implementation of a channel for message passing between threads. ```rust use std::sync::mpsc::{channel, Sender, Receiver}; use std::thread; fn main() { let (tx, rx): (Sender, Receiver) = channel(); thread::spawn(move || { let message = String::from("Hello from sender!"); tx.send(message).unwrap(); println!("Sender: Message sent."); }); let received_message = rx.recv().unwrap(); println!("Receiver: Received '{}'", received_message); } ``` -------------------------------- ### Atomic Fetch Add Operation Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates the `fetch_add` operation on an atomic integer for thread-safe increments. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; fn main() { let counter = Arc::new(AtomicUsize::new(0)); let mut handles = vec![]; for _ in 0..10 { let counter = Arc::clone(&counter); let handle = thread::spawn(move || { // Atomically add 1 to the counter counter.fetch_add(1, Ordering::Relaxed); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Result: {}", counter.load(Ordering::Relaxed)); } ``` -------------------------------- ### Scoped Threads in Rust Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Shows how to use scoped threads to borrow data from the parent thread safely. ```rust fn main() { let mut data = vec![1, 2, 3]; std::thread::scope(|s| { s.spawn(|| { data.push(4); println!("Scoped thread: {:?}", data); }); }); println!("Main thread: {:?}", data); } ``` -------------------------------- ### Basic Arc Implementation Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A fundamental implementation of `Arc` (Atomic Reference Counting) for shared ownership across threads. ```rust use std::sync::Arc; use std::thread; struct MyData { value: i32 } fn main() { let data = Arc::new(MyData { value: 10 }); let mut handles = vec![]; for _ in 0..3 { let data_clone = Arc::clone(&data); let handle = thread::spawn(move || { println!("Thread sees value: {}", data_clone.value); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } // Original Arc still valid here println!("Main thread sees value: {}", data.value); } ``` -------------------------------- ### Atomic Relaxed Memory Ordering Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates the use of `Ordering::Relaxed` for atomic operations where synchronization with other threads is not strictly required. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::thread; fn main() { let atomic_val = AtomicUsize::new(0); let mut handles = vec![]; for _ in 0..10 { let atomic_val = &atomic_val; let handle = thread::spawn(move || { // Using Relaxed ordering for a simple counter increment. // No other threads depend on the exact timing of this increment relative to others. atomic_val.fetch_add(1, Ordering::Relaxed); }); handles.push(handle); } for handle in handles { handle.join().unwrap(); } println!("Final value (Relaxed): {}", atomic_val.load(Ordering::Relaxed)); } ``` -------------------------------- ### RwLock Implementation 2 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A second RwLock implementation, potentially exploring different fairness policies (reader-preference vs. writer-preference). ```rust // Conceptual example, similar to RwLock Implementation 1. ``` -------------------------------- ### Minimal Spin Lock Implementation Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A basic, minimal implementation of a spin lock using `AtomicBool`. ```rust use std::sync::atomic::{AtomicBool, Ordering}; use std::hint; pub struct SpinLock { locked: AtomicBool, } impl SpinLock { pub const fn new() -> Self { SpinLock { locked: AtomicBool::new(false) } } pub fn lock(&self) { while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { hint::spin_loop(); } } pub fn unlock(&self) { self.locked.store(false, Ordering::Release); } } // Example usage would typically involve Arc for multi-threading. ``` -------------------------------- ### Using Rc for Shared Ownership (Single Thread) Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates `Rc` (Reference Counting) for shared ownership of data within a single thread. ```rust use std::rc::Rc; fn main() { let rc_data = Rc::new(vec![1, 2, 3]); let rc_data_clone = Rc::clone(&rc_data); println!("Original: {:?}", rc_data); println!("Clone: {:?}", rc_data_clone); } ``` -------------------------------- ### Using RefCell for Interior Mutability (Single Thread) Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates `RefCell` for interior mutability of non-`Copy` types within a single thread, with runtime borrow checking. ```rust use std::cell::RefCell; fn main() { let refcell_data = RefCell::new(vec![1, 2, 3]); let mut borrowed_mut = refcell_data.borrow_mut(); borrowed_mut.push(4); println!("Modified: {:?}", refcell_data.borrow()); } ``` -------------------------------- ### Arc with Weak Pointers Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates the use of `Weak` pointers alongside `Arc` to break reference cycles. ```rust use std::sync::Arc; use std::sync::Weak; struct Node { value: i32, // Example: parent could be Weak to avoid cycles // parent: Option>, } fn main() { let arc_node = Arc::new(Node { value: 10 }); let weak_node = Arc::downgrade(&arc_node); println!("Arc count: {}", Arc::strong_count(&arc_node)); // Should be 1 println!("Weak count: {}", Arc::weak_count(&arc_node)); // Should be 0 initially // Simulate creating a weak reference from another scope let weak_clone = { let weak_ref = Arc::downgrade(&arc_node); println!("Weak count inside scope: {}", Arc::weak_count(&arc_node)); // Should be 1 weak_ref }; // Try to upgrade the weak pointer if let Some(upgraded_arc) = weak_node.upgrade() { println!("Successfully upgraded weak pointer. Value: {}", upgraded_arc.value); } else { println!("Failed to upgrade weak pointer (Arc might have been dropped)."); } println!("Arc count after upgrade attempt: {}", Arc::strong_count(&arc_node)); // Should be 1 println!("Weak count after upgrade attempt: {}", Arc::weak_count(&arc_node)); // Should be 1 (from weak_clone) } ``` -------------------------------- ### Atomic ID Allocation with Panic Handling Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates ID allocation where a panic might occur after allocation but before use, requiring careful state management. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; struct IdAllocator { next_id: AtomicUsize, // In a real scenario, you might need a way to track allocated but potentially leaked IDs } impl IdAllocator { fn new() -> Self { IdAllocator { next_id: AtomicUsize::new(0) } } fn allocate(&self) -> usize { // This is a simplified example. Robust ID allocation in the face of panics // often requires more complex mechanisms like tracking allocated IDs and // potentially reusing them after a panic, or using RAII guards. self.next_id.fetch_add(1, Ordering::Relaxed) } } fn main() { let allocator = Arc::new(IdAllocator::new()); let mut handles = vec![]; for i in 0..3 { let allocator = Arc::clone(&allocator); let handle = thread::spawn(move || { let id = allocator.allocate(); println!("Thread {}: Allocated ID {}", i, id); if i == 1 { panic!("Simulating a panic after allocation!"); } // In a real app, use the ID here. If panic occurs before this, ID is leaked. }); handles.push(handle); } for handle in handles { // Use join() to observe panics, but the ID might still be leaked. let _ = handle.join(); } println!("Main thread finished. Some IDs might have been leaked due to panic."); } ``` -------------------------------- ### RwLock Implementation 1 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A basic implementation of a Read-Write Lock, allowing multiple readers or one writer. ```rust // Conceptual example. std::sync::RwLock is the standard way. // Custom implementations might focus on performance tuning. // Example structure (not runnable without full implementation): /* use std::sync::{RwLock, Arc}; pub struct CustomRwLock { lock: RwLock } impl CustomRwLock { pub fn new(data: T) -> Self { /* ... */ } pub fn read(&self) -> std::sync::RwLockReadGuard<'_, T> { /* ... */ } pub fn write(&self) -> std::sync::RwLockWriteGuard<'_, T> { /* ... */ } } */ ``` -------------------------------- ### Atomic Lazy One-Time Initialization Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Uses `AtomicPtr` and `Ordering::Acquire`/`Ordering::Release` for thread-safe lazy initialization, similar to `lazy_static` or `once_cell`. ```rust use std::sync::atomic::{AtomicPtr, Ordering}; use std::ptr; use std::thread; use std::time::Duration; static mut SHARED_RESOURCE: AtomicPtr = AtomicPtr::new(ptr::null_mut()); fn get_shared_resource() -> &'static mut String { unsafe { let mut ptr = SHARED_RESOURCE.load(Ordering::Acquire); if ptr.is_null() { println!("Initializing shared resource..."); let resource = Box::new(String::from("Initialized Data")); ptr = Box::into_raw(resource); // Use Release ordering to ensure the initialization is visible to other threads // that might load the pointer afterwards. SHARED_RESOURCE.store(ptr, Ordering::Release); println!("Shared resource initialized."); } &mut *ptr } } fn main() { let mut handles = vec![]; for i in 0..5 { handles.push(thread::spawn(move || { thread::sleep(Duration::from_millis(i * 100)); // Stagger threads let resource = get_shared_resource(); println!("Thread {}: Accessed resource: '{}'", i, resource); })); } for handle in handles { handle.join().unwrap(); } // Important: In a real application, you need a strategy to deallocate // the memory pointed to by SHARED_RESOURCE when it's no longer needed. // This often involves using compare_exchange to ensure only one thread // deallocates, or using RAII wrappers. // Example (simplified, assumes main thread is last user): // unsafe { // let ptr = SHARED_RESOURCE.load(Ordering::Acquire); // if !ptr.is_null() { // let _ = Box::from_raw(ptr); // } // } } ``` -------------------------------- ### Mutex Implementation 1 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A basic implementation of a Mutex, likely using underlying synchronization primitives. ```rust // This is a conceptual representation. A real Mutex implementation // would involve atomic operations, potentially Condvars, and unsafe code. // Example structure (not runnable without full implementation): /* use std::sync::{Mutex, Arc}; pub struct CustomMutex { data: Mutex // Using std::sync::Mutex as a base for illustration } impl CustomMutex { pub fn new(data: T) -> Self { CustomMutex { data: Mutex::new(data) } } pub fn lock(&self) -> std::sync::MutexGuard<'_, T> { self.data.lock().unwrap() } } */ ``` -------------------------------- ### Mutex Implementation 2 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Another Mutex implementation, possibly exploring different internal strategies or optimizations. ```rust // Conceptual example, similar to Mutex Implementation 1. // Real implementations often involve platform-specific details for efficiency. ``` -------------------------------- ### Channel with Type Checks Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A channel implementation demonstrating type safety and how different types can be sent. ```rust use std::sync::mpsc::channel; use std::thread; fn main() { let (tx_int, rx_int) = channel::(); let (tx_str, rx_str) = channel::(); thread::spawn(move || { tx_int.send(123).unwrap(); }); thread::spawn(move || { tx_str.send(String::from("Hello Type!")).unwrap(); }); println!("Received int: {}", rx_int.recv().unwrap()); println!("Received str: {}", rx_str.recv().unwrap()); } ``` -------------------------------- ### Atomic Increment with Compare Exchange Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Uses `compare_exchange` to implement an atomic increment operation, demonstrating a more manual atomic update. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; fn main() { let counter = AtomicUsize::new(0); // Increment the counter using compare_exchange let mut current = counter.load(Ordering::Relaxed); loop { // Try to set the value to current + 1, only if the current value is still 'current' match counter.compare_exchange(current, current + 1, Ordering::Relaxed, Ordering::Relaxed) { Ok(_) => break, // Success! Err(actual) => current = actual, // Value changed, retry with the new value } } println!("Counter after increment: {}", counter.load(Ordering::Relaxed)); } ``` -------------------------------- ### RwLock Implementation 3 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A third RwLock implementation, possibly focusing on specific performance optimizations or platform integrations. ```rust // Conceptual example, similar to RwLock Implementation 1 and 2. ``` -------------------------------- ### Condition Variable Usage Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates using `Condvar` (Condition Variable) with a `Mutex` for thread signaling. ```rust use std::sync::{Mutex, Condvar, Arc}; use std::thread; use std::time::Duration; fn main() { let pair = Arc::new((Mutex::new(false), Condvar::new())); let pair2 = Arc::clone(&pair); thread::spawn(move || { let (lock, cvar) = &*pair2; println!("Worker: Acquiring lock..."); let mut started = lock.lock().unwrap(); *started = true; println!("Worker: Signaling main thread."); cvar.notify_one(); println!("Worker: Releasing lock."); }); let (lock, cvar) = &*pair; println!("Main: Acquiring lock..."); let mut started = lock.lock().unwrap(); while !*started { println!("Main: Waiting for signal..."); started = cvar.wait(started).unwrap(); } println!("Main: Received signal, continuing."); } ``` -------------------------------- ### Using Cell for Interior Mutability (Single Thread) Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates `Cell` for interior mutability of `Copy` types within a single thread. ```rust use std::cell::Cell; fn main() { let cell_data = Cell::new(10); let cell_data_clone = cell_data.clone(); println!("Original: {}", cell_data.get()); cell_data.set(20); println!("Modified: {}", cell_data.get()); println!("Clone: {}", cell_data_clone.get()); } ``` -------------------------------- ### Atomic ID Allocation: Subtract Before Panic Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates a strategy to 'undo' an ID allocation by subtracting from the atomic counter if a panic occurs before the ID is fully utilized. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; struct IdAllocator { next_id: AtomicUsize, } impl IdAllocator { fn new() -> Self { IdAllocator { next_id: AtomicUsize::new(0) } } // Returns a guard that will decrement the counter if dropped without 'commit' fn allocate_guard(&self) -> IdGuard { let id = self.next_id.fetch_add(1, Ordering::Relaxed); IdGuard { allocator: self, id, committed: false } } } struct IdGuard<'a> { allocator: &'a IdAllocator, id: usize, committed: bool, } impl<'a> IdGuard<'a> { fn commit(mut self) { self.committed = true; } fn id(&self) -> usize { self.id } } impl<'a> Drop for IdGuard<'a> { fn drop(&mut self) { if !self.committed { // Decrement the counter to 'release' the ID if not committed self.allocator.next_id.fetch_sub(1, Ordering::Relaxed); println!("IdGuard: Rolled back ID {}", self.id); } } } fn main() { let allocator = Arc::new(IdAllocator::new()); let mut handles = vec![]; for i in 0..3 { let allocator = Arc::clone(&allocator); let handle = thread::spawn(move || { let id_guard = allocator.allocate_guard(); println!("Thread {}: Tentatively allocated ID {}", i, id_guard.id()); if i == 1 { println!("Thread {}: Simulating panic, rolling back ID {}", i, id_guard.id()); // id_guard is dropped here without commit, rolling back the allocation panic!("Simulating a panic!"); } else { // Simulate successful use of the ID id_guard.commit(); // Mark the allocation as successful println!("Thread {}: Committed ID {}", i, id_guard.id()); } }); handles.push(handle); } for handle in handles { let _ = handle.join(); // Observe panics } println!("Main thread finished."); } ``` -------------------------------- ### Channel with Borrowing Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates sending references or borrowed data through a channel, which requires careful lifetime management. ```rust use std::sync::mpsc::channel; use std::thread; fn main() { let data = vec![1, 2, 3]; // Cannot directly send a reference to 'data' because the receiver // might outlive the sender's scope. Need to send owned data or use // mechanisms like Arc. let (tx, rx) = channel::>(); thread::spawn(move || { // Clone the data to send an owned copy tx.send(data.clone()).unwrap(); println!("Sender sent cloned data."); }); let received_data = rx.recv().unwrap(); println!("Receiver got: {:?}", received_data); } ``` -------------------------------- ### Condvar Implementation 2 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Another Condition Variable implementation, possibly exploring different signaling strategies. ```rust // Conceptual example, similar to Condvar Implementation 1. ``` -------------------------------- ### Optimized Arc Implementation Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md An optimized version of `Arc`, potentially using more efficient atomic operations or data layout. ```rust // This example represents a conceptual optimization. The standard library's Arc // is already highly optimized. Optimizations might involve reducing atomic operations // under certain conditions or improving memory layout. // Example structure (not runnable without full implementation): /* use std::sync::atomic::{AtomicUsize, Ordering}; struct OptimizedArc { ptr: *mut T, // Potentially a custom atomic counter or a different strategy // for managing strong and weak counts. } impl OptimizedArc { // ... methods for new, clone, drop, etc. ... } */ ``` -------------------------------- ### Blocking Channel Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Illustrates a channel where the sender or receiver will block if the channel is full or empty, respectively. ```rust use std::sync::mpsc::channel; use std::thread; use std::time::Duration; fn main() { // Bounded channel example (e.g., capacity of 1) let (tx, rx) = channel::(); thread::spawn(move || { for i in 0..5 { println!("Sender: Sending {}", i); tx.send(i).unwrap(); // Blocks if channel is full thread::sleep(Duration::from_millis(100)); } println!("Sender finished."); }); thread::sleep(Duration::from_millis(50)); // Give sender a head start for _ in 0..5 { let received = rx.recv().unwrap(); // Blocks if channel is empty println!("Receiver: Received {}", received); thread::sleep(Duration::from_millis(200)); } println!("Receiver finished."); } ``` -------------------------------- ### Spin Lock with Guard Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Implements a spin lock using a RAII guard pattern for safer lock management. ```rust use std::sync::atomic::{AtomicBool, Ordering}; use std::hint; use std::ops::{Deref, DerefMut}; pub struct SpinLock { locked: AtomicBool, } impl SpinLock { pub const fn new() -> Self { SpinLock { locked: AtomicBool::new(false) } } pub fn lock(&self) -> SpinLockGuard { while self.locked.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed).is_err() { hint::spin_loop(); } SpinLockGuard { lock: self } } } pub struct SpinLockGuard<'a> { lock: &'a SpinLock, } impl<'a> Drop for SpinLockGuard<'a> { fn drop(&mut self) { self.lock.locked.store(false, Ordering::Release); } } // Deref and DerefMut would be implemented if the lock protected a specific data structure. ``` -------------------------------- ### Atomic Progress Reporting (Multiple Threads) Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Shows multiple worker threads updating a shared atomic progress counter. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; use std::time::Duration; fn main() { let total_steps = 100; let num_threads = 4; let progress = Arc::new(AtomicUsize::new(0)); let mut handles = vec![]; for _ in 0..num_threads { let progress = Arc::clone(&progress); let handle = thread::spawn(move || { for _ in 0..(total_steps / num_threads) { thread::sleep(Duration::from_millis(10)); progress.fetch_add(1, Ordering::Relaxed); } }); handles.push(handle); } while progress.load(Ordering::Relaxed) < total_steps { println!("Main: Progress: {} / {}", progress.load(Ordering::Relaxed), total_steps); thread::sleep(Duration::from_millis(100)); } for handle in handles { handle.join().unwrap(); } println!("Main: Task completed."); } ``` -------------------------------- ### Atomic Progress Reporting Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Employs an atomic integer (`AtomicUsize`) to report progress from a worker thread to the main thread. ```rust use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::thread; use std::time::Duration; fn main() { let progress = Arc::new(AtomicUsize::new(0)); let progress_clone = Arc::clone(&progress); let handle = thread::spawn(move || { for i in 0..10 { thread::sleep(Duration::from_millis(200)); progress_clone.store(i + 1, Ordering::Relaxed); println!("Worker: Progress {}", i + 1); } }); while progress.load(Ordering::Relaxed) < 10 { println!("Main: Current progress: {}", progress.load(Ordering::Relaxed)); thread::sleep(Duration::from_millis(300)); } handle.join().unwrap(); println!("Main: Task completed."); } ``` -------------------------------- ### Unsafe Channel Implementation Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md An unsafe implementation of a channel, likely involving manual synchronization primitives. ```rust // This is a conceptual example and requires careful implementation // using primitives like Mutex, Condvar, and potentially unsafe blocks. // A full unsafe channel implementation is complex and error-prone. // Example structure (not runnable without full implementation): /* use std::sync::{Mutex, Condvar}; use std::collections::VecDeque; pub struct UnsafeChannel { queue: Mutex>, condvar: Condvar, } impl UnsafeChannel { pub fn new() -> Self { UnsafeChannel { queue: Mutex::new(VecDeque::new()), condvar: Condvar::new() } } pub fn send(&self, value: T) { let mut queue = self.queue.lock().unwrap(); queue.push_back(value); self.condvar.notify_one(); } pub fn recv(&self) -> T { let mut queue = self.queue.lock().unwrap(); while queue.is_empty() { queue = self.condvar.wait(queue).unwrap(); } queue.pop_front().unwrap() } } */ ``` -------------------------------- ### Channel with Checks Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A channel implementation that includes checks for sender/receiver closure. ```rust use std::sync::mpsc::{channel, Sender, Receiver}; use std::thread; fn main() { let (tx, rx): (Sender, Receiver) = channel(); let sender_handle = thread::spawn(move || { tx.send(10).unwrap(); println!("Sender finished."); // Sender goes out of scope here, implicitly closing the channel end. }); println!("Receiver waiting..."); // recv() will return Err when the sender is dropped and the channel is empty. match rx.recv() { Ok(val) => println!("Receiver got: {}", val), Err(_) => println!("Receiver error: Channel closed."), } sender_handle.join().unwrap(); println!("Main thread finished."); } ``` -------------------------------- ### Mutex Unlock Before Sleep Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md Demonstrates a scenario where a thread unlocks a mutex before sleeping, allowing other threads to acquire it sooner. ```rust use std::sync::{Mutex, Arc}; use std::thread; use std::time::Duration; fn main() { let data = Arc::new(Mutex::new(0)); let data_clone = Arc::clone(&data); thread::spawn(move || { let mut num = data_clone.lock().unwrap(); println!("Thread 1: Acquired lock."); *num += 1; println!("Thread 1: Releasing lock before sleep."); drop(num); // Explicitly drop the lock guard thread::sleep(Duration::from_secs(2)); }); thread::sleep(Duration::from_secs(1)); // Give thread 1 time to acquire and release lock println!("Main thread: Trying to acquire lock..."); let mut num = data.lock().unwrap(); println!("Main thread: Acquired lock."); *num += 1; println!("Main thread: Result: {}", *num); } ``` -------------------------------- ### Mutex Implementation 3 Source: https://github.com/m-ou-se/rust-atomics-and-locks/blob/main/README.md A third Mutex implementation, potentially focusing on specific aspects like fairness or performance. ```rust // Conceptual example, similar to Mutex Implementation 1 and 2. ```