From f12db205b9b28d0a6cd2f300f6f515957217aaf2 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Mon, 13 Sep 2021 04:31:25 +1000 Subject: [PATCH] Make Fixed, GuardedFixed thread-safe --- src/numbers/fixed.rs | 67 ++++++++++++++++++++-------------- src/numbers/gfixed.rs | 84 ++++++++++++++++++++++++++++--------------- 2 files changed, 96 insertions(+), 55 deletions(-) diff --git a/src/numbers/fixed.rs b/src/numbers/fixed.rs index 15ab3e9..7f2fa84 100644 --- a/src/numbers/fixed.rs +++ b/src/numbers/fixed.rs @@ -20,21 +20,27 @@ use super::{Assign, Number}; use ibig::{IBig, ops::Abs}; use num_traits::{Num, One, Signed, Zero}; +use std::cell::{Cell, UnsafeCell}; use std::cmp::{Ord, PartialEq, PartialOrd}; use std::ops; use std::fmt; -static mut DPS: Option = None; -static mut FACTOR: Option = None; - -#[inline] -pub fn get_dps() -> usize { - unsafe { DPS.expect("Attempted Fixed arithmetic before dps set") } +thread_local! { + static DPS: Cell = Cell::new(5); + static FACTOR: UnsafeCell = UnsafeCell::new(IBig::from(10).pow(5)); } -#[inline] -fn get_factor() -> &'static IBig { - unsafe { FACTOR.as_ref().expect("Attempted Fixed arithmetic before dps set") } +pub fn get_dps() -> usize { + return DPS.with(|dps_cell| dps_cell.get()); +} + +fn get_factor<'a>() -> &'a IBig { + FACTOR.with(|factor_cell| { + let factor_ptr = factor_cell.get(); + // SAFETY: Safe if requirements of Fixed::set_dps met + let factor_ref = unsafe { &*factor_ptr }; + return factor_ref; + }) } /// Fixed-point number @@ -43,11 +49,20 @@ pub struct Fixed(IBig); impl Fixed { /// Set the number of decimal places to compute results to + /// + /// SAFETY: This must be called before, and never after, any operations on [Fixed]. pub fn set_dps(dps: usize) { - unsafe { - DPS = Some(dps); - FACTOR = Some(IBig::from(10).pow(dps)); - } + DPS.with(|dps_cell| { + dps_cell.set(dps); + }); + FACTOR.with(|factor_cell| { + let factor = IBig::from(10).pow(dps); + let factor_ptr = factor_cell.get(); + // SAFETY: Safe if requirements above met + unsafe { + *factor_ptr = factor; + } + }); } } @@ -125,22 +140,22 @@ impl Number for Fixed { #[test] fn rounding() { - Fixed::set_dps(2); + Fixed::set_dps(5); - let mut x = Fixed::parse("555.50"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); - let mut x = Fixed::parse("555.52"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); - let mut x = Fixed::parse("555.55"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); - let mut x = Fixed::parse("555.57"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); + let mut x = Fixed::parse("55.550"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55")); + let mut x = Fixed::parse("55.552"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55")); + let mut x = Fixed::parse("55.555"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55")); + let mut x = Fixed::parse("55.557"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55")); - let mut x = Fixed::parse("555.50"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.5")); - let mut x = Fixed::parse("555.52"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.6")); - let mut x = Fixed::parse("555.55"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.6")); - let mut x = Fixed::parse("555.57"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.6")); + let mut x = Fixed::parse("55.550"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.55")); + let mut x = Fixed::parse("55.552"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.56")); + let mut x = Fixed::parse("55.555"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.56")); + let mut x = Fixed::parse("55.557"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.56")); - let mut x = Fixed::parse("555.50"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.5")); - let mut x = Fixed::parse("555.52"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.5")); - let mut x = Fixed::parse("555.55"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.6")); - let mut x = Fixed::parse("555.57"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.6")); + let mut x = Fixed::parse("55.550"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.55")); + let mut x = Fixed::parse("55.552"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.55")); + let mut x = Fixed::parse("55.555"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.56")); + let mut x = Fixed::parse("55.557"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.56")); } impl Num for Fixed { diff --git a/src/numbers/gfixed.rs b/src/numbers/gfixed.rs index 931e416..44862d1 100644 --- a/src/numbers/gfixed.rs +++ b/src/numbers/gfixed.rs @@ -20,27 +20,37 @@ use super::{Assign, Number}; use ibig::{IBig, ops::Abs}; use num_traits::{Num, One, Signed, Zero}; +use std::cell::{Cell, UnsafeCell}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::ops; use std::fmt; -static mut DPS: Option = None; -static mut FACTOR: Option = None; -static mut FACTOR_CMP: Option = None; +thread_local! { + static DPS: Cell = Cell::new(5); + static FACTOR: UnsafeCell = UnsafeCell::new(IBig::from(10).pow(10)); + static FACTOR_CMP: UnsafeCell = UnsafeCell::new(IBig::from(10).pow(5) / IBig::from(2)); +} -#[inline] pub fn get_dps() -> usize { - unsafe { DPS.expect("Attempted GuardedFixed arithmetic before dps set") } + return DPS.with(|dps_cell| dps_cell.get()); } -#[inline] -fn get_factor() -> &'static IBig { - unsafe { FACTOR.as_ref().expect("Attempted GuardedFixed arithmetic before dps set") } +fn get_factor<'a>() -> &'a IBig { + FACTOR.with(|factor_cell| { + let factor_ptr = factor_cell.get(); + // SAFETY: Safe if requirements of GuardedFixed::set_dps met + let factor_ref = unsafe { &*factor_ptr }; + return factor_ref; + }) } -#[inline] -fn get_factor_cmp() -> &'static IBig { - unsafe { FACTOR_CMP.as_ref().expect("Attempted GuardedFixed arithmetic before dps set") } +fn get_factor_cmp<'a>() -> &'a IBig { + FACTOR_CMP.with(|factor_cmp_cell| { + let factor_cmp_ptr = factor_cmp_cell.get(); + // SAFETY: Safe if requirements of Fixed::set_dps met + let factor_cmp_ref = unsafe { &*factor_cmp_ptr }; + return factor_cmp_ref; + }) } /// Guarded fixed-point number @@ -49,12 +59,28 @@ pub struct GuardedFixed(IBig); impl GuardedFixed { /// Set the number of decimal places to compute results to + /// + /// SAFETY: This must be called before, and never after, any operations on [GuardedFixed]. pub fn set_dps(dps: usize) { - unsafe { - DPS = Some(dps); - FACTOR = Some(IBig::from(10).pow(dps * 2)); - FACTOR_CMP = Some(IBig::from(10).pow(dps) / IBig::from(2)); - } + DPS.with(|dps_cell| { + dps_cell.set(dps); + }); + FACTOR.with(|factor_cell| { + let factor = IBig::from(10).pow(dps * 2); + let factor_ptr = factor_cell.get(); + // SAFETY: Safe if requirements above met + unsafe { + *factor_ptr = factor; + } + }); + FACTOR_CMP.with(|factor_cmp_cell| { + let factor_cmp = IBig::from(10).pow(dps) / IBig::from(2); + let factor_cmp_ptr = factor_cmp_cell.get(); + // SAFETY: Safe if requirements above met + unsafe { + *factor_cmp_ptr = factor_cmp; + } + }); } } @@ -132,22 +158,22 @@ impl Number for GuardedFixed { #[test] fn rounding() { - GuardedFixed::set_dps(2); + GuardedFixed::set_dps(5); - let mut x = GuardedFixed::parse("555.50"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); - let mut x = GuardedFixed::parse("555.52"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); - let mut x = GuardedFixed::parse("555.55"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); - let mut x = GuardedFixed::parse("555.57"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); + let mut x = GuardedFixed::parse("55.550"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); + let mut x = GuardedFixed::parse("55.552"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); + let mut x = GuardedFixed::parse("55.555"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); + let mut x = GuardedFixed::parse("55.557"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); - let mut x = GuardedFixed::parse("555.50"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); - let mut x = GuardedFixed::parse("555.52"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); - let mut x = GuardedFixed::parse("555.55"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); - let mut x = GuardedFixed::parse("555.57"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); + let mut x = GuardedFixed::parse("55.550"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); + let mut x = GuardedFixed::parse("55.552"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56")); + let mut x = GuardedFixed::parse("55.555"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56")); + let mut x = GuardedFixed::parse("55.557"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56")); - let mut x = GuardedFixed::parse("555.50"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); - let mut x = GuardedFixed::parse("555.52"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); - let mut x = GuardedFixed::parse("555.55"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); - let mut x = GuardedFixed::parse("555.57"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); + let mut x = GuardedFixed::parse("55.550"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); + let mut x = GuardedFixed::parse("55.552"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.55")); + let mut x = GuardedFixed::parse("55.555"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.56")); + let mut x = GuardedFixed::parse("55.557"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.56")); } impl Num for GuardedFixed {