// SPDX-License-Identifier: MIT OR Apache-2.0 // // Copyright (c) 2020-2023 Andre Richter //! Synchronization primitives. //! //! # Resources //! //! - //! - //! - use core::cell::UnsafeCell; //-------------------------------------------------------------------------------------------------- // Public Definitions //-------------------------------------------------------------------------------------------------- /// Synchronization interfaces. pub mod interface { /// Any object implementing this trait guarantees exclusive access to the data wrapped within /// the Mutex for the duration of the provided closure. pub trait Mutex { /// The type of the data that is wrapped by this mutex. type Data; /// Locks the mutex and grants the closure temporary mutable access to the wrapped data. fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R; } } /// A pseudo-lock for teaching purposes. /// /// In contrast to a real Mutex implementation, does not protect against concurrent access from /// other cores to the contained data. This part is preserved for later lessons. /// /// The lock will only be used as long as it is safe to do so, i.e. as long as the kernel is /// executing single-threaded, aka only running on a single core with interrupts disabled. pub struct NullLock where T: ?Sized, { data: UnsafeCell, } //-------------------------------------------------------------------------------------------------- // Public Code //-------------------------------------------------------------------------------------------------- unsafe impl Send for NullLock where T: ?Sized + Send {} unsafe impl Sync for NullLock where T: ?Sized + Send {} impl NullLock { /// Create an instance. pub const fn new(data: T) -> Self { Self { data: UnsafeCell::new(data), } } } //------------------------------------------------------------------------------ // OS Interface Code //------------------------------------------------------------------------------ impl interface::Mutex for NullLock { type Data = T; fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R { // In a real lock, there would be code encapsulating this line that ensures that this // mutable reference will ever only be given out once at a time. let data = unsafe { &mut *self.data.get() }; f(data) } }