use arc_swap::ArcSwap;
use dashmap::DashMap;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
type CallbackId = u64;
type CallbackStorage<T> = Arc<DashMap<CallbackId, Arc<dyn Fn(&T) + Send + Sync>>>;
type CallbackSnapshot<T> = Arc<dyn Fn(&T) + Send + Sync>;
pub struct DynamicField<T: Clone + Send + Sync + 'static> {
value: ArcSwap<T>,
callbacks: CallbackStorage<T>,
next_id: AtomicU64,
}
impl<T: Clone + Send + Sync + 'static> DynamicField<T> {
#[inline]
pub fn new(initial: T) -> Self {
Self {
value: ArcSwap::from_pointee(initial),
callbacks: Arc::new(DashMap::new()),
next_id: AtomicU64::new(0),
}
}
#[inline]
pub fn get(&self) -> T {
use std::ops::Deref;
let guard = self.value.load();
let arc: &Arc<T> = guard.deref();
let t: &T = arc.deref();
t.clone()
}
#[inline]
pub fn get_ref(&self) -> Arc<T> {
Arc::clone(&*self.value.load())
}
pub fn on_change(&self, f: impl Fn(&T) + Send + Sync + 'static) -> CallbackGuard {
let id = self.next_id.fetch_add(1, Ordering::Relaxed);
self.callbacks.insert(id, Arc::new(f));
let callbacks = Arc::clone(&self.callbacks);
CallbackGuard::new(Box::new(move || {
callbacks.remove(&id);
}))
}
pub fn update(&self, new_val: T) {
self.value.store(Arc::new(new_val.clone()));
let snapshots: Vec<CallbackSnapshot<T>> = self
.callbacks
.iter()
.map(|e| Arc::clone(e.value()))
.collect();
for callback in &snapshots {
callback(&new_val);
}
}
pub fn callback_count(&self) -> usize {
self.callbacks.len()
}
pub fn builder() -> DynamicFieldBuilder<T> {
DynamicFieldBuilder { initial: None }
}
#[deprecated(since = "0.3.0", note = "Use `on_change` instead")]
pub fn subscribe(&self, f: impl Fn(&T) + Send + Sync + 'static) -> CallbackGuard {
self.on_change(f)
}
}
impl<T: Clone + Send + Sync + 'static> Default for DynamicField<T>
where
T: Default,
{
fn default() -> Self {
Self::new(T::default())
}
}
pub struct CallbackGuard {
remover: Option<Box<dyn FnOnce() + Send>>,
}
impl Drop for CallbackGuard {
fn drop(&mut self) {
if let Some(remover) = self.remover.take() {
remover();
}
}
}
impl CallbackGuard {
pub(crate) fn new(remover: Box<dyn FnOnce() + Send>) -> Self {
Self {
remover: Some(remover),
}
}
}
pub struct DynamicFieldBuilder<T: Clone + Send + Sync + 'static> {
initial: Option<T>,
}
impl<T: Clone + Send + Sync + 'static> DynamicFieldBuilder<T> {
pub fn initial(mut self, initial: T) -> Self {
self.initial = Some(initial);
self
}
pub fn build(self) -> DynamicField<T> {
DynamicField::new(
self.initial
.expect("DynamicFieldBuilder: call initial() before build()"),
)
}
}
impl<T: Clone + Send + Sync + 'static> Default for DynamicFieldBuilder<T> {
fn default() -> Self {
Self { initial: None }
}
}
#[cfg(feature = "watch")]
mod watcher {
use super::*;
use crate::interface::ConfigProvider;
use crate::types::ConfigValue;
use std::collections::HashMap;
use tokio::sync::watch;
pub struct FieldWatcher<T: ConfigProvider> {
rx: watch::Receiver<Arc<T>>,
fields: Vec<Arc<str>>,
last: HashMap<Arc<str>, ConfigValue>,
}
impl<T: ConfigProvider + Clone + 'static> FieldWatcher<T> {
pub fn new(rx: watch::Receiver<Arc<T>>, fields: Vec<Arc<str>>) -> Self {
Self {
rx,
fields,
last: HashMap::new(),
}
}
pub async fn changed_for(&mut self) -> (Arc<T>, Vec<Arc<str>>) {
loop {
self.rx.changed().await.expect("watch channel closed");
let cfg = self.rx.borrow().clone();
let changed: Vec<_> = self
.fields
.iter()
.filter(|f| {
let new_val = cfg.get_raw(f).map(|v| v.inner.clone());
new_val.as_ref() != self.last.get(*f)
})
.cloned()
.collect();
if !changed.is_empty() {
for f in &changed {
if let Some(v) = cfg.get_raw(f) {
self.last.insert(f.clone(), v.inner.clone());
}
}
return (cfg, changed);
}
}
}
pub fn watched_fields(&self) -> &[Arc<str>] {
&self.fields
}
}
}
#[cfg(feature = "watch")]
pub use watcher::FieldWatcher;
#[cfg(test)]
mod tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn test_dynamic_field_get_returns_initial() {
let field = DynamicField::new(42u32);
assert_eq!(field.get(), 42);
}
#[test]
fn test_dynamic_field_on_change_triggers() {
let field = DynamicField::new(10u32);
let called = Arc::new(AtomicUsize::new(0));
let called_clone = called.clone();
let _guard = field.on_change(move |&val| {
called_clone.fetch_add(1, Ordering::SeqCst);
assert_eq!(val, 20);
});
field.update(20);
assert_eq!(called.load(Ordering::SeqCst), 1);
}
#[test]
fn test_callback_guard_drops_on_scope_exit() {
let field = DynamicField::new(100u32);
{
let _guard = field.on_change(|_val| {});
assert_eq!(field.callback_count(), 1);
}
assert_eq!(field.callback_count(), 0);
}
#[test]
fn test_dynamic_field_builder() {
let field = DynamicField::builder().initial(999i64).build();
assert_eq!(field.get(), 999);
}
#[test]
fn test_dynamic_field_default() {
let field: DynamicField<u64> = DynamicField::default();
assert_eq!(field.get(), 0);
}
#[test]
fn test_dynamic_field_update_notifies_all() {
let field = DynamicField::new(0u32);
let count1 = Arc::new(AtomicUsize::new(0));
let count2 = Arc::new(AtomicUsize::new(0));
let c1 = count1.clone();
let c2 = count2.clone();
let _guard1 = field.on_change(move |&val| {
c1.fetch_add(val as usize, Ordering::SeqCst);
});
let _guard2 = field.on_change(move |&val| {
c2.fetch_add(val as usize, Ordering::SeqCst);
});
field.update(5);
assert_eq!(count1.load(Ordering::SeqCst), 5);
assert_eq!(count2.load(Ordering::SeqCst), 5);
}
#[test]
fn test_dynamic_field_get_ref() {
let field = DynamicField::new(vec![1, 2, 3]);
let arc = field.get_ref();
assert_eq!(&*arc, &[1, 2, 3]);
}
#[test]
fn test_callback_guard_deregisters_on_drop() {
let field = DynamicField::new(0u32);
{
let _guard = field.on_change(|_val| {});
assert_eq!(field.callback_count(), 1);
}
assert_eq!(field.callback_count(), 0);
}
#[test]
fn test_update_callback_that_registers_callback_does_not_deadlock() {
let field = DynamicField::new(0u32);
let inner = Arc::new(field);
let inner_clone = Arc::clone(&inner);
let _guard1 = inner.on_change(move |&val| {
if val == 1 {
let _guard2 = inner_clone.on_change(|_| {});
}
});
inner.update(1);
assert_eq!(
inner.callback_count(),
1,
"second callback should have been deregistered when its guard dropped"
);
}
#[test]
fn test_update_callback_that_calls_update_does_not_deadlock() {
let field = DynamicField::new(0u32);
let inner = Arc::new(field);
let call_count = Arc::new(AtomicUsize::new(0));
let inner_clone = Arc::clone(&inner);
let count_clone = Arc::clone(&call_count);
let _guard = inner.on_change(move |&val| {
count_clone.fetch_add(1, Ordering::SeqCst);
if val == 1 {
inner_clone.update(2);
}
});
inner.update(1);
assert_eq!(call_count.load(Ordering::SeqCst), 2);
}
}