use std::sync::atomic::{AtomicUsize, Ordering};
use serde::{Serialize, Serializer};
pub trait IncMetric {
fn add(&self, value: usize);
fn inc(&self) {
self.add(1);
}
fn count(&self) -> usize;
}
#[derive(Default)]
pub struct SharedIncMetric(AtomicUsize, AtomicUsize);
impl IncMetric for SharedIncMetric {
fn add(&self, value: usize) {
self.0.fetch_add(value, Ordering::Relaxed);
}
fn count(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
}
impl Serialize for SharedIncMetric {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
let snapshot = self.0.load(Ordering::Relaxed);
let res = serializer.serialize_u64(snapshot as u64 - self.1.load(Ordering::Relaxed) as u64);
if res.is_ok() {
self.1.store(snapshot, Ordering::Relaxed);
}
res
}
}
pub trait StoreMetric {
fn fetch(&self) -> usize;
fn store(&self, value: usize);
}
#[derive(Default)]
pub struct SharedStoreMetric(AtomicUsize);
impl StoreMetric for SharedStoreMetric {
fn fetch(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
fn store(&self, value: usize) {
self.0.store(value, Ordering::Relaxed);
}
}
impl IncMetric for SharedStoreMetric {
fn add(&self, value: usize) {
self.0.fetch_add(value, Ordering::Relaxed);
}
fn count(&self) -> usize {
self.0.load(Ordering::Relaxed)
}
}
impl Serialize for SharedStoreMetric {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u64(self.0.load(Ordering::Relaxed) as u64)
}
}
#[cfg(test)]
mod tests {
use std::sync::atomic::fence;
use std::sync::Arc;
use std::thread;
use super::*;
#[test]
fn test_shared_inc_metric() {
let metric = Arc::new(SharedIncMetric::default());
const NUM_THREADS_TO_SPAWN: usize = 4;
const NUM_INCREMENTS_PER_THREAD: usize = 10_0000;
const M2_INITIAL_COUNT: usize = 123;
metric.add(M2_INITIAL_COUNT);
let mut v = Vec::with_capacity(NUM_THREADS_TO_SPAWN);
for _ in 0..NUM_THREADS_TO_SPAWN {
let r = metric.clone();
v.push(thread::spawn(move || {
for _ in 0..NUM_INCREMENTS_PER_THREAD {
r.inc();
}
}));
}
for handle in v {
handle.join().unwrap();
}
assert_eq!(
metric.count(),
M2_INITIAL_COUNT + NUM_THREADS_TO_SPAWN * NUM_INCREMENTS_PER_THREAD
);
}
#[test]
fn test_shared_store_metric() {
let m1 = Arc::new(SharedStoreMetric::default());
m1.store(1);
fence(Ordering::SeqCst);
assert_eq!(1, m1.fetch());
}
#[test]
fn test_serialize() {
let s = serde_json::to_string(&SharedIncMetric(
AtomicUsize::new(123),
AtomicUsize::new(111),
));
assert!(s.is_ok());
}
#[test]
fn test_wraps_around() {
let m = SharedStoreMetric(AtomicUsize::new(usize::MAX));
m.add(1);
assert_eq!(m.count(), 0);
}
}