Remove blocking read-write lock module and its references and refactor rwlock for a simpler approach
This commit is contained in:
		
							parent
							
								
									82c0ab01f1
								
							
						
					
					
						commit
						e557ca9606
					
				| @ -1,221 +0,0 @@ | ||||
| //! Blocking read-write lock.
 | ||||
| //!
 | ||||
| //! This module provides a blocking read-write lock that can be used to synchronize data.
 | ||||
| pub mod raw; | ||||
| 
 | ||||
| use core::cell::UnsafeCell; | ||||
| 
 | ||||
| use self::raw::RawRwLock; | ||||
| 
 | ||||
| /// Blocking read-write lock (not async)
 | ||||
| ///
 | ||||
| /// Provides a blocking read-write lock primitive backed by an implementation of [`raw::RawRwLock`].
 | ||||
| ///
 | ||||
| /// Which implementation you select depends on the context in which you're using the read-write lock, and you can choose which kind
 | ||||
| /// of interior mutability fits your use case.
 | ||||
| ///
 | ||||
| /// Use [`CriticalSectionRwLock`] when data can be shared between threads and interrupts.
 | ||||
| ///
 | ||||
| /// Use [`NoopRwLock`] when data is only shared between tasks running on the same executor.
 | ||||
| ///
 | ||||
| /// Use [`ThreadModeRwLock`] when data is shared between tasks running on the same executor but you want a global singleton.
 | ||||
| ///
 | ||||
| /// In all cases, the blocking read-write lock is intended to be short lived and not held across await points.
 | ||||
| /// Use the async [`RwLock`](crate::rwlock::RwLock) if you need a lock that is held across await points.
 | ||||
| pub struct RwLock<R, T: ?Sized> { | ||||
|     // NOTE: `raw` must be FIRST, so when using ThreadModeRwLock the "can't drop in non-thread-mode" gets
 | ||||
|     // to run BEFORE dropping `data`.
 | ||||
|     raw: R, | ||||
|     data: UnsafeCell<T>, | ||||
| } | ||||
| 
 | ||||
| unsafe impl<R: RawRwLock + Send, T: ?Sized + Send> Send for RwLock<R, T> {} | ||||
| unsafe impl<R: RawRwLock + Sync, T: ?Sized + Send> Sync for RwLock<R, T> {} | ||||
| 
 | ||||
