use std::cell::Cell;
use std::sync::atomic::{AtomicU32, AtomicUsize, Ordering};
pub trait FloatCell: Default {
fn load(&self) -> f32;
fn store(&self, v: f32);
}
pub trait CounterCell: Default {
fn load(&self) -> usize;
fn fetch_incr(&self) -> usize;
}
pub trait StorageBackend {
type Float: FloatCell;
type Counter: CounterCell;
}
impl FloatCell for Cell<f32> {
fn load(&self) -> f32 {
self.get()
}
fn store(&self, v: f32) {
self.set(v);
}
}
impl CounterCell for Cell<usize> {
fn load(&self) -> usize {
self.get()
}
fn fetch_incr(&self) -> usize {
let prev = self.get();
self.set(prev + 1);
prev
}
}
pub struct Local;
impl StorageBackend for Local {
type Float = Cell<f32>;
type Counter = Cell<usize>;
}
impl FloatCell for AtomicU32 {
fn load(&self) -> f32 {
f32::from_bits(AtomicU32::load(self, Ordering::Relaxed))
}
fn store(&self, v: f32) {
AtomicU32::store(self, v.to_bits(), Ordering::Relaxed);
}
}
impl CounterCell for AtomicUsize {
fn load(&self) -> usize {
AtomicUsize::load(self, Ordering::Relaxed)
}
fn fetch_incr(&self) -> usize {
self.fetch_add(1, Ordering::Relaxed)
}
}
pub struct Atomic;
impl StorageBackend for Atomic {
type Float = AtomicU32;
type Counter = AtomicUsize;
}
#[cfg(test)]
mod tests {
use super::*;
fn float_cell_roundtrips<C: FloatCell>() {
let c = C::default();
assert_eq!(c.load(), 0.0);
for &v in &[1.0f32, -2.5, 0.0, 1e9, -1e-9] {
c.store(v);
assert_eq!(c.load(), v, "round-trip {v}");
}
}
#[test]
fn local_float_cell_roundtrips() {
float_cell_roundtrips::<<Local as StorageBackend>::Float>();
}
#[test]
fn atomic_float_cell_roundtrips_via_bits() {
float_cell_roundtrips::<<Atomic as StorageBackend>::Float>();
}
fn counter_increments<C: CounterCell>() {
let c = C::default();
assert_eq!(c.load(), 0);
assert_eq!(c.fetch_incr(), 0); assert_eq!(c.fetch_incr(), 1);
assert_eq!(c.load(), 2);
}
#[test]
fn local_counter_increments() {
counter_increments::<<Local as StorageBackend>::Counter>();
}
#[test]
fn atomic_counter_increments() {
counter_increments::<<Atomic as StorageBackend>::Counter>();
}
#[test]
fn atomic_backend_cells_are_shareable() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<<Atomic as StorageBackend>::Float>();
assert_send_sync::<<Atomic as StorageBackend>::Counter>();
}
}