diff --git a/src/locking/rwlock.rs b/src/locking/rwlock.rs index b8dfb3509..f1b30a8bb 100644 --- a/src/locking/rwlock.rs +++ b/src/locking/rwlock.rs @@ -9,33 +9,45 @@ use core::fmt::Debug; use core::ops::{Deref, DerefMut}; use core::sync::atomic::{AtomicU64, Ordering}; +/// A guard that provides read access to the data protected by [`RWLock`] #[derive(Debug)] #[must_use = "if unused the RWLock will immediately unlock"] pub struct ReadLockGuard<'a, T: Debug> { + /// Reference to the associated `AtomicU64` in the [`RWLock`] rwlock: &'a AtomicU64, + /// Reference to the protected data data: &'a T, } +/// Implements the behavior of the [`ReadLockGuard`] when it is dropped impl<'a, T: Debug> Drop for ReadLockGuard<'a, T> { + /// Release the read lock fn drop(&mut self) { self.rwlock.fetch_sub(1, Ordering::Release); } } +/// Implements the behavior of dereferencing the [`ReadLockGuard`] to +/// access the protected data. impl<'a, T: Debug> Deref for ReadLockGuard<'a, T> { type Target = T; + /// Allow reading the protected data through deref fn deref(&self) -> &T { self.data } } +/// A guard that provides exclusive write access to the data protected by [`RWLock`] #[derive(Debug)] #[must_use = "if unused the RWLock will immediately unlock"] pub struct WriteLockGuard<'a, T: Debug> { + /// Reference to the associated `AtomicU64` in the [`RWLock`] rwlock: &'a AtomicU64, + /// Reference to the protected data (mutable) data: &'a mut T, } +/// Implements the behavior of the [`WriteLockGuard`] when it is dropped impl<'a, T: Debug> Drop for WriteLockGuard<'a, T> { fn drop(&mut self) { // There are no readers - safe to just set lock to 0 @@ -43,6 +55,8 @@ impl<'a, T: Debug> Drop for WriteLockGuard<'a, T> { } } +/// Implements the behavior of dereferencing the [`WriteLockGuard`] to +/// access the protected data. impl<'a, T: Debug> Deref for WriteLockGuard<'a, T> { type Target = T; fn deref(&self) -> &T { @@ -50,31 +64,93 @@ impl<'a, T: Debug> Deref for WriteLockGuard<'a, T> { } } +/// Implements the behavior of dereferencing the [`WriteLockGuard`] to +/// access the protected data in a mutable way. impl<'a, T: Debug> DerefMut for WriteLockGuard<'a, T> { fn deref_mut(&mut self) -> &mut T { self.data } } +/// A simple Read-Write Lock (RWLock) that allows multiple readers or +/// one exclusive writer. #[derive(Debug)] pub struct RWLock { + /// An atomic 64-bit integer used for synchronization rwlock: AtomicU64, + /// An UnsafeCell for interior mutability data: UnsafeCell, } +/// Implements the trait `Sync` for the [`RWLock`], allowing safe +/// concurrent access across threads. unsafe impl Sync for RWLock {} +/// Splits a 64-bit value into two parts: readers (low 32 bits) and +/// writers (high 32 bits). +/// +/// # Parameters +/// +/// - `val`: A 64-bit unsigned integer value to be split. +/// +/// # Returns +/// +/// A tuple containing two 32-bit unsigned integer values. The first +/// element of the tuple is the lower 32 bits of input value, and the +/// second is the upper 32 bits. +/// #[inline] fn split_val(val: u64) -> (u64, u64) { (val & 0xffff_ffffu64, val >> 32) } +/// Composes a 64-bit value by combining the number of readers (low 32 +/// bits) and writers (high 32 bits). This function is used to create a +/// 64-bit synchronization value that represents the current state of the +/// RWLock, including the count of readers and writers. +/// +/// # Parameters +/// +/// - `readers`: The number of readers (low 32 bits) currently holding read locks. +/// - `writers`: The number of writers (high 32 bits) currently holding write locks. +/// +/// # Returns +/// +/// A 64-bit value representing the combined state of readers and writers in the RWLock. +/// #[inline] fn compose_val(readers: u64, writers: u64) -> u64 { (readers & 0xffff_ffffu64) | (writers << 32) } +/// A reader-writer lock that allows multiple readers or a single writer +/// to access the protected data. [`RWLock`] provides exclusive access for +/// writers and shared access for readers, for efficient synchronization. +/// impl RWLock { + /// Creates a new [`RWLock`] instance with the provided initial data. + /// + /// # Parameters + /// + /// - `data`: The initial data to be protected by the [`RWLock`]. + /// + /// # Returns + /// + /// A new [`RWLock`] instance with the specified initial data. + /// + /// # Example + /// + /// ```rust + /// use svsm::locking::RWLock; + /// + /// #[derive(Debug)] + /// struct MyData { + /// value: i32, + /// } + /// + /// let data = MyData { value: 42 }; + /// let rwlock = RWLock::new(data); + /// ``` pub const fn new(data: T) -> Self { RWLock { rwlock: AtomicU64::new(0), @@ -82,6 +158,14 @@ impl RWLock { } } + /// This function is used to wait until all writers have finished their + /// operations and retrieve the current state of the [`RWLock`]. + /// + /// # Returns + /// + /// A 64-bit value representing the current state of the [`RWLock`], + /// including the count of readers and writers. + /// #[inline] fn wait_for_writers(&self) -> u64 { loop { @@ -95,6 +179,14 @@ impl RWLock { } } + /// This function is used to wait until all readers have finished their + /// operations and retrieve the current state of the [`RWLock`]. + /// + /// # Returns + /// + /// A 64-bit value representing the current state of the [`RWLock`], + /// including the count of readers and writers. + /// #[inline] fn wait_for_readers(&self) -> u64 { loop { @@ -108,6 +200,12 @@ impl RWLock { } } + /// This function allows multiple readers to access the data concurrently. + /// + /// # Returns + /// + /// A [`ReadLockGuard`] that provides read access to the protected data. + /// pub fn lock_read(&self) -> ReadLockGuard { loop { let val = self.wait_for_writers(); @@ -130,6 +228,13 @@ impl RWLock { } } + /// This function ensures exclusive access for a single writer and waits + /// for all readers to finish before granting access to the writer. + /// + /// # Returns + /// + /// A [`WriteLockGuard`] that provides write access to the protected data. + /// pub fn lock_write(&self) -> WriteLockGuard { // Waiting for current writer to finish loop { diff --git a/src/locking/spinlock.rs b/src/locking/spinlock.rs index aae1ff28b..97e012422 100644 --- a/src/locking/spinlock.rs +++ b/src/locking/spinlock.rs @@ -9,6 +9,23 @@ use core::fmt::Debug; use core::ops::{Deref, DerefMut}; use core::sync::atomic::{AtomicU64, Ordering}; +/// A lock guard obtained from a [`SpinLock`]. This lock guard +/// provides exclusive access to the data protected by a [`SpinLock`], +/// ensuring that the lock is released when it goes out of scope. +/// +/// # Examples +/// +/// ``` +/// use svsm::locking::SpinLock; +/// +/// let data = 42; +/// let spin_lock = SpinLock::new(data); +/// +/// { +/// let mut guard = spin_lock.lock(); +/// *guard += 1; // Modify the protected data. +/// }; // Lock is automatically released when `guard` goes out of scope. +/// ``` #[derive(Debug)] #[must_use = "if unused the SpinLock will immediately unlock"] pub struct LockGuard<'a, T: Debug> { @@ -16,29 +33,66 @@ pub struct LockGuard<'a, T: Debug> { data: &'a mut T, } +/// Implements the behavior of the [`LockGuard`] when it is dropped impl<'a, T: Debug> Drop for LockGuard<'a, T> { + /// Automatically releases the lock when the guard is dropped fn drop(&mut self) { self.holder.fetch_add(1, Ordering::Release); } } +/// Implements the behavior of dereferencing the [`LockGuard`] to +/// access the protected data. impl<'a, T: Debug> Deref for LockGuard<'a, T> { type Target = T; + /// Provides read-only access to the protected data fn deref(&self) -> &T { self.data } } +/// Implements the behavior of dereferencing the [`LockGuard`] to +/// access the protected data in a mutable way. impl<'a, T: Debug> DerefMut for LockGuard<'a, T> { + /// Provides mutable access to the protected data fn deref_mut(&mut self) -> &mut T { self.data } } +/// A simple spinlock implementation for protecting concurrent data access. +/// +/// # Examples +/// +/// ``` +/// use svsm::locking::SpinLock; +/// +/// let data = 42; +/// let spin_lock = SpinLock::new(data); +/// +/// // Acquire the lock and modify the protected data. +/// { +/// let mut guard = spin_lock.lock(); +/// *guard += 1; +/// }; // Lock is automatically released when `guard` goes out of scope. +/// +/// // Try to acquire the lock without blocking +/// if let Some(mut guard) = spin_lock.try_lock() { +/// *guard += 2; +/// }; +/// ``` #[derive(Debug)] pub struct SpinLock { + /// This atomic counter is incremented each time a thread attempts to + /// acquire the lock. It helps to determine the order in which threads + /// acquire the lock. current: AtomicU64, + /// This counter represents the thread that currently holds the lock + /// and has access to the protected data. holder: AtomicU64, + /// This `UnsafeCell` is used to provide interior mutability of the + /// protected data. That is, it allows the data to be accessed/modified + /// while enforcing the locking mechanism. data: UnsafeCell, } @@ -46,6 +100,16 @@ unsafe impl Send for SpinLock {} unsafe impl Sync for SpinLock {} impl SpinLock { + /// Creates a new SpinLock instance with the specified initial data. + /// + /// # Examples + /// + /// ``` + /// use svsm::locking::SpinLock; + /// + /// let data = 42; + /// let spin_lock = SpinLock::new(data); + /// ``` pub const fn new(data: T) -> Self { SpinLock { current: AtomicU64::new(0), @@ -54,6 +118,21 @@ impl SpinLock { } } + /// Acquires the lock, providing access to the protected data. + /// + /// # Examples + /// + /// ``` + /// use svsm::locking::SpinLock; + /// + /// let spin_lock = SpinLock::new(42); + /// + /// // Acquire the lock and modify the protected data. + /// { + /// let mut guard = spin_lock.lock(); + /// *guard += 1; + /// }; // Lock is automatically released when `guard` goes out of scope. + /// ``` pub fn lock(&self) -> LockGuard { let ticket = self.current.fetch_add(1, Ordering::Relaxed); loop { @@ -69,6 +148,10 @@ impl SpinLock { } } + /// This method tries to acquire the lock without blocking. If the + /// lock is not available, it returns `None`. If the lock is + /// successfully acquired, it returns a [`LockGuard`] that automatically + /// releases the lock when it goes out of scope. pub fn try_lock(&self) -> Option> { let current = self.current.load(Ordering::Relaxed); let holder = self.holder.load(Ordering::Acquire);