tuwunel_core/config/
manager.rs1use std::{
2 cell::{Cell, RefCell},
3 ops::Deref,
4 ptr,
5 ptr::null_mut,
6 sync::{
7 Arc,
8 atomic::{AtomicPtr, Ordering},
9 },
10};
11
12use super::Config;
13use crate::{Result, implement};
14
15pub struct Manager {
20 active: AtomicPtr<Config>,
21}
22
23thread_local! {
24 static INDEX: Cell<usize> = const { Cell::new(0_usize) };
25 static HANDLE: RefCell<Handles> = const {
26 RefCell::new([const { None }; HISTORY])
27 };
28}
29
30type Handle = Option<Arc<Config>>;
31type Handles = [Handle; HISTORY];
32
33const HISTORY: usize = 8;
34
35impl Manager {
36 pub(crate) fn new(config: Config) -> Self {
37 let config = Arc::new(config);
38 Self {
39 active: AtomicPtr::new(Arc::into_raw(config).cast_mut()),
40 }
41 }
42}
43
44impl Drop for Manager {
45 fn drop(&mut self) {
46 let config = self.active.swap(null_mut(), Ordering::AcqRel);
47
48 unsafe { Arc::from_raw(config) };
51 }
52}
53
54impl Deref for Manager {
55 type Target = Arc<Config>;
56
57 fn deref(&self) -> &Self::Target { HANDLE.with_borrow_mut(|handle| self.load(handle)) }
58}
59
60#[implement(Manager)]
62#[tracing::instrument(skip_all)]
63pub fn update(&self, config: Config) -> Result<Arc<Config>> {
64 let config = Arc::new(config);
65 let new = Arc::into_raw(config);
66 let old = self.active.swap(new.cast_mut(), Ordering::AcqRel);
67
68 Ok(unsafe { Arc::from_raw(old) })
71}
72
73#[implement(Manager)]
74fn load(&self, handle: &mut [Option<Arc<Config>>]) -> &'static Arc<Config> {
75 let config = self.active.load(Ordering::Acquire);
76
77 if handle[INDEX.get()]
79 .as_ref()
80 .is_none_or(|handle| !ptr::eq(config, Arc::as_ptr(handle)))
81 {
82 INDEX.set(INDEX.get().wrapping_add(1).wrapping_rem(HISTORY));
83 return load_miss(handle, INDEX.get(), config);
84 }
85
86 let config: &Arc<Config> = handle[INDEX.get()]
87 .as_ref()
88 .expect("handle was already cached for this thread");
89
90 unsafe { std::mem::transmute(config) }
102}
103
104#[tracing::instrument(
105 name = "miss",
106 level = "trace",
107 skip_all,
108 fields(%index, ?config)
109)]
110#[expect(clippy::transmute_ptr_to_ptr)]
111fn load_miss(
112 handle: &mut [Option<Arc<Config>>],
113 index: usize,
114 config: *const Config,
115) -> &'static Arc<Config> {
116 let config = unsafe {
120 Arc::increment_strong_count(config);
121 Arc::from_raw(config)
122 };
123
124 unsafe { std::mem::transmute(handle[index].insert(config)) }
128}