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
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) } } /// A [`GlobalRef`] that can be initialized once. This is useful for long-lived values that should /// be initialized when the module is loaded, such as frequently-used symbols. /// /// [`GlobalRef`]: struct.GlobalRef.html #[derive(Debug)] #[repr(transparent)] pub struct OnceGlobalRef { inner: OnceCell<GlobalRef> } impl OnceGlobalRef { pub(crate) 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<()> { let g = f(env)?.make_global_ref(); self.inner.set(g).expect("Cannot initialize a global reference more than once"); Ok(()) } /// Points this global reference to an interned Lisp symbol with the given name. #[doc(hidden)] pub fn init_to_symbol(&self, env: &Env, name: &str) -> Result<()> { self.init(env, |env| env.intern(name)) } /// Points this global reference to the function bound to the Lisp symbol with the given name. #[doc(hidden)] pub fn init_to_function(&self, env: &Env, name: &str) -> Result<()> { self.init(env, |env| { let symbol = env.intern(name)?; env.call("indirect-function", [symbol]) }) } } impl Deref for OnceGlobalRef { type Target = GlobalRef; #[inline] fn deref(&self) -> &Self::Target { self.inner.get().expect("Cannot access an uninitialized global reference") } }