diff --git a/src/function.rs b/src/function.rs index c4ff2815..3495236a 100644 --- a/src/function.rs +++ b/src/function.rs @@ -594,9 +594,12 @@ impl IntoLua for WrappedAsyncFunction { } } -// #[cfg(test)] -// mod assertions { -// use super::*; - -// static_assertions::assert_not_impl_any!(Function: Send); -// } +#[cfg(test)] +mod assertions { + use super::*; + + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(Function: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(Function: Send, Sync); +} diff --git a/src/hook.rs b/src/hook.rs index 03cf5549..37f4b1e8 100644 --- a/src/hook.rs +++ b/src/hook.rs @@ -6,9 +6,9 @@ use std::ops::{BitOr, BitOrAssign}; use std::os::raw::c_int; use ffi::lua_Debug; -use parking_lot::ReentrantMutexGuard; use crate::state::RawLua; +use crate::types::ReentrantMutexGuard; use crate::util::{linenumber_to_usize, ptr_to_lossy_str, ptr_to_str}; /// Contains information about currently executing Lua code. diff --git a/src/state.rs b/src/state.rs index 903cd628..42e630c0 100644 --- a/src/state.rs +++ b/src/state.rs @@ -6,12 +6,10 @@ use std::marker::PhantomData; use std::ops::Deref; use std::os::raw::{c_int, c_void}; use std::panic::Location; +use std::rc::Rc; use std::result::Result as StdResult; -use std::sync::{Arc, Weak}; use std::{mem, ptr}; -use parking_lot::{ReentrantMutex, ReentrantMutexGuard}; - use crate::chunk::{AsChunk, Chunk}; use crate::error::{Error, Result}; use crate::function::Function; @@ -24,7 +22,7 @@ use crate::table::Table; use crate::thread::Thread; use crate::types::{ AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LightUserData, MaybeSend, Number, - RegistryKey, + ReentrantMutex, ReentrantMutexGuard, RegistryKey, XRc, XWeak, }; use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataVariant}; use crate::util::{assert_stack, check_stack, push_string, push_table, rawset_field, StackGuard}; @@ -49,11 +47,11 @@ use util::{callback_error_ext, StateGuard}; /// Top level Lua struct which represents an instance of Lua VM. #[derive(Clone)] #[repr(transparent)] -pub struct Lua(Arc>); +pub struct Lua(XRc>); #[derive(Clone)] #[repr(transparent)] -pub(crate) struct WeakLua(Weak>); +pub(crate) struct WeakLua(XWeak>); pub(crate) struct LuaGuard(ArcReentrantMutexGuard); @@ -142,11 +140,6 @@ impl LuaOptions { } } -/// Requires `feature = "send"` -#[cfg(feature = "send")] -#[cfg_attr(docsrs, doc(cfg(feature = "send")))] -unsafe impl Send for Lua {} - #[cfg(not(feature = "module"))] impl Drop for Lua { fn drop(&mut self) { @@ -605,7 +598,7 @@ impl Lua { let interrupt_cb = (*extra).interrupt_callback.clone(); let interrupt_cb = mlua_expect!(interrupt_cb, "no interrupt callback set in interrupt_proc"); - if Arc::strong_count(&interrupt_cb) > 2 { + if Rc::strong_count(&interrupt_cb) > 2 { return Ok(VmState::Continue); // Don't allow recursion } let _guard = StateGuard::new((*extra).raw_lua(), state); @@ -622,7 +615,7 @@ impl Lua { // Set interrupt callback let lua = self.lock(); unsafe { - (*lua.extra.get()).interrupt_callback = Some(Arc::new(callback)); + (*lua.extra.get()).interrupt_callback = Some(Rc::new(callback)); (*ffi::lua_callbacks(lua.main_state)).interrupt = Some(interrupt_proc); } } @@ -947,7 +940,8 @@ impl Lua { #[cfg(any(feature = "luau-jit", doc))] #[cfg_attr(docsrs, doc(cfg(feature = "luau-jit")))] pub fn enable_jit(&self, enable: bool) { - unsafe { (*self.extra.get()).enable_jit = enable }; + let lua = self.lock(); + unsafe { (*lua.extra.get()).enable_jit = enable }; } /// Sets Luau feature flag (global setting). @@ -1879,7 +1873,7 @@ impl Lua { #[inline(always)] pub(crate) fn weak(&self) -> WeakLua { - WeakLua(Arc::downgrade(&self.0)) + WeakLua(XRc::downgrade(&self.0)) } } @@ -1887,7 +1881,7 @@ impl WeakLua { #[track_caller] #[inline(always)] pub(crate) fn lock(&self) -> LuaGuard { - LuaGuard::new(self.0.upgrade().unwrap()) + LuaGuard::new(self.0.upgrade().expect("Lua instance is destroyed")) } #[inline(always)] @@ -1898,15 +1892,21 @@ impl WeakLua { impl PartialEq for WeakLua { fn eq(&self, other: &Self) -> bool { - Weak::ptr_eq(&self.0, &other.0) + XWeak::ptr_eq(&self.0, &other.0) } } impl Eq for WeakLua {} impl LuaGuard { - pub(crate) fn new(handle: Arc>) -> Self { - Self(handle.lock_arc()) + #[cfg(feature = "send")] + pub(crate) fn new(handle: XRc>) -> Self { + LuaGuard(handle.lock_arc()) + } + + #[cfg(not(feature = "send"))] + pub(crate) fn new(handle: XRc>) -> Self { + LuaGuard(handle.into_lock_arc()) } } @@ -1922,15 +1922,15 @@ pub(crate) mod extra; mod raw; pub(crate) mod util; -// #[cfg(test)] -// mod assertions { -// use super::*; +#[cfg(test)] +mod assertions { + use super::*; -// // Lua has lots of interior mutability, should not be RefUnwindSafe -// static_assertions::assert_not_impl_any!(Lua: std::panic::RefUnwindSafe); + // Lua has lots of interior mutability, should not be RefUnwindSafe + static_assertions::assert_not_impl_any!(Lua: std::panic::RefUnwindSafe); -// #[cfg(not(feature = "send"))] -// static_assertions::assert_not_impl_any!(Lua: Send); -// #[cfg(feature = "send")] -// static_assertions::assert_impl_all!(Lua: Send); -// } + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(Lua: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(Lua: Send, Sync); +} diff --git a/src/state/extra.rs b/src/state/extra.rs index 37753c47..1ab671d4 100644 --- a/src/state/extra.rs +++ b/src/state/extra.rs @@ -5,15 +5,15 @@ use std::rc::Rc; use std::mem::{self, MaybeUninit}; use std::os::raw::{c_int, c_void}; use std::ptr; -use std::sync::{Arc, Weak}; +use std::sync::Arc; -use parking_lot::{Mutex, ReentrantMutex}; +use parking_lot::Mutex; use rustc_hash::FxHashMap; use crate::error::Result; use crate::state::RawLua; use crate::stdlib::StdLib; -use crate::types::AppData; +use crate::types::{AppData, ReentrantMutex, XRc, XWeak}; use crate::util::{get_gc_metatable, push_gc_userdata, WrappedFailure}; #[cfg(any(feature = "luau", doc))] @@ -34,9 +34,9 @@ const REF_STACK_RESERVE: c_int = 1; /// Data associated with the Lua state. pub(crate) struct ExtraData { // Same layout as `Lua` - pub(super) lua: MaybeUninit>>, + pub(super) lua: MaybeUninit>>, // Same layout as `WeakLua` - pub(super) weak: MaybeUninit>>, + pub(super) weak: MaybeUninit>>, pub(super) registered_userdata: FxHashMap, pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option>, @@ -179,12 +179,12 @@ impl ExtraData { extra } - pub(super) unsafe fn set_lua(&mut self, lua: &Arc>) { - self.lua.write(Arc::clone(lua)); + pub(super) unsafe fn set_lua(&mut self, lua: &XRc>) { + self.lua.write(XRc::clone(lua)); if cfg!(not(feature = "module")) { - Arc::decrement_strong_count(Arc::as_ptr(lua)); + XRc::decrement_strong_count(XRc::as_ptr(lua)); } - self.weak.write(Arc::downgrade(lua)); + self.weak.write(XRc::downgrade(lua)); } pub(super) unsafe fn get(state: *mut ffi::lua_State) -> *mut Self { diff --git a/src/state/raw.rs b/src/state/raw.rs index ed3f21d4..4c7de552 100644 --- a/src/state/raw.rs +++ b/src/state/raw.rs @@ -8,8 +8,6 @@ use std::result::Result as StdResult; use std::sync::Arc; use std::{mem, ptr}; -use parking_lot::ReentrantMutex; - use crate::chunk::ChunkMode; use crate::error::{Error, Result}; use crate::function::Function; @@ -21,7 +19,7 @@ use crate::table::Table; use crate::thread::Thread; use crate::types::{ AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, - LightUserData, MaybeSend, RegistryKey, SubtypeId, ValueRef, + LightUserData, MaybeSend, ReentrantMutex, RegistryKey, SubtypeId, ValueRef, XRc, }; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataVariant}; use crate::util::{ @@ -69,6 +67,9 @@ impl Drop for RawLua { } } +#[cfg(feature = "send")] +unsafe impl Send for RawLua {} + impl RawLua { #[inline(always)] pub(crate) fn lua(&self) -> &Lua { @@ -96,7 +97,7 @@ impl RawLua { unsafe { (*self.extra.get()).ref_thread } } - pub(super) unsafe fn new(libs: StdLib, options: LuaOptions) -> Arc> { + pub(super) unsafe fn new(libs: StdLib, options: LuaOptions) -> XRc> { let mem_state: *mut MemoryState = Box::into_raw(Box::default()); let mut state = ffi::lua_newstate(ALLOCATOR, mem_state as *mut c_void); // If state is null then switch to Lua internal allocator @@ -154,7 +155,7 @@ impl RawLua { rawlua } - pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> Arc> { + pub(super) unsafe fn init_from_ptr(state: *mut ffi::lua_State) -> XRc> { assert!(!state.is_null(), "Lua state is NULL"); if let Some(lua) = Self::try_from_ptr(state) { return lua; @@ -209,7 +210,7 @@ impl RawLua { assert_stack(main_state, ffi::LUA_MINSTACK); #[allow(clippy::arc_with_non_send_sync)] - let rawlua = Arc::new(ReentrantMutex::new(RawLua { + let rawlua = XRc::new(ReentrantMutex::new(RawLua { state: Cell::new(state), main_state, extra: Rc::clone(&extra), @@ -221,10 +222,10 @@ impl RawLua { pub(super) unsafe fn try_from_ptr( state: *mut ffi::lua_State, - ) -> Option>> { + ) -> Option>> { match ExtraData::get(state) { extra if extra.is_null() => None, - extra => Some(Arc::clone(&(*extra).lua().0)), + extra => Some(XRc::clone(&(*extra).lua().0)), } } @@ -369,7 +370,7 @@ impl RawLua { callback_error_ext(state, extra, move |_| { let hook_cb = (*extra).hook_callback.clone(); let hook_cb = mlua_expect!(hook_cb, "no hook callback set in hook_proc"); - if Arc::strong_count(&hook_cb) > 2 { + if Rc::strong_count(&hook_cb) > 2 { return Ok(()); // Don't allow recursion } let rawlua = (*extra).raw_lua(); @@ -379,7 +380,7 @@ impl RawLua { }) } - (*self.extra.get()).hook_callback = Some(Arc::new(callback)); + (*self.extra.get()).hook_callback = Some(Rc::new(callback)); (*self.extra.get()).hook_thread = state; // Mark for what thread the hook is set ffi::lua_sethook(state, Some(hook_proc), triggers.mask(), triggers.count()); } diff --git a/src/string.rs b/src/string.rs index cde30eef..ac026f12 100644 --- a/src/string.rs +++ b/src/string.rs @@ -202,9 +202,12 @@ impl Serialize for String { } } -// #[cfg(test)] -// mod assertions { -// use super::*; - -// static_assertions::assert_not_impl_any!(String: Send); -// } +#[cfg(test)] +mod assertions { + use super::*; + + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(String: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(String: Send, Sync); +} diff --git a/src/table.rs b/src/table.rs index e8d6d34a..478da12e 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1219,9 +1219,12 @@ where } } -// #[cfg(test)] -// mod assertions { -// use super::*; - -// static_assertions::assert_not_impl_any!(Table: Send); -// } +#[cfg(test)] +mod assertions { + use super::*; + + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(Table: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(Table: Send, Sync); +} diff --git a/src/thread.rs b/src/thread.rs index 177ce189..db654963 100644 --- a/src/thread.rs +++ b/src/thread.rs @@ -46,6 +46,11 @@ pub enum ThreadStatus { #[derive(Clone, Debug)] pub struct Thread(pub(crate) ValueRef, pub(crate) *mut ffi::lua_State); +#[cfg(feature = "send")] +unsafe impl Send for Thread {} +#[cfg(feature = "send")] +unsafe impl Sync for Thread {} + /// Thread (coroutine) representation as an async [`Future`] or [`Stream`]. /// /// Requires `feature = "async"` @@ -526,5 +531,8 @@ impl<'lua, 'a> Drop for WakerGuard<'lua, 'a> { mod assertions { use super::*; + #[cfg(not(feature = "send"))] static_assertions::assert_not_impl_any!(Thread: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(Thread: Send, Sync); } diff --git a/src/types.rs b/src/types.rs index be3b0dae..dc91cc1a 100644 --- a/src/types.rs +++ b/src/types.rs @@ -9,7 +9,7 @@ use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::Arc; use std::{fmt, mem, ptr}; -use parking_lot::{Mutex, RawMutex, RawThreadId}; +use parking_lot::Mutex; use rustc_hash::FxHashMap; use crate::error::Result; @@ -23,6 +23,9 @@ use {crate::value::MultiValue, futures_util::future::LocalBoxFuture}; #[cfg(all(feature = "luau", feature = "serialize"))] use serde::ser::{Serialize, SerializeTupleStruct, Serializer}; +// Re-export mutex wrappers +pub(crate) use sync::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak}; + /// Type of Lua integer numbers. pub type Integer = ffi::lua_Integer; /// Type of Lua floating point numbers. @@ -70,16 +73,16 @@ pub enum VmState { } #[cfg(all(feature = "send", not(feature = "luau")))] -pub(crate) type HookCallback = Arc Result<()> + Send>; +pub(crate) type HookCallback = Rc Result<()> + Send>; #[cfg(all(not(feature = "send"), not(feature = "luau")))] -pub(crate) type HookCallback = Arc Result<()>>; +pub(crate) type HookCallback = Rc Result<()>>; -#[cfg(all(feature = "luau", feature = "send"))] -pub(crate) type InterruptCallback = Arc Result + Send>; +#[cfg(all(feature = "send", feature = "luau"))] +pub(crate) type InterruptCallback = Rc Result + Send>; -#[cfg(all(feature = "luau", not(feature = "send")))] -pub(crate) type InterruptCallback = Arc Result>; +#[cfg(all(not(feature = "send"), feature = "luau"))] +pub(crate) type InterruptCallback = Rc Result>; #[cfg(all(feature = "send", feature = "lua54"))] pub(crate) type WarnCallback = Box Result<()> + Send>; @@ -183,9 +186,6 @@ impl PartialEq<[f32; Self::SIZE]> for Vector { } } -pub(crate) type ArcReentrantMutexGuard = - parking_lot::lock_api::ArcReentrantMutexGuard; - pub(crate) struct DestructedUserdata; /// An auto generated key into the Lua registry. @@ -482,10 +482,16 @@ impl fmt::Debug for AppDataRefMut<'_, T> { } } -// #[cfg(test)] -// mod assertions { -// use super::*; +mod sync; + +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_impl_all!(RegistryKey: Send, Sync); -// static_assertions::assert_impl_all!(RegistryKey: Send, Sync); -// static_assertions::assert_not_impl_any!(ValueRef: Send); -// } + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(ValueRef: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(ValueRef: Send, Sync); +} diff --git a/src/types/sync.rs b/src/types/sync.rs new file mode 100644 index 00000000..7f9adbd6 --- /dev/null +++ b/src/types/sync.rs @@ -0,0 +1,77 @@ +#[cfg(feature = "send")] +mod inner { + use parking_lot::{RawMutex, RawThreadId}; + use std::sync::{Arc, Weak}; + + pub(crate) type XRc = Arc; + pub(crate) type XWeak = Weak; + + pub(crate) type ReentrantMutex = parking_lot::ReentrantMutex; + + pub(crate) type ReentrantMutexGuard<'a, T> = parking_lot::ReentrantMutexGuard<'a, T>; + + pub(crate) type ArcReentrantMutexGuard = + parking_lot::lock_api::ArcReentrantMutexGuard; +} + +#[cfg(not(feature = "send"))] +mod inner { + use std::ops::Deref; + use std::rc::{Rc, Weak}; + + pub(crate) type XRc = Rc; + pub(crate) type XWeak = Weak; + + pub(crate) struct ReentrantMutex(T); + + impl ReentrantMutex { + #[inline(always)] + pub(crate) fn new(val: T) -> Self { + ReentrantMutex(val) + } + + #[inline(always)] + pub(crate) fn lock(&self) -> ReentrantMutexGuard { + ReentrantMutexGuard(&self.0) + } + + #[inline(always)] + pub(crate) fn lock_arc(self: &XRc) -> ArcReentrantMutexGuard { + ArcReentrantMutexGuard(Rc::clone(self)) + } + + #[inline(always)] + pub(crate) fn into_lock_arc(self: XRc) -> ArcReentrantMutexGuard { + ArcReentrantMutexGuard(self) + } + + #[inline(always)] + pub(crate) fn data_ptr(&self) -> *const T { + &self.0 as *const _ + } + } + + pub(crate) struct ReentrantMutexGuard<'a, T>(&'a T); + + impl<'a, T> Deref for ReentrantMutexGuard<'a, T> { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + self.0 + } + } + + pub(crate) struct ArcReentrantMutexGuard(XRc>); + + impl Deref for ArcReentrantMutexGuard { + type Target = T; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.0 .0 + } + } +} + +pub(crate) use inner::{ArcReentrantMutexGuard, ReentrantMutex, ReentrantMutexGuard, XRc, XWeak}; diff --git a/src/userdata.rs b/src/userdata.rs index 81889d85..4eb01c2a 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -1190,9 +1190,13 @@ mod cell; mod ext; mod registry; -// #[cfg(test)] -// mod assertions { -// use super::*; - -// static_assertions::assert_not_impl_any!(AnyUserData: Send); -// } +#[cfg(test)] +mod assertions { + use super::*; + + static_assertions::assert_not_impl_any!(AnyUserData: Send); + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_any!(AnyUserData: Send); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(AnyUserData: Send, Sync); +} diff --git a/src/value.rs b/src/value.rs index d733ce73..82dc8951 100644 --- a/src/value.rs +++ b/src/value.rs @@ -9,14 +9,6 @@ use std::{fmt, mem, ptr, str}; use num_traits::FromPrimitive; -#[cfg(feature = "serialize")] -use { - crate::table::SerializableTable, - rustc_hash::FxHashSet, - serde::ser::{self, Serialize, Serializer}, - std::{cell::RefCell, rc::Rc, result::Result as StdResult}, -}; - use crate::error::{Error, Result}; use crate::function::Function; use crate::state::{Lua, RawLua}; @@ -27,6 +19,14 @@ use crate::types::{Integer, LightUserData, Number, SubtypeId}; use crate::userdata::AnyUserData; use crate::util::{check_stack, StackGuard}; +#[cfg(feature = "serialize")] +use { + crate::table::SerializableTable, + rustc_hash::FxHashSet, + serde::ser::{self, Serialize, Serializer}, + std::{cell::RefCell, rc::Rc, result::Result as StdResult}, +}; + /// A dynamically typed Lua value. The `String`, `Table`, `Function`, `Thread`, and `UserData` /// variants contain handle types into the internal Lua state. It is a logic error to mix handle /// types between separate `Lua` instances, and doing so will result in a panic.