Skip to content

Commit 5ff77b1

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 5ff77b1

File tree

2 files changed

+226
-0
lines changed

2 files changed

+226
-0
lines changed

src/locking/rwlock.rs

+143
Original file line numberDiff line numberDiff line change
@@ -9,79 +9,193 @@ 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+
///
130+
/// # Example
131+
///
132+
/// ```rust
133+
/// use core::sync::atomic::{AtomicU64, Ordering};
134+
/// use std::fmt::Debug;
135+
/// use svsm::locking::*;
136+
///
137+
/// #[derive(Debug)]
138+
/// struct MyData {
139+
/// value: i32,
140+
/// }
141+
///
142+
/// // Create a new RWLock with some initial data.
143+
/// let data = MyData { value: 42 };
144+
/// let rwlock = RWLock::new(data);
145+
///
146+
/// // Lock the RWLock for reading
147+
/// let reader = rwlock.lock_read();
148+
/// assert_eq!(reader.value, 42);
149+
///
150+
/// // Lock the RWLock for writing
151+
/// let mut writer = rwlock.lock_write();
152+
/// writer.value = 100;
153+
///
154+
/// // Readers can access the data simultaneously
155+
/// let reader2 = rwlock.lock_read();
156+
/// assert_eq!(reader2.value, 100);
157+
/// ```
77158
impl<T: Debug> RWLock<T> {
159+
/// Creates a new `RWLock` instance with the provided initial data.
160+
///
161+
/// # Parameters
162+
///
163+
/// - `data`: The initial data to be protected by the `RWLock`.
164+
///
165+
/// # Returns
166+
///
167+
/// A new `RWLock` instance with the specified initial data.
168+
///
169+
/// # Example
170+
///
171+
/// ```rust
172+
/// use core::sync::atomic::{AtomicU64, Ordering};
173+
/// use std::fmt::Debug;
174+
/// use svsm::locking::*;
175+
///
176+
/// #[derive(Debug)]
177+
/// struct MyData {
178+
/// value: i32,
179+
/// }
180+
///
181+
/// let data = MyData { value: 42 };
182+
/// let rwlock = RWLock::new(data);
183+
/// ```
78184
pub const fn new(data: T) -> Self {
79185
RWLock {
80186
rwlock: AtomicU64::new(0),
81187
data: UnsafeCell::new(data),
82188
}
83189
}
84190

191+
/// This function is used to wait until all writers have finished their
192+
/// operations and retrieve the current state of the RWLock.
193+
///
194+
/// # Returns
195+
///
196+
/// A 64-bit value representing the current state of the RWLock,
197+
/// including the count of readers and writers.
198+
///
85199
#[inline]
86200
fn wait_for_writers(&self) -> u64 {
87201
loop {
@@ -95,6 +209,14 @@ impl<T: Debug> RWLock<T> {
95209
}
96210
}
97211

212+
/// This function is used to wait until all readers have finished their
213+
/// operations and retrieve the current state of the RWLock.
214+
///
215+
/// # Returns
216+
///
217+
/// A 64-bit value representing the current state of the RWLock,
218+
/// including the count of readers and writers.
219+
///
98220
#[inline]
99221
fn wait_for_readers(&self) -> u64 {
100222
loop {
@@ -108,6 +230,12 @@ impl<T: Debug> RWLock<T> {
108230
}
109231
}
110232

233+
/// This function allows multiple readers to access the data concurrently.
234+
///
235+
/// # Returns
236+
///
237+
/// A `ReadLockGuard` that provides read access to the protected data.
238+
///
111239
pub fn lock_read(&self) -> ReadLockGuard<T> {
112240
loop {
113241
let val = self.wait_for_writers();
@@ -130,6 +258,21 @@ impl<T: Debug> RWLock<T> {
130258
}
131259
}
132260

261+
/// This function ensures exclusive access for a single writer and waits
262+
/// for all readers to finish before granting access to the writer.
263+
///
264+
/// # Returns
265+
///
266+
/// A `WriteLockGuard` that provides write access to the protected data.
267+
///
268+
/// # Example
269+
///
270+
/// ```rust
271+
/// use svsm::locking::*;
272+
/// let rwlock = RWLock::new(42);
273+
/// let mut writer = rwlock.lock_write();
274+
/// writer.value = 100;
275+
/// ```
133276
pub fn lock_write(&self) -> WriteLockGuard<T> {
134277
// Waiting for current writer to finish
135278
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 used in conjunction with `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, LockGuard};
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, this is, it allows the data to be accessed/modified
95+
/// while respecting 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)