Skip to content

Commit

Permalink
Add map/try_map for MappedRwLock*Guard and fix documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
udoprog committed May 5, 2020
1 parent 3a4fad7 commit 89b46b8
Showing 1 changed file with 261 additions and 16 deletions.
277 changes: 261 additions & 16 deletions tokio/src/sync/rwlock.rs
Expand Up @@ -80,16 +80,131 @@ pub struct RwLock<T> {
c: UnsafeCell<T>,
}

/// An RAII read lock guard returned by [`RwLockReadGuard::map`], which can
/// point to a subfield of the protected data.
/// An RAII read lock guard returned by [`RwLockReadGuard::map`] or
/// [`MappedRwLockReadGuard::map`], which can point to a subfield of the
/// protected data.
///
/// [`RwLockReadGuard::map`]: method@RwLockReadGuard::map
/// [`MappedRwLockReadGuard::map`]: method@MappedRwLockReadGuard::map
pub struct MappedRwLockReadGuard<'a, T> {
s: &'a Semaphore,
data: *const T,
marker: marker::PhantomData<&'a T>,
}

impl<'a, T> MappedRwLockReadGuard<'a, T> {
/// Make a new `MappedRwLockReadGuard` for a component of the locked data.
///
/// This operation cannot fail as the `MappedRwLockReadGuard` passed in
/// already locked the data.
///
/// This is an associated function that needs to be
/// used as `MappedRwLockReadGuard::map(...)`. A method would interfere with
/// methods of the same name on the contents of the locked data.
///
/// This is an asynchronous version of [`MappedRwLockReadGuard::map`] from the
/// [parking_lot crate].
///
/// [`MappedRwLockReadGuard::map`]: https://docs.rs/lock_api/latest/lock_api/struct.MappedRwLockReadGuard.html#method.map
/// [parking_lot crate]: https://crates.io/crates/parking_lot
///
/// # Examples
///
/// ```
/// use tokio::sync::{RwLock, RwLockReadGuard, MappedRwLockReadGuard};
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Bar(u32);
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Foo(Bar);
///
/// # #[tokio::main]
/// # async fn main() {
/// let lock = RwLock::new(Foo(Bar(1)));
///
/// let guard = lock.read().await;
/// let guard = RwLockReadGuard::map(guard, |f| &f.0);
/// let guard = MappedRwLockReadGuard::map(guard, |f| &f.0);
///
/// assert_eq!(1, *guard);
/// # }
/// ```
#[inline]
pub fn map<U, F>(this: Self, f: F) -> MappedRwLockReadGuard<'a, U>
where
F: FnOnce(&T) -> &U,
{
let data = f(unsafe { &*this.data });
let s = this.s;
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
MappedRwLockReadGuard {
s,
data,
marker: marker::PhantomData,
}
}

/// Attempts to make a new [`MappedRwLockReadGuard`] for a component of the
/// locked data. The original guard is returned if the closure returns
/// `None`.
///
/// This operation cannot fail as the `MappedRwLockReadGuard` passed in
/// already locked the data.
///
/// This is an associated function that needs to be used as
/// `MappedRwLockReadGuard::map(..)`. A method would interfere with methods
/// of the same name on the contents of the locked data.
///
/// This is an asynchronous version of [`MappedRwLockReadGuard::try_map`]
/// from the [parking_lot crate].
///
/// [`MappedRwLockReadGuard::try_map`]: https://docs.rs/lock_api/latest/lock_api/struct.MappedRwLockReadGuard.html#method.try_map
/// [parking_lot crate]: https://crates.io/crates/parking_lot
///
/// # Examples
///
/// ```
/// use tokio::sync::{RwLock, RwLockReadGuard, MappedRwLockReadGuard};
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Bar(u32);
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Foo(Bar);
///
/// # #[tokio::main]
/// # async fn main() {
/// let lock = RwLock::new(Foo(Bar(1)));
///
/// let guard = lock.read().await;
/// let guard = RwLockReadGuard::try_map(guard, |f| Some(&f.0)).expect("should not fail");
/// let guard = MappedRwLockReadGuard::try_map(guard, |f| Some(&f.0)).expect("should not fail");
///
/// assert_eq!(1, *guard);
/// # }
/// ```
#[inline]
pub fn try_map<U, F>(this: Self, f: F) -> Result<MappedRwLockReadGuard<'a, U>, Self>
where
F: FnOnce(&T) -> Option<&U>,
{
let data = match f(unsafe { &*this.data }) {
Some(data) => data,
None => return Err(this),
};
let s = this.s;
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(MappedRwLockReadGuard {
s,
data,
marker: marker::PhantomData,
})
}
}

