Skip to content

Commit 5776c72

Browse files
committed
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.
1 parent 943c3ae commit 5776c72

File tree

2 files changed

+76
-40
lines changed

2 files changed

+76
-40
lines changed

src/userdata/cell.rs

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
use std::cell::{RefCell, UnsafeCell};
1+
use std::cell::RefCell;
22

33
#[cfg(feature = "serde")]
44
use serde::ser::{Serialize, Serializer};
55

66
use crate::error::{Error, Result};
77
use crate::types::XRc;
88

9-
use super::lock::{RawLock, UserDataLock};
9+
use super::lock::{RawLock, RwLock, UserDataLock};
1010
use super::r#ref::{UserDataRef, UserDataRefMut};
1111

1212
#[cfg(all(feature = "serde", not(feature = "send")))]
@@ -80,10 +80,12 @@ impl<T> UserDataVariant<T> {
8080
return Err(Error::UserDataBorrowMutError);
8181
}
8282
Ok(match self {
83-
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
83+
Self::Default(inner) => XRc::into_inner(inner).unwrap().into_value(),
8484
#[cfg(feature = "serde")]
8585
Self::Serializable(inner) => unsafe {
86-
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
86+
// The serde variant erases `T` to `Box<DynSerialize>`, so we
87+
// must cast the raw pointer back to recover the concrete type.
88+
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().into_value());
8789
*Box::from_raw(raw as *mut T)
8890
},
8991
})
@@ -101,18 +103,18 @@ impl<T> UserDataVariant<T> {
101103
#[inline(always)]
102104
pub(super) fn raw_lock(&self) -> &RawLock {
103105
match self {
104-
Self::Default(inner) => &inner.raw_lock,
106+
Self::Default(inner) => unsafe { inner.raw_lock() },
105107
#[cfg(feature = "serde")]
106-
Self::Serializable(inner) => &inner.raw_lock,
108+
Self::Serializable(inner) => unsafe { inner.raw_lock() },
107109
}
108110
}
109111

110112
#[inline(always)]
111113
pub(super) fn as_ptr(&self) -> *mut T {
112114
match self {
113-
Self::Default(inner) => inner.value.get(),
115+
Self::Default(inner) => inner.as_ptr(),
114116
#[cfg(feature = "serde")]
115-
Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
117+
Self::Serializable(inner) => unsafe { (&mut **inner.as_ptr()) as *mut DynSerialize as *mut T },
116118
}
117119
}
118120
}
@@ -124,31 +126,40 @@ impl Serialize for UserDataStorage<()> {
124126
Self::Owned(variant @ UserDataVariant::Serializable(inner)) => unsafe {
125127
let _guard = (variant.raw_lock().try_lock_shared_guarded())
126128
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
127-
(*inner.value.get()).serialize(serializer)
129+
(*inner.as_ptr()).serialize(serializer)
128130
},
129131
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
130132
}
131133
}
132134
}
133135

134136
/// A type that provides interior mutability for a userdata value (thread-safe).
135-
pub(crate) struct UserDataCell<T> {
136-
raw_lock: RawLock,
137-
value: UnsafeCell<T>,
138-
}
139-
140-
#[cfg(feature = "send")]
141-
unsafe impl<T: Send> Send for UserDataCell<T> {}
142-
#[cfg(feature = "send")]
143-
unsafe impl<T: Send> Sync for UserDataCell<T> {}
137+
pub(crate) struct UserDataCell<T>(RwLock<T>);
144138

145139
impl<T> UserDataCell<T> {
146140
#[inline(always)]
147141
fn new(value: T) -> Self {
148-
UserDataCell {
149-
raw_lock: RawLock::INIT,
150-
value: UnsafeCell::new(value),
151-
}
142+
UserDataCell(RwLock::new(value))
143+
}
144+
145+
/// Returns a reference to the underlying raw lock.
146+
#[inline(always)]
147+
pub(super) unsafe fn raw_lock(&self) -> &RawLock {
148+
self.0.raw()
149+
}
150+
151+
/// Returns a raw pointer to the wrapped value.
152+
///
153+
/// The caller is responsible for ensuring the appropriate lock is held.
154+
#[inline(always)]
155+
pub(super) fn as_ptr(&self) -> *mut T {
156+
self.0.data_ptr()
157+
}
158+
159+
/// Consumes the cell and returns the inner value.
160+
#[inline(always)]
161+
pub(super) fn into_value(self) -> T {
162+
self.0.into_inner()
152163
}
153164
}
154165

