1#![doc(html_favicon_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo-icon.png")]
2#![doc(html_logo_url = "https://raw.githubusercontent.com/zng-ui/zng/main/examples/image/res/zng-logo.png")]
3#![doc = include_str!(concat!("../", std::env!("CARGO_PKG_README")))]
8#![warn(unused_extern_crates)]
9#![warn(missing_docs)]
10
11use std::{any::Any, cell::RefCell, fmt, mem, sync::Arc, thread::LocalKey};
12
13mod util;
14pub use util::*;
15
16mod app_local;
17pub use app_local::*;
18
19mod context_local;
20pub use context_local::*;
21
22use parking_lot::*;
23use zng_txt::Txt;
24use zng_unique_id::unique_id_32;
25
26#[doc(hidden)]
27pub use zng_unique_id::{hot_static, hot_static_ref};
28
29unique_id_32! {
30 pub struct AppId;
32}
33zng_unique_id::impl_unique_id_name!(AppId);
34zng_unique_id::impl_unique_id_fmt!(AppId);
35zng_unique_id::impl_unique_id_bytemuck!(AppId);
36
37impl serde::Serialize for AppId {
38 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
39 where
40 S: serde::Serializer,
41 {
42 let name = self.name();
43 if name.is_empty() {
44 use serde::ser::Error;
45 return Err(S::Error::custom("cannot serialize unnamed `AppId`"));
46 }
47 name.serialize(serializer)
48 }
49}
50impl<'de> serde::Deserialize<'de> for AppId {
51 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
52 where
53 D: serde::Deserializer<'de>,
54 {
55 let name = Txt::deserialize(deserializer)?;
56 Ok(AppId::named(name))
57 }
58}
59
60#[derive(Clone, Copy)]
61enum LocalValueKind {
62 Local,
63 Var,
64 App,
65}
66impl LocalValueKind {
67 fn include_local(self) -> bool {
69 !matches!(self, Self::Var)
70 }
71
72 fn include_var(self) -> bool {
74 !matches!(self, Self::Local)
75 }
76}
77
78type LocalValue = (Arc<dyn Any + Send + Sync>, LocalValueKind);
80type LocalData = std::collections::HashMap<AppLocalId, LocalValue, BuildFxHasher>;
82#[derive(Clone, Default)]
83struct BuildFxHasher;
84impl std::hash::BuildHasher for BuildFxHasher {
85 type Hasher = rustc_hash::FxHasher;
86
87 fn build_hasher(&self) -> Self::Hasher {
88 rustc_hash::FxHasher::default()
89 }
90}
91const fn new_local_data() -> LocalData {
92 LocalData::with_hasher(BuildFxHasher)
93}
94
95type LocalSet = std::collections::HashSet<AppLocalId, BuildFxHasher>;
96const fn new_local_set() -> LocalSet {
97 LocalSet::with_hasher(BuildFxHasher)
98}
99
100#[must_use = "ends the app scope on drop"]
104pub struct AppScope {
105 id: AppId,
106 _same_thread: std::rc::Rc<()>,
107}
108impl Drop for AppScope {
109 fn drop(&mut self) {
110 LocalContext::end_app(self.id);
111 }
112}
113
114impl AppId {
115 fn local_id() -> AppLocalId {
116 hot_static! {
117 static ID: u8 = 0;
118 }
119 AppLocalId(hot_static_ref!(ID) as *const u8 as *const () as _)
120 }
121}
122fn cleanup_list_id() -> AppLocalId {
123 hot_static! {
124 static ID: u8 = 0;
125 }
126 AppLocalId(hot_static_ref!(ID) as *const u8 as *const () as _)
127}
128
129#[derive(Clone)]
133pub struct LocalContext {
134 data: LocalData,
135 tracing: Option<tracing::dispatcher::Dispatch>,
136}
137impl fmt::Debug for LocalContext {
138 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
139 let app = self
140 .data
141 .get(&AppId::local_id())
142 .map(|(v, _)| v.downcast_ref::<AppId>().unwrap())
143 .copied();
144
145 f.debug_struct("LocalContext")
146 .field("<app>", &app)
147 .field("<entries>", &(self.data.len() - 1))
148 .finish()
149 }
150}
151impl Default for LocalContext {
152 fn default() -> Self {
153 Self::new()
154 }
155}
156impl LocalContext {
157 pub const fn new() -> Self {
159 Self {
160 data: new_local_data(),
161 tracing: None,
162 }
163 }
164
165 pub fn start_app(id: AppId) -> AppScope {
167 let valid = LOCAL.with_borrow_mut_dyn(|c| match c.entry(AppId::local_id()) {
168 std::collections::hash_map::Entry::Occupied(_) => false,
169 std::collections::hash_map::Entry::Vacant(e) => {
170 e.insert((Arc::new(id), LocalValueKind::App));
171 true
172 }
173 });
174 assert!(valid, "cannot start app, another app is already in the thread context");
175
176 AppScope {
177 id,
178 _same_thread: std::rc::Rc::new(()),
179 }
180 }
181 fn end_app(id: AppId) {
182 let valid = LOCAL.with_borrow_mut_dyn(|c| {
183 if c.get(&AppId::local_id())
184 .map(|(v, _)| v.downcast_ref::<AppId>() == Some(&id))
185 .unwrap_or(false)
186 {
187 Some(mem::take(&mut *c))
188 } else {
189 None
190 }
191 });
192
193 if let Some(data) = valid {
194 let r = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
196 drop(data); }));
198 if let Err(p) = r {
199 tracing::error!("panic on app drop. {}", panic_str(&p));
200 eprintln!("panic on app drop. {}", panic_str(&p));
201 zng_env::exit(i32::from_le_bytes(*b"appa"));
202 }
203 } else {
204 tracing::error!("can only drop app in one of its threads");
205 eprintln!("can only drop app in one of its threads");
206 zng_env::exit(i32::from_le_bytes(*b"appa"));
207 }
208 }
209
210 pub fn current_app() -> Option<AppId> {
212 LOCAL.with_borrow_dyn(|c| c.get(&AppId::local_id()).map(|(v, _)| v.downcast_ref::<AppId>().unwrap()).copied())
213 }
214
215 pub fn register_cleanup(cleanup: impl FnOnce(AppId) + Send + 'static) {
217 let id = Self::current_app().expect("no app in context");
218 Self::register_cleanup_dyn(Box::new(move || cleanup(id)));
219 }
220 fn register_cleanup_dyn(cleanup: Box<dyn FnOnce() + Send>) {
221 let cleanup = RunOnDrop::new(cleanup);
222
223 type CleanupList = Vec<RunOnDrop<Box<dyn FnOnce() + Send>>>;
224 LOCAL.with_borrow_mut_dyn(|c| {
225 let c = c
226 .entry(cleanup_list_id())
227 .or_insert_with(|| (Arc::new(Mutex::new(CleanupList::new())), LocalValueKind::App));
228 c.0.downcast_ref::<Mutex<CleanupList>>().unwrap().lock().push(cleanup);
229 });
230 }
231
232 pub fn capture() -> Self {
239 Self {
240 data: LOCAL.with_borrow_dyn(|c| c.clone()),
241 tracing: Some(tracing::dispatcher::get_default(|d| d.clone())),
242 }
243 }
244
245 pub fn capture_filtered(filter: CaptureFilter) -> Self {
247 match filter {
248 CaptureFilter::None => Self::new(),
249 CaptureFilter::All => Self::capture(),
250 CaptureFilter::ContextVars { exclude } => {
251 let mut data = new_local_data();
252 LOCAL.with_borrow_dyn(|c| {
253 for (k, (v, kind)) in c.iter() {
254 if kind.include_var() && !exclude.0.contains(k) {
255 data.insert(*k, (v.clone(), *kind));
256 }
257 }
258 });
259 Self { data, tracing: None }
260 }
261 CaptureFilter::ContextLocals { exclude } => {
262 let mut data = new_local_data();
263 LOCAL.with_borrow_dyn(|c| {
264 for (k, (v, kind)) in c.iter() {
265 if kind.include_local() && !exclude.0.contains(k) {
266 data.insert(*k, (v.clone(), *kind));
267 }
268 }
269 });
270 Self {
271 data,
272 tracing: Some(tracing::dispatcher::get_default(|d| d.clone())),
273 }
274 }
275 CaptureFilter::Include(set) => {
276 let mut data = new_local_data();
277 LOCAL.with_borrow_dyn(|c| {
278 for (k, v) in c.iter() {
279 if set.0.contains(k) {
280 data.insert(*k, v.clone());
281 }
282 }
283 });
284 Self {
285 data,
286 tracing: if set.contains(&TracingDispatcherContext) {
287 Some(tracing::dispatcher::get_default(|d| d.clone()))
288 } else {
289 None
290 },
291 }
292 }
293 CaptureFilter::Exclude(set) => {
294 let mut data = new_local_data();
295 LOCAL.with_borrow_dyn(|c| {
296 for (k, v) in c.iter() {
297 if !set.0.contains(k) {
298 data.insert(*k, v.clone());
299 }
300 }
301 });
302 Self {
303 data,
304 tracing: if !set.contains(&TracingDispatcherContext) {
305 Some(tracing::dispatcher::get_default(|d| d.clone()))
306 } else {
307 None
308 },
309 }
310 }
311 }
312 }
313
314 pub fn value_set(&self) -> ContextValueSet {
316 let mut set = ContextValueSet::new();
317 LOCAL.with_borrow_dyn(|c| {
318 for k in c.keys() {
319 set.0.insert(*k);
320 }
321 });
322 set
323 }
324
325 pub fn with_context<R>(&mut self, f: impl FnOnce() -> R) -> R {
332 let data = mem::take(&mut self.data);
333 let prev = LOCAL.with_borrow_mut_dyn(|c| mem::replace(c, data));
334 let _tracing_restore = self.tracing.as_ref().map(tracing::dispatcher::set_default);
335 let _restore = RunOnDrop::new(|| {
336 self.data = LOCAL.with_borrow_mut_dyn(|c| mem::replace(c, prev));
337 });
338 f()
339 }
340
341 pub fn with_context_blend<R>(&mut self, over: bool, f: impl FnOnce() -> R) -> R {
351 if self.data.is_empty() {
352 f()
353 } else {
354 let prev = LOCAL.with_borrow_mut_dyn(|c| {
355 let (mut base, over) = if over { (c.clone(), &self.data) } else { (self.data.clone(), &*c) };
356 for (k, v) in over {
357 base.insert(*k, v.clone());
358 }
359
360 mem::replace(c, base)
361 });
362
363 let mut _tracing_restore = None;
364 if let Some(d) = &self.tracing {
365 if over {
366 _tracing_restore = Some(tracing::dispatcher::set_default(d));
367 }
368 }
369
370 let _restore = RunOnDrop::new(|| {
371 LOCAL.with_borrow_mut_dyn(|c| {
372 *c = prev;
373 });
374 });
375
376 f()
377 }
378 }
379
380 pub fn extend(&mut self, ctx: Self) {
382 self.data.extend(ctx.data);
383 }
384
385 fn contains(key: AppLocalId) -> bool {
386 LOCAL.with_borrow_dyn(|c| c.contains_key(&key))
387 }
388
389 fn get(key: AppLocalId) -> Option<LocalValue> {
390 LOCAL.with_borrow_dyn(|c| c.get(&key).cloned())
391 }
392
393 fn set(key: AppLocalId, value: LocalValue) -> Option<LocalValue> {
394 LOCAL.with_borrow_mut_dyn(|c| c.insert(key, value))
395 }
396 fn remove(key: AppLocalId) -> Option<LocalValue> {
397 LOCAL.with_borrow_mut_dyn(|c| c.remove(&key))
398 }
399
400 fn with_value_ctx<T: Send + Sync + 'static>(
401 key: &'static ContextLocal<T>,
402 kind: LocalValueKind,
403 value: &mut Option<Arc<T>>,
404 f: impl FnOnce(),
405 ) {
406 let key = key.id();
407 let prev = Self::set(key, (value.take().expect("no `value` to set"), kind));
408 let _restore = RunOnDrop::new(move || {
409 let back = if let Some(prev) = prev {
410 Self::set(key, prev)
411 } else {
412 Self::remove(key)
413 }
414 .unwrap();
415 *value = Some(Arc::downcast(back.0).unwrap());
416 });
417
418 f();
419 }
420
421 fn with_default_ctx<T: Send + Sync + 'static>(key: &'static ContextLocal<T>, f: impl FnOnce()) {
422 let key = key.id();
423 let prev = Self::remove(key);
424 let _restore = RunOnDrop::new(move || {
425 if let Some(prev) = prev {
426 Self::set(key, prev);
427 }
428 });
429
430 f()
431 }
432}
433thread_local! {
434 static LOCAL: RefCell<LocalData> = const { RefCell::new(new_local_data()) };
435}
436
437trait LocalKeyDyn {
438 fn with_borrow_dyn<R>(&'static self, f: impl FnOnce(&LocalData) -> R) -> R;
439 fn with_borrow_mut_dyn<R>(&'static self, f: impl FnOnce(&mut LocalData) -> R) -> R;
440}
441impl LocalKeyDyn for LocalKey<RefCell<LocalData>> {
442 fn with_borrow_dyn<R>(&'static self, f: impl FnOnce(&LocalData) -> R) -> R {
443 let mut r = None;
444 let f = |l: &LocalData| r = Some(f(l));
445
446 #[cfg(feature = "dyn_closure")]
447 let f: Box<dyn FnOnce(&LocalData)> = Box::new(f);
448
449 self.with_borrow(f);
450
451 r.unwrap()
452 }
453
454 fn with_borrow_mut_dyn<R>(&'static self, f: impl FnOnce(&mut LocalData) -> R) -> R {
455 let mut r = None;
456 let f = |l: &mut LocalData| r = Some(f(l));
457
458 #[cfg(feature = "dyn_closure")]
459 let f: Box<dyn FnOnce(&mut LocalData)> = Box::new(f);
460
461 self.with_borrow_mut(f);
462
463 r.unwrap()
464 }
465}
466
467#[derive(Debug, Clone, PartialEq, Eq)]
469pub enum CaptureFilter {
470 None,
472
473 All,
475 ContextVars {
477 exclude: ContextValueSet,
479 },
480 ContextLocals {
482 exclude: ContextValueSet,
484 },
485
486 Include(ContextValueSet),
488
489 Exclude(ContextValueSet),
491}
492impl CaptureFilter {
493 pub const fn context_vars() -> Self {
495 Self::ContextVars {
496 exclude: ContextValueSet::new(),
497 }
498 }
499
500 pub const fn context_locals() -> Self {
502 Self::ContextLocals {
503 exclude: ContextValueSet::new(),
504 }
505 }
506
507 pub fn app_only() -> Self {
509 let mut set = ContextValueSet::new();
510 set.insert_app();
511 Self::Include(set)
512 }
513}
514
515pub trait ContextLocalKeyProvider {
519 fn context_local_key(&'static self) -> AppLocalId;
521}
522impl<T: Send + Sync + 'static> ContextLocalKeyProvider for ContextLocal<T> {
523 fn context_local_key(&'static self) -> AppLocalId {
524 self.id()
525 }
526}
527
528#[allow(clippy::exhaustive_structs)]
532pub struct TracingDispatcherContext;
533
534impl ContextLocalKeyProvider for TracingDispatcherContext {
535 fn context_local_key(&'static self) -> AppLocalId {
536 static ID: bool = true;
537 AppLocalId(&ID as *const bool as *const () as usize)
538 }
539}
540
541#[derive(Default, Clone, PartialEq, Eq)]
543pub struct ContextValueSet(LocalSet);
544impl ContextValueSet {
545 pub const fn new() -> Self {
547 Self(new_local_set())
548 }
549
550 pub fn insert(&mut self, value: &'static impl ContextLocalKeyProvider) -> bool {
552 self.0.insert(value.context_local_key())
553 }
554
555 pub fn remove(&mut self, value: &'static impl ContextLocalKeyProvider) -> bool {
557 self.0.remove(&value.context_local_key())
558 }
559
560 pub fn contains(&self, value: &'static impl ContextLocalKeyProvider) -> bool {
562 self.0.contains(&value.context_local_key())
563 }
564
565 pub fn len(&self) -> usize {
567 self.0.len()
568 }
569
570 pub fn is_empty(&self) -> bool {
572 self.0.is_empty()
573 }
574
575 pub fn insert_all(&mut self, other: &Self) {
577 self.0.extend(other.0.iter().copied());
578 }
579
580 pub fn remove_all(&mut self, other: &Self) {
582 for o in other.0.iter() {
583 self.0.remove(o);
584 }
585 }
586
587 pub fn insert_app(&mut self) -> bool {
589 let inserted_app = self.0.insert(AppId::local_id());
590 static TRACING: TracingDispatcherContext = TracingDispatcherContext;
591 self.insert(&TRACING) || inserted_app
592 }
593}
594impl fmt::Debug for ContextValueSet {
595 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
596 f.debug_struct("ContextValueSet").field("len()", &self.len()).finish()
597 }
598}