impl<'a, T> ops::Deref for MappedRwLockReadGuard<'a, T> {
type Target = T;

Expand Down Expand Up @@ -123,16 +238,137 @@ impl<'a, T> Drop for MappedRwLockReadGuard<'a, T> {
}
}

/// An RAII write lock guard returned by [`RwLockWriteGuard::map`], which can
/// point to a subfield of the protected data.
/// An RAII write lock guard returned by [`RwLockWriteGuard::map`] or
/// [`MappedRwLockWriteGuard::map`], which can point to a subfield of the
/// protected data.
///
/// [`RwLockWriteGuard::map`]: method@RwLockWriteGuard::map
/// [`MappedRwLockWriteGuard::map`]: method@MappedRwLockWriteGuard::map
pub struct MappedRwLockWriteGuard<'a, T> {
s: &'a Semaphore,
data: *mut T,
marker: marker::PhantomData<&'a mut T>,
}

impl<'a, T> MappedRwLockWriteGuard<'a, T> {
/// Make a new `MappedRwLockWriteGuard` for a component of the locked data.
///
/// This operation cannot fail as the `MappedRwLockWriteGuard` passed in
/// already locked the data.
///
/// This is an associated function that needs to be
/// used as `MappedRwLockWriteGuard::map(...)`. A method would interfere with
/// methods of the same name on the contents of the locked data.
///
/// This is an asynchronous version of [`MappedRwLockWriteGuard::map`] from the
/// [parking_lot crate].
///
/// [`MappedRwLockWriteGuard::map`]: https://docs.rs/lock_api/latest/lock_api/struct.MappedRwLockWriteGuard.html#method.map
/// [parking_lot crate]: https://crates.io/crates/parking_lot
///
/// # Examples
///
/// ```
/// use tokio::sync::{RwLock, RwLockWriteGuard, MappedRwLockWriteGuard};
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Bar(u32);
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Foo(Bar);
///
/// # #[tokio::main]
/// # async fn main() {
/// let lock = RwLock::new(Foo(Bar(1)));
///
/// {
/// let guard = lock.write().await;
/// let guard = RwLockWriteGuard::map(guard, |f| &mut f.0);
/// let mut guard = MappedRwLockWriteGuard::map(guard, |f| &mut f.0);
/// *guard = 2;
/// }
///
/// assert_eq!(Foo(Bar(2)), *lock.read().await);
/// # }
/// ```
#[inline]
pub fn map<U, F>(this: Self, f: F) -> MappedRwLockWriteGuard<'a, U>
where
F: FnOnce(&mut T) -> &mut U,
{
let data = f(unsafe { &mut *this.data });
let s = this.s;
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
MappedRwLockWriteGuard {
s,
data,
marker: marker::PhantomData,
}
}

/// Attempts to make a new [`MappedRwLockWriteGuard`] for a component of the
/// locked data. The original guard is returned if the closure returns
/// `None`.
///
/// This operation cannot fail as the `MappedRwLockWriteGuard` passed in
/// already locked the data.
///
/// This is an associated function that needs to be used as
/// `MappedRwLockWriteGuard::map(..)`. A method would interfere with methods
/// of the same name on the contents of the locked data.
///
/// This is an asynchronous version of [`MappedRwLockWriteGuard::try_map`]
/// from the [parking_lot crate].
///
/// [`MappedRwLockWriteGuard::try_map`]: https://docs.rs/lock_api/latest/lock_api/struct.MappedRwLockWriteGuard.html#method.try_map
/// [parking_lot crate]: https://crates.io/crates/parking_lot
///
/// # Examples
///
/// ```
/// use tokio::sync::{RwLock, RwLockWriteGuard, MappedRwLockWriteGuard};
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Bar(u32);
///
/// #[derive(Debug, Clone, Copy, PartialEq, Eq)]
/// struct Foo(Bar);
///
/// # #[tokio::main]
/// # async fn main() {
/// let lock = RwLock::new(Foo(Bar(1)));
///
/// {
/// let guard = lock.write().await;
/// let guard = RwLockWriteGuard::try_map(guard, |f| Some(&mut f.0)).expect("should not fail");
/// let mut guard = MappedRwLockWriteGuard::try_map(guard, |f| Some(&mut f.0)).expect("should not fail");
/// *guard = 2;
/// }
///
/// assert_eq!(Foo(Bar(2)), *lock.read().await);
/// # }
/// ```
#[inline]
pub fn try_map<U, F>(this: Self, f: F) -> Result<MappedRwLockWriteGuard<'a, U>, Self>
where
F: FnOnce(&mut T) -> Option<&mut U>,
{
let data = match f(unsafe { &mut *this.data }) {
Some(data) => data,
None => return Err(this),
};
let s = this.s;
// NB: Forget to avoid drop impl from being called.
mem::forget(this);
Ok(MappedRwLockWriteGuard {
s,
data,
marker: marker::PhantomData,
})
}
}

