1use std::cell::{Cell, UnsafeCell};
2use std::sync::{Arc, PoisonError, RwLock};
3use std::thread;
45use crate::Scope;
6use crate::{scope::Stack, Client, Hub};
78use once_cell::sync::Lazy;
910static 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});
1617thread_local! {
18static 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}
2324/// A Hub switch guard used to temporarily swap
25/// active hub in thread local storage.
26pub struct SwitchGuard {
27 inner: Option<(Arc<Hub>, bool)>,
28}
2930impl SwitchGuard {
31/// Swaps the current thread's Hub by the one provided
32 /// and returns a guard that, when dropped, replaces it
33 /// to the previous one.
34pub fn new(mut hub: Arc<Hub>) -> Self {
35let inner = THREAD_HUB.with(|(thread_hub, is_process_hub)| {
36// SAFETY: `thread_hub` will always be a valid thread local hub,
37 // by definition not shared between threads.
38let thread_hub = unsafe { &mut *thread_hub.get() };
39if std::ptr::eq(thread_hub.as_ref(), hub.as_ref()) {
40return None;
41 }
42 std::mem::swap(thread_hub, &mut hub);
43let was_process_hub = is_process_hub.replace(false);
44Some((hub, was_process_hub))
45 });
46 SwitchGuard { inner }
47 }
4849fn swap(&mut self) -> Option<Arc<Hub>> {
50if let Some((mut hub, was_process_hub)) = self.inner.take() {
51Some(THREAD_HUB.with(|(thread_hub, is_process_hub)| {
52let thread_hub = unsafe { &mut *thread_hub.get() };
53 std::mem::swap(thread_hub, &mut hub);
54if was_process_hub {
55 is_process_hub.set(true);
56 }
57 hub
58 }))
59 } else {
60None
61}
62 }
63}
6465impl Drop for SwitchGuard {
66fn drop(&mut self) {
67let _ = self.swap();
68 }
69}
7071#[derive(Debug)]
72pub(crate) struct HubImpl {
73pub(crate) stack: Arc<RwLock<Stack>>,
74}
7576impl HubImpl {
77pub(crate) fn with<F: FnOnce(&Stack) -> R, R>(&self, f: F) -> R {
78let guard = self.stack.read().unwrap_or_else(PoisonError::into_inner);
79 f(&guard)
80 }
8182pub(crate) fn with_mut<F: FnOnce(&mut Stack) -> R, R>(&self, f: F) -> R {
83let mut guard = self.stack.write().unwrap_or_else(PoisonError::into_inner);
84 f(&mut guard)
85 }
8687pub(crate) fn is_active_and_usage_safe(&self) -> bool {
88let guard = match self.stack.read() {
89Err(err) => err.into_inner(),
90Ok(guard) => guard,
91 };
9293 guard.top().client.as_ref().is_some_and(|c| c.is_enabled())
94 }
95}
9697impl Hub {
98/// Creates a new hub from the given client and scope.
99pub fn new(client: Option<Arc<Client>>, scope: Arc<Scope>) -> Hub {
100 Hub {
101 inner: HubImpl {
102 stack: Arc::new(RwLock::new(Stack::from_client_and_scope(client, scope))),
103 },
104 last_event_id: RwLock::new(None),
105 }
106 }
107108/// Creates a new hub based on the top scope of the given hub.
109pub fn new_from_top<H: AsRef<Hub>>(other: H) -> Hub {
110let hub = other.as_ref();
111 hub.inner.with(|stack| {
112let top = stack.top();
113 Hub::new(top.client.clone(), top.scope.clone())
114 })
115 }
116117/// Returns the current, thread-local hub.
118 ///
119 /// Invoking this will return the current thread-local hub. The first
120 /// time it is called on a thread, a new thread-local hub will be
121 /// created based on the topmost scope of the hub on the main thread as
122 /// returned by [`Hub::main`]. If the main thread did not yet have a
123 /// hub it will be created when invoking this function.
124 ///
125 /// To have control over which hub is installed as the current
126 /// thread-local hub, use [`Hub::run`].
127 ///
128 /// This method is unavailable if the client implementation is disabled.
129 /// When using the minimal API set use [`Hub::with_active`] instead.
130pub fn current() -> Arc<Hub> {
131 Hub::with(Arc::clone)
132 }
133134/// Returns the main thread's hub.
135 ///
136 /// This is similar to [`Hub::current`] but instead of picking the
137 /// current thread's hub it returns the main thread's hub instead.
138pub fn main() -> Arc<Hub> {
139 PROCESS_HUB.0.clone()
140 }
141142/// Invokes the callback with the default hub.
143 ///
144 /// This is a slightly more efficient version than [`Hub::current`] and
145 /// also unavailable in minimal mode.
146pub fn with<F, R>(f: F) -> R
147where
148F: FnOnce(&Arc<Hub>) -> R,
149 {
150 THREAD_HUB.with(|(hub, is_process_hub)| {
151if is_process_hub.get() {
152 f(&PROCESS_HUB.0)
153 } else {
154 f(unsafe { &*hub.get() })
155 }
156 })
157 }
158159/// Binds a hub to the current thread for the duration of the call.
160 ///
161 /// During the execution of `f` the given hub will be installed as the
162 /// thread-local hub. So any call to [`Hub::current`] during this time
163 /// will return the provided hub.
164 ///
165 /// Once the function is finished executing, including after it
166 /// panicked, the original hub is re-installed if one was present.
167pub fn run<F: FnOnce() -> R, R>(hub: Arc<Hub>, f: F) -> R {
168let _guard = SwitchGuard::new(hub);
169 f()
170 }
171172/// Returns the currently bound client.
173pub fn client(&self) -> Option<Arc<Client>> {
174self.inner.with(|stack| stack.top().client.clone())
175 }
176177/// Binds a new client to the hub.
178pub fn bind_client(&self, client: Option<Arc<Client>>) {
179self.inner.with_mut(|stack| {
180 stack.top_mut().client = client;
181 })
182 }
183184pub(crate) fn is_active_and_usage_safe(&self) -> bool {
185self.inner.is_active_and_usage_safe()
186 }
187188pub(crate) fn with_current_scope<F: FnOnce(&Scope) -> R, R>(&self, f: F) -> R {
189self.inner.with(|stack| f(&stack.top().scope))
190 }
191192pub(crate) fn with_current_scope_mut<F: FnOnce(&mut Scope) -> R, R>(&self, f: F) -> R {
193self.inner
194 .with_mut(|stack| f(Arc::make_mut(&mut stack.top_mut().scope)))
195 }
196}