Use parking_lot::RwLock in UserDataCell container in "send" mode.

In non-send mode, mimic the `RwLock` API (using `Cell<isize>` counter).
We're continue manually operating the underlying `RawRwLock` for flexibility.
This commit is contained in:
Alex Orlenko
2026-02-21 23:15:48 +00:00
parent 943c3aed58
commit 5776c72208
2 changed files with 76 additions and 40 deletions
+33 -22
View File
@@ -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<T> UserDataVariant<T> {
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<DynSerialize>`, 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<T> UserDataVariant<T> {
#[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<T>) },
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 <userdata>")),
}
@@ -132,23 +134,32 @@ impl Serialize for UserDataStorage<()> {
}
/// A type that provides interior mutability for a userdata value (thread-safe).
pub(crate) struct UserDataCell<T> {
raw_lock: RawLock,
value: UnsafeCell<T>,
}
#[cfg(feature = "send")]
unsafe impl<T: Send> Send for UserDataCell<T> {}
#[cfg(feature = "send")]
unsafe impl<T: Send> Sync for UserDataCell<T> {}
pub(crate) struct UserDataCell<T>(RwLock<T>);
impl<T> UserDataCell<T> {
#[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()
}
}
+43 -18
View File
@@ -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<L: UserDataLock + ?Sized> 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<T> {
lock: RawLock,
data: UnsafeCell<T>,
}
impl<T> RwLock<T> {
/// 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 = <Self as parking_lot::lock_api::RawRwLock>::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)
}
}
}