src/userdata/lock.rs

Lines changed: 43 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
pub(crate) trait UserDataLock {
2-
const INIT: Self;
3-
42
fn is_locked(&self) -> bool;
53
fn try_lock_shared(&self) -> bool;
64
fn try_lock_exclusive(&self) -> bool;
@@ -48,12 +46,12 @@ impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
4846
}
4947
}
5048

51-
pub(crate) use lock_impl::RawLock;
49+
pub(crate) use lock_impl::{RawLock, RwLock};
5250

5351
#[cfg(not(feature = "send"))]
5452
#[cfg(not(tarpaulin_include))]
5553
mod lock_impl {
56-
use std::cell::Cell;
54+
use std::cell::{Cell, UnsafeCell};
5755

5856
// Positive values represent the number of read references.
5957
// Negative values represent the number of write references (only one allowed).
@@ -62,9 +60,6 @@ mod lock_impl {
6260
const UNUSED: isize = 0;
6361

6462
impl super::UserDataLock for RawLock {
65-
#[allow(clippy::declare_interior_mutable_const)]
66-
const INIT: Self = Cell::new(UNUSED);
67-
6863
#[inline(always)]
6964
fn is_locked(&self) -> bool {
7065
self.get() != UNUSED
@@ -104,41 +99,71 @@ mod lock_impl {
10499
self.set(flag + 1);
105100
}
106101
}
102+
103+
/// A cheap single-threaded read-write lock pairing a `parking_lot::RwLock` type.
104+
pub(crate) struct RwLock<T> {
105+
lock: RawLock,
106+
data: UnsafeCell<T>,
107+
}
108+
109+
impl<T> RwLock<T> {
110+
/// Creates a new `RwLock` containing the given value.
111+
#[inline(always)]
112+
pub(crate) fn new(value: T) -> Self {
113+
RwLock {
114+
lock: RawLock::new(UNUSED),
115+
data: UnsafeCell::new(value),
116+
}
117+
}
118+
119+
/// Returns a reference to the underlying raw lock.
120+
#[inline(always)]
121+
pub(crate) unsafe fn raw(&self) -> &RawLock {
122+
&self.lock
123+
}
124+
125+
/// Returns a raw pointer to the underlying data.
126+
#[inline(always)]
127+
pub(crate) fn data_ptr(&self) -> *mut T {
128+
self.data.get()
129+
}
130+
131+
/// Consumes this `RwLock`, returning the underlying data.
132+
#[inline(always)]
133+
pub(crate) fn into_inner(self) -> T {
134+
self.data.into_inner()
135+
}
136+
}
107137
}
108138

109139
#[cfg(feature = "send")]
110140
mod lock_impl {
111-
use parking_lot::lock_api::RawRwLock;
112-
113-
pub(crate) type RawLock = parking_lot::RawRwLock;
141+
pub(crate) use parking_lot::{RawRwLock as RawLock, RwLock};
114142

115143
impl super::UserDataLock for RawLock {
116-
#[allow(clippy::declare_interior_mutable_const)]
117-
const INIT: Self = <Self as parking_lot::lock_api::RawRwLock>::INIT;
118-
119144
#[inline(always)]
120145
fn is_locked(&self) -> bool {
121-
RawRwLock::is_locked(self)
146+
parking_lot::lock_api::RawRwLock::is_locked(self)
122147
}
123148

124149
#[inline(always)]
125150
fn try_lock_shared(&self) -> bool {
126-
RawRwLock::try_lock_shared(self)
151+
parking_lot::lock_api::RawRwLock::try_lock_shared(self)
127152
}
128153

129154
#[inline(always)]
130155
fn try_lock_exclusive(&self) -> bool {
131-
RawRwLock::try_lock_exclusive(self)
156+
parking_lot::lock_api::RawRwLock::try_lock_exclusive(self)
132157
}
133158

134159
#[inline(always)]
135160
unsafe fn unlock_shared(&self) {
136-
RawRwLock::unlock_shared(self)
161+
parking_lot::lock_api::RawRwLock::unlock_shared(self)
137162
}
138163

139164
#[inline(always)]
140165
unsafe fn unlock_exclusive(&self) {
141-
RawRwLock::unlock_exclusive(self)
166+
parking_lot::lock_api::RawRwLock::unlock_exclusive(self)
142167
}
143168
}
144169
}

0 commit comments

Comments
 (0)