| impl<R: RawRwLock, T> RwLock<R, T> { | ||||
|     /// Creates a new read-write lock in an unlocked state ready for use.
 | ||||
|     #[inline] | ||||
|     pub const fn new(val: T) -> RwLock<R, T> { | ||||
|         RwLock { | ||||
|             raw: R::INIT, | ||||
|             data: UnsafeCell::new(val), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a critical section and grants temporary read access to the protected data.
 | ||||
|     pub fn read_lock<U>(&self, f: impl FnOnce(&T) -> U) -> U { | ||||
|         self.raw.read_lock(|| { | ||||
|             let ptr = self.data.get() as *const T; | ||||
|             let inner = unsafe { &*ptr }; | ||||
|             f(inner) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// Creates a critical section and grants temporary write access to the protected data.
 | ||||
|     pub fn write_lock<U>(&self, f: impl FnOnce(&mut T) -> U) -> U { | ||||
|         self.raw.write_lock(|| { | ||||
|             let ptr = self.data.get() as *mut T; | ||||
|             let inner = unsafe { &mut *ptr }; | ||||
|             f(inner) | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<R, T> RwLock<R, T> { | ||||
|     /// Creates a new read-write lock based on a pre-existing raw read-write lock.
 | ||||
|     ///
 | ||||
|     /// This allows creating a read-write lock in a constant context on stable Rust.
 | ||||
|     #[inline] | ||||
|     pub const fn const_new(raw_rwlock: R, val: T) -> RwLock<R, T> { | ||||
|         RwLock { | ||||
|             raw: raw_rwlock, | ||||
|             data: UnsafeCell::new(val), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Consumes this read-write lock, returning the underlying data.
 | ||||
|     #[inline] | ||||
|     pub fn into_inner(self) -> T { | ||||
|         self.data.into_inner() | ||||
|     } | ||||
| 
 | ||||
|     /// Returns a mutable reference to the underlying data.
 | ||||
|     ///
 | ||||
|     /// Since this call borrows the `RwLock` mutably, no actual locking needs to
 | ||||
|     /// take place---the mutable borrow statically guarantees no locks exist.
 | ||||
|     #[inline] | ||||
|     pub fn get_mut(&mut self) -> &mut T { | ||||
|         unsafe { &mut *self.data.get() } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A read-write lock that allows borrowing data across executors and interrupts.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// This read-write lock is safe to share between different executors and interrupts.
 | ||||
| pub type CriticalSectionRwLock<T> = RwLock<raw::CriticalSectionRawRwLock, T>; | ||||
| 
 | ||||
| /// A read-write lock that allows borrowing data in the context of a single executor.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// **This Read-Write Lock is only safe within a single executor.**
 | ||||
| pub type NoopRwLock<T> = RwLock<raw::NoopRawRwLock, T>; | ||||
| 
 | ||||
| impl<T> RwLock<raw::CriticalSectionRawRwLock, T> { | ||||
|     /// Borrows the data for the duration of the critical section
 | ||||
|     pub fn borrow<'cs>(&'cs self, _cs: critical_section::CriticalSection<'cs>) -> &'cs T { | ||||
|         let ptr = self.data.get() as *const T; | ||||
|         unsafe { &*ptr } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<T> RwLock<raw::NoopRawRwLock, T> { | ||||
|     /// Borrows the data
 | ||||
|     #[allow(clippy::should_implement_trait)] | ||||
|     pub fn borrow(&self) -> &T { | ||||
|         let ptr = self.data.get() as *const T; | ||||
|         unsafe { &*ptr } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // ThreadModeRwLock does NOT use the generic read-write lock from above because it's special:
 | ||||
| // it's Send+Sync even if T: !Send. There's no way to do that without specialization (I think?).
 | ||||
| //
 | ||||
| // There's still a ThreadModeRawRwLock for use with the generic RwLock (handy with Channel, for example),
 | ||||
| // but that will require T: Send even though it shouldn't be needed.
 | ||||
| 
 | ||||
| #[cfg(any(cortex_m, feature = "std"))] | ||||
| pub use thread_mode_rwlock::*; | ||||
| #[cfg(any(cortex_m, feature = "std"))] | ||||
| mod thread_mode_rwlock { | ||||
|     use super::*; | ||||
| 
 | ||||
|     /// A "read-write lock" that only allows borrowing from thread mode.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// **This Read-Write Lock is only safe on single-core systems.**
 | ||||
|     ///
 | ||||
|     /// On multi-core systems, a `ThreadModeRwLock` **is not sufficient** to ensure exclusive access.
 | ||||
|     pub struct ThreadModeRwLock<T: ?Sized> { | ||||
|         inner: UnsafeCell<T>, | ||||
|     } | ||||
| 
 | ||||
|     // NOTE: ThreadModeRwLock only allows borrowing from one execution context ever: thread mode.
 | ||||
|     // Therefore it cannot be used to send non-sendable stuff between execution contexts, so it can
 | ||||
|     // be Send+Sync even if T is not Send (unlike CriticalSectionRwLock)
 | ||||
|     unsafe impl<T: ?Sized> Sync for ThreadModeRwLock<T> {} | ||||
|     unsafe impl<T: ?Sized> Send for ThreadModeRwLock<T> {} | ||||
| 
 | ||||
|     impl<T> ThreadModeRwLock<T> { | ||||
|         /// Creates a new read-write lock
 | ||||
|         pub const fn new(value: T) -> Self { | ||||
|             ThreadModeRwLock { | ||||
|                 inner: UnsafeCell::new(value), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl<T: ?Sized> ThreadModeRwLock<T> { | ||||
|         /// Lock the `ThreadModeRwLock` for reading, granting access to the data.
 | ||||
|         ///
 | ||||
|         /// # Panics
 | ||||
|         ///
 | ||||
|         /// This will panic if not currently running in thread mode.
 | ||||
|         pub fn read_lock<R>(&self, f: impl FnOnce(&T) -> R) -> R { | ||||
|             f(self.borrow()) | ||||
|         } | ||||
| 
 | ||||
|         /// Lock the `ThreadModeRwLock` for writing, granting access to the data.
 | ||||
|         ///
 | ||||
|         /// # Panics
 | ||||
|         ///
 | ||||
|         /// This will panic if not currently running in thread mode.
 | ||||
|         pub fn write_lock<R>(&self, f: impl FnOnce(&mut T) -> R) -> R { | ||||
|             f(self.borrow_mut()) | ||||
|         } | ||||
| 
 | ||||
|         /// Borrows the data
 | ||||
|         ///
 | ||||
|         /// # Panics
 | ||||
|         ///
 | ||||
|         /// This will panic if not currently running in thread mode.
 | ||||
|         pub fn borrow(&self) -> &T { | ||||
|             assert!( | ||||
|                 raw::in_thread_mode(), | ||||
|                 "ThreadModeRwLock can only be borrowed from thread mode." | ||||
|             ); | ||||
|             unsafe { &*self.inner.get() } | ||||
|         } | ||||
| 
 | ||||
|         /// Mutably borrows the data
 | ||||
|         ///
 | ||||
|         /// # Panics
 | ||||
|         ///
 | ||||
|         /// This will panic if not currently running in thread mode.
 | ||||
|         pub fn borrow_mut(&self) -> &mut T { | ||||
|             assert!( | ||||
|                 raw::in_thread_mode(), | ||||
|                 "ThreadModeRwLock can only be borrowed from thread mode." | ||||
|             ); | ||||
|             unsafe { &mut *self.inner.get() } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl<T: ?Sized> Drop for ThreadModeRwLock<T> { | ||||
|         fn drop(&mut self) { | ||||
|             // Only allow dropping from thread mode. Dropping calls drop on the inner `T`, so
 | ||||
|             // `drop` needs the same guarantees as `lock`. `ThreadModeRwLock<T>` is Send even if
 | ||||
|             // T isn't, so without this check a user could create a ThreadModeRwLock in thread mode,
 | ||||
|             // send it to interrupt context and drop it there, which would "send" a T even if T is not Send.
 | ||||
|             assert!( | ||||
|                 raw::in_thread_mode(), | ||||
|                 "ThreadModeRwLock can only be dropped from thread mode." | ||||
|             ); | ||||
| 
 | ||||
|             // Drop of the inner `T` happens after this.
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,209 +0,0 @@ | ||||
| //! Read-Write Lock primitives.
 | ||||
| //!
 | ||||
| //! This module provides a trait for read-write locks that can be used in different contexts.
 | ||||
| use core::cell::RefCell; | ||||
| use core::marker::PhantomData; | ||||
| 
 | ||||
| /// Raw read-write lock trait.
 | ||||
| ///
 | ||||
| /// This read-write lock is "raw", which means it does not actually contain the protected data, it
 | ||||
| /// just implements the read-write lock mechanism. For most uses you should use [`super::RwLock`] instead,
 | ||||
| /// which is generic over a RawRwLock and contains the protected data.
 | ||||
| ///
 | ||||
| /// Note that, unlike other read-write locks, implementations only guarantee no
 | ||||
| /// concurrent access from other threads: concurrent access from the current
 | ||||
| /// thread is allowed. For example, it's possible to lock the same read-write lock multiple times reentrantly.
 | ||||
| ///
 | ||||
| /// Therefore, locking a `RawRwLock` is only enough to guarantee safe shared (`&`) access
 | ||||
| /// to the data, it is not enough to guarantee exclusive (`&mut`) access.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// RawRwLock implementations must ensure that, while locked, no other thread can lock
 | ||||
| /// the RawRwLock concurrently.
 | ||||
| ///
 | ||||
| /// Unsafe code is allowed to rely on this fact, so incorrect implementations will cause undefined behavior.
 | ||||
| pub unsafe trait RawRwLock { | ||||
|     /// Create a new `RawRwLock` instance.
 | ||||
|     ///
 | ||||
|     /// This is a const instead of a method to allow creating instances in const context.
 | ||||
|     const INIT: Self; | ||||
| 
 | ||||
|     /// Lock this `RawRwLock` for reading.
 | ||||
|     fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R; | ||||
| 
 | ||||
|     /// Lock this `RawRwLock` for writing.
 | ||||
|     fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R; | ||||
| } | ||||
| 
 | ||||
| /// A read-write lock that allows borrowing data across executors and interrupts.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// This read-write lock is safe to share between different executors and interrupts.
 | ||||
| pub struct CriticalSectionRawRwLock { | ||||
|     state: RefCell<isize>, | ||||
| } | ||||
| 
 | ||||
| unsafe impl Send for CriticalSectionRawRwLock {} | ||||
| unsafe impl Sync for CriticalSectionRawRwLock {} | ||||
| 
 | ||||
| impl CriticalSectionRawRwLock { | ||||
|     /// Creates a new [`CriticalSectionRawRwLock`].
 | ||||
|     pub const fn new() -> Self { | ||||
|         Self { state: RefCell::new(0) } | ||||
|     } | ||||
| 
 | ||||
|     fn lock_read(&self) { | ||||
|         critical_section::with(|_| { | ||||
|             let mut state = self.state.borrow_mut(); | ||||
| 
 | ||||
|             while *state & WRITER != 0 { | ||||
|                 // Spin until the writer releases the lock
 | ||||
|             } | ||||
|             *state += 1; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     fn unlock_read(&self) { | ||||
|         critical_section::with(|_| { | ||||
|             *self.state.borrow_mut() -= 1; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     fn lock_write(&self) { | ||||
|         critical_section::with(|_| { | ||||
|             let mut state = self.state.borrow_mut(); | ||||
| 
 | ||||
|             while *state != 0 { | ||||
|                 // Spin until all readers and writers release the lock
 | ||||
|             } | ||||
|             *state = WRITER; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     fn unlock_write(&self) { | ||||
|         critical_section::with(|_| { | ||||
|             *self.state.borrow_mut() = 0; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| unsafe impl RawRwLock for CriticalSectionRawRwLock { | ||||
|     const INIT: Self = Self::new(); | ||||
| 
 | ||||
|     fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||||
|         self.lock_read(); | ||||
|         let result = f(); | ||||
|         self.unlock_read(); | ||||
|         result | ||||
|     } | ||||
| 
 | ||||
|     fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||||
|         self.lock_write(); | ||||
|         let result = f(); | ||||
|         self.unlock_write(); | ||||
|         result | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| const WRITER: isize = -1; | ||||
| 
 | ||||
| // ================
 | ||||
| 
 | ||||
| /// A read-write lock that allows borrowing data in the context of a single executor.
 | ||||
| ///
 | ||||
| /// # Safety
 | ||||
| ///
 | ||||
| /// **This Read-Write Lock is only safe within a single executor.**
 | ||||
| pub struct NoopRawRwLock { | ||||
|     _phantom: PhantomData<*mut ()>, | ||||
| } | ||||
| 
 | ||||
| unsafe impl Send for NoopRawRwLock {} | ||||
| 
 | ||||
| impl NoopRawRwLock { | ||||
|     /// Create a new `NoopRawRwLock`.
 | ||||
|     pub const fn new() -> Self { | ||||
|         Self { _phantom: PhantomData } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| unsafe impl RawRwLock for NoopRawRwLock { | ||||
|     const INIT: Self = Self::new(); | ||||
|     fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||||
|         f() | ||||
|     } | ||||
| 
 | ||||
|     fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||||
|         f() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // ================
 | ||||
| 
 | ||||
| #[cfg(any(cortex_m, feature = "std"))] | ||||
| mod thread_mode { | ||||
|     use super::*; | ||||
| 
 | ||||
|     /// A "read-write lock" that only allows borrowing from thread mode.
 | ||||
|     ///
 | ||||
|     /// # Safety
 | ||||
|     ///
 | ||||
|     /// **This Read-Write Lock is only safe on single-core systems.**
 | ||||
|     ///
 | ||||
|     /// On multi-core systems, a `ThreadModeRawRwLock` **is not sufficient** to ensure exclusive access.
 | ||||
|     pub struct ThreadModeRawRwLock { | ||||
|         _phantom: PhantomData<()>, | ||||
|     } | ||||
| 
 | ||||
|     unsafe impl Send for ThreadModeRawRwLock {} | ||||
|     unsafe impl Sync for ThreadModeRawRwLock {} | ||||
| 
 | ||||
|     impl ThreadModeRawRwLock { | ||||
|         /// Create a new `ThreadModeRawRwLock`.
 | ||||
|         pub const fn new() -> Self { | ||||
|             Self { _phantom: PhantomData } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     unsafe impl RawRwLock for ThreadModeRawRwLock { | ||||
|         const INIT: Self = Self::new(); | ||||
|         fn read_lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||||
|             assert!( | ||||
|                 in_thread_mode(), | ||||
|                 "ThreadModeRwLock can only be locked from thread mode." | ||||
|             ); | ||||
| 
 | ||||
|             f() | ||||
|         } | ||||
| 
 | ||||
|         fn write_lock<R>(&self, f: impl FnOnce() -> R) -> R { | ||||
|             assert!( | ||||
|                 in_thread_mode(), | ||||
|                 "ThreadModeRwLock can only be locked from thread mode." | ||||
|             ); | ||||
| 
 | ||||
|             f() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     impl Drop for ThreadModeRawRwLock { | ||||
|         fn drop(&mut self) { | ||||
|             assert!( | ||||
|                 in_thread_mode(), | ||||
|                 "ThreadModeRwLock can only be dropped from thread mode." | ||||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub(crate) fn in_thread_mode() -> bool { | ||||
|         #[cfg(feature = "std")] | ||||
|         return Some("main") == std::thread::current().name(); | ||||
| 
 | ||||
|         #[cfg(not(feature = "std"))] | ||||
|         return unsafe { (0xE000ED04 as *const u32).read_volatile() } & 0x1FF == 0; | ||||
|     } | ||||
| } | ||||
| #[cfg(any(cortex_m, feature = "std"))] | ||||
| pub use thread_mode::*; | ||||
| @ -11,7 +11,6 @@ pub(crate) mod fmt; | ||||
| mod ring_buffer; | ||||
| 
 | ||||
| pub mod blocking_mutex; | ||||
| pub mod blocking_rwlock; | ||||
| pub mod channel; | ||||
| pub mod lazy_lock; | ||||
| pub mod mutex; | ||||
|  | ||||
| @ -7,8 +7,8 @@ use core::future::{poll_fn, Future}; | ||||
| use core::ops::{Deref, DerefMut}; | ||||
| use core::task::Poll; | ||||
| 
 | ||||
| use crate::blocking_rwlock::raw::RawRwLock; | ||||
| use crate::blocking_rwlock::RwLock as BlockingRwLock; | ||||
| use crate::blocking_mutex::raw::RawMutex; | ||||
| use crate::blocking_mutex::Mutex as BlockingMutex; | ||||
| use crate::waitqueue::WakerRegistration; | ||||
| 
 | ||||
| /// Error returned by [`RwLock::try_read_lock`] and [`RwLock::try_write_lock`]
 | ||||
| @ -31,53 +31,48 @@ struct State { | ||||
| ///
 | ||||
| /// Which implementation you select depends on the context in which you're using the read-write lock.
 | ||||
| ///
 | ||||
| /// Use [`CriticalSectionRawRwLock`](crate::blocking_mutex::raw_rwlock::CriticalSectionRawRwLock) when data can be shared between threads and interrupts.
 | ||||
| /// Use [`CriticalSectionMutex`] when data can be shared between threads and interrupts.
 | ||||
| ///
 | ||||
| /// Use [`NoopRawRwLock`](crate::blocking_mutex::raw_rwlock::NoopRawRwLock) when data is only shared between tasks running on the same executor.
 | ||||
| /// Use [`NoopMutex`] when data is only shared between tasks running on the same executor.
 | ||||
| ///
 | ||||
| /// Use [`ThreadModeRawRwLock`](crate::blocking_mutex::raw_rwlock::ThreadModeRawRwLock) when data is shared between tasks running on the same executor but you want a singleton.
 | ||||
| /// Use [`ThreadModeMutex`] when data is shared between tasks running on the same executor but you want a global singleton.
 | ||||
| ///
 | ||||
| pub struct RwLock<R, T> | ||||
| 
 | ||||
| pub struct RwLock<M, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     M: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     state: BlockingRwLock<R, RefCell<State>>, | ||||
|     state: BlockingMutex<M, RefCell<State>>, | ||||
|     inner: UnsafeCell<T>, | ||||
| } | ||||
| 
 | ||||
| unsafe impl<R: RawRwLock + Send, T: ?Sized + Send> Send for RwLock<R, T> {} | ||||
| unsafe impl<R: RawRwLock + Sync, T: ?Sized + Send> Sync for RwLock<R, T> {} | ||||
| unsafe impl<M: RawMutex + Send, T: ?Sized + Send> Send for RwLock<M, T> {} | ||||
| unsafe impl<M: RawMutex + Sync, T: ?Sized + Send> Sync for RwLock<M, T> {} | ||||
| 
 | ||||
| /// Async read-write lock.
 | ||||
| impl<R, T> RwLock<R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
| { | ||||
|     /// Create a new read-write lock with the given value.
 | ||||
|     pub const fn new(value: T) -> Self { | ||||
|         Self { | ||||
|             inner: UnsafeCell::new(value), | ||||
|             state: BlockingRwLock::new(RefCell::new(State { | ||||
|             state: BlockingMutex::new(RefCell::new(State { | ||||
|                 readers: 0, | ||||
|                 writer: false, | ||||
|                 waker: WakerRegistration::new(), | ||||
|             })), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<R, T> RwLock<R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     /// Lock the read-write lock for reading.
 | ||||
|     ///
 | ||||
|     /// This will wait for the lock to be available if it's already locked for writing.
 | ||||
|     pub fn read_lock(&self) -> impl Future<Output = RwLockReadGuard<'_, R, T>> { | ||||
|     pub fn read(&self) -> impl Future<Output = RwLockReadGuard<'_, R, T>> { | ||||
|         poll_fn(|cx| { | ||||
|             let ready = self.state.write_lock(|s| { | ||||
|             let ready = self.state.lock(|s| { | ||||
|                 let mut s = s.borrow_mut(); | ||||
|                 if s.writer { | ||||
|                     s.waker.register(cx.waker()); | ||||
| @ -99,11 +94,11 @@ where | ||||
|     /// Lock the read-write lock for writing.
 | ||||
|     ///
 | ||||
|     /// This will wait for the lock to be available if it's already locked for reading or writing.
 | ||||
|     pub fn write_lock(&self) -> impl Future<Output = RwLockWriteGuard<'_, R, T>> { | ||||
|     pub fn write(&self) -> impl Future<Output = RwLockWriteGuard<'_, R, T>> { | ||||
|         poll_fn(|cx| { | ||||
|             let ready = self.state.write_lock(|s| { | ||||
|             let ready = self.state.lock(|s| { | ||||
|                 let mut s = s.borrow_mut(); | ||||
|                 if s.readers > 0 || s.writer { | ||||
|                 if s.writer || s.readers > 0 { | ||||
|                     s.waker.register(cx.waker()); | ||||
|                     false | ||||
|                 } else { | ||||
| @ -119,41 +114,13 @@ where | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     /// Attempt to immediately lock the read-write lock for reading.
 | ||||
|     ///
 | ||||
|     /// If the lock is already locked for writing, this will return an error instead of waiting.
 | ||||
|     pub fn try_read_lock(&self) -> Result<RwLockReadGuard<'_, R, T>, TryLockError> { | ||||
|         self.state.read_lock(|s| { | ||||
|             let mut s = s.borrow_mut(); | ||||
|             if s.writer { | ||||
|                 Err(TryLockError) | ||||
|             } else { | ||||
|                 s.readers += 1; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         })?; | ||||
| 
 | ||||
|         Ok(RwLockReadGuard { rwlock: self }) | ||||
|     } | ||||
| 
 | ||||
|     /// Attempt to immediately lock the read-write lock for writing.
 | ||||
|     ///
 | ||||
|     /// If the lock is already locked for reading or writing, this will return an error instead of waiting.
 | ||||
|     pub fn try_write_lock(&self) -> Result<RwLockWriteGuard<'_, R, T>, TryLockError> { | ||||
|         self.state.write_lock(|s| { | ||||
|             let mut s = s.borrow_mut(); | ||||
|             if s.readers > 0 || s.writer { | ||||
|                 Err(TryLockError) | ||||
|             } else { | ||||
|                 s.writer = true; | ||||
|                 Ok(()) | ||||
|             } | ||||
|         })?; | ||||
| 
 | ||||
|         Ok(RwLockWriteGuard { rwlock: self }) | ||||
|     } | ||||
| 
 | ||||
| impl<R, T> RwLock<R, T> | ||||
| where | ||||
|     R: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     /// Consumes this read-write lock, returning the underlying data.
 | ||||
|     pub fn into_inner(self) -> T | ||||
|     where | ||||
| @ -171,7 +138,7 @@ where | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<R: RawRwLock, T> From<T> for RwLock<R, T> { | ||||
| impl<R: RawMutex, T> From<T> for RwLock<R, T> { | ||||
|     fn from(from: T) -> Self { | ||||
|         Self::new(from) | ||||
|     } | ||||
| @ -179,7 +146,7 @@ impl<R: RawRwLock, T> From<T> for RwLock<R, T> { | ||||
| 
 | ||||
| impl<R, T> Default for RwLock<R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: Default, | ||||
| { | ||||
|     fn default() -> Self { | ||||
| @ -187,26 +154,6 @@ where | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<R, T> fmt::Debug for RwLock<R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     T: ?Sized + fmt::Debug, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
|         let mut d = f.debug_struct("RwLock"); | ||||
|         match self.try_write_lock() { | ||||
|             Ok(value) => { | ||||
|                 d.field("inner", &&*value); | ||||
|             } | ||||
|             Err(TryLockError) => { | ||||
|                 d.field("inner", &format_args!("<locked>")); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         d.finish_non_exhaustive() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// Async read lock guard.
 | ||||
| ///
 | ||||
| /// Owning an instance of this type indicates having
 | ||||
| @ -217,19 +164,19 @@ where | ||||
| #[must_use = "if unused the RwLock will immediately unlock"] | ||||
| pub struct RwLockReadGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     rwlock: &'a RwLock<R, T>, | ||||
| } | ||||
| 
 | ||||
| impl<'a, R, T> Drop for RwLockReadGuard<'a, R, T> | ||||
| impl<'a, M, T> Drop for RwLockReadGuard<'a, M, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     M: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         self.rwlock.state.write_lock(|s| { | ||||
|         self.rwlock.state.lock(|s| { | ||||
|             let mut s = unwrap!(s.try_borrow_mut()); | ||||
|             s.readers -= 1; | ||||
|             if s.readers == 0 { | ||||
| @ -239,9 +186,9 @@ where | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, R, T> Deref for RwLockReadGuard<'a, R, T> | ||||
| impl<'a, M, T> Deref for RwLockReadGuard<'a, M, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     M: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     type Target = T; | ||||
| @ -252,9 +199,9 @@ where | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, R, T> fmt::Debug for RwLockReadGuard<'a, R, T> | ||||
| impl<'a, M, T> fmt::Debug for RwLockReadGuard<'a, M, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     M: RawMutex, | ||||
|     T: ?Sized + fmt::Debug, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
| @ -262,9 +209,9 @@ where | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<'a, R, T> fmt::Display for RwLockReadGuard<'a, R, T> | ||||
| impl<'a, M, T> fmt::Display for RwLockReadGuard<'a, M, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     M: RawMutex, | ||||
|     T: ?Sized + fmt::Display, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
| @ -282,7 +229,7 @@ where | ||||
| #[must_use = "if unused the RwLock will immediately unlock"] | ||||
| pub struct RwLockWriteGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     rwlock: &'a RwLock<R, T>, | ||||
| @ -290,11 +237,11 @@ where | ||||
| 
 | ||||
| impl<'a, R, T> Drop for RwLockWriteGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     fn drop(&mut self) { | ||||
|         self.rwlock.state.write_lock(|s| { | ||||
|         self.rwlock.state.lock(|s| { | ||||
|             let mut s = unwrap!(s.try_borrow_mut()); | ||||
|             s.writer = false; | ||||
|             s.waker.wake(); | ||||
| @ -304,7 +251,7 @@ where | ||||
| 
 | ||||
| impl<'a, R, T> Deref for RwLockWriteGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     type Target = T; | ||||
| @ -317,7 +264,7 @@ where | ||||
| 
 | ||||
| impl<'a, R, T> DerefMut for RwLockWriteGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized, | ||||
| { | ||||
|     fn deref_mut(&mut self) -> &mut Self::Target { | ||||
| @ -329,7 +276,7 @@ where | ||||
| 
 | ||||
| impl<'a, R, T> fmt::Debug for RwLockWriteGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized + fmt::Debug, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
| @ -339,7 +286,7 @@ where | ||||
| 
 | ||||
| impl<'a, R, T> fmt::Display for RwLockWriteGuard<'a, R, T> | ||||
| where | ||||
|     R: RawRwLock, | ||||
|     R: RawMutex, | ||||
|     T: ?Sized + fmt::Display, | ||||
| { | ||||
|     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||||
| @ -349,41 +296,41 @@ where | ||||
| 
 | ||||
| #[cfg(test)] | ||||
| mod tests { | ||||
|     use crate::blocking_rwlock::raw::NoopRawRwLock; | ||||
|     use crate::blocking_mutex::raw::NoopRawMutex; | ||||
|     use crate::rwlock::RwLock; | ||||
| 
 | ||||
|     #[futures_test::test] | ||||
|     async fn read_guard_releases_lock_when_dropped() { | ||||
|         let rwlock: RwLock<NoopRawRwLock, [i32; 2]> = RwLock::new([0, 1]); | ||||
|         let rwlock: RwLock<NoopRawMutex, [i32; 2]> = RwLock::new([0, 1]); | ||||
| 
 | ||||
|         { | ||||
|             let guard = rwlock.read_lock().await; | ||||
|             let guard = rwlock.read().await; | ||||
|             assert_eq!(*guard, [0, 1]); | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             let guard = rwlock.read_lock().await; | ||||
|             let guard = rwlock.read().await; | ||||
|             assert_eq!(*guard, [0, 1]); | ||||
|         } | ||||
| 
 | ||||
|         assert_eq!(*rwlock.read_lock().await, [0, 1]); | ||||
|         assert_eq!(*rwlock.read().await, [0, 1]); | ||||
|     } | ||||
| 
 | ||||
|     #[futures_test::test] | ||||
|     async fn write_guard_releases_lock_when_dropped() { | ||||
|         let rwlock: RwLock<NoopRawRwLock, [i32; 2]> = RwLock::new([0, 1]); | ||||
|         let rwlock: RwLock<NoopRawMutex, [i32; 2]> = RwLock::new([0, 1]); | ||||
| 
 | ||||
|         { | ||||
|             let mut guard = rwlock.write_lock().await; | ||||
|             let mut guard = rwlock.write().await; | ||||
|             assert_eq!(*guard, [0, 1]); | ||||
|             guard[1] = 2; | ||||
|         } | ||||
| 
 | ||||
|         { | ||||
|             let guard = rwlock.read_lock().await; | ||||
|             let guard = rwlock.read().await; | ||||
|             assert_eq!(*guard, [0, 2]); | ||||
|         } | ||||
| 
 | ||||
|         assert_eq!(*rwlock.read_lock().await, [0, 2]); | ||||
|         assert_eq!(*rwlock.read().await, [0, 2]); | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user