diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6823d676..d186abb9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,6 +96,8 @@ jobs: continue-on-error: ${{matrix.experimental}} strategy: matrix: + flags: + - "" include: - {os: ubuntu-latest, flags: "--profile unix", experimental: false} - {os: windows-latest, flags: "--profile windows", experimental: true} diff --git a/.gitignore b/.gitignore index fe87b992..10e86a86 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ Rhai.toml **/*.bat doc/rhai-sync.json doc/rhai.json +.idea/ +.idea +.idea/* diff --git a/build.rs b/build.rs index 66903154..39c11238 100644 --- a/build.rs +++ b/build.rs @@ -8,7 +8,6 @@ fn main() { // Tell Cargo that if the given environment variable changes, to rerun this build script. println!("cargo:rerun-if-changed=build.template"); println!("cargo:rerun-if-env-changed=RHAI_AHASH_SEED"); - let mut contents = String::new(); File::open("build.template") @@ -20,8 +19,8 @@ fn main() { contents = contents.replace("{{AHASH_SEED}}", &seed); - File::create("src/config.rs") + File::create("src/config/hashing_env.rs") .expect("cannot create `config.rs`") - .write(contents.as_bytes()) - .expect("cannot write to `config.rs`"); + .write_all(contents.as_bytes()) + .expect("cannot write to `config/hashing_env.rs`"); } diff --git a/build.template b/build.template index 6f09909f..a165e5ed 100644 --- a/build.template +++ b/build.template @@ -1,7 +1,2 @@ -//! Configuration settings for this Rhai build -//! -//! This file is auto-generated from `build.template` - -/// Fixed hashing seeds for stable hashing. -/// Set to [`None`] to disable stable hashing. -pub const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; +// This file is automatically set during build time by build.rs and build.template. +pub(crate) const AHASH_SEED: Option<[u64; 4]> = {{AHASH_SEED}}; diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index 93db79d5..00000000 --- a/src/config.rs +++ /dev/null @@ -1,7 +0,0 @@ -//! Configuration settings for this Rhai build -//! -//! This file is auto-generated from `build.template` - -/// Fixed hashing seeds for stable hashing. -/// Set to [`None`] to disable stable hashing. -pub const AHASH_SEED: Option<[u64; 4]> = None; diff --git a/src/config/hashing.rs b/src/config/hashing.rs new file mode 100644 index 00000000..2355a405 --- /dev/null +++ b/src/config/hashing.rs @@ -0,0 +1,183 @@ +//! Fixed hashing seeds for stable hashing. +//! +//! Set to [`None`] to disable stable hashing. +//! +//! See [`set_rhai_ahash_seed`] for more. +//! +//! Alternatively, You can also set this at compile time by setting the `RHAI_AHASH_SEED` +//! environment variable instead. +//! +//! E.g. `env RHAI_AHASH_SEED ="[236,800,954,213]"` +// [236,800,954,213], haha funny yume nikki reference epic uboachan face numberworld nexus moment 100 + +use crate::config::hashing_env; +use core::{ + cell::UnsafeCell, + marker::PhantomData, + mem::MaybeUninit, + panic::{RefUnwindSafe, UnwindSafe}, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, +}; + +// omg its hokma from record team here to record our locks +// what does this do? +// so what this does is keep track of a global address in memory that acts as a global lock +// i stole this from crossbeam so read their docs for more +struct HokmaLock { + lock: AtomicUsize, +} + +impl HokmaLock { + pub const fn new() -> Self { + Self { + lock: AtomicUsize::new(0), + } + } + + pub fn write(&'static self) -> WhenTheHokmaSupression { + loop { + let previous = self.lock.swap(1, Ordering::SeqCst); + + if previous != 1 { + return WhenTheHokmaSupression { + hokma: self, + state: previous, + }; + } + } + } +} + +struct WhenTheHokmaSupression { + hokma: &'static HokmaLock, + + state: usize, +} + +impl WhenTheHokmaSupression { + pub fn the_price_of_silence(self) { + self.hokma.lock.store(self.state, Ordering::SeqCst); + + core::mem::forget(self) + } +} + +impl Drop for WhenTheHokmaSupression { + fn drop(&mut self) { + self.hokma + .lock + .store(self.state.wrapping_add(2), Ordering::SeqCst) + } +} + +fn hokmalock(address: usize) -> &'static HokmaLock { + const LEN: usize = 787; + const LCK: HokmaLock = HokmaLock::new(); + static RECORDS: [HokmaLock; LEN] = [LCK; LEN]; + + &RECORDS[address % LEN] +} + +// Safety: lol, there is a reason its called "SusLock" +struct SusLock +where + T: 'static + Copy, +{ + initalized: AtomicBool, + data: UnsafeCell>, + _marker: PhantomData, +} + +impl SusLock +where + T: 'static + Copy, +{ + pub const fn new() -> SusLock { + SusLock { + initalized: AtomicBool::new(false), + data: UnsafeCell::new(MaybeUninit::uninit()), + _marker: PhantomData, + } + } + + pub fn get(&self) -> Option { + if self.initalized.load(Ordering::SeqCst) { + let hokma = hokmalock(unsafe { core::mem::transmute(self.data.get()) }); + // we forgo the optimistic read, because we don't really care + let guard = hokma.write(); + let val = { + let cast: *const T = self.data.get().cast(); + unsafe { cast.read() } + }; + guard.the_price_of_silence(); + Some(val) + } else { + return None; + } + } + + pub fn get_or_init(&self, f: impl FnOnce() -> T) -> Option { + let value = f(); + if !self.initalized.load(Ordering::SeqCst) { + self.initalized.store(true, Ordering::SeqCst); + let hokma = hokmalock(unsafe { core::mem::transmute(self.data.get()) }); + hokma.write(); + unsafe { + self.data.get().write(MaybeUninit::new(value)); + } + } + + self.get() + } + + pub fn set(&self, value: T) -> Result<(), T> { + if self.initalized.load(Ordering::SeqCst) { + Err(value) + } else { + let _ = self.get_or_init(|| value); + Ok(()) + } + } +} + +unsafe impl Sync for SusLock where T: 'static + Copy {} +unsafe impl Send for SusLock where T: 'static + Copy {} +impl RefUnwindSafe for SusLock where T: 'static + Copy {} + +impl Drop for SusLock +where + T: 'static + Copy, +{ + fn drop(&mut self) { + if self.initalized.load(Ordering::SeqCst) { + unsafe { (&mut *self.data.get()).assume_init_drop() }; + } + } +} + +static AHASH_SEED: SusLock> = SusLock::new(); + +// #[doc(cfg(feature = "stable_hash"))] +/// Sets the Rhai Ahash seed. This is used to hash functions and the like. +/// +/// This is a global variable, and thus will affect every Rhai instance. +/// This should not be used _unless_ you know you need it. +/// +/// # Warnings +/// - You can only call this function **ONCE** for the whole of your program execution. +/// - You should gracefully handle the `Err(())`. +/// - You **MUST** call this before **ANY** Rhai operation occurs (e.g. creating an [`Engine`]). +/// +/// # Errors +/// This will error if the AHashSeed is already set. +pub fn set_ahash_seed(new_seed: Option<[u64; 4]>) -> Result<(), Option<[u64; 4]>> { + AHASH_SEED.set(new_seed) +} + +/// Gets the current Rhai Ahash Seed. If the seed is not yet defined, this will automatically set a seed. +/// The default seed is not stable and may change between versions. +/// +/// See [`set_rhai_ahash_seed`] for more. +pub fn get_ahash_seed() -> Option<[u64; 4]> { + AHASH_SEED.get_or_init(|| hashing_env::AHASH_SEED).flatten() +} diff --git a/src/config/hashing_env.rs b/src/config/hashing_env.rs new file mode 100644 index 00000000..ee2b35db --- /dev/null +++ b/src/config/hashing_env.rs @@ -0,0 +1,2 @@ +// This file is automatically set during build time by build.rs and build.template. +pub(crate) const AHASH_SEED: Option<[u64; 4]> = None; diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 00000000..db5448f6 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,4 @@ +//! Contains Configuration for Rhai. + +pub mod hashing; +mod hashing_env; diff --git a/src/func/hashing.rs b/src/func/hashing.rs index 5f6a806c..855dcd3d 100644 --- a/src/func/hashing.rs +++ b/src/func/hashing.rs @@ -77,7 +77,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] #[must_use] pub fn get_hasher() -> ahash::AHasher { - match config::AHASH_SEED { + match config::hashing::get_ahash_seed() { Some([seed1, seed2, seed3, seed4]) if seed1 | seed2 | seed3 | seed4 != 0 => { ahash::RandomState::with_seeds(seed1, seed2, seed3, seed4).build_hasher() } diff --git a/src/lib.rs b/src/lib.rs index 199edb75..8879012f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,7 +83,7 @@ use std::prelude::v1::*; // Internal modules mod api; mod ast; -mod config; +pub mod config; mod engine; mod eval; mod func;