keos/sync/
spinlock.rs

1//! SMP-supported spinlock.
2//!
3//! The implementing unicore spinlock uniprocessor is simple; it just requires
4//! preventing thread preemption while holding a lock. By disabling preemption
5//! of the lock-holding thread,  other threads cannot access shared resource as
6//! they can't be scheduled.
7//!
8//! However, when it comes to multiprocessor, disabling preemption is not
9//! sufficient; as multiple threads run concurrently in different cores, they
10//! can access shared resource at the same time even when a core disable
11//! preemption. Therefore, to acquire a lock on multi-processor, a processor 1)
12//! polls a variable that represents a value is locked or not  2) set the
13//! variable when a thread holds the `lock`, and 3) unset the variable when the
14//! thread `unlock`.
15//!
16//! The step 1 and 2 must be executed ATOMICALLY with the atomic
17//! read-modify-write instructions of the CPU.
18//!
19//! This module introduce the support of the SMP-supported spinlock in KeOS.
20
21pub use abyss::spinlock::WouldBlock;
22
23/// A mutual exclusion primitive useful for protecting shared data
24///
25/// This spinlock will block threads waiting for the lock to become available.
26/// The spinlock can be created via a [`new`] constructor. Each spinlock has a
27/// type parameter which represents the data that it is protecting. The data can
28/// only be accessed through the guards returned from [`lock`] and
29/// [`try_lock`], which guarantees that the data is only ever accessed when the
30/// spinlock is locked.
31///
32/// [`new`]: Self::new
33/// [`lock`]: Self::lock
34/// [`try_lock`]: Self::try_lock
35/// [`unwrap()`]: Result::unwrap
36///
37/// # Examples
38///
39/// ```
40/// use alloc::sync::Arc;
41/// use keos::sync::SpinLock;
42/// use keos::thread;
43///
44/// const N: usize = 10;
45///
46/// // Spawn a few threads to increment a shared variable (non-atomically), and
47/// // let the main thread know once all increments are done.
48/// //
49/// // Here we're using an Arc to share memory among threads, and the data inside
50/// // the Arc is protected with a spinlock.
51/// let data = Arc::new(SpinLock::new(0));
52///
53/// for _ in 0..N {
54///     let data = Arc::clone(&data);
55///     thread::ThreadBuilder::new("work").spawn(move || {
56///         // The shared state can only be accessed once the lock is held.
57///         // Our non-atomic increment is safe because we're the only thread
58///         // which can access the shared state when the lock is held.
59///         //
60///         // We unwrap() the return value to assert that we are not expecting
61///         // threads to ever fail while holding the lock.
62///         let mut data = data.lock().unwrap();
63///         *data += 1;
64///         // the lock must be "explicitly" unlocked.
65///         data.unlock();
66///     });
67/// }
68/// ```
69pub use abyss::spinlock::SpinLock;
70pub use abyss::spinlock::SpinLockGuard;