diff --git a/src/userdata/cell.rs b/src/userdata/cell.rs index 40025fa..e954b61 100644 --- a/src/userdata/cell.rs +++ b/src/userdata/cell.rs @@ -1,4 +1,4 @@ -use std::cell::{RefCell, UnsafeCell}; +use std::cell::RefCell; #[cfg(feature = "serde")] use serde::ser::{Serialize, Serializer}; @@ -6,7 +6,7 @@ use serde::ser::{Serialize, Serializer}; use crate::error::{Error, Result}; use crate::types::XRc; -use super::lock::{RawLock, UserDataLock}; +use super::lock::{RawLock, RwLock, UserDataLock}; use super::r#ref::{UserDataRef, UserDataRefMut}; #[cfg(all(feature = "serde", not(feature = "send")))] @@ -80,10 +80,12 @@ impl UserDataVariant { return Err(Error::UserDataBorrowMutError); } Ok(match self { - Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(), + Self::Default(inner) => XRc::into_inner(inner).unwrap().into_value(), #[cfg(feature = "serde")] Self::Serializable(inner) => unsafe { - let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner()); + // The serde variant erases `T` to `Box`, so we + // must cast the raw pointer back to recover the concrete type. + let raw = Box::into_raw(XRc::into_inner(inner).unwrap().into_value()); *Box::from_raw(raw as *mut T) }, }) @@ -101,18 +103,18 @@ impl UserDataVariant { #[inline(always)] pub(super) fn raw_lock(&self) -> &RawLock { match self { - Self::Default(inner) => &inner.raw_lock, + Self::Default(inner) => unsafe { inner.raw_lock() }, #[cfg(feature = "serde")] - Self::Serializable(inner) => &inner.raw_lock, + Self::Serializable(inner) => unsafe { inner.raw_lock() }, } } #[inline(always)] pub(super) fn as_ptr(&self) -> *mut T { match self { - Self::Default(inner) => inner.value.get(), + Self::Default(inner) => inner.as_ptr(), #[cfg(feature = "serde")] - Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box) }, + Self::Serializable(inner) => unsafe { (&mut **inner.as_ptr()) as *mut DynSerialize as *mut T }, } } } @@ -124,7 +126,7 @@ impl Serialize for UserDataStorage<()> { Self::Owned(variant @ UserDataVariant::Serializable(inner)) => unsafe { let _guard = (variant.raw_lock().try_lock_shared_guarded()) .map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?; - (*inner.value.get()).serialize(serializer) + (*inner.as_ptr()).serialize(serializer) }, _ => Err(serde::ser::Error::custom("cannot serialize ")), } @@ -132,23 +134,32 @@ impl Serialize for UserDataStorage<()> { } /// A type that provides interior mutability for a userdata value (thread-safe). -pub(crate) struct UserDataCell { - raw_lock: RawLock, - value: UnsafeCell, -} - -#[cfg(feature = "send")] -unsafe impl Send for UserDataCell {} -#[cfg(feature = "send")] -unsafe impl Sync for UserDataCell {} +pub(crate) struct UserDataCell(RwLock); impl UserDataCell { #[inline(always)] fn new(value: T) -> Self { - UserDataCell { - raw_lock: RawLock::INIT, - value: UnsafeCell::new(value), - } + UserDataCell(RwLock::new(value)) + } + + /// Returns a reference to the underlying raw lock. + #[inline(always)] + pub(super) unsafe fn raw_lock(&self) -> &RawLock { + self.0.raw() + } + + /// Returns a raw pointer to the wrapped value. + /// + /// The caller is responsible for ensuring the appropriate lock is held. + #[inline(always)] + pub(super) fn as_ptr(&self) -> *mut T { + self.0.data_ptr() + } + + /// Consumes the cell and returns the inner value. + #[inline(always)] + pub(super) fn into_value(self) -> T { + self.0.into_inner() } } diff --git a/src/userdata/lock.rs b/src/userdata/lock.rs index b749ed7..901a557 100644 --- a/src/userdata/lock.rs +++ b/src/userdata/lock.rs @@ -1,6 +1,4 @@ pub(crate) trait UserDataLock { - const INIT: Self; - fn is_locked(&self) -> bool; fn try_lock_shared(&self) -> bool; fn try_lock_exclusive(&self) -> bool; @@ -48,12 +46,12 @@ impl Drop for LockGuard<'_, L> { } } -pub(crate) use lock_impl::RawLock; +pub(crate) use lock_impl::{RawLock, RwLock}; #[cfg(not(feature = "send"))] #[cfg(not(tarpaulin_include))] mod lock_impl { - use std::cell::Cell; + use std::cell::{Cell, UnsafeCell}; // Positive values represent the number of read references. // Negative values represent the number of write references (only one allowed). @@ -62,9 +60,6 @@ mod lock_impl { const UNUSED: isize = 0; impl super::UserDataLock for RawLock { - #[allow(clippy::declare_interior_mutable_const)] - const INIT: Self = Cell::new(UNUSED); - #[inline(always)] fn is_locked(&self) -> bool { self.get() != UNUSED @@ -104,41 +99,71 @@ mod lock_impl { self.set(flag + 1); } } + + /// A cheap single-threaded read-write lock pairing a `parking_lot::RwLock` type. + pub(crate) struct RwLock { + lock: RawLock, + data: UnsafeCell, + } + + impl RwLock { + /// Creates a new `RwLock` containing the given value. + #[inline(always)] + pub(crate) fn new(value: T) -> Self { + RwLock { + lock: RawLock::new(UNUSED), + data: UnsafeCell::new(value), + } + } + + /// Returns a reference to the underlying raw lock. + #[inline(always)] + pub(crate) unsafe fn raw(&self) -> &RawLock { + &self.lock + } + + /// Returns a raw pointer to the underlying data. + #[inline(always)] + pub(crate) fn data_ptr(&self) -> *mut T { + self.data.get() + } + + /// Consumes this `RwLock`, returning the underlying data. + #[inline(always)] + pub(crate) fn into_inner(self) -> T { + self.data.into_inner() + } + } } #[cfg(feature = "send")] mod lock_impl { - use parking_lot::lock_api::RawRwLock; - - pub(crate) type RawLock = parking_lot::RawRwLock; + pub(crate) use parking_lot::{RawRwLock as RawLock, RwLock}; impl super::UserDataLock for RawLock { - #[allow(clippy::declare_interior_mutable_const)] - const INIT: Self = ::INIT; - #[inline(always)] fn is_locked(&self) -> bool { - RawRwLock::is_locked(self) + parking_lot::lock_api::RawRwLock::is_locked(self) } #[inline(always)] fn try_lock_shared(&self) -> bool { - RawRwLock::try_lock_shared(self) + parking_lot::lock_api::RawRwLock::try_lock_shared(self) } #[inline(always)] fn try_lock_exclusive(&self) -> bool { - RawRwLock::try_lock_exclusive(self) + parking_lot::lock_api::RawRwLock::try_lock_exclusive(self) } #[inline(always)] unsafe fn unlock_shared(&self) { - RawRwLock::unlock_shared(self) + parking_lot::lock_api::RawRwLock::unlock_shared(self) } #[inline(always)] unsafe fn unlock_exclusive(&self) { - RawRwLock::unlock_exclusive(self) + parking_lot::lock_api::RawRwLock::unlock_exclusive(self) } } }