impl<'a, T> ops::Deref for MappedRwLockWriteGuard<'a, T> {
type Target = T;

Expand Down Expand Up @@ -200,6 +436,7 @@ impl<'a, T> RwLockReadGuard<'a, T> {
///
/// [`RwLockReadGuard::map`]: https://docs.rs/lock_api/latest/lock_api/struct.RwLockReadGuard.html#method.map
/// [parking_lot crate]: https://crates.io/crates/parking_lot
///
/// # Examples
///
/// ```
Expand All @@ -211,8 +448,11 @@ impl<'a, T> RwLockReadGuard<'a, T> {
/// # #[tokio::main]
/// # async fn main() {
/// let lock = RwLock::new(Foo(1));
/// let mapped = RwLockReadGuard::map(lock.read().await, |f| &f.0);
/// assert_eq!(1, *mapped);
///
/// let guard = lock.read().await;
/// let guard = RwLockReadGuard::map(guard, |f| &f.0);
///
/// assert_eq!(1, *guard);
/// # }
/// ```
#[inline]
Expand All @@ -232,13 +472,14 @@ impl<'a, T> RwLockReadGuard<'a, T> {
}

/// Attempts to make a new [`MappedRwLockReadGuard`] for a component of the
/// locked data. The original guard is return if the closure returns `None`.
/// locked data. The original guard is returned if the closure returns
/// `None`.
///
/// This operation cannot fail as the `RwLockReadGuard` passed in already
/// locked the data.
///
/// This is an associated function that needs to be used as
/// `RwLockReadGuard::map(..)`. A method would interfere with methods of the
/// `RwLockReadGuard::try_map(..)`. A method would interfere with methods of the
/// same name on the contents of the locked data.
///
/// This is an asynchronous version of [`RwLockReadGuard::try_map`] from the
Expand All @@ -258,8 +499,11 @@ impl<'a, T> RwLockReadGuard<'a, T> {
/// # #[tokio::main]
/// # async fn main() {
/// let lock = RwLock::new(Foo(1));
/// let mapped = RwLockReadGuard::try_map(lock.read().await, |f| Some(&f.0)).expect("should not fail");
/// assert_eq!(1, *mapped);
///
/// let guard = lock.read().await;
/// let guard = RwLockReadGuard::try_map(guard, |f| Some(&f.0)).expect("should not fail");
///
/// assert_eq!(1, *guard);
/// # }
/// ```
#[inline]
Expand Down Expand Up @@ -371,18 +615,18 @@ impl<'a, T> RwLockWriteGuard<'a, T> {
}

/// Attempts to make a new [`MappedRwLockWriteGuard`] for a component of
/// the locked data. The original guard is return if the closure returns
/// the locked data. The original guard is returned if the closure returns
/// `None`.
///
/// This operation cannot fail as the `RwLockWriteGuard` passed in already
/// locked the data.
///
/// This is an associated function that needs to be
/// used as `RwLockWriteGuard::map(...)`. A method would interfere with
/// used as `RwLockWriteGuard::try_map(...)`. A method would interfere with
/// methods of the same name on the contents of the locked data.
///
/// This is an asynchronous version of [`RwLockWriteGuard::try_map`] from the
/// [parking_lot crate].
/// This is an asynchronous version of [`RwLockWriteGuard::try_map`] from
/// the [parking_lot crate].
///
/// [`RwLockWriteGuard::try_map`]: https://docs.rs/lock_api/latest/lock_api/struct.RwLockWriteGuard.html#method.try_map
/// [parking_lot crate]: https://crates.io/crates/parking_lot
Expand All @@ -400,8 +644,9 @@ impl<'a, T> RwLockWriteGuard<'a, T> {
/// let lock = RwLock::new(Foo(1));
///
/// {
/// let mut mapped = RwLockWriteGuard::try_map(lock.write().await, |f| Some(&mut f.0)).expect("should not fail");
/// *mapped = 2;
/// let guard = lock.write().await;
/// let mut guard = RwLockWriteGuard::try_map(guard, |f| Some(&mut f.0)).expect("should not fail");
/// *guard = 2;
/// }
///
/// assert_eq!(Foo(2), *lock.read().await);
Expand Down

0 comments on commit 89b46b8

Please sign in to comment.