Make Fixed, GuardedFixed thread-safe

This commit is contained in:
RunasSudo 2021-09-13 04:31:25 +10:00
parent e1e347c255
commit f12db205b9
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
2 changed files with 96 additions and 55 deletions

View File

@ -20,21 +20,27 @@ use super::{Assign, Number};
use ibig::{IBig, ops::Abs}; use ibig::{IBig, ops::Abs};
use num_traits::{Num, One, Signed, Zero}; use num_traits::{Num, One, Signed, Zero};
use std::cell::{Cell, UnsafeCell};
use std::cmp::{Ord, PartialEq, PartialOrd}; use std::cmp::{Ord, PartialEq, PartialOrd};
use std::ops; use std::ops;
use std::fmt; use std::fmt;
static mut DPS: Option<usize> = None; thread_local! {
static mut FACTOR: Option<IBig> = None; static DPS: Cell<usize> = Cell::new(5);
static FACTOR: UnsafeCell<IBig> = UnsafeCell::new(IBig::from(10).pow(5));
#[inline]
pub fn get_dps() -> usize {
unsafe { DPS.expect("Attempted Fixed arithmetic before dps set") }
} }
#[inline] pub fn get_dps() -> usize {
fn get_factor() -> &'static IBig { return DPS.with(|dps_cell| dps_cell.get());
unsafe { FACTOR.as_ref().expect("Attempted Fixed 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 Fixed::set_dps met
let factor_ref = unsafe { &*factor_ptr };
return factor_ref;
})
} }
/// Fixed-point number /// Fixed-point number
@ -43,11 +49,20 @@ pub struct Fixed(IBig);
impl Fixed { impl Fixed {
/// Set the number of decimal places to compute results to /// 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) { pub fn set_dps(dps: usize) {
unsafe { DPS.with(|dps_cell| {
DPS = Some(dps); dps_cell.set(dps);
FACTOR = Some(IBig::from(10).pow(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] #[test]
fn rounding() { 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("55.550"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55"));
let mut x = Fixed::parse("555.52"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); let mut x = Fixed::parse("55.552"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55"));
let mut x = Fixed::parse("555.55"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); let mut x = Fixed::parse("55.555"); x.floor_mut(2); assert_eq!(x, Fixed::parse("55.55"));
let mut x = Fixed::parse("555.57"); x.floor_mut(1); assert_eq!(x, Fixed::parse("555.5")); 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("55.550"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.55"));
let mut x = Fixed::parse("555.52"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.6")); let mut x = Fixed::parse("55.552"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.56"));
let mut x = Fixed::parse("555.55"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.6")); let mut x = Fixed::parse("55.555"); x.ceil_mut(2); assert_eq!(x, Fixed::parse("55.56"));
let mut x = Fixed::parse("555.57"); x.ceil_mut(1); assert_eq!(x, Fixed::parse("555.6")); 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("55.550"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.55"));
let mut x = Fixed::parse("555.52"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.5")); let mut x = Fixed::parse("55.552"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.55"));
let mut x = Fixed::parse("555.55"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.6")); let mut x = Fixed::parse("55.555"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.56"));
let mut x = Fixed::parse("555.57"); x.round_mut(1); assert_eq!(x, Fixed::parse("555.6")); let mut x = Fixed::parse("55.557"); x.round_mut(2); assert_eq!(x, Fixed::parse("55.56"));
} }
impl Num for Fixed { impl Num for Fixed {

View File

@ -20,27 +20,37 @@ use super::{Assign, Number};
use ibig::{IBig, ops::Abs}; use ibig::{IBig, ops::Abs};
use num_traits::{Num, One, Signed, Zero}; use num_traits::{Num, One, Signed, Zero};
use std::cell::{Cell, UnsafeCell};
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::ops; use std::ops;
use std::fmt; use std::fmt;
static mut DPS: Option<usize> = None; thread_local! {
static mut FACTOR: Option<IBig> = None; static DPS: Cell<usize> = Cell::new(5);
static mut FACTOR_CMP: Option<IBig> = None; static FACTOR: UnsafeCell<IBig> = UnsafeCell::new(IBig::from(10).pow(10));
static FACTOR_CMP: UnsafeCell<IBig> = UnsafeCell::new(IBig::from(10).pow(5) / IBig::from(2));
}
#[inline]
pub fn get_dps() -> usize { 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<'a>() -> &'a IBig {
fn get_factor() -> &'static IBig { FACTOR.with(|factor_cell| {
unsafe { FACTOR.as_ref().expect("Attempted GuardedFixed arithmetic before dps set") } 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<'a>() -> &'a IBig {
fn get_factor_cmp() -> &'static IBig { FACTOR_CMP.with(|factor_cmp_cell| {
unsafe { FACTOR_CMP.as_ref().expect("Attempted GuardedFixed arithmetic before dps set") } 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 /// Guarded fixed-point number
@ -49,12 +59,28 @@ pub struct GuardedFixed(IBig);
impl GuardedFixed { impl GuardedFixed {
/// Set the number of decimal places to compute results to /// 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) { pub fn set_dps(dps: usize) {
unsafe { DPS.with(|dps_cell| {
DPS = Some(dps); dps_cell.set(dps);
FACTOR = Some(IBig::from(10).pow(dps * 2)); });
FACTOR_CMP = Some(IBig::from(10).pow(dps) / IBig::from(2)); 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] #[test]
fn rounding() { 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("55.550"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("555.52"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); let mut x = GuardedFixed::parse("55.552"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("555.55"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); let mut x = GuardedFixed::parse("55.555"); x.floor_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("555.57"); x.floor_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); 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("55.550"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("555.52"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); let mut x = GuardedFixed::parse("55.552"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("555.55"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); let mut x = GuardedFixed::parse("55.555"); x.ceil_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("555.57"); x.ceil_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); 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("55.550"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("555.52"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.5")); let mut x = GuardedFixed::parse("55.552"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.55"));
let mut x = GuardedFixed::parse("555.55"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); let mut x = GuardedFixed::parse("55.555"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
let mut x = GuardedFixed::parse("555.57"); x.round_mut(1); assert_eq!(x, GuardedFixed::parse("555.6")); let mut x = GuardedFixed::parse("55.557"); x.round_mut(2); assert_eq!(x, GuardedFixed::parse("55.56"));
} }
impl Num for GuardedFixed { impl Num for GuardedFixed {