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