Skip to content

Commit 130c1d7

Browse files
committed
docs: Document SpinLock and RWLock
Document locking support. This is related to issue #74 Document COCONUT-SVSM. Signed-off-by: Carlos Bilbao <[email protected]>
1 parent dde36a0 commit 130c1d7

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

src/locking/rwlock.rs

+105
Original file line numberDiff line numberDiff line change
@@ -9,79 +9,163 @@ use core::fmt::Debug;
99
use core::ops::{Deref, DerefMut};
1010
use core::sync::atomic::{AtomicU64, Ordering};
1111

12+
/// A guard that provides read access to the data protected by [`RWLock`]
1213
#[derive(Debug)]
1314
#[must_use = "if unused the RWLock will immediately unlock"]
1415
pub struct ReadLockGuard<'a, T: Debug> {
16+
/// Reference to the associated `AtomicU64` in the [`RWLock`]
1517
rwlock: &'a AtomicU64,
18+
/// Reference to the protected data
1619
data: &'a T,
1720
}
1821

22+
/// Implements the behavior of the [`ReadLockGuard`] when it is dropped
1923
impl<'a, T: Debug> Drop for ReadLockGuard<'a, T> {
24+
/// Release the read lock
2025
fn drop(&mut self) {
2126
self.rwlock.fetch_sub(1, Ordering::Release);
2227
}
2328
}
2429

30+
/// Implements the behavior of dereferencing the [`ReadLockGuard`] to
31+
/// access the protected data.
2532
impl<'a, T: Debug> Deref for ReadLockGuard<'a, T> {
2633
type Target = T;
34+
/// Allow reading the protected data through deref
2735
fn deref(&self) -> &T {
2836
self.data
2937
}
3038
}
3139

40+
/// A guard that provides exclusive write access to the data protected by [`RWLock`]
3241
#[derive(Debug)]
3342
#[must_use = "if unused the RWLock will immediately unlock"]
3443
pub struct WriteLockGuard<'a, T: Debug> {
44+
/// Reference to the associated `AtomicU64` in the [`RWLock`]
3545
rwlock: &'a AtomicU64,
46+
/// Reference to the protected data (mutable)
3647
data: &'a mut T,
3748
}
3849

50+
/// Implements the behavior of the [`WriteLockGuard`] when it is dropped
3951
impl<'a, T: Debug> Drop for WriteLockGuard<'a, T> {
4052
fn drop(&mut self) {
4153
// There are no readers - safe to just set lock to 0
4254
self.rwlock.store(0, Ordering::Release);
4355
}
4456
}
4557

58+
/// Implements the behavior of dereferencing the [`WriteLockGuard`] to
59+
/// access the protected data.
4660
impl<'a, T: Debug> Deref for WriteLockGuard<'a, T> {
4761
type Target = T;
4862
fn deref(&self) -> &T {
4963
self.data
5064
}
5165
}
5266

67+
/// Implements the behavior of dereferencing the [`WriteLockGuard`] to
68+
/// access the protected data in a mutable way.
5369
impl<'a, T: Debug> DerefMut for WriteLockGuard<'a, T> {
5470
fn deref_mut(&mut self) -> &mut T {
5571
self.data
5672
}
5773
}
5874

75+
/// A simple Read-Write Lock (RWLock) that allows multiple readers or
76+
/// one exclusive writer.
5977
#[derive(Debug)]
6078
pub struct RWLock<T: Debug> {
79+
/// An atomic 64-bit integer used for synchronization
6180
rwlock: AtomicU64,
81+
/// An UnsafeCell for interior mutability
6282
data: UnsafeCell<T>,
6383
}
6484

85+
/// Implements the trait `Sync` for the [`RWLock`], allowing safe
86+
/// concurrent access across threads.
6587
unsafe impl<T: Debug> Sync for RWLock<T> {}
6688

89+
/// Splits a 64-bit value into two parts: readers (low 32 bits) and
90+
/// writers (high 32 bits).
91+
///
92+
/// # Parameters
93+
///
94+
/// - `val`: A 64-bit unsigned integer value to be split.
95+
///
96+
/// # Returns
97+
///
98+
/// A tuple containing two 32-bit unsigned integer values. The first
99+
/// element of the tuple is the lower 32 bits of input value, and the
100+
/// second is the upper 32 bits.
101+
///
67102
#[inline]
68103
fn split_val(val: u64) -> (u64, u64) {
69104
(val & 0xffff_ffffu64, val >> 32)
70105
}
71106

