Static Lua reference

Hi, this is my first post here!
I know this is a Rust forum, but I do find that dealing with foreign interfaces is difficult.

I'm trying to get a static reference to a mlua::Lua but all of the normal things seem to break. This is being put in a dll and i want it to just work if possible, I'm pretending i will know better ways to handle this in the future.

static luaScope:mlua::Lua = Lua::new();

results in angry compiler.

calls in statics are limited to constant functions, tuple structs and tuple variants

Oh, constant function maybe I can make one!

static luaScope:mlua::Lua = LuaInit();

const fn LuaInit()->mlua::Lua
{
	let m = Lua::new();
	m
}

Big fail Turns out const functions can only call const functions also I got a million (hyperbole) errors saying that

*mut c_void` cannot be shared between threads safely

I looked and found lazy_static!

use lazy_static::lazy_static;
use std::sync::Mutex;

lazy_static! {
    static ref luaScope: Mutex<mlua::Lua> = {
        let mut m = Lua::new();
        Mutex::new(m)
    };    
}

This gives another handling error with

 *mut c_void` cannot be shared between threads safely
   = help: within `mlua::Lua`, the trait `Send` is not implemented for `*mut c_void`
   = note: required because it appears within the type `mlua::Lua`
   = note: required because of the requirements on the impl of `Sync` for `Mutex<mlua::Lua>

https://doc.rust-lang.org/nomicon/send-and-sync.html
provided some answers. on why but not how to fix this.

Well how to handle those? Arc<Mutex> perhaps?

No this did not work either

So how should I wrap this up to make it acceptable?

Basically, the lazy_static with a Mutex approach is almost correct, but the Mutex needs to be Sync, but a Mutex is only Sync if its contents are Send. If you look at the documentation for mlua::Lua, you'll see that there is an implementation for Send, but it requires the send feature to be activated on the crate. The addtional information in the top-level documentation indicates that it is separated out into a feature because it restricts you to only using Send types as UserData. So, what it looks like you need to do is add send to the features list for mlua in your Cargo.toml.

Here's how I understand it (somebody let me know if this is wrong):

Since static data is stored in the binary, multiple threads accessing it do so by treating it as shared data, so it requires Sync.

Mutex provides Sync and Send if its contents are Send. This is because the Mutex's job is to hand out references to its data on multiple threads. Of course, it ensures only one thread at a time can get that reference, but that doesn't mean it is safe to send the reference to another thread: that's what Send is supposed to guarantee.

Arc provides Send and Sync to any type that is already Send and Sync. It only allows shared ownership of some data, to ensure it isn't dropped before everyone is done using it. That is required when you're trying to mutate something, but it doesn't make it so you can share non-Send data on multiple threads.

The problem is that *mut c_void tells you nothing: *mut c_void makes no guarantees about what is valid to do with it, so it is neither Sync, nor Send.

E.g., it might point at the stack of a recently destroyed thread, in which case dereferencing it at all is undefined behavior. Arc<Mutex<*mut c_void>> would just allow multiple threads to communicate about who gets to write through a pointer that should never be written through.

You need other information from the source of the pointer to know that it points to some data that lives long enough and can be mutated through from multiple threads. To do that, you have to unsafe impl Send for Wrapper, where Wrapper contains your pointer.

2 Likes