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 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<usize> = None;
static mut FACTOR: Option<IBig> = None;
#[inline]
pub fn get_dps() -> usize {
unsafe { DPS.expect("Attempted Fixed arithmetic before dps set") }
thread_local! {
static DPS: Cell<usize> = Cell::new(5);
static FACTOR: UnsafeCell<IBig> = 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 {

View File

@ -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<usize> = None;
static mut FACTOR: Option<IBig> = None;
static mut FACTOR_CMP: Option<IBig> = None;
thread_local! {
static DPS: Cell<usize> = Cell::new(5);
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 {
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 {