1use std::cell::{Cell, UnsafeCell};
2use std::marker::PhantomData;
3use std::sync::{Arc, LazyLock, MutexGuard, PoisonError, RwLock};
4use std::thread;
5
6use crate::Scope;
7use crate::{scope::Stack, Client, Hub};
8
9static PROCESS_HUB: LazyLock<(Arc<Hub>, thread::ThreadId)> = LazyLock::new(|| {
10 (
11 Arc::new(Hub::new(None, Arc::new(Default::default()))),
12 thread::current().id(),
13 )
14});
15
16thread_local! {
17 static THREAD_HUB: (UnsafeCell<Arc<Hub>>, Cell<bool>) = (
18 UnsafeCell::new(Arc::new(Hub::new_from_top(&PROCESS_HUB.0))),
19 Cell::new(PROCESS_HUB.1 == thread::current().id())
20 );
21}
22
23pub struct SwitchGuard {
28 inner: Option<(Arc<Hub>, bool)>,
29 _not_send: PhantomData<MutexGuard<'static, ()>>,
45}
46
47impl SwitchGuard {
48 pub fn new(mut hub: Arc<Hub>) -> Self {
52 let inner = THREAD_HUB.with(|(thread_hub, is_process_hub)| {
53 let thread_hub = unsafe { &mut *thread_hub.get() };
56 if std::ptr::eq(thread_hub.as_ref(), hub.as_ref()) {
57 return None;
58 }
59 std::mem::swap(thread_hub, &mut hub);
60 let was_process_hub = is_process_hub.replace(false);
61 Some((hub, was_process_hub))
62 });
63 SwitchGuard {
64 inner,
65 _not_send: PhantomData,
66 }
67 }
68
69 fn swap(&mut self) -> Option<Arc<Hub>> {
70 if let Some((mut hub, was_process_hub)) = self.inner.take() {
71 Some(THREAD_HUB.with(|(thread_hub, is_process_hub)| {
72 let thread_hub = unsafe { &mut *thread_hub.get() };
73 std::mem::swap(thread_hub, &mut hub);
74 if was_process_hub {
75 is_process_hub.set(true);
76 }
77 hub
78 }))
79 } else {
80 None
81 }
82 }
83}
84
85impl Drop for SwitchGuard {
86 fn drop(&mut self) {
87 let _ = self.swap();
88 }
89}
90
91#[derive(Debug)]
92pub(crate) struct HubImpl {
93 pub(crate) stack: Arc<RwLock<Stack>>,
94}
95
96impl HubImpl {
97 pub(crate) fn with<F: FnOnce(&Stack) -> R, R>(&self, f: F) -> R {
98 let guard = self.stack.read().unwrap_or_else(PoisonError::into_inner);
99 f(&guard)
100 }
101
102 pub(crate) fn with_mut<F: FnOnce(&mut Stack) -> R, R>(&self, f: F) -> R {
103 let mut guard = self.stack.write().unwrap_or_else(PoisonError::into_inner);
104 f(&mut guard)
105 }
106
107 pub(crate) fn is_active_and_usage_safe(&self) -> bool {
108 let guard = match self.stack.read() {
109 Err(err) => err.into_inner(),
110 Ok(guard) => guard,
111 };
112
113 guard.top().client.as_ref().is_some_and(|c| c.is_enabled())
114 }
115}
116
117impl Hub {
118 pub fn new(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Hub {
120 Hub {
121 inner: HubImpl {
122 stack: Arc::new(RwLock::new(Stack::from_client_and_scope(client, scope))),
123 },
124 last_event_id: RwLock::new(None),
125 }
126 }
127
128 pub fn new_from_top<H: AsRef<Hub>>(other: H) -> Hub {
130 let hub = other.as_ref();
131 hub.inner.with(|stack| {
132 let top = stack.top();
133 Hub::new(top.client.clone(), top.scope.clone())
134 })
135 }
136
137 pub fn current() -> Arc<Hub> {
151 Hub::with(Arc::clone)
152 }
153
154 pub fn main() -> Arc<Hub> {
159 PROCESS_HUB.0.clone()
160 }
161
162 pub fn with<F, R>(f: F) -> R
167 where
168 F: FnOnce(&Arc<Hub>) -> R,
169 {
170 THREAD_HUB.with(|(hub, is_process_hub)| {
171 if is_process_hub.get() {
172 f(&PROCESS_HUB.0)
173 } else {
174 f(unsafe { &*hub.get() })
175 }
176 })
177 }
178
179 pub fn run<F: FnOnce() -> R, R>(hub: Arc<Hub>, f: F) -> R {
188 let _guard = SwitchGuard::new(hub);
189 f()
190 }
191
192 pub fn client(&self) -> Option<Arc<Client>> {
194 self.inner.with(|stack| stack.top().client.clone())
195 }
196
197 pub fn bind_client(&self, client: Option<Arc<Client>>) {
199 self.inner.with_mut(|stack| {
200 stack.top_mut().client = client;
201 })
202 }
203
204 pub(crate) fn is_active_and_usage_safe(&self) -> bool {
205 self.inner.is_active_and_usage_safe()
206 }
207
208 pub(crate) fn with_current_scope<F: FnOnce(&Scope) -> R, R>(&self, f: F) -> R {
209 self.inner.with(|stack| f(&stack.top().scope))
210 }
211
212 pub(crate) fn with_current_scope_mut<F: FnOnce(&mut Scope) -> R, R>(&self, f: F) -> R {
213 self.inner
214 .with_mut(|stack| f(Arc::make_mut(&mut stack.top_mut().scope)))
215 }
216}