1use std::cell::{Cell, UnsafeCell};
2use std::sync::{Arc, LazyLock, PoisonError, RwLock};
3use std::thread;
4
5use crate::Scope;
6use crate::{scope::Stack, Client, Hub};
7
8static PROCESS_HUB: LazyLock<(Arc<Hub>, thread::ThreadId)> = LazyLock::new(|| {
9 (
10 Arc::new(Hub::new(None, Arc::new(Default::default()))),
11 thread::current().id(),
12 )
13});
14
15thread_local! {
16 static THREAD_HUB: (UnsafeCell<Arc<Hub>>, Cell<bool>) = (
17 UnsafeCell::new(Arc::new(Hub::new_from_top(&PROCESS_HUB.0))),
18 Cell::new(PROCESS_HUB.1 == thread::current().id())
19 );
20}
21
22pub struct SwitchGuard {
25 inner: Option<(Arc<Hub>, bool)>,
26}
27
28impl SwitchGuard {
29 pub fn new(mut hub: Arc<Hub>) -> Self {
33 let inner = THREAD_HUB.with(|(thread_hub, is_process_hub)| {
34 let thread_hub = unsafe { &mut *thread_hub.get() };
37 if std::ptr::eq(thread_hub.as_ref(), hub.as_ref()) {
38 return None;
39 }
40 std::mem::swap(thread_hub, &mut hub);
41 let was_process_hub = is_process_hub.replace(false);
42 Some((hub, was_process_hub))
43 });
44 SwitchGuard { inner }
45 }
46
47 fn swap(&mut self) -> Option<Arc<Hub>> {
48 if let Some((mut hub, was_process_hub)) = self.inner.take() {
49 Some(THREAD_HUB.with(|(thread_hub, is_process_hub)| {
50 let thread_hub = unsafe { &mut *thread_hub.get() };
51 std::mem::swap(thread_hub, &mut hub);
52 if was_process_hub {
53 is_process_hub.set(true);
54 }
55 hub
56 }))
57 } else {
58 None
59 }
60 }
61}
62
63impl Drop for SwitchGuard {
64 fn drop(&mut self) {
65 let _ = self.swap();
66 }
67}
68
69#[derive(Debug)]
70pub(crate) struct HubImpl {
71 pub(crate) stack: Arc<RwLock<Stack>>,
72}
73
74impl HubImpl {
75 pub(crate) fn with<F: FnOnce(&Stack) -> R, R>(&self, f: F) -> R {
76 let guard = self.stack.read().unwrap_or_else(PoisonError::into_inner);
77 f(&guard)
78 }
79
80 pub(crate) fn with_mut<F: FnOnce(&mut Stack) -> R, R>(&self, f: F) -> R {
81 let mut guard = self.stack.write().unwrap_or_else(PoisonError::into_inner);
82 f(&mut guard)
83 }
84
85 pub(crate) fn is_active_and_usage_safe(&self) -> bool {
86 let guard = match self.stack.read() {
87 Err(err) => err.into_inner(),
88 Ok(guard) => guard,
89 };
90
91 guard.top().client.as_ref().is_some_and(|c| c.is_enabled())
92 }
93}
94
95impl Hub {
96 pub fn new(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Hub {
98 Hub {
99 inner: HubImpl {
100 stack: Arc::new(RwLock::new(Stack::from_client_and_scope(client, scope))),
101 },
102 last_event_id: RwLock::new(None),
103 }
104 }
105
106 pub fn new_from_top<H: AsRef<Hub>>(other: H) -> Hub {
108 let hub = other.as_ref();
109 hub.inner.with(|stack| {
110 let top = stack.top();
111 Hub::new(top.client.clone(), top.scope.clone())
112 })
113 }
114
115 pub fn current() -> Arc<Hub> {
129 Hub::with(Arc::clone)
130 }
131
132 pub fn main() -> Arc<Hub> {
137 PROCESS_HUB.0.clone()
138 }
139
140 pub fn with<F, R>(f: F) -> R
145 where
146 F: FnOnce(&Arc<Hub>) -> R,
147 {
148 THREAD_HUB.with(|(hub, is_process_hub)| {
149 if is_process_hub.get() {
150 f(&PROCESS_HUB.0)
151 } else {
152 f(unsafe { &*hub.get() })
153 }
154 })
155 }
156
157 pub fn run<F: FnOnce() -> R, R>(hub: Arc<Hub>, f: F) -> R {
166 let _guard = SwitchGuard::new(hub);
167 f()
168 }
169
170 pub fn client(&self) -> Option<Arc<Client>> {
172 self.inner.with(|stack| stack.top().client.clone())
173 }
174
175 pub fn bind_client(&self, client: Option<Arc<Client>>) {
177 self.inner.with_mut(|stack| {
178 stack.top_mut().client = client;
179 })
180 }
181
182 pub(crate) fn is_active_and_usage_safe(&self) -> bool {
183 self.inner.is_active_and_usage_safe()
184 }
185
186 pub(crate) fn with_current_scope<F: FnOnce(&Scope) -> R, R>(&self, f: F) -> R {
187 self.inner.with(|stack| f(&stack.top().scope))
188 }
189
190 pub(crate) fn with_current_scope_mut<F: FnOnce(&mut Scope) -> R, R>(&self, f: F) -> R {
191 self.inner
192 .with_mut(|stack| f(Arc::make_mut(&mut stack.top_mut().scope)))
193 }
194}