#![allow(unused_macros)]
#[cfg(parking_lot)]
use parking_lot::Mutex;
#[cfg(not(parking_lot))]
use std::sync::Mutex;
pub trait Inc {
fn inc(&mut self);
}
macro_rules! imp {
($( $t:ty ) *) => {
$(
impl Inc for $t{
#[inline]
fn inc(&mut self){
*self += 1;
}
}
)*
};
}
imp![u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize];
#[derive(Debug, Default)]
pub struct Counter<T: Inc>(Mutex<T>);
#[macro_export]
macro_rules! global_counter {
($name:ident, $type:ident, $value:expr) => {
static $name: ::global_counter::global_counter_macro_dependencies::Lazy<::global_counter::generic::Counter<$type>> =
::global_counter::global_counter_macro_dependencies::Lazy::new(|| ::global_counter::generic::Counter::new($value));
};
}
macro_rules! global_counter_2 {
($name:ident, $type:ident, $value:expr) => {
use once_cell::sync::Lazy;
static $name: Lazy<Counter<$type>> =
Lazy::new(|| Counter::new($value));
};
}
#[macro_export]
macro_rules! global_default_counter {
($name:ident, $type:ident) => {
global_counter!($name, $type, $type::default());
};
}
macro_rules! global_default_counter_2{
($name:ident, $type:ident) => {
global_counter_2!($name, $type, $type::default());
};
}
impl<T: Inc> Counter<T> {
#[inline]
pub fn new(val: T) -> Counter<T> {
Counter(Mutex::new(val))
}
#[inline]
pub fn get_borrowed(&self) -> impl std::ops::Deref<Target = T> + '_ {
self.lock()
}
#[inline]
pub fn get_mut_borrowed(&self) -> impl std::ops::DerefMut<Target = T> + '_ {
self.lock()
}
#[inline]
pub fn set(&self, val: T) {
*self.lock() = val;
}
#[inline]
pub fn inc(&self) {
self.lock().inc();
}
#[cfg(parking_lot)]
#[inline]
fn lock(&self) -> impl std::ops::DerefMut<Target = T> + '_ {
self.0.lock()
}
#[cfg(not(parking_lot))]
#[inline]
fn lock(&self) -> impl std::ops::DerefMut<Target = T> + '_ {
self.0.lock().expect("Global counter lock failed. This indicates another user paniced while holding a lock to the counter.")
}
}
impl<T: Inc + Clone> Counter<T> {
#[inline]
pub fn get_cloned(&self) -> T {
self.lock().clone()
}
#[inline]
pub fn inc_cloning(&self) -> T {
let mut locked = self.lock();
let prev = locked.clone();
locked.inc();
prev
}
}
impl<T: Inc + Default> Counter<T> {
#[inline]
pub fn reset(&self) {
self.set(T::default());
}
}
#[cfg(test)]
mod tests {
use crate::generic::Counter;
#[derive(Default, PartialEq, Eq, Debug)]
struct PanicOnClone(i32);
impl Clone for PanicOnClone {
fn clone(&self) -> Self {
panic!("PanicOnClone cloned");
}
}
impl crate::generic::Inc for PanicOnClone {
fn inc(&mut self) {
self.0.inc();
}
}
#[test]
fn get_borrowed_doesnt_clone() {
global_default_counter_2!(COUNTER, PanicOnClone);
assert_eq!(*COUNTER.get_borrowed(), PanicOnClone(0));
}
#[test]
fn get_mut_borrowed_doesnt_clone() {
global_counter_2!(COUNTER, PanicOnClone, PanicOnClone(0));
assert_eq!(*COUNTER.get_mut_borrowed(), PanicOnClone(0));
}
#[test]
fn count_to_five_single_threaded() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 1);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 2);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 3);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 4);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 5);
}
#[derive(Clone, Default, PartialEq, Eq, Debug)]
struct Baz<T> {
i: i32,
u: i32,
_marker: std::marker::PhantomData<T>,
}
impl<T> crate::generic::Inc for Baz<T> {
fn inc(&mut self) {
self.i += 1;
}
}
type Bar = Baz<std::cell::RefCell<u32>>;
#[test]
fn count_struct() {
global_default_counter_2!(COUNTER, Bar);
assert_eq!(
COUNTER.get_cloned(),
Baz {
i: 0,
u: 0,
_marker: std::marker::PhantomData
}
);
COUNTER.inc();
assert_eq!(
COUNTER.get_cloned(),
Baz {
i: 1,
u: 0,
_marker: std::marker::PhantomData
}
);
COUNTER.inc();
assert_eq!(
COUNTER.get_cloned(),
Baz {
i: 2,
u: 0,
_marker: std::marker::PhantomData
}
);
COUNTER.inc();
assert_eq!(
COUNTER.get_cloned(),
Baz {
i: 3,
u: 0,
_marker: std::marker::PhantomData
}
);
COUNTER.inc();
assert_eq!(
COUNTER.get_cloned(),
Baz {
i: 4,
u: 0,
_marker: std::marker::PhantomData
}
);
COUNTER.inc();
assert_eq!(
COUNTER.get_cloned(),
Baz {
i: 5,
u: 0,
_marker: std::marker::PhantomData
}
);
}
#[test]
fn count_to_50000_single_threaded() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
for _ in 0..50000 {
COUNTER.inc();
}
assert_eq!(COUNTER.get_cloned(), 50000);
}
#[test]
fn count_to_five_seq_threaded() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
let t_0 = std::thread::spawn(|| {
COUNTER.inc();
});
t_0.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 1);
let t_1 = std::thread::spawn(|| {
COUNTER.inc();
});
t_1.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 2);
let t_2 = std::thread::spawn(|| {
COUNTER.inc();
});
t_2.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 3);
let t_3 = std::thread::spawn(|| {
COUNTER.inc();
});
t_3.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 4);
let t_4 = std::thread::spawn(|| {
COUNTER.inc();
});
t_4.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 5);
}
#[test]
fn count_to_50000_seq_threaded() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
let t_0 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
t_0.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 10000);
let t_1 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
t_1.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 20000);
let t_2 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
t_2.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 30000);
let t_3 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
t_3.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 40000);
let t_4 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
t_4.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 50000);
}
#[test]
fn count_to_five_par_threaded() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
let t_0 = std::thread::spawn(|| {
COUNTER.inc();
});
let t_1 = std::thread::spawn(|| {
COUNTER.inc();
});
let t_2 = std::thread::spawn(|| {
COUNTER.inc();
});
let t_3 = std::thread::spawn(|| {
COUNTER.inc();
});
let t_4 = std::thread::spawn(|| {
COUNTER.inc();
});
t_0.join().expect("Err joining thread");
t_1.join().expect("Err joining thread");
t_2.join().expect("Err joining thread");
t_3.join().expect("Err joining thread");
t_4.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 5);
}
#[test]
fn count_to_50000_par_threaded() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
let t_0 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
let t_1 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
let t_2 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
let t_3 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
let t_4 = std::thread::spawn(|| {
for _ in 0..10000 {
COUNTER.inc();
}
});
t_0.join().expect("Err joining thread");
t_1.join().expect("Err joining thread");
t_2.join().expect("Err joining thread");
t_3.join().expect("Err joining thread");
t_4.join().expect("Err joining thread");
assert_eq!(COUNTER.get_cloned(), 50000);
}
#[test]
fn reset() {
global_default_counter_2!(COUNTER, u32);
assert_eq!(COUNTER.get_cloned(), 0);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 1);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 2);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 3);
COUNTER.reset();
assert_eq!(COUNTER.get_cloned(), 0);
COUNTER.inc();
assert_eq!(COUNTER.get_cloned(), 1);
}
}