Refactor userdata-wrappers feature.

Support borrowing underlying data in `UserDataRef` and `UserDataRefMut`.
This commit is contained in:
Alex Orlenko
2025-03-21 12:29:47 +00:00
parent edbf1e9150
commit 4fe7d151a1
10 changed files with 1146 additions and 644 deletions
+1 -2
View File
@@ -1330,7 +1330,7 @@ impl Lua {
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
pub fn register_userdata_type<T: 'static>(&self, f: impl FnOnce(&mut UserDataRegistry<T>)) -> Result<()> {
let type_id = TypeId::of::<T>();
let mut registry = UserDataRegistry::new(self, type_id);
let mut registry = UserDataRegistry::new(self);
f(&mut registry);
let lua = self.lock();
@@ -1499,7 +1499,6 @@ impl Lua {
&self,
f: impl for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> Result<R>,
) -> Result<R> {
// TODO: Update to `&Scope` in next major release
f(&Scope::new(self.lock_arc()))
}
+13 -8
View File
@@ -864,7 +864,7 @@ impl RawLua {
}
// Create a new metatable from `UserData` definition
let mut registry = UserDataRegistry::new(self.lua(), type_id);
let mut registry = UserDataRegistry::new(self.lua());
T::register(&mut registry);
self.create_userdata_metatable(registry.into_raw())
@@ -885,7 +885,7 @@ impl RawLua {
// Check if metatable creation is pending or create an empty metatable otherwise
let registry = match (*self.extra.get()).pending_userdata_reg.remove(&type_id) {
Some(registry) => registry,
None => UserDataRegistry::<T>::new(self.lua(), type_id).into_raw(),
None => UserDataRegistry::<T>::new(self.lua()).into_raw(),
};
self.create_userdata_metatable(registry)
})
@@ -1103,17 +1103,22 @@ impl RawLua {
// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
//
// Returns `None` if the userdata is registered but non-static.
pub(crate) unsafe fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
self.get_userdata_type_id_inner(self.ref_thread(), vref.index)
#[inline(always)]
pub(crate) fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
unsafe { self.get_userdata_type_id_inner(self.ref_thread(), vref.index) }
}
// Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
pub(crate) unsafe fn get_userdata_type_id<T>(&self, idx: c_int) -> Result<Option<TypeId>> {
match self.get_userdata_type_id_inner(self.state(), idx) {
pub(crate) unsafe fn get_userdata_type_id<T>(
&self,
state: *mut ffi::lua_State,
idx: c_int,
) -> Result<Option<TypeId>> {
match self.get_userdata_type_id_inner(state, idx) {
Ok(type_id) => Ok(type_id),
Err(Error::UserDataTypeMismatch) if ffi::lua_type(self.state(), idx) != ffi::LUA_TUSERDATA => {
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
// Report `FromLuaConversionError` instead
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(self.state(), idx));
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(state, idx));
let idx_type_name = idx_type_name.to_str().unwrap();
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))
+27 -28
View File
@@ -12,7 +12,10 @@ use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{MaybeSend, ValueRef};
use crate::util::{check_stack, get_userdata, push_string, take_userdata, StackGuard};
use crate::util::{
borrow_userdata_scoped, borrow_userdata_scoped_mut, check_stack, get_userdata, push_string,
take_userdata, StackGuard, TypeIdHints,
};
use crate::value::Value;
#[cfg(feature = "async")]
@@ -26,7 +29,7 @@ use {
// Re-export for convenience
pub(crate) use cell::UserDataStorage;
pub use cell::{UserDataRef, UserDataRefMut};
pub use r#ref::{UserDataRef, UserDataRefMut};
pub use registry::UserDataRegistry;
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
@@ -622,7 +625,10 @@ impl AnyUserData {
/// Checks whether the type of this userdata is `T`.
#[inline]
pub fn is<T: 'static>(&self) -> bool {
self.inspect::<T, _, _>(|_| Ok(())).is_ok()
let lua = self.0.lua.lock();
let type_id = lua.get_userdata_ref_type_id(&self.0);
// We do not use wrapped types here, rather prefer to check the "real" type of the userdata
matches!(type_id, Ok(Some(type_id)) if type_id == TypeId::of::<T>())
}
/// Borrow this userdata immutably if it is of type `T`.
@@ -637,7 +643,8 @@ impl AnyUserData {
/// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
self.inspect(|ud| ud.try_borrow_owned())
let lua = self.0.lua.lock();
unsafe { UserDataRef::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
}
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
@@ -645,7 +652,10 @@ impl AnyUserData {
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud)))
let lua = self.0.lua.lock();
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
let type_hints = TypeIdHints::new::<T>();
unsafe { borrow_userdata_scoped(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
}
/// Borrow this userdata mutably if it is of type `T`.
@@ -660,7 +670,8 @@ impl AnyUserData {
/// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
self.inspect(|ud| ud.try_borrow_owned_mut())
let lua = self.0.lua.lock();
unsafe { UserDataRefMut::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
}
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
@@ -668,7 +679,10 @@ impl AnyUserData {
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud)))
let lua = self.0.lua.lock();
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
let type_hints = TypeIdHints::new::<T>();
unsafe { borrow_userdata_scoped_mut(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
}
/// Takes the value out of this userdata.
@@ -687,9 +701,11 @@ impl AnyUserData {
let type_id = lua.push_userdata_ref(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
// Try to borrow userdata exclusively
let _ = (*get_userdata::<UserDataStorage<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataStorage<T>>(state).into_inner()
if (*get_userdata::<UserDataStorage<T>>(state, -1)).has_exclusive_access() {
take_userdata::<UserDataStorage<T>>(state).into_inner()
} else {
Err(Error::UserDataBorrowMutError)
}
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -965,24 +981,6 @@ impl AnyUserData {
};
is_serializable().unwrap_or(false)
}
pub(crate) fn inspect<T, F, R>(&self, func: F) -> Result<R>
where
T: 'static,
F: FnOnce(&UserDataStorage<T>) -> Result<R>,
{
let lua = self.0.lua.lock();
unsafe {
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ud = get_userdata::<UserDataStorage<T>>(lua.ref_thread(), self.0.index);
func(&*ud)
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
}
/// Handle to a [`AnyUserData`] metatable.
@@ -1106,6 +1104,7 @@ where
mod cell;
mod lock;
mod object;
mod r#ref;
mod registry;
mod util;
+55 -307
View File
@@ -1,22 +1,13 @@
use std::any::{type_name, TypeId};
use std::cell::{RefCell, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
#[cfg(feature = "serialize")]
use serde::ser::{Serialize, Serializer};
use crate::error::{Error, Result};
use crate::state::{Lua, RawLua};
use crate::traits::FromLua;
use crate::types::XRc;
use crate::userdata::AnyUserData;
use crate::util::get_userdata;
use crate::value::Value;
use super::lock::{RawLock, UserDataLock};
use super::util::is_sync;
use super::r#ref::{UserDataRef, UserDataRefMut};
#[cfg(all(feature = "serialize", not(feature = "send")))]
type DynSerialize = dyn erased_serde::Serialize;
@@ -34,7 +25,7 @@ pub(crate) enum UserDataStorage<T> {
pub(crate) enum UserDataVariant<T> {
Default(XRc<UserDataCell<T>>),
#[cfg(feature = "serialize")]
Serializable(XRc<UserDataCell<Box<DynSerialize>>>),
Serializable(XRc<UserDataCell<Box<DynSerialize>>>, bool), // bool is `is_sync`
}
impl<T> Clone for UserDataVariant<T> {
@@ -43,16 +34,28 @@ impl<T> Clone for UserDataVariant<T> {
match self {
Self::Default(inner) => Self::Default(XRc::clone(inner)),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)),
Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync),
}
}
}
impl<T> UserDataVariant<T> {
// Immutably borrows the wrapped value in-place.
#[inline(always)]
fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
UserDataBorrowRef::try_from(self)
pub(super) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
// We don't need to check for `T: Sync` because when this method is used (internally),
// Lua mutex is already locked.
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
// exclusively locked.
let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?;
Ok(f(unsafe { &*self.as_ptr() }))
}
// Mutably borrows the wrapped value in-place.
#[inline(always)]
fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
let _guard =
(self.raw_lock().try_lock_exclusive_guarded()).map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(unsafe { &mut *self.as_ptr() }))
}
// Immutably borrows the wrapped value and returns an owned reference.
@@ -61,12 +64,6 @@ impl<T> UserDataVariant<T> {
UserDataRef::try_from(self.clone())
}
// Mutably borrows the wrapped value in-place.
#[inline(always)]
fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
UserDataBorrowMut::try_from(self)
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
@@ -83,7 +80,7 @@ impl<T> UserDataVariant<T> {
Ok(match self {
Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => unsafe {
Self::Serializable(inner, _) => unsafe {
let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner());
*Box::from_raw(raw as *mut T)
},
@@ -95,25 +92,25 @@ impl<T> UserDataVariant<T> {
match self {
Self::Default(inner) => XRc::strong_count(inner),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => XRc::strong_count(inner),
Self::Serializable(inner, _) => XRc::strong_count(inner),
}
}
#[inline(always)]
fn raw_lock(&self) -> &RawLock {
pub(super) fn raw_lock(&self) -> &RawLock {
match self {
Self::Default(inner) => &inner.raw_lock,
#[cfg(feature = "serialize")]
Self::Serializable(inner) => &inner.raw_lock,
Self::Serializable(inner, _) => &inner.raw_lock,
}
}
#[inline(always)]
fn as_ptr(&self) -> *mut T {
pub(super) fn as_ptr(&self) -> *mut T {
match self {
Self::Default(inner) => inner.value.get(),
#[cfg(feature = "serialize")]
Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box<T>) },
}
}
}
@@ -122,14 +119,24 @@ impl<T> UserDataVariant<T> {
impl Serialize for UserDataStorage<()> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
Self::Owned(UserDataVariant::Serializable(inner)) => unsafe {
// We need to borrow the inner value exclusively to serialize it.
Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe {
#[cfg(feature = "send")]
let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?;
// No need to do this if the `send` feature is disabled.
if *is_sync {
let _guard = (variant.raw_lock().try_lock_shared_guarded())
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
(*inner.value.get()).serialize(serializer)
} else {
let _guard = (variant.raw_lock().try_lock_exclusive_guarded())
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
(*inner.value.get()).serialize(serializer)
}
#[cfg(not(feature = "send"))]
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
(*inner.value.get()).serialize(serializer)
{
let _ = is_sync;
let _guard = (variant.raw_lock().try_lock_shared_guarded())
.map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?;
(*inner.value.get()).serialize(serializer)
}
},
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
}
@@ -157,232 +164,6 @@ impl<T> UserDataCell<T> {
}
}
/// A wrapper type for a userdata value that provides read access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRef<T>(UserDataVariant<T>);
impl<T> Deref for UserDataRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.0.as_ptr() }
}
}
impl<T> Drop for UserDataRef<T> {
#[inline]
fn drop(&mut self) {
if !cfg!(feature = "send") || is_sync::<T>() {
unsafe { self.0.raw_lock().unlock_shared() };
} else {
unsafe { self.0.raw_lock().unlock_exclusive() };
}
}
}
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
type Error = Error;
#[inline]
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
if !cfg!(feature = "send") || is_sync::<T>() {
if !variant.raw_lock().try_lock_shared() {
return Err(Error::UserDataBorrowError);
}
} else if !variant.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowError);
}
Ok(UserDataRef(variant))
}
}
impl<T: 'static> FromLua for UserDataRef<T> {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
try_value_to_userdata::<T>(value)?.borrow()
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id::<T>(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned()
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
/// A wrapper type for a userdata value that provides read and write access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRefMut<T>(UserDataVariant<T>);
impl<T> Deref for UserDataRefMut<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
unsafe { &*self.0.as_ptr() }
}
}
impl<T> DerefMut for UserDataRefMut<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0.as_ptr() }
}
}
impl<T> Drop for UserDataRefMut<T> {
#[inline]
fn drop(&mut self) {
unsafe { self.0.raw_lock().unlock_exclusive() };
}
}
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
type Error = Error;
#[inline]
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
if !variant.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
Ok(UserDataRefMut(variant))
}
}
impl<T: 'static> FromLua for UserDataRefMut<T> {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
try_value_to_userdata::<T>(value)?.borrow_mut()
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
let type_id = lua.get_userdata_type_id::<T>(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned_mut()
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
/// A type that provides read access to a userdata value (borrowing the value).
pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant<T>);
impl<T> Drop for UserDataBorrowRef<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe {
self.0.raw_lock().unlock_shared();
}
}
}
impl<T> Deref for UserDataBorrowRef<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
// SAFETY: `UserDataBorrowRef` is only created with shared access to the value.
unsafe { &*self.0.as_ptr() }
}
}
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowRef<'a, T> {
type Error = Error;
#[inline(always)]
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
// We don't need to check for `T: Sync` because when this method is used (internally),
// Lua mutex is already locked.
// If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be
// exclusively locked.
if !variant.raw_lock().try_lock_shared() {
return Err(Error::UserDataBorrowError);
}
Ok(UserDataBorrowRef(variant))
}
}
pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant<T>);
impl<T> Drop for UserDataBorrowMut<'_, T> {
#[inline]
fn drop(&mut self) {
unsafe {
self.0.raw_lock().unlock_exclusive();
}
}
}
impl<T> Deref for UserDataBorrowMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
unsafe { &*self.0.as_ptr() }
}
}
impl<T> DerefMut for UserDataBorrowMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.0.as_ptr() }
}
}
impl<'a, T> TryFrom<&'a UserDataVariant<T>> for UserDataBorrowMut<'a, T> {
type Error = Error;
#[inline(always)]
fn try_from(variant: &'a UserDataVariant<T>) -> Result<Self> {
if !variant.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
Ok(UserDataBorrowMut(variant))
}
}
#[inline]
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
match value {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata".to_string(),
message: Some(format!("expected userdata of type {}", type_name::<T>())),
}),
}
}
pub(crate) enum ScopedUserDataVariant<T> {
Ref(*const T),
RefMut(RefCell<*mut T>),
@@ -423,13 +204,15 @@ impl<T: 'static> UserDataStorage<T> {
T: Serialize + crate::types::MaybeSend,
{
let data = Box::new(data) as Box<DynSerialize>;
Self::Owned(UserDataVariant::Serializable(XRc::new(UserDataCell::new(data))))
let is_sync = super::util::is_sync::<T>();
let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync);
Self::Owned(variant)
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn is_serializable(&self) -> bool {
matches!(self, Self::Owned(UserDataVariant::Serializable(_)))
matches!(self, Self::Owned(UserDataVariant::Serializable(..)))
}
// Immutably borrows the wrapped value and returns an owned reference.
@@ -441,23 +224,6 @@ impl<T: 'static> UserDataStorage<T> {
}
}
#[allow(unused)]
#[inline(always)]
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
match self {
Self::Owned(data) => data.try_borrow(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
@@ -495,10 +261,19 @@ impl<T> UserDataStorage<T> {
}
}
/// Returns `true` if the container has exclusive access to the value.
#[inline(always)]
pub(crate) fn has_exclusive_access(&self) -> bool {
match self {
Self::Owned(variant) => !variant.raw_lock().is_locked(),
Self::Scoped(_) => false,
}
}
#[inline]
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&*data.try_borrow()?)),
Self::Owned(data) => data.try_borrow_scoped(f),
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
@@ -510,7 +285,7 @@ impl<T> UserDataStorage<T> {
#[inline]
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&mut *data.try_borrow_mut()?)),
Self::Owned(data) => data.try_borrow_scoped_mut(f),
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let mut t = value
@@ -521,30 +296,3 @@ impl<T> UserDataStorage<T> {
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
#[cfg(feature = "send")]
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync);
}
+39
View File
@@ -7,6 +7,45 @@ pub(crate) trait UserDataLock {
unsafe fn unlock_shared(&self);
unsafe fn unlock_exclusive(&self);
fn try_lock_shared_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
if self.try_lock_shared() {
Ok(LockGuard {
lock: self,
exclusive: false,
})
} else {
Err(())
}
}
fn try_lock_exclusive_guarded(&self) -> Result<LockGuard<'_, Self>, ()> {
if self.try_lock_exclusive() {
Ok(LockGuard {
lock: self,
exclusive: true,
})
} else {
Err(())
}
}
}
pub(crate) struct LockGuard<'a, L: UserDataLock + ?Sized> {
lock: &'a L,
exclusive: bool,
}
impl<L: UserDataLock + ?Sized> Drop for LockGuard<'_, L> {
fn drop(&mut self) {
unsafe {
if self.exclusive {
self.lock.unlock_exclusive();
} else {
self.lock.unlock_shared();
}
}
}
}
pub(crate) use lock_impl::RawLock;
+474
View File
@@ -0,0 +1,474 @@
use std::any::{type_name, TypeId};
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
use std::{fmt, mem};
use crate::error::{Error, Result};
use crate::state::{Lua, RawLua};
use crate::traits::FromLua;
use crate::userdata::AnyUserData;
use crate::util::get_userdata;
use crate::value::Value;
use super::cell::{UserDataStorage, UserDataVariant};
use super::lock::{LockGuard, RawLock, UserDataLock};
use super::util::is_sync;
#[cfg(feature = "userdata-wrappers")]
use {
parking_lot::{
Mutex as MutexPL, MutexGuard as MutexGuardPL, RwLock as RwLockPL,
RwLockReadGuard as RwLockReadGuardPL, RwLockWriteGuard as RwLockWriteGuardPL,
},
std::sync::Arc,
};
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
use {
std::cell::{Ref, RefCell, RefMut},
std::rc::Rc,
};
/// A wrapper type for a userdata value that provides read access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRef<T: 'static> {
// It's important to drop the guard first, as it refers to the `inner` data.
_guard: LockGuard<'static, RawLock>,
inner: UserDataRefInner<T>,
}
impl<T> Deref for UserDataRef<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
&self.inner
}
}
impl<T: fmt::Debug> fmt::Debug for UserDataRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for UserDataRef<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T> TryFrom<UserDataVariant<T>> for UserDataRef<T> {
type Error = Error;
#[inline]
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
let guard = if !cfg!(feature = "send") || is_sync::<T>() {
variant.raw_lock().try_lock_shared_guarded()
} else {
variant.raw_lock().try_lock_exclusive_guarded()
};
let guard = guard.map_err(|_| Error::UserDataBorrowError)?;
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
Ok(UserDataRef::from_parts(UserDataRefInner::Default(variant), guard))
}
}
impl<T: 'static> FromLua for UserDataRef<T> {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
try_value_to_userdata::<T>(value)?.borrow()
}
#[inline]
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::borrow_from_stack(lua, lua.state(), idx)
}
}
impl<T: 'static> UserDataRef<T> {
#[inline(always)]
fn from_parts(inner: UserDataRefInner<T>, guard: LockGuard<'static, RawLock>) -> Self {
Self { _guard: guard, inner }
}
#[cfg(feature = "userdata-wrappers")]
fn remap<U>(
self,
f: impl FnOnce(UserDataVariant<T>) -> Result<UserDataRefInner<U>>,
) -> Result<UserDataRef<U>> {
match &self.inner {
UserDataRefInner::Default(variant) => {
let inner = f(variant.clone())?;
Ok(UserDataRef::from_parts(inner, self._guard))
}
_ => Err(Error::UserDataTypeMismatch),
}
}
pub(crate) unsafe fn borrow_from_stack(
lua: &RawLua,
state: *mut ffi::lua_State,
idx: c_int,
) -> Result<Self> {
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
(*ud).try_borrow_owned()
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == TypeId::of::<Rc<T>>() => {
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, idx);
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc())
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, idx);
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc_refcell())
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == TypeId::of::<Arc<T>>() => {
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, idx);
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc())
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == TypeId::of::<Arc<MutexPL<T>>>() => {
let ud = get_userdata::<UserDataStorage<Arc<MutexPL<T>>>>(state, idx);
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_mutex_pl())
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == TypeId::of::<Arc<RwLockPL<T>>>() => {
let ud = get_userdata::<UserDataStorage<Arc<RwLockPL<T>>>>(state, idx);
((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_rwlock_pl())
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
impl<T> UserDataRef<Rc<T>> {
fn transform_rc(self) -> Result<UserDataRef<T>> {
self.remap(|variant| Ok(UserDataRefInner::Rc(variant)))
}
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
impl<T> UserDataRef<Rc<RefCell<T>>> {
fn transform_rc_refcell(self) -> Result<UserDataRef<T>> {
self.remap(|variant| unsafe {
let obj = &*variant.as_ptr();
let r#ref = obj.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
let borrow = std::mem::transmute::<Ref<T>, Ref<'static, T>>(r#ref);
Ok(UserDataRefInner::RcRefCell(borrow, variant))
})
}
}
#[cfg(feature = "userdata-wrappers")]
impl<T> UserDataRef<Arc<T>> {
fn transform_arc(self) -> Result<UserDataRef<T>> {
self.remap(|variant| Ok(UserDataRefInner::Arc(variant)))
}
}
#[cfg(feature = "userdata-wrappers")]
impl<T> UserDataRef<Arc<MutexPL<T>>> {
fn transform_arc_mutex_pl(self) -> Result<UserDataRef<T>> {
self.remap(|variant| unsafe {
let obj = &*variant.as_ptr();
let guard = obj.try_lock().ok_or(Error::UserDataBorrowError)?;
let borrow = std::mem::transmute::<MutexGuardPL<T>, MutexGuardPL<'static, T>>(guard);
Ok(UserDataRefInner::ArcMutexPL(borrow, variant))
})
}
}
#[cfg(feature = "userdata-wrappers")]
impl<T> UserDataRef<Arc<RwLockPL<T>>> {
fn transform_arc_rwlock_pl(self) -> Result<UserDataRef<T>> {
self.remap(|variant| unsafe {
let obj = &*variant.as_ptr();
let guard = obj.try_read().ok_or(Error::UserDataBorrowError)?;
let borrow = std::mem::transmute::<RwLockReadGuardPL<T>, RwLockReadGuardPL<'static, T>>(guard);
Ok(UserDataRefInner::ArcRwLockPL(borrow, variant))
})
}
}
#[allow(unused)]
enum UserDataRefInner<T: 'static> {
Default(UserDataVariant<T>),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Rc(UserDataVariant<Rc<T>>),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
RcRefCell(Ref<'static, T>, UserDataVariant<Rc<RefCell<T>>>),
#[cfg(feature = "userdata-wrappers")]
Arc(UserDataVariant<Arc<T>>),
#[cfg(feature = "userdata-wrappers")]
ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant<Arc<MutexPL<T>>>),
#[cfg(feature = "userdata-wrappers")]
ArcRwLockPL(RwLockReadGuardPL<'static, T>, UserDataVariant<Arc<RwLockPL<T>>>),
}
impl<T> Deref for UserDataRefInner<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
match self {
Self::Default(inner) => unsafe { &*inner.as_ptr() },
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Self::Rc(inner) => unsafe { &*Rc::as_ptr(&*inner.as_ptr()) },
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Self::RcRefCell(x, ..) => x,
#[cfg(feature = "userdata-wrappers")]
Self::Arc(inner) => unsafe { &*Arc::as_ptr(&*inner.as_ptr()) },
#[cfg(feature = "userdata-wrappers")]
Self::ArcMutexPL(x, ..) => x,
#[cfg(feature = "userdata-wrappers")]
Self::ArcRwLockPL(x, ..) => x,
}
}
}
/// A wrapper type for a userdata value that provides read and write access.
///
/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua.
pub struct UserDataRefMut<T: 'static> {
// It's important to drop the guard first, as it refers to the `inner` data.
_guard: LockGuard<'static, RawLock>,
inner: UserDataRefMutInner<T>,
}
impl<T> Deref for UserDataRefMut<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<T> DerefMut for UserDataRefMut<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.inner
}
}
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T: fmt::Display> fmt::Display for UserDataRefMut<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(**self).fmt(f)
}
}
impl<T> TryFrom<UserDataVariant<T>> for UserDataRefMut<T> {
type Error = Error;
#[inline]
fn try_from(variant: UserDataVariant<T>) -> Result<Self> {
let guard = variant.raw_lock().try_lock_exclusive_guarded();
let guard = guard.map_err(|_| Error::UserDataBorrowMutError)?;
let guard = unsafe { mem::transmute::<LockGuard<_>, LockGuard<'static, _>>(guard) };
Ok(UserDataRefMut::from_parts(
UserDataRefMutInner::Default(variant),
guard,
))
}
}
impl<T: 'static> FromLua for UserDataRefMut<T> {
fn from_lua(value: Value, _: &Lua) -> Result<Self> {
try_value_to_userdata::<T>(value)?.borrow_mut()
}
unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
Self::borrow_from_stack(lua, lua.state(), idx)
}
}
impl<T: 'static> UserDataRefMut<T> {
#[inline(always)]
fn from_parts(inner: UserDataRefMutInner<T>, guard: LockGuard<'static, RawLock>) -> Self {
Self { _guard: guard, inner }
}
#[cfg(feature = "userdata-wrappers")]
fn remap<U>(
self,
f: impl FnOnce(UserDataVariant<T>) -> Result<UserDataRefMutInner<U>>,
) -> Result<UserDataRefMut<U>> {
match &self.inner {
UserDataRefMutInner::Default(variant) => {
let inner = f(variant.clone())?;
Ok(UserDataRefMut::from_parts(inner, self._guard))
}
_ => Err(Error::UserDataTypeMismatch),
}
}
pub(crate) unsafe fn borrow_from_stack(
lua: &RawLua,
state: *mut ffi::lua_State,
idx: c_int,
) -> Result<Self> {
let type_id = lua.get_userdata_type_id::<T>(state, idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
(*ud).try_borrow_owned_mut()
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == TypeId::of::<Rc<T>>() => Err(Error::UserDataBorrowMutError),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == TypeId::of::<Rc<RefCell<T>>>() => {
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, idx);
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_rc_refcell())
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == TypeId::of::<Arc<T>>() => Err(Error::UserDataBorrowMutError),
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == TypeId::of::<Arc<MutexPL<T>>>() => {
let ud = get_userdata::<UserDataStorage<Arc<MutexPL<T>>>>(state, idx);
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_mutex_pl())
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == TypeId::of::<Arc<RwLockPL<T>>>() => {
let ud = get_userdata::<UserDataStorage<Arc<RwLockPL<T>>>>(state, idx);
((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_rwlock_pl())
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
impl<T> UserDataRefMut<Rc<RefCell<T>>> {
fn transform_rc_refcell(self) -> Result<UserDataRefMut<T>> {
self.remap(|variant| unsafe {
let obj = &*variant.as_ptr();
let refmut = obj.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
let borrow = std::mem::transmute::<RefMut<T>, RefMut<'static, T>>(refmut);
Ok(UserDataRefMutInner::RcRefCell(borrow, variant))
})
}
}
#[cfg(feature = "userdata-wrappers")]
impl<T> UserDataRefMut<Arc<MutexPL<T>>> {
fn transform_arc_mutex_pl(self) -> Result<UserDataRefMut<T>> {
self.remap(|variant| unsafe {
let obj = &*variant.as_ptr();
let guard = obj.try_lock().ok_or(Error::UserDataBorrowMutError)?;
let borrow = std::mem::transmute::<MutexGuardPL<T>, MutexGuardPL<'static, T>>(guard);
Ok(UserDataRefMutInner::ArcMutexPL(borrow, variant))
})
}
}
#[cfg(feature = "userdata-wrappers")]
impl<T> UserDataRefMut<Arc<RwLockPL<T>>> {
fn transform_arc_rwlock_pl(self) -> Result<UserDataRefMut<T>> {
self.remap(|variant| unsafe {
let obj = &*variant.as_ptr();
let guard = obj.try_write().ok_or(Error::UserDataBorrowMutError)?;
let borrow = std::mem::transmute::<RwLockWriteGuardPL<T>, RwLockWriteGuardPL<'static, T>>(guard);
Ok(UserDataRefMutInner::ArcRwLockPL(borrow, variant))
})
}
}
#[allow(unused)]
enum UserDataRefMutInner<T: 'static> {
Default(UserDataVariant<T>),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
RcRefCell(RefMut<'static, T>, UserDataVariant<Rc<RefCell<T>>>),
#[cfg(feature = "userdata-wrappers")]
ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant<Arc<MutexPL<T>>>),
#[cfg(feature = "userdata-wrappers")]
ArcRwLockPL(RwLockWriteGuardPL<'static, T>, UserDataVariant<Arc<RwLockPL<T>>>),
}
impl<T> Deref for UserDataRefMutInner<T> {
type Target = T;
#[inline]
fn deref(&self) -> &T {
match self {
Self::Default(inner) => unsafe { &*inner.as_ptr() },
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Self::RcRefCell(x, ..) => x,
#[cfg(feature = "userdata-wrappers")]
Self::ArcMutexPL(x, ..) => x,
#[cfg(feature = "userdata-wrappers")]
Self::ArcRwLockPL(x, ..) => x,
}
}
}
impl<T> DerefMut for UserDataRefMutInner<T> {
#[inline]
fn deref_mut(&mut self) -> &mut T {
match self {
Self::Default(inner) => unsafe { &mut *inner.as_ptr() },
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Self::RcRefCell(x, ..) => x,
#[cfg(feature = "userdata-wrappers")]
Self::ArcMutexPL(x, ..) => x,
#[cfg(feature = "userdata-wrappers")]
Self::ArcRwLockPL(x, ..) => x,
}
}
}
#[inline]
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
match value {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata".to_string(),
message: Some(format!("expected userdata of type {}", type_name::<T>())),
}),
}
}
#[cfg(test)]
mod assertions {
use super::*;
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_not_impl_all!(UserDataRef<std::rc::Rc<()>>: Send, Sync);
#[cfg(feature = "send")]
static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send);
#[cfg(feature = "send")]
static_assertions::assert_not_impl_all!(UserDataRefMut<std::rc::Rc<()>>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync);
#[cfg(not(feature = "send"))]
static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync);
}
+46 -232
View File
@@ -11,7 +11,9 @@ use crate::state::{Lua, LuaGuard};
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
use crate::types::{Callback, MaybeSend};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage};
use crate::util::{get_userdata, short_type_name};
use crate::util::{
borrow_userdata_scoped, borrow_userdata_scoped_mut, get_userdata, short_type_name, TypeIdHints,
};
use crate::value::Value;
#[cfg(feature = "async")]
@@ -21,38 +23,18 @@ use {
std::future::{self, Future},
};
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
use std::rc::Rc;
#[cfg(feature = "userdata-wrappers")]
use std::sync::{Arc, Mutex, RwLock};
#[derive(Clone, Copy)]
enum UserDataTypeId {
Shared(TypeId),
enum UserDataType {
Shared(TypeIdHints),
Unique(*mut c_void),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Rc(TypeId),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
RcRefCell(TypeId),
#[cfg(feature = "userdata-wrappers")]
Arc(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcMutex(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcRwLock(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcParkingLotMutex(TypeId),
#[cfg(feature = "userdata-wrappers")]
ArcParkingLotRwLock(TypeId),
}
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<T> {
lua: LuaGuard,
raw: RawUserDataRegistry,
ud_type_id: UserDataTypeId,
_type: PhantomData<T>,
r#type: UserDataType,
_phantom: PhantomData<T>,
}
pub(crate) struct RawUserDataRegistry {
@@ -75,46 +57,34 @@ pub(crate) struct RawUserDataRegistry {
pub(crate) type_name: StdString,
}
impl UserDataTypeId {
impl UserDataType {
#[inline]
pub(crate) fn type_id(self) -> Option<TypeId> {
pub(crate) fn type_id(&self) -> Option<TypeId> {
match self {
UserDataTypeId::Shared(type_id) => Some(type_id),
UserDataTypeId::Unique(_) => None,
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
UserDataTypeId::Rc(type_id) => Some(type_id),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
UserDataTypeId::RcRefCell(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::Arc(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcMutex(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcRwLock(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id),
#[cfg(feature = "userdata-wrappers")]
UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id),
UserDataType::Shared(hints) => Some(hints.type_id()),
UserDataType::Unique(_) => None,
}
}
}
#[cfg(feature = "send")]
unsafe impl Send for UserDataTypeId {}
unsafe impl Send for UserDataType {}
impl<T: 'static> UserDataRegistry<T> {
#[inline(always)]
pub(crate) fn new(lua: &Lua) -> Self {
Self::with_type(lua, UserDataType::Shared(TypeIdHints::new::<T>()))
}
}
impl<T> UserDataRegistry<T> {
#[inline(always)]
pub(crate) fn new(lua: &Lua, type_id: TypeId) -> Self {
Self::with_type_id(lua, UserDataTypeId::Shared(type_id))
}
#[inline(always)]
pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self {
Self::with_type_id(lua, UserDataTypeId::Unique(ud_ptr))
Self::with_type(lua, UserDataType::Unique(ud_ptr))
}
#[inline(always)]
fn with_type_id(lua: &Lua, ud_type_id: UserDataTypeId) -> Self {
fn with_type(lua: &Lua, r#type: UserDataType) -> Self {
let raw = RawUserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
@@ -127,15 +97,15 @@ impl<T> UserDataRegistry<T> {
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
destructor: super::util::userdata_destructor::<T>,
type_id: ud_type_id.type_id(),
type_id: r#type.type_id(),
type_name: short_type_name::<T>(),
};
UserDataRegistry {
lua: lua.lock_arc(),
raw,
ud_type_id,
_type: PhantomData,
r#type,
_phantom: PhantomData,
}
}
@@ -152,7 +122,7 @@ impl<T> UserDataRegistry<T> {
};
}
let target_type_id = self.ud_type_id;
let target_type = self.r#type;
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
@@ -164,18 +134,16 @@ impl<T> UserDataRegistry<T> {
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match target_type_id {
match target_type {
#[rustfmt::skip]
UserDataTypeId::Shared(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
UserDataType::Shared(type_hints) => {
let type_id = try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
try_self_arg!(borrow_userdata_scoped(state, self_index, type_id, type_hints, |ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[rustfmt::skip]
UserDataTypeId::Unique(target_ptr)
UserDataType::Unique(target_ptr)
if get_userdata::<UserDataStorage<T>>(state, self_index) as *mut c_void == target_ptr =>
{
let ud = target_ptr as *mut UserDataStorage<T>;
@@ -183,83 +151,6 @@ impl<T> UserDataRegistry<T> {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::Rc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Rc<T>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::RcRefCell(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::Arc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<T>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
@@ -279,7 +170,7 @@ impl<T> UserDataRegistry<T> {
}
let method = RefCell::new(method);
let target_type_id = self.ud_type_id;
let target_type = self.r#type;
Box::new(move |rawlua, nargs| unsafe {
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
if nargs == 0 {
@@ -292,18 +183,16 @@ impl<T> UserDataRegistry<T> {
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match target_type_id {
match target_type {
#[rustfmt::skip]
UserDataTypeId::Shared(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<T>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
UserDataType::Shared(type_hints) => {
let type_id = try_self_arg!(rawlua.get_userdata_type_id::<T>(state, self_index));
try_self_arg!(borrow_userdata_scoped_mut(state, self_index, type_id, type_hints, |ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[rustfmt::skip]
UserDataTypeId::Unique(target_ptr)
UserDataType::Unique(target_ptr)
if get_userdata::<UserDataStorage<T>>(state, self_index) as *mut c_void == target_ptr =>
{
let ud = target_ptr as *mut UserDataStorage<T>;
@@ -311,77 +200,6 @@ impl<T> UserDataRegistry<T> {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::Rc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<T>>(self_index)) == Some(target_type_id) =>
{
Err(Error::UserDataBorrowMutError)
},
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
#[rustfmt::skip]
UserDataTypeId::RcRefCell(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Rc<RefCell<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Rc<RefCell<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::Arc(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<T>>(self_index)) == Some(target_type_id) =>
{
Err(Error::UserDataBorrowMutError)
},
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<Mutex<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<RwLock<T>>>(self_index)) == Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotMutex(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::Mutex<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::Mutex<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
#[cfg(feature = "userdata-wrappers")]
#[rustfmt::skip]
UserDataTypeId::ArcParkingLotRwLock(target_type_id)
if try_self_arg!(rawlua.get_userdata_type_id::<Arc<parking_lot::RwLock<T>>>(self_index))
== Some(target_type_id) =>
{
let ud = get_userdata::<UserDataStorage<Arc<parking_lot::RwLock<T>>>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
@@ -789,14 +607,10 @@ impl<T> UserDataMethods<T> for UserDataRegistry<T> {
}
macro_rules! lua_userdata_impl {
($type:ty => $type_variant:tt) => {
lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>()));
};
($type:ty, $type_id:expr) => {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
fn register(registry: &mut UserDataRegistry<Self>) {
let mut orig_registry = UserDataRegistry::with_type_id(registry.lua.lua(), $type_id);
let mut orig_registry = UserDataRegistry::new(registry.lua.lua());
T::register(&mut orig_registry);
// Copy all fields, methods, etc. from the original registry
@@ -818,22 +632,22 @@ macro_rules! lua_userdata_impl {
// A special proxy object for UserData
pub(crate) struct UserDataProxy<T>(pub(crate) PhantomData<T>);
lua_userdata_impl!(UserDataProxy<T>, UserDataTypeId::Shared(TypeId::of::<T>()));
lua_userdata_impl!(UserDataProxy<T>);
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
lua_userdata_impl!(Rc<T> => Rc);
lua_userdata_impl!(std::rc::Rc<T>);
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
lua_userdata_impl!(Rc<RefCell<T>> => RcRefCell);
lua_userdata_impl!(std::rc::Rc<std::cell::RefCell<T>>);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<T> => Arc);
lua_userdata_impl!(std::sync::Arc<T>);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<Mutex<T>> => ArcMutex);
lua_userdata_impl!(std::sync::Arc<std::sync::Mutex<T>>);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<RwLock<T>> => ArcRwLock);
lua_userdata_impl!(std::sync::Arc<std::sync::RwLock<T>>);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<parking_lot::Mutex<T>> => ArcParkingLotMutex);
lua_userdata_impl!(std::sync::Arc<parking_lot::Mutex<T>>);
#[cfg(feature = "userdata-wrappers")]
lua_userdata_impl!(Arc<parking_lot::RwLock<T>> => ArcParkingLotRwLock);
lua_userdata_impl!(std::sync::Arc<parking_lot::RwLock<T>>);
#[cfg(test)]
mod assertions {
+3 -2
View File
@@ -12,8 +12,9 @@ pub(crate) use error::{
pub(crate) use short_names::short_type_name;
pub(crate) use types::TypeKey;
pub(crate) use userdata::{
get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata,
init_internal_metatable, init_userdata_metatable, push_internal_userdata, take_userdata,
borrow_userdata_scoped, borrow_userdata_scoped_mut, get_destructed_userdata_metatable,
get_internal_metatable, get_internal_userdata, get_userdata, init_internal_metatable,
init_userdata_metatable, push_internal_userdata, take_userdata, TypeIdHints,
DESTRUCTED_USERDATA_METATABLE,
};
+196 -1
View File
@@ -1,7 +1,9 @@
use std::any::TypeId;
use std::os::raw::{c_int, c_void};
use std::{ptr, str};
use crate::error::Result;
use crate::error::{Error, Result};
use crate::userdata::UserDataStorage;
use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, rawset_field, TypeKey};
// Pushes the userdata and attaches a metatable with __gc method.
@@ -339,6 +341,199 @@ unsafe extern "C-unwind" fn userdata_destructor<T>(state: *mut ffi::lua_State) -
0
}
// Userdata type hints, used to match types of wrapped userdata
#[derive(Clone, Copy)]
pub(crate) struct TypeIdHints {
t: TypeId,
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
rc: TypeId,
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
rc_refcell: TypeId,
#[cfg(feature = "userdata-wrappers")]
arc: TypeId,
#[cfg(feature = "userdata-wrappers")]
arc_mutex: TypeId,
#[cfg(feature = "userdata-wrappers")]
arc_rwlock: TypeId,
#[cfg(feature = "userdata-wrappers")]
arc_pl_mutex: TypeId,
#[cfg(feature = "userdata-wrappers")]
arc_pl_rwlock: TypeId,
}
impl TypeIdHints {
pub(crate) fn new<T: 'static>() -> Self {
Self {
t: TypeId::of::<T>(),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
rc: TypeId::of::<std::rc::Rc<T>>(),
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
rc_refcell: TypeId::of::<std::rc::Rc<std::cell::RefCell<T>>>(),
#[cfg(feature = "userdata-wrappers")]
arc: TypeId::of::<std::sync::Arc<T>>(),
#[cfg(feature = "userdata-wrappers")]
arc_mutex: TypeId::of::<std::sync::Arc<std::sync::Mutex<T>>>(),
#[cfg(feature = "userdata-wrappers")]
arc_rwlock: TypeId::of::<std::sync::Arc<std::sync::RwLock<T>>>(),
#[cfg(feature = "userdata-wrappers")]
arc_pl_mutex: TypeId::of::<std::sync::Arc<parking_lot::Mutex<T>>>(),
#[cfg(feature = "userdata-wrappers")]
arc_pl_rwlock: TypeId::of::<std::sync::Arc<parking_lot::RwLock<T>>>(),
}
}
#[inline(always)]
pub(crate) fn type_id(&self) -> TypeId {
self.t
}
}
pub(crate) unsafe fn borrow_userdata_scoped<T, R>(
state: *mut ffi::lua_State,
idx: c_int,
type_id: Option<TypeId>,
type_hints: TypeIdHints,
f: impl FnOnce(&T) -> R,
) -> Result<R> {
match type_id {
Some(type_id) if type_id == type_hints.t => {
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
(*ud).try_borrow_scoped(|ud| f(ud))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == type_hints.rc => {
let ud = get_userdata::<UserDataStorage<std::rc::Rc<T>>>(state, idx);
(*ud).try_borrow_scoped(|ud| f(ud))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == type_hints.rc_refcell => {
let ud = get_userdata::<UserDataStorage<std::rc::Rc<std::cell::RefCell<T>>>>(state, idx);
(*ud).try_borrow_scoped(|ud| {
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(f(&ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<T>>>(state, idx);
(*ud).try_borrow_scoped(|ud| f(ud))
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_mutex => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::Mutex<T>>>>(state, idx);
(*ud).try_borrow_scoped(|ud| {
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
Ok(f(&ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_rwlock => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::RwLock<T>>>>(state, idx);
(*ud).try_borrow_scoped(|ud| {
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
Ok(f(&ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_pl_mutex => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::Mutex<T>>>>(state, idx);
(*ud).try_borrow_scoped(|ud| {
let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?;
Ok(f(&ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_pl_rwlock => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::RwLock<T>>>>(state, idx);
(*ud).try_borrow_scoped(|ud| {
let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?;
Ok(f(&ud))
})?
}
_ => Err(Error::UserDataTypeMismatch),
}
}
pub(crate) unsafe fn borrow_userdata_scoped_mut<T, R>(
state: *mut ffi::lua_State,
idx: c_int,
type_id: Option<TypeId>,
type_hints: TypeIdHints,
f: impl FnOnce(&mut T) -> R,
) -> Result<R> {
match type_id {
Some(type_id) if type_id == type_hints.t => {
let ud = get_userdata::<UserDataStorage<T>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| f(ud))
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == type_hints.rc => {
let ud = get_userdata::<UserDataStorage<std::rc::Rc<T>>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| match std::rc::Rc::get_mut(ud) {
Some(ud) => Ok(f(ud)),
None => Err(Error::UserDataBorrowMutError),
})?
}
#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))]
Some(type_id) if type_id == type_hints.rc_refcell => {
let ud = get_userdata::<UserDataStorage<std::rc::Rc<std::cell::RefCell<T>>>>(state, idx);
(*ud).try_borrow_scoped(|ud| {
let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(&mut ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<T>>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| match std::sync::Arc::get_mut(ud) {
Some(ud) => Ok(f(ud)),
None => Err(Error::UserDataBorrowMutError),
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_mutex => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::Mutex<T>>>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| {
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(&mut ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_rwlock => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<std::sync::RwLock<T>>>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| {
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(&mut ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_pl_mutex => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::Mutex<T>>>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| {
let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?;
Ok(f(&mut ud))
})?
}
#[cfg(feature = "userdata-wrappers")]
Some(type_id) if type_id == type_hints.arc_pl_rwlock => {
let ud = get_userdata::<UserDataStorage<std::sync::Arc<parking_lot::RwLock<T>>>>(state, idx);
(*ud).try_borrow_scoped_mut(|ud| {
let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?;
Ok(f(&mut ud))
})?
}
_ => Err(Error::UserDataTypeMismatch),
}
}
pub(crate) static DESTRUCTED_USERDATA_METATABLE: u8 = 0;
static USERDATA_METATABLE_INDEX: u8 = 0;
static USERDATA_METATABLE_NEWINDEX: u8 = 0;
+292 -64
View File
@@ -913,6 +913,7 @@ fn test_nested_userdata_gc() -> Result<()> {
#[cfg(feature = "userdata-wrappers")]
#[test]
fn test_userdata_wrappers() -> Result<()> {
#[derive(Debug)]
struct MyUserData(i64);
impl UserData for MyUserData {
@@ -924,6 +925,10 @@ fn test_userdata_wrappers() -> Result<()> {
Ok(())
})
}
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("dbg", |_, this, ()| Ok(format!("{this:?}")));
}
}
let lua = Lua::new();
@@ -932,136 +937,359 @@ fn test_userdata_wrappers() -> Result<()> {
// Rc<T>
#[cfg(not(feature = "send"))]
{
let ud = std::rc::Rc::new(MyUserData(1));
globals.set("rc_ud", ud.clone())?;
use std::rc::Rc;
let ud = Rc::new(MyUserData(1));
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(rc_ud.static == "constant")
local ok, err = pcall(function() rc_ud.data = 2 end)
assert(ud.static == "constant")
local ok, err = pcall(function() ud.data = 2 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
tostring(err):find("error mutably borrowing userdata") ~= nil,
"expected 'error mutably borrowing userdata', got '" .. tostring(err) .. "'"
)
assert(rc_ud.data == 1)
assert(ud.data == 1)
assert(ud:dbg(), "MyUserData(1)")
"#,
)
.exec()
.unwrap();
globals.set("rc_ud", Nil)?;
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Rc<MyUserData>>());
assert!(!ud.is::<MyUserData>());
assert_eq!(ud.borrow::<MyUserData>()?.0, 1);
assert!(matches!(
ud.borrow_mut::<MyUserData>(),
Err(Error::UserDataBorrowMutError)
));
assert!(ud.borrow_mut::<Rc<MyUserData>>().is_ok());
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 1);
assert!(matches!(
ud.borrow_mut_scoped::<MyUserData, _>(|_| ()),
Err(Error::UserDataBorrowMutError)
));
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
assert_eq!(Rc::strong_count(&ud), 1);
// We must be able to mutate userdata when having one reference only
globals.set("ud", ud)?;
lua.load(
r#"
ud.data = 2
assert(ud.data == 2)
"#,
)
.exec()
.unwrap();
}
// Rc<RefCell<T>>
#[cfg(not(feature = "send"))]
{
let ud = std::rc::Rc::new(std::cell::RefCell::new(MyUserData(2)));
globals.set("rc_refcell_ud", ud.clone())?;
use std::cell::RefCell;
use std::rc::Rc;
let ud = Rc::new(RefCell::new(MyUserData(2)));
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(rc_refcell_ud.static == "constant")
rc_refcell_ud.data = rc_refcell_ud.data + 1
assert(rc_refcell_ud.data == 3)
"#,
assert(ud.static == "constant")
assert(ud.data == 2)
ud.data = 10
assert(ud.data == 10)
assert(ud:dbg() == "MyUserData(10)")
"#,
)
.exec()?;
assert_eq!(ud.borrow().0, 3);
globals.set("rc_refcell_ud", Nil)?;
.exec()
.unwrap();
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Rc<RefCell<MyUserData>>>());
assert!(!ud.is::<MyUserData>());
assert_eq!(ud.borrow::<MyUserData>()?.0, 10);
assert_eq!(ud.borrow_mut::<MyUserData>()?.0, 10);
ud.borrow_mut::<MyUserData>()?.0 = 20;
assert_eq!(ud.borrow::<MyUserData>()?.0, 20);
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 20);
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 30)?;
assert_eq!(ud.borrow::<MyUserData>()?.0, 30);
// Double (read) borrow is okay
let _borrow = ud.borrow::<MyUserData>()?;
assert_eq!(ud.borrow::<MyUserData>()?.0, 30);
assert!(matches!(
ud.borrow_mut::<MyUserData>(),
Err(Error::UserDataBorrowMutError)
));
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(std::rc::Rc::strong_count(&ud), 1);
assert_eq!(Rc::strong_count(&ud), 1);
// Check destroying wrapped UserDataRef without references in Lua
let ud = lua.convert::<UserDataRef<MyUserData>>(ud)?;
lua.gc_collect()?;
assert_eq!(ud.0, 30);
drop(ud);
}
// Arc<T>
{
let ud = Arc::new(MyUserData(3));
globals.set("arc_ud", ud.clone())?;
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(arc_ud.static == "constant")
local ok, err = pcall(function() arc_ud.data = 10 end)
assert(ud.static == "constant")
local ok, err = pcall(function() ud.data = 4 end)
assert(
tostring(err):sub(1, 32) == "error mutably borrowing userdata",
"expected error mutably borrowing userdata, got " .. tostring(err)
tostring(err):find("error mutably borrowing userdata") ~= nil,
"expected 'error mutably borrowing userdata', got '" .. tostring(err) .. "'"
)
assert(arc_ud.data == 3)
"#,
assert(ud.data == 3)
assert(ud:dbg() == "MyUserData(3)")
"#,
)
.exec()?;
globals.set("arc_ud", Nil)?;
.exec()
.unwrap();
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Arc<MyUserData>>());
assert!(!ud.is::<MyUserData>());
assert_eq!(ud.borrow::<MyUserData>()?.0, 3);
assert!(matches!(
ud.borrow_mut::<MyUserData>(),
Err(Error::UserDataBorrowMutError)
));
assert!(ud.borrow_mut::<Arc<MyUserData>>().is_ok());
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 3);
assert!(matches!(
ud.borrow_mut_scoped::<MyUserData, _>(|_| ()),
Err(Error::UserDataBorrowMutError)
));
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud), 1);
// We must be able to mutate userdata when having one reference only
globals.set("ud", ud)?;
lua.load(
r#"
ud.data = 4
assert(ud.data == 4)
"#,
)
.exec()
.unwrap();
}
// Arc<Mutex<T>>
{
let ud = Arc::new(std::sync::Mutex::new(MyUserData(4)));
globals.set("arc_mutex_ud", ud.clone())?;
use std::sync::Mutex;
let ud = Arc::new(Mutex::new(MyUserData(5)));
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(arc_mutex_ud.static == "constant")
arc_mutex_ud.data = arc_mutex_ud.data + 1
assert(arc_mutex_ud.data == 5)
"#,
assert(ud.static == "constant")
assert(ud.data == 5)
ud.data = 6
assert(ud.data == 6)
assert(ud:dbg() == "MyUserData(6)")
"#,
)
.exec()?;
assert_eq!(ud.lock().unwrap().0, 5);
globals.set("arc_mutex_ud", Nil)?;
.exec()
.unwrap();
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Arc<Mutex<MyUserData>>>());
assert!(!ud.is::<MyUserData>());
#[rustfmt::skip]
assert!(matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
#[rustfmt::skip]
assert!(matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 6);
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 8)?;
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 8);
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud), 1);
}
// Arc<RwLock<T>>
{
let ud = Arc::new(std::sync::RwLock::new(MyUserData(6)));
globals.set("arc_rwlock_ud", ud.clone())?;
use std::sync::RwLock;
let ud = Arc::new(RwLock::new(MyUserData(9)));
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(arc_rwlock_ud.static == "constant")
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
assert(arc_rwlock_ud.data == 7)
"#,
assert(ud.static == "constant")
assert(ud.data == 9)
ud.data = 10
assert(ud.data == 10)
assert(ud:dbg() == "MyUserData(10)")
"#,
)
.exec()?;
assert_eq!(ud.read().unwrap().0, 7);
globals.set("arc_rwlock_ud", Nil)?;
.exec()
.unwrap();
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Arc<RwLock<MyUserData>>>());
assert!(!ud.is::<MyUserData>());
#[rustfmt::skip]
assert!(matches!(ud.borrow::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
#[rustfmt::skip]
assert!(matches!(ud.borrow_mut::<MyUserData>(), Err(Error::UserDataTypeMismatch)));
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 10);
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 12)?;
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 12);
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud), 1);
}
// Arc<parking_lot::Mutex<T>>
{
let ud = Arc::new(parking_lot::Mutex::new(MyUserData(8)));
globals.set("arc_parking_lot_mutex_ud", ud.clone())?;
use parking_lot::Mutex;
let ud = Arc::new(Mutex::new(MyUserData(13)));
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(arc_parking_lot_mutex_ud.static == "constant")
arc_parking_lot_mutex_ud.data = arc_parking_lot_mutex_ud.data + 1
assert(arc_parking_lot_mutex_ud.data == 9)
"#,
assert(ud.static == "constant")
assert(ud.data == 13)
ud.data = 14
assert(ud.data == 14)
assert(ud:dbg() == "MyUserData(14)")
"#,
)
.exec()?;
assert_eq!(ud.lock().0, 9);
globals.set("arc_parking_lot_mutex_ud", Nil)?;
.exec()
.unwrap();
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Arc<Mutex<MyUserData>>>());
assert!(!ud.is::<MyUserData>());
assert_eq!(ud.borrow::<MyUserData>()?.0, 14);
assert_eq!(ud.borrow_mut::<MyUserData>()?.0, 14);
ud.borrow_mut::<MyUserData>()?.0 = 15;
assert_eq!(ud.borrow::<MyUserData>()?.0, 15);
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 15);
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 16)?;
assert_eq!(ud.borrow::<MyUserData>()?.0, 16);
// Double borrow is not allowed
let _borrow = ud.borrow::<MyUserData>()?;
assert!(matches!(
ud.borrow::<MyUserData>(),
Err(Error::UserDataBorrowError)
));
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud), 1);
// Check destroying wrapped UserDataRef without references in Lua
let ud = lua.convert::<UserDataRef<MyUserData>>(ud)?;
lua.gc_collect()?;
assert_eq!(ud.0, 16);
drop(ud);
}
// Arc<parking_lot::RwLock<T>>
{
let ud = Arc::new(parking_lot::RwLock::new(MyUserData(10)));
globals.set("arc_parking_lot_rwlock_ud", ud.clone())?;
use parking_lot::RwLock;
let ud = Arc::new(RwLock::new(MyUserData(17)));
globals.set("ud", ud.clone())?;
lua.load(
r#"
assert(arc_parking_lot_rwlock_ud.static == "constant")
arc_parking_lot_rwlock_ud.data = arc_parking_lot_rwlock_ud.data + 1
assert(arc_parking_lot_rwlock_ud.data == 11)
"#,
assert(ud.static == "constant")
assert(ud.data == 17)
ud.data = 18
assert(ud.data == 18)
assert(ud:dbg() == "MyUserData(18)")
"#,
)
.exec()?;
assert_eq!(ud.read().0, 11);
globals.set("arc_parking_lot_rwlock_ud", Nil)?;
.exec()
.unwrap();
// Test borrowing original userdata
{
let ud = globals.get::<AnyUserData>("ud")?;
assert!(ud.is::<Arc<RwLock<MyUserData>>>());
assert!(!ud.is::<MyUserData>());
assert_eq!(ud.borrow::<MyUserData>()?.0, 18);
assert_eq!(ud.borrow_mut::<MyUserData>()?.0, 18);
ud.borrow_mut::<MyUserData>()?.0 = 19;
assert_eq!(ud.borrow::<MyUserData>()?.0, 19);
assert_eq!(ud.borrow_scoped::<MyUserData, _>(|x| x.0)?, 19);
ud.borrow_mut_scoped::<MyUserData, _>(|x| x.0 = 20)?;
assert_eq!(ud.borrow::<MyUserData>()?.0, 20);
// Multiple read borrows are allowed with parking_lot::RwLock
let _borrow1 = ud.borrow::<MyUserData>()?;
let _borrow2 = ud.borrow::<MyUserData>()?;
assert!(matches!(
ud.borrow_mut::<MyUserData>(),
Err(Error::UserDataBorrowMutError)
));
}
// Collect userdata
globals.set("ud", Nil)?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&ud), 1);
// Check destroying wrapped UserDataRef without references in Lua
let ud = lua.convert::<UserDataRef<MyUserData>>(ud)?;
lua.gc_collect()?;
assert_eq!(ud.0, 20);
drop(ud);
}
Ok(())