Skip to main content

luaur_rt/
sync.rs

1//! Send-portability primitives, gated on the `send` feature.
2//!
3//! This module mirrors mlua's `XRc` / `MaybeSend` / `MaybeSync` machinery
4//! ([`mlua-ref/src/types.rs`] + `types/sync.rs`) but in the simpler shape that
5//! luaur-rt needs: luaur-rt is **never** `Sync`. Under the `send` feature a
6//! [`Lua`](crate::Lua) and all of its handles become `Send` so the whole VM can
7//! be **moved** to another thread; they are *not* usable concurrently. The user
8//! guarantees serialized access (exactly mlua's documented `send` contract).
9//!
10//! ## What `send` changes
11//!
12//! - [`XRc<T>`] aliases `Arc<T>` (atomically reference-counted, `Send`) instead
13//!   of `Rc<T>`. The shared `LuaInner` / `LuaRef` handles use it, so cloning a
14//!   handle across the `Send` boundary keeps the refcount sound.
15//! - [`MaybeSend`] gains a `Send` super-bound, so every type-erased callback /
16//!   userdata closure box stored inside the VM is `Send`, and the captured
17//!   environment can hold `Send` data moved in from another thread.
18//! - The raw `*mut lua_State` pointers held by `LuaInner` (and, transitively,
19//!   every handle) are made `Send` with a documented `unsafe impl Send` (the
20//!   move-not-share contract). We deliberately do **not** impl `Sync`.
21//!
22//! Without the feature everything is byte-for-byte identical to the original
23//! single-threaded build: `XRc` = `Rc`, and `MaybeSend` / `MaybeSync` are empty
24//! marker traits blanket-implemented for every type (zero bound, zero cost).
25
26/// Reference-counted shared pointer used for the VM's shared interior
27/// (`LuaInner`) and the registry references (`LuaRef`) every handle clones.
28///
29/// `Arc` under the `send` feature (so handles can cross a thread boundary),
30/// `Rc` otherwise (single-threaded, the default — byte-identical to before).
31#[cfg(feature = "send")]
32pub(crate) type XRc<T> = std::sync::Arc<T>;
33
34/// See the `send`-gated variant above.
35#[cfg(not(feature = "send"))]
36pub(crate) type XRc<T> = std::rc::Rc<T>;
37
38/// The weak counterpart of [`XRc`]: `Weak` from `Arc`/`Rc` depending on `send`.
39/// Used by [`WeakLua`](crate::state::WeakLua) to hold a non-owning reference to
40/// the VM's shared interior. Mirrors mlua's `XWeak`.
41#[cfg(feature = "send")]
42pub(crate) type XWeak<T> = std::sync::Weak<T>;
43
44/// See the `send`-gated variant above.
45#[cfg(not(feature = "send"))]
46pub(crate) type XWeak<T> = std::rc::Weak<T>;
47
48/// A trait that adds a `Send` requirement **iff** the `send` feature is enabled.
49///
50/// Mirrors `mlua::MaybeSend`. It is applied to every Rust closure that gets
51/// type-erased and stored inside the VM (the `create_function` closure, every
52/// userdata method/field/function closure, and — under `async` — the async
53/// callback) and to the userdata payload type `T`. Under `send` that forces the
54/// stored boxes (and their captured environment) to be `Send`; without the
55/// feature it is an empty marker implemented for all types, so the extra bound
56/// is a no-op and the default build is unchanged.
57#[cfg(feature = "send")]
58pub trait MaybeSend: Send {}
59#[cfg(feature = "send")]
60impl<T: Send> MaybeSend for T {}
61
62/// See the `send`-gated variant above.
63#[cfg(not(feature = "send"))]
64pub trait MaybeSend {}
65#[cfg(not(feature = "send"))]
66impl<T> MaybeSend for T {}
67
68/// A trait that adds a `Sync` requirement **iff** the `send` feature is enabled.
69///
70/// Mirrors `mlua::MaybeSync`. Provided for signature parity with mlua's
71/// userdata bounds (`T: UserData + MaybeSend + MaybeSync`). Because luaur-rt's
72/// `Lua` is itself never `Sync`, this only constrains the *payload* type, again
73/// matching mlua.
74#[cfg(feature = "send")]
75pub trait MaybeSync: Sync {}
76#[cfg(feature = "send")]
77impl<T: Sync> MaybeSync for T {}
78
79/// See the `send`-gated variant above.
80#[cfg(not(feature = "send"))]
81pub trait MaybeSync {}
82#[cfg(not(feature = "send"))]
83impl<T> MaybeSync for T {}
84
85/// A zero-sized phantom marker that makes the public `Lua` handle (and every
86/// other handle that embeds it) **`Send` but not `Sync`** under the `send`
87/// feature.
88///
89/// We need `LuaInner` to be `Sync` internally so that `XRc<LuaInner>`
90/// (`Arc<LuaInner>`) is `Send`. But the public contract is move-only, never
91/// shared: a handle must not be `Sync`. Embedding a `NotSync` field re-imposes
92/// `!Sync` on the wrapper (`PhantomData<std::cell::Cell<()>>` is `Send` but
93/// `!Sync`) without affecting `Send`.
94///
95/// Under the default (no `send`) build it is the unit type `()`, so the handle
96/// structs are byte-for-byte identical to before (the field is a zero-sized
97/// `()`; the whole `Lua` is already `!Send`/`!Sync` via `Rc`).
98#[cfg(feature = "send")]
99pub(crate) type NotSync = std::marker::PhantomData<std::cell::Cell<()>>;
100
101/// See the `send`-gated variant above.
102#[cfg(not(feature = "send"))]
103pub(crate) type NotSync = ();
104
105/// The value of a [`NotSync`] field, written once at each handle construction
106/// site so the same source compiles under both feature settings.
107#[cfg(feature = "send")]
108pub(crate) const NOT_SYNC: NotSync = std::marker::PhantomData;
109
110/// See the `send`-gated variant above.
111#[cfg(not(feature = "send"))]
112pub(crate) const NOT_SYNC: NotSync = ();