use std::sync::atomic::{AtomicPtr, Ordering};
use biometrics::{Collector, Counter};
static REGISTERED: Counter = Counter::new("sync42.registry.registered");
pub fn register_biometrics(collector: &Collector) {
collector.register_counter(®ISTERED);
}
#[derive(Debug)]
pub struct Registry<T> {
list: AtomicPtr<Registration<T>>,
}
impl<T> Registry<T> {
pub const fn new() -> Self {
let list = AtomicPtr::new(std::ptr::null_mut());
Self { list }
}
pub fn walk(&self) -> Walker<T> {
Walker::new(self)
}
}
impl<T> Default for Registry<T> {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct Registration<T> {
this: AtomicPtr<T>,
next: AtomicPtr<Registration<T>>,
}
impl<T> Registration<T> {
pub const fn new() -> Self {
let this = AtomicPtr::new(std::ptr::null_mut());
let next = AtomicPtr::new(std::ptr::null_mut());
Self { this, next }
}
#[inline(always)]
pub fn ensure_registered(&'static self, registry: &Registry<T>, t: &'static T) {
if self.this.load(Ordering::Relaxed).is_null() {
self.ensure_registered_slow(registry, t);
}
}
#[inline(never)]
fn ensure_registered_slow(&'static self, registry: &Registry<T>, t: &'static T) {
let t: *mut T = t as *const T as *mut T;
if self
.this
.compare_exchange(std::ptr::null_mut(), t, Ordering::AcqRel, Ordering::Relaxed)
.is_err()
{
return;
}
let this: *mut Registration<T> = self as *const Registration<T> as *mut Registration<T>;
loop {
let list = registry.list.load(Ordering::Relaxed);
self.next.store(list, Ordering::Release);
if registry
.list
.compare_exchange(list, this, Ordering::AcqRel, Ordering::Relaxed)
.is_ok()
{
break;
}
}
}
}
impl<T> Default for Registration<T> {
fn default() -> Self {
Self::new()
}
}
pub struct Walker<T> {
next: *mut Registration<T>,
}
impl<T> Walker<T> {
pub fn new(registry: &Registry<T>) -> Self {
let next = registry.list.load(Ordering::Acquire);
Self { next }
}
}
impl<T: 'static> Iterator for Walker<T> {
type Item = &'static T;
fn next(&mut self) -> Option<Self::Item> {
if !self.next.is_null() {
let registration: &Registration<T> = unsafe { &*self.next };
self.next = registration.next.load(Ordering::Acquire);
let item: &'static T = unsafe { &*registration.this.load(Ordering::Acquire) };
Some(item)
} else {
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestItem {
value: u64,
registration: Registration<TestItem>,
}
impl TestItem {
const fn new(value: u64) -> Self {
let registration = Registration::<TestItem>::new();
Self {
value,
registration,
}
}
fn ensure_registered(&'static self) {
self.registration.ensure_registered(®ISTRY, self);
}
}
static ITEM_1: TestItem = TestItem::new(1);
static ITEM_2: TestItem = TestItem::new(2);
static ITEM_3: TestItem = TestItem::new(3);
static ITEM_4: TestItem = TestItem::new(4);
static ITEM_5: TestItem = TestItem::new(5);
static REGISTRY: Registry<TestItem> = Registry::new();
#[test]
fn registration() {
assert_eq!(0, REGISTRY.walk().count());
ITEM_1.ensure_registered();
assert_eq!(
vec![1],
REGISTRY.walk().map(|item| item.value).collect::<Vec<_>>()
);
ITEM_5.ensure_registered();
assert_eq!(
vec![5, 1],
REGISTRY.walk().map(|item| item.value).collect::<Vec<_>>()
);
ITEM_2.ensure_registered();
assert_eq!(
vec![2, 5, 1],
REGISTRY.walk().map(|item| item.value).collect::<Vec<_>>()
);
ITEM_4.ensure_registered();
assert_eq!(
vec![4, 2, 5, 1],
REGISTRY.walk().map(|item| item.value).collect::<Vec<_>>()
);
ITEM_1.ensure_registered();
ITEM_5.ensure_registered();
ITEM_2.ensure_registered();
ITEM_4.ensure_registered();
ITEM_3.ensure_registered();
assert_eq!(
vec![3, 4, 2, 5, 1],
REGISTRY.walk().map(|item| item.value).collect::<Vec<_>>()
);
}
}