use std::fmt::Debug;
use std::marker::PhantomData;
use crate::{
implementation::{LockMetadata, SharedImpl},
synchronizer::{SynchronizerMetadata, SynchronizerReadLock, SynchronizerWriteLock},
};
type Unsend = PhantomData<std::sync::MutexGuard<'static, ()>>;
unsafe impl<'a, T: ?Sized> Send for SharedReadLockInner<'a, T> {}
unsafe impl<'a, T: ?Sized> Send for SharedWriteLockInner<'a, T> {}
pub enum SharedReadLockInner<'a, T: ?Sized> {
Sync(SynchronizerReadLock<'a, LockMetadata, T>),
SyncBox(SynchronizerReadLock<'a, LockMetadata, Box<T>>),
Projection(Box<dyn std::ops::Deref<Target = T> + 'a>),
}
#[must_use = "if unused the lock will immediately unlock"]
pub struct SharedReadLock<'a, T: ?Sized> {
pub(crate) inner: SharedReadLockInner<'a, T>,
#[cfg(feature = "deadlock_detection")]
pub(crate) lockdep: Option<crate::lockdep::HeldToken>,
pub(crate) _unsend: Unsend,
}
impl<'a, T: ?Sized> SharedReadLock<'a, T> {
pub(crate) fn from_projection(inner: Box<dyn std::ops::Deref<Target = T> + 'a>) -> Self {
SharedReadLock {
inner: SharedReadLockInner::Projection(inner),
#[cfg(feature = "deadlock_detection")]
lockdep: None,
_unsend: PhantomData,
}
}
pub fn map<P: ?Sized, F>(self, project: F) -> SharedReadLock<'a, P>
where
F: for<'x> Fn(&'x T) -> &'x P + Send + 'a,
{
struct Mapped<'a, T: ?Sized, P: ?Sized, F> {
lock: SharedReadLock<'a, T>,
project: F,
_marker: std::marker::PhantomData<&'a P>,
}
impl<'a, T: ?Sized, P: ?Sized, F> std::ops::Deref for Mapped<'a, T, P, F>
where
F: for<'x> Fn(&'x T) -> &'x P,
{
type Target = P;
fn deref(&self) -> &P {
(self.project)(&*self.lock)
}
}
SharedReadLock::from_projection(Box::new(Mapped {
lock: self,
project,
_marker: std::marker::PhantomData,
}))
}
}
impl<'a, T: ?Sized> Drop for SharedReadLock<'a, T> {
fn drop(&mut self) {
#[cfg(feature = "deadlock_detection")]
if let Some(token) = self.lockdep.take() {
crate::lockdep::release_held(token);
}
if let SharedReadLockInner::Sync(lock) = &self.inner {
let metadata = lock.metadata();
if let Some(poison) = &metadata.poison {
if crate::implementation::panicking_should_poison() {
poison.store(true, std::sync::atomic::Ordering::Release);
}
}
}
if let SharedReadLockInner::SyncBox(lock) = &self.inner {
let metadata = lock.metadata();
if let Some(poison) = &metadata.poison {
if crate::implementation::panicking_should_poison() {
poison.store(true, std::sync::atomic::Ordering::Release);
}
}
}
}
}
impl<'a, T: ?Sized> From<SynchronizerReadLock<'a, LockMetadata, T>> for SharedReadLock<'a, T> {
fn from(value: SynchronizerReadLock<'a, LockMetadata, T>) -> Self {
#[cfg(feature = "deadlock_detection")]
let lockdep = crate::lockdep::read_guard_token(&value);
SharedReadLock {
inner: SharedReadLockInner::Sync(value),
#[cfg(feature = "deadlock_detection")]
lockdep,
_unsend: PhantomData,
}
}
}
impl<'a, T: ?Sized> From<SynchronizerReadLock<'a, LockMetadata, Box<T>>> for SharedReadLock<'a, T> {
fn from(value: SynchronizerReadLock<'a, LockMetadata, Box<T>>) -> Self {
#[cfg(feature = "deadlock_detection")]
let lockdep = crate::lockdep::read_guard_token(&value);
SharedReadLock {
inner: SharedReadLockInner::SyncBox(value),
#[cfg(feature = "deadlock_detection")]
lockdep,
_unsend: PhantomData,
}
}
}
pub struct SharedReadLockOwned<T: ?Sized + 'static> {
pub(crate) inner: SharedReadLock<'static, T>,
#[allow(unused)]
pub(crate) container: SharedImpl<T>,
}
unsafe impl<T: ?Sized + 'static> Send for SharedReadLockOwned<T> {}
unsafe impl<T: ?Sized + 'static> Send for SharedWriteLockOwned<T> {}
impl<T: ?Sized + 'static> SharedReadLockOwned<T> {
#[cfg(feature = "async_experimental")]
#[allow(clippy::needless_lifetimes)]
pub(crate) unsafe fn unsafe_reattach<'a>(
self,
_to: &'a SharedImpl<T>,
) -> SharedReadLock<'a, T> {
std::mem::transmute(self.inner)
}
}
pub struct SharedWriteLockOwned<T: ?Sized + 'static> {
pub(crate) inner: SharedWriteLock<'static, T>,
#[allow(unused)]
pub(crate) container: SharedImpl<T>,
}
impl<T: ?Sized + 'static> SharedWriteLockOwned<T> {
#[cfg(feature = "async_experimental")]
#[allow(clippy::needless_lifetimes)]
pub(crate) unsafe fn unsafe_reattach<'a>(
self,
_to: &'a SharedImpl<T>,
) -> SharedWriteLock<'a, T> {
std::mem::transmute(self.inner)
}
}
impl<T: ?Sized + 'static> std::ops::Deref for SharedReadLockOwned<T> {
type Target = <SharedReadLock<'static, T> as std::ops::Deref>::Target;
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<T: ?Sized + 'static> std::ops::Deref for SharedWriteLockOwned<T> {
type Target = <SharedWriteLock<'static, T> as std::ops::Deref>::Target;
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<T: ?Sized + 'static> std::ops::DerefMut for SharedWriteLockOwned<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
pub enum SharedWriteLockInner<'a, T: ?Sized> {
Sync(SynchronizerWriteLock<'a, LockMetadata, T>),
SyncBox(SynchronizerWriteLock<'a, LockMetadata, Box<T>>),
Projection(Box<dyn std::ops::DerefMut<Target = T> + 'a>),
}
#[must_use = "if unused the lock will immediately unlock"]
pub struct SharedWriteLock<'a, T: ?Sized> {
pub(crate) inner: SharedWriteLockInner<'a, T>,
#[cfg(feature = "deadlock_detection")]
pub(crate) lockdep: Option<crate::lockdep::HeldToken>,
pub(crate) _unsend: Unsend,
}
impl<'a, T: ?Sized> SharedWriteLock<'a, T> {
pub(crate) fn from_projection(inner: Box<dyn std::ops::DerefMut<Target = T> + 'a>) -> Self {
SharedWriteLock {
inner: SharedWriteLockInner::Projection(inner),
#[cfg(feature = "deadlock_detection")]
lockdep: None,
_unsend: PhantomData,
}
}
pub fn map<P: ?Sized, FR, FW>(self, project: FR, project_mut: FW) -> SharedWriteLock<'a, P>
where
FR: for<'x> Fn(&'x T) -> &'x P + Send + 'a,
FW: for<'x> Fn(&'x mut T) -> &'x mut P + Send + 'a,
{
struct Mapped<'a, T: ?Sized, P: ?Sized, FR, FW> {
lock: SharedWriteLock<'a, T>,
project: FR,
project_mut: FW,
_marker: std::marker::PhantomData<&'a P>,
}
impl<'a, T: ?Sized, P: ?Sized, FR, FW> std::ops::Deref for Mapped<'a, T, P, FR, FW>
where
FR: for<'x> Fn(&'x T) -> &'x P,
{
type Target = P;
fn deref(&self) -> &P {
(self.project)(&*self.lock)
}
}
impl<'a, T: ?Sized, P: ?Sized, FR, FW> std::ops::DerefMut for Mapped<'a, T, P, FR, FW>
where
FR: for<'x> Fn(&'x T) -> &'x P,
FW: for<'x> Fn(&'x mut T) -> &'x mut P,
{
fn deref_mut(&mut self) -> &mut P {
(self.project_mut)(&mut *self.lock)
}
}
SharedWriteLock::from_projection(Box::new(Mapped {
lock: self,
project,
project_mut,
_marker: std::marker::PhantomData,
}))
}
}
impl<'a, T: ?Sized> From<SynchronizerWriteLock<'a, LockMetadata, T>> for SharedWriteLock<'a, T> {
fn from(value: SynchronizerWriteLock<'a, LockMetadata, T>) -> Self {
#[cfg(feature = "deadlock_detection")]
let lockdep = crate::lockdep::write_guard_token(&value);
SharedWriteLock {
inner: SharedWriteLockInner::Sync(value),
#[cfg(feature = "deadlock_detection")]
lockdep,
_unsend: PhantomData,
}
}
}
impl<'a, T: ?Sized> From<SynchronizerWriteLock<'a, LockMetadata, Box<T>>>
for SharedWriteLock<'a, T>
{
fn from(value: SynchronizerWriteLock<'a, LockMetadata, Box<T>>) -> Self {
#[cfg(feature = "deadlock_detection")]
let lockdep = crate::lockdep::write_guard_token(&value);
SharedWriteLock {
inner: SharedWriteLockInner::SyncBox(value),
#[cfg(feature = "deadlock_detection")]
lockdep,
_unsend: PhantomData,
}
}
}
impl<'a, T: ?Sized> std::ops::Deref for SharedReadLock<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
use SharedReadLockInner::*;
match &self.inner {
Sync(x) => x,
SyncBox(x) => x,
Projection(x) => x,
}
}
}
impl<'a, T: ?Sized> std::ops::Deref for SharedWriteLock<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
use SharedWriteLockInner::*;
match &self.inner {
Sync(x) => x,
SyncBox(x) => x,
Projection(x) => x,
}
}
}
impl<'a, T: ?Sized> std::ops::DerefMut for SharedWriteLock<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
use SharedWriteLockInner::*;
match &mut self.inner {
Sync(x) => &mut *x,
SyncBox(x) => &mut *x,
Projection(x) => &mut *x,
}
}
}
impl<'a, T: ?Sized> Drop for SharedWriteLock<'a, T> {
fn drop(&mut self) {
#[cfg(feature = "deadlock_detection")]
if let Some(token) = self.lockdep.take() {
crate::lockdep::release_held(token);
}
if let SharedWriteLockInner::Sync(lock) = &self.inner {
let metadata = lock.metadata();
if let Some(poison) = &metadata.poison {
if crate::implementation::panicking_should_poison() {
poison.store(true, std::sync::atomic::Ordering::Release);
}
}
}
if let SharedWriteLockInner::SyncBox(lock) = &self.inner {
let metadata = lock.metadata();
if let Some(poison) = &metadata.poison {
if crate::implementation::panicking_should_poison() {
poison.store(true, std::sync::atomic::Ordering::Release);
}
}
}
}
}
#[must_use = "if unused the lock will immediately unlock"]
pub struct SendGuard<G> {
inner: G,
}
unsafe impl<G> Send for SendGuard<G> {}
impl<G> SendGuard<G> {
pub(crate) fn new(inner: G) -> Self {
SendGuard { inner }
}
pub fn into_inner(self) -> G {
self.inner
}
}
impl<G: std::ops::Deref> std::ops::Deref for SendGuard<G> {
type Target = G::Target;
fn deref(&self) -> &Self::Target {
self.inner.deref()
}
}
impl<G: std::ops::DerefMut> std::ops::DerefMut for SendGuard<G> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
impl<G: Debug> Debug for SendGuard<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
impl<G: std::fmt::Display> std::fmt::Display for SendGuard<G> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.inner.fmt(f)
}
}
macro_rules! implement_lock_delegates {
($for:ty) => {
impl<'a, T: ?Sized> std::fmt::Debug for $for
where
T: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(**self).fmt(f)
}
}
impl<'a, T: ?Sized> std::fmt::Display for $for
where
T: std::fmt::Display,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
(**self).fmt(f)
}
}
impl<'a, T: ?Sized> std::error::Error for $for
where
T: std::error::Error,
{
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn std::error::Error> {
(**self).cause()
}
#[allow(deprecated)]
fn description(&self) -> &str {
(**self).description()
}
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
(**self).source()
}
}
impl<'a, T: ?Sized> std::borrow::Borrow<T> for $for {
fn borrow(&self) -> &T {
&**self
}
}
impl<'a, T: ?Sized, U: ?Sized> AsRef<U> for $for
where
T: AsRef<U>,
{
fn as_ref(&self) -> &U {
(**self).as_ref()
}
}
impl<'a, T: ?Sized, U: ?Sized> AsMut<U> for $for
where
T: AsMut<U>,
Self: std::ops::DerefMut<Target = T>,
{
fn as_mut(&mut self) -> &mut U {
(**self).as_mut()
}
}
impl<'a, T: ?Sized, Rhs: ?Sized> PartialEq<Rhs> for $for
where
T: PartialEq<Rhs>,
{
fn eq(&self, other: &Rhs) -> bool {
(**self).eq(other)
}
}
impl<'a, T: ?Sized, Rhs: ?Sized> PartialOrd<Rhs> for $for
where
T: PartialOrd<Rhs>,
{
fn partial_cmp(&self, other: &Rhs) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl<'a, T: ?Sized> Unpin for $for {}
};
}
implement_lock_delegates!(SharedReadLock<'a, T>);
implement_lock_delegates!(SharedReadLockOwned<T>);
implement_lock_delegates!(SharedWriteLock<'a, T>);
implement_lock_delegates!(SharedWriteLockOwned<T>);
#[cfg(doctest)]
mod send_test {}
#[cfg(test)]
mod test {
use super::*;
#[allow(unused)]
fn ensure_guard_auto_traits() {
fn ensure_send<T: Send>() {}
ensure_send::<SendGuard<SharedReadLock<'static, ()>>>();
ensure_send::<SendGuard<SharedWriteLock<'static, ()>>>();
}
#[test]
fn test_guard_map() {
let shared = crate::SharedMut::new((1u32, "x".to_string()));
let read = shared.read().map(|t| &t.0);
assert_eq!(*read, 1);
assert!(shared.try_write().is_none());
drop(read);
let mut write = shared.write().map(|t| &t.1, |t| &mut t.1);
*write += "y";
drop(write);
assert_eq!(shared.read().1, "xy");
}
#[allow(unused)]
fn ensure_locks_coerce_deref(read: SharedReadLock<String>) {
fn takes_as_ref(_: &String) {}
fn takes_as_ref_str(_: &impl AsRef<str>) {}
takes_as_ref(&read);
takes_as_ref_str(&read);
assert!(read == *"123");
}
}