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