Skip to content

Commit de0ecf1

Browse files
committed
Use global refs for nil and t, to avoid repeated intern calls
1 parent 3c8b305 commit de0ecf1

File tree

8 files changed

+89
-10
lines changed

8 files changed

+89
-10
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ failure = { version = "0.1.5", features = ["std"] }
2727
failure_derive = "0.1.5"
2828
lazy_static = "1.4"
2929
ctor = "0.1.12"
30+
once_cell = "1.2.0"
3031
emacs_module = { path = "emacs-module", version = "0.12.0" }
3132
emacs-macros = { path = "emacs-macros", version = "0.12.0" }
3233

src/call.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,6 @@ impl<'e, T: AsRef<str>> IntoLispCallable<'e> for T {
121121
impl<'e> IntoLispCallable<'e> for &'e GlobalRef {
122122
#[inline(always)]
123123
fn into_lisp_callable(self, env: &'e Env) -> Result<Value<'e>> {
124-
self.within(env).into_lisp_callable(env)
124+
self.bind(env).into_lisp_callable(env)
125125
}
126126
}

src/init.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ type InitFn = dyn Fn(&Env) -> Result<()> + Send + 'static;
6363

6464
type FnMap = HashMap<String, Box<InitFn>>;
6565

66+
// TODO: Use once_cell instead of lazy_static
6667
// TODO: How about defining these in user crate, and requiring #[module] to be at the crate's root?
6768
// TODO: We probably don't need the mutexes.
6869
lazy_static! {

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ mod value;
5454
mod types;
5555
mod error;
5656
mod call;
57+
mod symbol;
5758

5859
/// This exposes some raw types for module to use (e.g. in `emacs_module_init`) without having to
5960
/// declare the raw `emacs_module` as a dependency.

src/symbol.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#![allow(non_upper_case_globals)]
2+
3+
use crate::{Result, Env, types::OnceGlobalRef, init::__INIT_FNS__};
4+
use ctor::ctor;
5+
6+
macro_rules! decl {
7+
($($name:ident)*) => {
8+
$(pub static $name: OnceGlobalRef = OnceGlobalRef::new();)*
9+
}
10+
}
11+
12+
decl! {
13+
nil t
14+
}
15+
16+
fn initialize(env: &Env) -> Result<()> {
17+
macro_rules! init {
18+
($($name:ident)*) => {
19+
$(OnceGlobalRef::init_to_symbol(&$name, env, stringify!($name))?;)*
20+
};
21+
}
22+
init! {
23+
nil t
24+
}
25+
Ok(())
26+
}
27+
28+
#[ctor]
29+
fn register_initializer() {
30+
__INIT_FNS__.lock().expect("Failed to acquire a write lock on map of initializers")
31+
// XXX: In theory, this can conflict with a user-defined function name.
32+
.insert("__emacs_module_rs_initialize_symbols_ ".into(), Box::new(initialize));
33+
}

src/types/global.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
use std::ops::Deref;
2+
3+
use once_cell::sync::OnceCell;
4+
15
use emacs_module::emacs_value;
26

37
use super::*;
@@ -51,7 +55,7 @@ impl GlobalRef {
5155
/// [`Env`]: struct.Env.html
5256
/// [`Value`]: struct.Value.html
5357
#[inline]
54-
pub fn within<'e>(&'e self, env: &'e Env) -> Value<'e> {
58+
pub fn bind<'e, 'g: 'e>(&'g self, env: &'e Env) -> Value<'e> {
5559
unsafe { Value::new(self.raw, env) }
5660
}
5761

@@ -75,7 +79,7 @@ impl<'e> FromLisp<'e> for GlobalRef {
7579
impl<'e> IntoLisp<'e> for &'e GlobalRef {
7680
#[inline(always)]
7781
fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
78-
Ok(self.within(env))
82+
Ok(self.bind(env))
7983
}
8084
}
8185

@@ -88,3 +92,42 @@ impl<'e> Value<'e> {
8892
GlobalRef::new(self)
8993
}
9094
}
95+
96+
/// A [`GlobalRef`] that can be initialized once. This is useful for long-lived values that should
97+
/// be initialized when the module is loaded, such as frequently-used symbols.
98+
///
99+
/// [`GlobalRef`]: struct.GlobalRef.html
100+
#[derive(Debug)]
101+
#[repr(transparent)]
102+
pub struct OnceGlobalRef {
103+
inner: OnceCell<GlobalRef>
104+
}
105+
106+
impl OnceGlobalRef {
107+
pub(crate) const fn new() -> Self {
108+
Self { inner: OnceCell::new() }
109+
}
110+
111+
/// Initializes this global reference with the given function.
112+
#[doc(hidden)]
113+
pub fn init<F: FnOnce(&Env) -> Result<Value>>(&self, env: &Env, f: F) -> Result<()> {
114+
let g = f(env)?.make_global_ref();
115+
self.inner.set(g).expect("Cannot initialize a global reference more than once");
116+
Ok(())
117+
}
118+
119+
/// Points this global reference to an interned Lisp symbol with the given name.
120+
#[doc(hidden)]
121+
pub fn init_to_symbol(&self, env: &Env, name: &str) -> Result<()> {
122+
self.init(env, |env| env.intern(name))
123+
}
124+
}
125+
126+
impl Deref for OnceGlobalRef {
127+
type Target = GlobalRef;
128+
129+
#[inline]
130+
fn deref(&self) -> &Self::Target {
131+
self.inner.get().expect("Cannot access an uninitialized global reference")
132+
}
133+
}

src/types/mod.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
use crate::{Env, Value, Result};
1+
use crate::{symbol, Env, Value, Result};
22

3-
pub use {user_ptr::Transfer, vector::Vector, global::GlobalRef};
3+
pub use {user_ptr::Transfer, vector::Vector, global::{GlobalRef, OnceGlobalRef}};
44

55
mod integer;
66
mod float;
@@ -64,23 +64,23 @@ impl<'e, T: IntoLisp<'e>> IntoLisp<'e> for Option<T> {
6464
fn into_lisp(self, env: &'e Env) -> Result<Value<'_>> {
6565
match self {
6666
Some(t) => t.into_lisp(env),
67-
None => env.intern("nil"),
67+
None => symbol::nil.into_lisp(env),
6868
}
6969
}
7070
}
7171

7272
impl IntoLisp<'_> for () {
7373
fn into_lisp(self, env: &Env) -> Result<Value<'_>> {
74-
env.intern("nil")
74+
symbol::nil.into_lisp(env)
7575
}
7676
}
7777

7878
impl IntoLisp<'_> for bool {
7979
fn into_lisp(self, env: &Env) -> Result<Value<'_>> {
8080
if self {
81-
env.intern("t")
81+
symbol::t.into_lisp(env)
8282
} else {
83-
env.intern("nil")
83+
symbol::nil.into_lisp(env)
8484
}
8585
}
8686
}

src/value.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ impl<'e> Value<'e> {
2323
///
2424
/// # Safety
2525
///
26-
/// The raw value must come from the given [`Env`].
26+
/// The raw value must not live longer than the given [`Env`].
2727
///
2828
/// [`Env`]: struct.Env.html
2929
#[doc(hidden)]

0 commit comments

Comments
 (0)