1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
use std::ops::Deref;
use once_cell::sync::OnceCell;
use emacs_module::emacs_value;
use super::*;
/// A "global reference" that can live outside the scope of an [`Env`]. This is useful for sharing
/// an otherwise short-lived Lisp [`Value`] across multiple invocations of Rust functions defined
/// with [`defun`]. Examples include efficient access to interned symbols or Lisp functions, and
/// Rust-based multi-threading.
///
/// # Implementation
///
/// Cloning this struct requires an [`Env`], so it doesn't implement [`Clone`].
///
/// [`free_global_ref`] requires an [`Env`]. Therefore, to avoid leaking the underlying [`Value`],
/// [`free`] should be used to free a global reference, instead of [`drop`]. For the use case of
/// accessing interned symbols and Lisp functions, this is a non-issue, as the values are
/// supposed to be "static" anyway.
///
/// The above is a shortcoming in the design of emacs-module. There are 2 possible ways to fix it:
/// - Make [`free_global_ref`] work without an env, like Erlang's `enif_release_resource`.
/// - Allow `user_ptr`'s finalizer to access the env, to properly free associated global refs.
///
/// [`Env`]: struct.Env.html
/// [`Value`]: struct.Value.html
/// [`defun`]: attr.defun.html
/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
/// [`free_global_ref`]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Module-Values.html
/// [`free`]: #method.free
/// [`drop`]: https://doc.rust-lang.org/std/mem/fn.drop.html
#[derive(Debug)]
#[repr(transparent)]
pub struct GlobalRef {
raw: emacs_value
}
impl GlobalRef {
/// Creates a new global reference for the given [`Value`].
///
/// [`Value`]: struct.Value.html
pub fn new(value: Value) -> Self {
let env = value.env;
// TODO: Check whether this really is `no_exit`.
let raw = unsafe_raw_call_no_exit!(env, make_global_ref, value.raw);
// NOTE: raw != value.raw
Self { raw }
}
// For testing.
pub(crate) unsafe fn from_raw(raw: emacs_value) -> Self {
Self { raw }
}
/// Frees this global reference.
pub fn free(self, env: &Env) -> Result<()> {
// Safety: We assume user code doesn't directly call C function `free_global_ref`.
unsafe_raw_call!(env, free_global_ref, self.raw)?;
Ok(())
}
/// Returns the underlying [`Value`], scoping its lifetime to the given [`Env`].
///
/// [`Env`]: struct.Env.html
/// [`Value`]: struct.Value.html
#[inline]
pub fn bind<'e, 'g: 'e>(&'g self, env: &'e Env) -> Value<'e> {
// Safety: This global ref keeps the underlying Lisp object alive.
unsafe { Value::new(self.raw, env) }
}
/// Returns a copy of this global reference.
pub fn clone(&self, env: &Env) -> Self {
self.bind(env).make_global_ref()
}
}
// Safety: Doing anything useful with a GlobalRef requires an &Env, which means holding the GIL.
unsafe impl Send for GlobalRef {}
unsafe impl Sync for GlobalRef {}
impl<'e> FromLisp<'e> for GlobalRef {
#[inline(always)]
fn from_lisp(value: Value<'e>) -> Result<Self> {
Ok(Self::new(value))
}
}
impl<'e> IntoLisp<'e> for &'e GlobalRef {
#[inline(always)]
fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
Ok(self.bind(env))
}
}
impl<'e> Value<'e> {
/// Creates a new [`GlobalRef`] for this value.
///
/// [`GlobalRef`]: struct.GlobalRef.html
#[inline(always)]
pub fn make_global_ref(self) -> GlobalRef {
GlobalRef::new(self)
}
}
/// Declares global references. These will be initialized when the module is loaded.
#[doc(hidden)]
#[macro_export]
macro_rules! global_refs {
($($name:ident)*) => {
$(
#[allow(non_upper_case_globals)]
pub static $name: &'static $crate::OnceGlobalRef = {
static X: $crate::OnceGlobalRef = $crate::OnceGlobalRef::new();
&X
};
)*
};
($registrator_name:ident ($init_method:ident) =>
$(
$name:ident $( => $lisp_name:expr )?
)*
) => {
$crate::global_refs! {
$($name)*
}
#[$crate::deps::ctor::ctor]
fn $registrator_name() {
$crate::init::__GLOBAL_REFS__.try_lock()
.expect("Failed to acquire a write lock on the list of initializers for global refs")
.push(::std::boxed::Box::new(|env| {
$(
#[allow(unused_variables)]
let name = $crate::deps::emacs_macros::lisp_name!($name);
$( let name = $lisp_name; )?
$crate::OnceGlobalRef::$init_method(&$name, env, name)?;
)*
Ok(())
}));
}
};
}
/// A [`GlobalRef`] that can be initialized once. This is useful for long-lived values that should
/// be initialized when the dynamic module is loaded. A typical use case is specifying
/// frequently-used symbols, which can be done with the help of the macro [`use_symbols!`].
///
/// [`use_symbols`]: crate::use_symbols
#[derive(Debug)]
#[repr(transparent)]
pub struct OnceGlobalRef {
inner: OnceCell<GlobalRef>
}
impl OnceGlobalRef {
pub const fn new() -> Self {
Self { inner: OnceCell::new() }
}
/// Initializes this global reference with the given function.
#[doc(hidden)]
pub fn init<F: FnOnce(&Env) -> Result<Value>>(&self, env: &Env, f: F) -> Result<&GlobalRef> {
let g = f(env)?.make_global_ref();
self.inner.set(g).expect("Cannot initialize a global reference more than once");
Ok(self.inner.get().expect("Failed to get an initialized OnceGlobalRef"))
}
/// Points this global reference to an interned Lisp symbol with the given name.
///
/// This should be called once, during module initialization.
#[doc(hidden)]
pub fn init_to_symbol(&self, env: &Env, name: &str) -> Result<&GlobalRef> {
self.init(env, |env| env.intern(name))
}
/// Points this global reference to the function bound to the Lisp symbol with the given name.
///
/// This should be called once, during module initialization.
///
/// If the symbol is later bound to another function, this global reference will still point to
/// the old function. Therefore, this is best used for built-in and primitive functions.
#[doc(hidden)]
pub fn init_to_function(&self, env: &Env, name: &str) -> Result<&GlobalRef> {
self.init(env, |env| {
let symbol = env.intern(name)?;
env.call("indirect-function", [symbol])
})
}
}
impl<'e> IntoLisp<'e> for &'e OnceGlobalRef {
#[inline(always)]
fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
Ok(self.bind(env))
}
}
impl Deref for OnceGlobalRef {
type Target = GlobalRef;
#[inline]
fn deref(&self) -> &Self::Target {
self.inner.get().expect("Cannot access an uninitialized global reference")
}
}