107+
/// Composes a 64-bit value by combining the number of readers (low 32
108+
/// bits) and writers (high 32 bits). This function is used to create a
109+
/// 64-bit synchronization value that represents the current state of the
110+
/// RWLock, including the count of readers and writers.
111+
///
112+
/// # Parameters
113+
///
114+
/// - `readers`: The number of readers (low 32 bits) currently holding read locks.
115+
/// - `writers`: The number of writers (high 32 bits) currently holding write locks.
116+
///
117+
/// # Returns
118+
///
119+
/// A 64-bit value representing the combined state of readers and writers in the RWLock.
120+
///
72121
#[inline]
73122
fn compose_val(readers: u64, writers: u64) -> u64 {
74123
(readers & 0xffff_ffffu64) | (writers << 32)
75124
}
76125

126+
/// A reader-writer lock that allows multiple readers or a single writer
127+
/// to access the protected data. [`RWLock`] provides exclusive access for
128+
/// writers and shared access for readers, for efficient synchronization.
129+
///
77130
impl<T: Debug> RWLock<T> {
131+
/// Creates a new [`RWLock`] instance with the provided initial data.
132+
///
133+
/// # Parameters
134+
///
135+
/// - `data`: The initial data to be protected by the [`RWLock`].
136+
///
137+
/// # Returns
138+
///
139+
/// A new [`RWLock`] instance with the specified initial data.
140+
///
141+
/// # Example
142+
///
143+
/// ```rust
144+
/// use svsm::locking::RWLock;
145+
///
146+
/// #[derive(Debug)]
147+
/// struct MyData {
148+
/// value: i32,
149+
/// }
150+
///
151+
/// let data = MyData { value: 42 };
152+
/// let rwlock = RWLock::new(data);
153+
/// ```
78154
pub const fn new(data: T) -> Self {
79155
RWLock {
80156
rwlock: AtomicU64::new(0),
81157
data: UnsafeCell::new(data),
82158
}
83159
}
84160

161+
/// This function is used to wait until all writers have finished their
162+
/// operations and retrieve the current state of the [`RWLock`].
163+
///
164+
/// # Returns
165+
///
166+
/// A 64-bit value representing the current state of the [`RWLock`],
167+
/// including the count of readers and writers.
168+
///
85169
#[inline]
86170
fn wait_for_writers(&self) -> u64 {
87171
loop {
@@ -95,6 +179,14 @@ impl<T: Debug> RWLock<T> {
95179
}
96180
}
97181

182+
/// This function is used to wait until all readers have finished their
183+
/// operations and retrieve the current state of the [`RWLock`].
184+
///
185+
/// # Returns
186+
///
187+
/// A 64-bit value representing the current state of the [`RWLock`],
188+
/// including the count of readers and writers.
189+
///
98190
#[inline]
99191
fn wait_for_readers(&self) -> u64 {
100192
loop {
@@ -108,6 +200,12 @@ impl<T: Debug> RWLock<T> {
108200
}
109201
}
110202

203+
/// This function allows multiple readers to access the data concurrently.
204+
///
205+
/// # Returns
206+
///
207+
/// A [`ReadLockGuard`] that provides read access to the protected data.
208+
///
111209
pub fn lock_read(&self) -> ReadLockGuard<T> {
112210
loop {
113211
let val = self.wait_for_writers();
@@ -130,6 +228,13 @@ impl<T: Debug> RWLock<T> {
130228
}
131229
}
132230

231+
/// This function ensures exclusive access for a single writer and waits
232+
/// for all readers to finish before granting access to the writer.
233+
///
234+
/// # Returns
235+
///
236+
/// A [`WriteLockGuard`] that provides write access to the protected data.
237+
///
133238
pub fn lock_write(&self) -> WriteLockGuard<T> {
134239
// Waiting for current writer to finish
135240
loop {

src/locking/spinlock.rs

+83
Original file line numberDiff line numberDiff line change
@@ -9,43 +9,107 @@ use core::fmt::Debug;
99
use core::ops::{Deref, DerefMut};
1010
use core::sync::atomic::{AtomicU64, Ordering};
1111

12+
/// A lock guard obtained from a [`SpinLock`]. This lock guard
13+
/// provides exclusive access to the data protected by a [`SpinLock`],
14+
/// ensuring that the lock is released when it goes out of scope.
15+
///
16+
/// # Examples
17+
///
18+
/// ```
19+
/// use svsm::locking::SpinLock;
20+
///
21+
/// let data = 42;
22+
/// let spin_lock = SpinLock::new(data);
23+
///
24+
/// {
25+
/// let mut guard = spin_lock.lock();
26+
/// *guard += 1; // Modify the protected data.
27+
/// }; // Lock is automatically released when `guard` goes out of scope.
28+
/// ```
1229
#[derive(Debug)]
1330
#[must_use = "if unused the SpinLock will immediately unlock"]
1431
pub struct LockGuard<'a, T: Debug> {
1532
holder: &'a AtomicU64,
1633
data: &'a mut T,
1734
}
1835

36+
/// Implements the behavior of the [`LockGuard`] when it is dropped
1937
impl<'a, T: Debug> Drop for LockGuard<'a, T> {
38+
/// Automatically releases the lock when the guard is dropped
2039
fn drop(&mut self) {
2140
self.holder.fetch_add(1, Ordering::Release);
2241
}
2342
}
2443

44+
/// Implements the behavior of dereferencing the [`LockGuard`] to
45+
/// access the protected data.
2546
impl<'a, T: Debug> Deref for LockGuard<'a, T> {
2647
type Target = T;
48+
/// Provides read-only access to the protected data
2749
fn deref(&self) -> &T {
2850
self.data
2951
}
3052
}
3153

54+
/// Implements the behavior of dereferencing the [`LockGuard`] to
55+
/// access the protected data in a mutable way.
3256
impl<'a, T: Debug> DerefMut for LockGuard<'a, T> {
57+
/// Provides mutable access to the protected data
3358
fn deref_mut(&mut self) -> &mut T {
3459
self.data
3560
}
3661
}
3762

63+
/// A simple spinlock implementation for protecting concurrent data access.
64+
///
65+
/// # Examples
66+
///
67+
/// ```
68+
/// use svsm::locking::SpinLock;
69+
///
70+
/// let data = 42;
71+
/// let spin_lock = SpinLock::new(data);
72+
///
73+
/// // Acquire the lock and modify the protected data.
74+
/// {
75+
/// let mut guard = spin_lock.lock();
76+
/// *guard += 1;
77+
/// }; // Lock is automatically released when `guard` goes out of scope.
78+
///
79+
/// // Try to acquire the lock without blocking
80+
/// if let Some(mut guard) = spin_lock.try_lock() {
81+
/// *guard += 2;
82+
/// };
83+
/// ```
3884
#[derive(Debug)]
3985
pub struct SpinLock<T: Debug> {
86+
/// This atomic counter is incremented each time a thread attempts to
87+
/// acquire the lock. It helps to determine the order in which threads
88+
/// acquire the lock.
4089
current: AtomicU64,
90+
/// This counter represents the thread that currently holds the lock
91+
/// and has access to the protected data.
4192
holder: AtomicU64,
93+
/// This `UnsafeCell` is used to provide interior mutability of the
94+
/// protected data. That is, it allows the data to be accessed/modified
95+
/// while enforcing the locking mechanism.
4296
data: UnsafeCell<T>,
4397
}
4498

4599
unsafe impl<T: Debug + Send> Send for SpinLock<T> {}
46100
unsafe impl<T: Debug + Send> Sync for SpinLock<T> {}
47101

48102
impl<T: Debug> SpinLock<T> {
103+
/// Creates a new SpinLock instance with the specified initial data.
104+
///
105+
/// # Examples
106+
///
107+
/// ```
108+
/// use svsm::locking::SpinLock;
109+
///
110+
/// let data = 42;
111+
/// let spin_lock = SpinLock::new(data);
112+
/// ```
49113
pub const fn new(data: T) -> Self {
50114
SpinLock {
51115
current: AtomicU64::new(0),
@@ -54,6 +118,21 @@ impl<T: Debug> SpinLock<T> {
54118
}
55119
}
56120

121+
/// Acquires the lock, providing access to the protected data.
122+
///
123+
/// # Examples
124+
///
125+
/// ```
126+
/// use svsm::locking::SpinLock;
127+
///
128+
/// let spin_lock = SpinLock::new(42);
129+
///
130+
/// // Acquire the lock and modify the protected data.
131+
/// {
132+
/// let mut guard = spin_lock.lock();
133+
/// *guard += 1;
134+
/// }; // Lock is automatically released when `guard` goes out of scope.
135+
/// ```
57136
pub fn lock(&self) -> LockGuard<T> {
58137
let ticket = self.current.fetch_add(1, Ordering::Relaxed);
59138
loop {
@@ -69,6 +148,10 @@ impl<T: Debug> SpinLock<T> {
69148
}
70149
}
71150

151+
/// This method tries to acquire the lock without blocking. If the
152+
/// lock is not available, it returns `None`. If the lock is
153+
/// successfully acquired, it returns a [`LockGuard`] that automatically
154+
/// releases the lock when it goes out of scope.
72155
pub fn try_lock(&self) -> Option<LockGuard<T>> {
73156
let current = self.current.load(Ordering::Relaxed);
74157
let holder = self.holder.load(Ordering::Acquire);

0 commit comments

Comments
 (0)