Replace lazy_static Lua Shared struct with a much faster single-threaded abstraction

This commit is contained in:
William Venner 2021-10-21 15:05:01 +01:00
parent 50f1bcb851
commit 8d0bacec52
8 changed files with 68 additions and 8 deletions

4
Cargo.lock generated
View File

@ -109,7 +109,7 @@ dependencies = [
[[package]] [[package]]
name = "gmod" name = "gmod"
version = "5.0.0" version = "6.0.0"
dependencies = [ dependencies = [
"cfg_table", "cfg_table",
"cstr", "cstr",
@ -127,7 +127,7 @@ dependencies = [
[[package]] [[package]]
name = "gmod-macros" name = "gmod-macros"
version = "0.1.0" version = "1.0.0"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View File

@ -8,6 +8,15 @@ A swiss army knife for creating binary modules for Garry's Mod in Rust.
# Example # Example
### rust-toolchain.toml
Because we're using the [`C-unwind`](https://rust-lang.github.io/rfcs/2797-project-ffi-unwind.html) ABI, this crate must be used on a [Nightly Rust](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html) compiler.
```toml
[toolchain]
channel = "nightly"
```
### Cargo.toml ### Cargo.toml
```toml ```toml

View File

@ -1,6 +1,6 @@
[package] [package]
name = "gmod-macros" name = "gmod-macros"
version = "0.1.0" version = "1.0.0"
authors = ["William Venner <william@venner.io>"] authors = ["William Venner <william@venner.io>"]
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"

View File

@ -21,6 +21,7 @@ fn check_lua_function(input: &mut ItemFn) {
pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream { pub fn gmod13_open(_attr: TokenStream, tokens: TokenStream) -> TokenStream {
let mut input = parse_macro_input!(tokens as ItemFn); let mut input = parse_macro_input!(tokens as ItemFn);
check_lua_function(&mut input); check_lua_function(&mut input);
input.block.stmts.insert(0, syn::parse2(quote!(#[allow(unused_unsafe)] unsafe { ::gmod::lua::load() })).unwrap());
TokenStream::from(quote!(#[no_mangle] #input)) TokenStream::from(quote!(#[no_mangle] #input))
} }

View File

@ -1,6 +1,6 @@
[package] [package]
name = "gmod" name = "gmod"
version = "5.0.0" version = "6.0.0"
authors = ["William Venner <william@venner.io>"] authors = ["William Venner <william@venner.io>"]
edition = "2018" edition = "2018"
license = "MIT" license = "MIT"
@ -16,7 +16,7 @@ skidscan = "0"
cstr = "0" cstr = "0"
lazy_static = "1" lazy_static = "1"
ctor = "0" ctor = "0"
gmod-macros = { version = "0.1.0", path = "../gmod-macros" } gmod-macros = { version = "1.0.0", path = "../gmod-macros" }
fn_type_alias = "0" fn_type_alias = "0"
fn_abi = "2" fn_abi = "2"

View File

@ -1,5 +1,6 @@
#![doc = include_str!("../../README.md")] #![doc = include_str!("../../README.md")]
#![feature(c_unwind)] #![feature(c_unwind)]
#![feature(thread_id_value)]
pub use libloading; pub use libloading;
pub use detour; pub use detour;

View File

@ -1,4 +1,7 @@
use std::ffi::c_void; #[cfg(debug_assertions)]
use std::sync::atomic::AtomicI64;
use std::{cell::UnsafeCell, ffi::c_void};
use libloading::{Library, Symbol}; use libloading::{Library, Symbol};
@ -70,9 +73,49 @@ impl LuaError {
} }
} }
lazy_static::lazy_static! { #[cfg_attr(not(debug_assertions), repr(transparent))]
pub static ref LUA_SHARED: LuaShared = LuaShared::import(); pub struct LuaSharedInterface(UnsafeCell<*mut LuaShared>, #[cfg(debug_assertions)] AtomicI64);
impl LuaSharedInterface {
#[cfg(debug_assertions)]
fn debug_assertions(&self) {
assert!(!unsafe { *self.0.get() }.is_null(), "The Lua state has not been initialized yet. Add `#[gmod::gmod13_open]` to your module's gmod13_open function to fix this. You can also manually load the Lua state with `gmod::load_lua_state()` or `gmod::set_lua_state(*mut c_void)`");
let thread_id = u64::from(std::thread::current().id().as_u64()) as i64;
match self.1.compare_exchange(-1, thread_id, std::sync::atomic::Ordering::SeqCst, std::sync::atomic::Ordering::SeqCst) {
Ok(-1) => {}, // This is the first thread to use this Lua state.
Ok(_) => unreachable!(),
Err(remembered_thread_id) => assert_eq!(thread_id, remembered_thread_id, "Tried to access the Lua state from another thread! The Lua state is NOT thread-safe, and should only be accessed from the main thread.")
}
}
pub(super) unsafe fn load(&self) {
*self.0.get() = Box::leak(Box::new(LuaShared::import()));
}
pub(super) unsafe fn set(&self, ptr: *mut c_void) {
*self.0.get() = ptr as *mut LuaShared;
}
} }
impl std::ops::Deref for LuaSharedInterface {
type Target = LuaShared;
fn deref(&self) -> &Self::Target {
#[cfg(debug_assertions)]
self.debug_assertions();
unsafe { &**self.0.get() }
}
}
impl std::ops::DerefMut for LuaSharedInterface {
fn deref_mut(&mut self) -> &mut Self::Target {
#[cfg(debug_assertions)]
self.debug_assertions();
unsafe { &mut **self.0.get_mut() }
}
}
pub static mut LUA_SHARED: LuaSharedInterface = LuaSharedInterface(UnsafeCell::new(std::ptr::null_mut()), #[cfg(debug_assertions)] AtomicI64::new(-1));
pub struct LuaShared { pub struct LuaShared {
pub lual_newstate: Symbol<'static, unsafe extern "C-unwind" fn() -> LuaState>, pub lual_newstate: Symbol<'static, unsafe extern "C-unwind" fn() -> LuaState>,

View File

@ -93,4 +93,10 @@ pub struct LuaDebug {
pub lastlinedefined: i32, pub lastlinedefined: i32,
pub short_src: [std::os::raw::c_char; LUA_IDSIZE], pub short_src: [std::os::raw::c_char; LUA_IDSIZE],
pub i_ci: i32 pub i_ci: i32
}
#[inline(always)]
/// Loads lua_shared and imports all functions. This is already done for you if you add `#[gmod::gmod13_open]` to your `gmod13_open` function.
pub unsafe fn load() {
import::LUA_SHARED.load()
} }