From 0286a520842bd56da229bb4b3e670eb23c370013 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 25 Nov 2022 11:49:00 +0800 Subject: [PATCH] Use SusLock to hold pre-calculated indexer hashes. --- src/config/hashing.rs | 29 +++++++++++++++++--- src/eval/chaining.rs | 57 +++++++++++++++++++++++++++++++--------- src/eval/global_state.rs | 25 ------------------ 3 files changed, 70 insertions(+), 41 deletions(-) diff --git a/src/config/hashing.rs b/src/config/hashing.rs index dee62dfe..fcd655f1 100644 --- a/src/config/hashing.rs +++ b/src/config/hashing.rs @@ -94,15 +94,25 @@ fn hokmalock(address: usize) -> &'static HokmaLock { &RECORDS[address % LEN] } -// Safety: lol, there is a reason its called `SusLock` +/// # Safety +/// +/// LOL, there is a reason its called `SusLock` #[must_use] -struct SusLock { +pub struct SusLock { initialized: AtomicBool, data: UnsafeCell>, _marker: PhantomData, } +impl Default for SusLock { + #[inline(always)] + fn default() -> Self { + Self::new() + } +} + impl SusLock { + /// Create a new [`SusLock`]. #[inline] pub const fn new() -> Self { Self { @@ -112,12 +122,15 @@ impl SusLock { } } + /// Is the [`SusLock`] initialized? #[inline(always)] #[must_use] pub fn is_initialized(&self) -> bool { self.initialized.load(Ordering::SeqCst) } + /// Return the value of the [`SusLock`] (if initialized). + #[inline] #[must_use] pub fn get(&self) -> Option<&'static T> { if self.initialized.load(Ordering::SeqCst) { @@ -133,6 +146,8 @@ impl SusLock { } } + /// Return the value of the [`SusLock`], initializing it if not yet done. + #[inline] #[must_use] pub fn get_or_init(&self, f: impl FnOnce() -> T) -> &'static T { if !self.initialized.load(Ordering::SeqCst) { @@ -147,7 +162,13 @@ impl SusLock { self.get().unwrap() } - pub fn set(&self, value: T) -> Result<(), T> { + /// Initialize the value of the [`SusLock`]. + /// + /// # Error + /// + /// If the [`SusLock`] has already been initialized, the current value is returned as error. + #[inline] + pub fn init(&self, value: T) -> Result<(), T> { if self.initialized.load(Ordering::SeqCst) { Err(value) } else { @@ -198,7 +219,7 @@ static AHASH_SEED: SusLock> = SusLock::new(); /// ``` #[inline(always)] pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { - AHASH_SEED.set(new_seed) + AHASH_SEED.init(new_seed) } /// Get the current hashing Seed. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 3eab4d46..2b516a36 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -3,13 +3,36 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, Expr, OpAssignment}; +use crate::config::hashing::SusLock; +use crate::engine::{FN_IDX_GET, FN_IDX_SET}; use crate::types::dynamic::Union; use crate::types::RestoreOnDrop; -use crate::{Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR}; +use crate::{ + calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR, +}; use std::hash::Hash; #[cfg(feature = "no_std")] use std::prelude::v1::*; +/// Function call hashes to index getters and setters. +/// +/// # Safety +/// +/// Uses the extremely unsafe [`SusLock`]. Change to [`OnceCell`] when it is stabilized. +static INDEXER_HASHES: SusLock<(u64, u64)> = SusLock::new(); + +/// Get the pre-calculated index getter/setter hashes. +#[inline(always)] +#[must_use] +fn hash_idx() -> (u64, u64) { + *INDEXER_HASHES.get_or_init(|| { + ( + calc_fn_hash(None, FN_IDX_GET, 2), + calc_fn_hash(None, FN_IDX_SET, 3), + ) + }) +} + /// Method of chaining. #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub enum ChainType { @@ -45,16 +68,21 @@ impl Engine { idx: &mut Dynamic, pos: Position, ) -> RhaiResultOf { - let args = &mut [target, idx]; - let hash = global.hash_idx_get(); - let fn_name = crate::engine::FN_IDX_GET; - let orig_level = global.level; global.level += 1; let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); - self.exec_native_fn_call(global, caches, fn_name, None, hash, args, true, pos) - .map(|(r, ..)| r) + self.exec_native_fn_call( + global, + caches, + FN_IDX_GET, + None, + hash_idx().0, + &mut [target, idx], + true, + pos, + ) + .map(|(r, ..)| r) } /// Call a set indexer. @@ -69,15 +97,20 @@ impl Engine { is_ref_mut: bool, pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { - let hash = global.hash_idx_set(); - let args = &mut [target, idx, new_val]; - let fn_name = crate::engine::FN_IDX_SET; - let orig_level = global.level; global.level += 1; let global = &mut *RestoreOnDrop::lock(global, move |g| g.level = orig_level); - self.exec_native_fn_call(global, caches, fn_name, None, hash, args, is_ref_mut, pos) + self.exec_native_fn_call( + global, + caches, + FN_IDX_SET, + None, + hash_idx().1, + &mut [target, idx, new_val], + is_ref_mut, + pos, + ) } /// Get the value at the indexed position of a base type. diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 6070ca11..408f71b0 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -58,9 +58,6 @@ pub struct GlobalRuntimeState { /// /// When that happens, this flag is turned on. pub always_search_scope: bool, - /// Function call hashes to index getters and setters. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_indexing: (u64, u64), /// Embedded [module][crate::Module] resolver. #[cfg(not(feature = "no_module"))] pub embedded_module_resolver: @@ -99,11 +96,6 @@ impl GlobalRuntimeState { always_search_scope: false, #[cfg(not(feature = "no_module"))] embedded_module_resolver: None, - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - fn_hash_indexing: ( - crate::calc_fn_hash(None, crate::engine::FN_IDX_GET, 2), - crate::calc_fn_hash(None, crate::engine::FN_IDX_SET, 3), - ), #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] constants: None, @@ -314,20 +306,6 @@ impl GlobalRuntimeState { pub(crate) const fn source_raw(&self) -> Option<&ImmutableString> { self.source.as_ref() } - /// Get the pre-calculated index getter hash. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[inline(always)] - #[must_use] - pub(crate) fn hash_idx_get(&mut self) -> u64 { - self.fn_hash_indexing.0 - } - /// Get the pre-calculated index setter hash. - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - #[inline(always)] - #[must_use] - pub(crate) fn hash_idx_set(&mut self) -> u64 { - self.fn_hash_indexing.1 - } /// Return a reference to the debugging interface. /// @@ -387,9 +365,6 @@ impl fmt::Debug for GlobalRuntimeState { .field("scope_level", &self.scope_level) .field("always_search_scope", &self.always_search_scope); - #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] - f.field("fn_hash_indexing", &self.fn_hash_indexing); - #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] f.field("constants", &self.constants);