mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
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:
+33
-22
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user