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 #[inline(always)]
332 pub fn with_context<R>(&mut self, f: impl FnOnce() -> R) -> R {
333 struct Restore<'a> {
334 prev_data: LocalData,
335 _tracing_restore: Option<tracing::dispatcher::DefaultGuard>,
336 ctx: &'a mut LocalContext,
337 }
338 impl<'a> Restore<'a> {
339 fn new(ctx: &'a mut LocalContext) -> Self {
340 let data = mem::take(&mut ctx.data);
341 Self {
342 prev_data: LOCAL.with_borrow_mut_dyn(|c| mem::replace(c, data)),
343 _tracing_restore: ctx.tracing.as_ref().map(tracing::dispatcher::set_default),
344 ctx,
345 }
346 }
347 }
348 impl<'a> Drop for Restore<'a> {
349 fn drop(&mut self) {
350 self.ctx.data = LOCAL.with_borrow_mut_dyn(|c| mem::replace(c, mem::take(&mut self.prev_data)));
351 }
352 }
353 let _restore = Restore::new(self);
354
355 f()
356 }
357
358 #[inline(always)]
368 pub fn with_context_blend<R>(&mut self, over: bool, f: impl FnOnce() -> R) -> R {
369 if self.data.is_empty() {
370 f()
371 } else {
372 struct Restore {
373 prev_data: LocalData,
374 _tracing_restore: Option<tracing::dispatcher::DefaultGuard>,
375 }
376 impl Restore {
377 fn new(ctx: &mut LocalContext, over: bool) -> Self {
378 let prev_data = LOCAL.with_borrow_mut_dyn(|c| {
379 let mut new_data = c.clone();
380 if over {
381 for (k, v) in &ctx.data {
382 new_data.insert(*k, v.clone());
383 }
384 } else {
385 for (k, v) in &ctx.data {
386 new_data.entry(*k).or_insert_with(|| v.clone());
387 }
388 }
389
390 mem::replace(c, new_data)
391 });
392
393 let mut _tracing_restore = None;
394 if let Some(d) = &ctx.tracing
395 && over
396 {
397 _tracing_restore = Some(tracing::dispatcher::set_default(d));
398 }
399
400 Self {
401 prev_data,
402 _tracing_restore,
403 }
404 }
405 }
406 impl Drop for Restore {
407 fn drop(&mut self) {
408 LOCAL.with_borrow_mut_dyn(|c| {
409 *c = mem::take(&mut self.prev_data);
410 });
411 }
412 }
413 let _restore = Restore::new(self, over);
414
415 f()
416 }
417 }
418
419 pub fn extend(&mut self, ctx: Self) {
421 self.data.extend(ctx.data);
422 }
423
424 fn contains(key: AppLocalId) -> bool {
425 LOCAL.with_borrow_dyn(|c| c.contains_key(&key))
426 }
427
428 fn get(key: AppLocalId) -> Option<LocalValue> {
429 LOCAL.with_borrow_dyn(|c| c.get(&key).cloned())
430 }
431
432 fn set(key: AppLocalId, value: LocalValue) -> Option<LocalValue> {
433 LOCAL.with_borrow_mut_dyn(|c| c.insert(key, value))
434 }
435 fn remove(key: AppLocalId) -> Option<LocalValue> {
436 LOCAL.with_borrow_mut_dyn(|c| c.remove(&key))
437 }
438
439 #[inline(always)]
440 fn with_value_ctx<T: Send + Sync + 'static>(
441 key: &'static ContextLocal<T>,
442 kind: LocalValueKind,
443 value: &mut Option<Arc<T>>,
444 f: impl FnOnce(),
445 ) {
446 struct Restore<'a, T: Send + Sync + 'static> {
447 key: AppLocalId,
448 prev: Option<LocalValue>,
449 value: &'a mut Option<Arc<T>>,
450 }
451 impl<'a, T: Send + Sync + 'static> Restore<'a, T> {
452 fn new(key: &'static ContextLocal<T>, kind: LocalValueKind, value: &'a mut Option<Arc<T>>) -> Self {
453 Self {
454 key: key.id(),
455 prev: LocalContext::set(key.id(), (value.take().expect("no `value` to set"), kind)),
456 value,
457 }
458 }
459 }
460 impl<'a, T: Send + Sync + 'static> Drop for Restore<'a, T> {
461 fn drop(&mut self) {
462 let back = if let Some(prev) = self.prev.take() {
463 LocalContext::set(self.key, prev)
464 } else {
465 LocalContext::remove(self.key)
466 }
467 .unwrap();
468 *self.value = Some(Arc::downcast(back.0).unwrap());
469 }
470 }
471 let _restore = Restore::new(key, kind, value);
472
473 f()
474 }
475
476 #[inline(always)]
477 fn with_default_ctx<T: Send + Sync + 'static>(key: &'static ContextLocal<T>, f: impl FnOnce()) {
478 struct Restore {
479 key: AppLocalId,
480 prev: Option<LocalValue>,
481 }
482 impl Drop for Restore {
483 fn drop(&mut self) {
484 if let Some(prev) = self.prev.take() {
485 LocalContext::set(self.key, prev);
486 }
487 }
488 }
489 let _restore = Restore {
490 key: key.id(),
491 prev: Self::remove(key.id()),
492 };
493
494 f()
495 }
496}
497thread_local! {
498 static LOCAL: RefCell<LocalData> = const { RefCell::new(new_local_data()) };
499}
500
501trait LocalKeyDyn {
502 fn with_borrow_dyn<R>(&'static self, f: impl FnOnce(&LocalData) -> R) -> R;
503 fn with_borrow_mut_dyn<R>(&'static self, f: impl FnOnce(&mut LocalData) -> R) -> R;
504}
505impl LocalKeyDyn for LocalKey<RefCell<LocalData>> {
506 fn with_borrow_dyn<R>(&'static self, f: impl FnOnce(&LocalData) -> R) -> R {
507 let mut r = None;
508 let f = |l: &LocalData| r = Some(f(l));
509
510 self.with_borrow(f);
511
512 r.unwrap()
513 }
514
515 fn with_borrow_mut_dyn<R>(&'static self, f: impl FnOnce(&mut LocalData) -> R) -> R {
516 let mut r = None;
517 let f = |l: &mut LocalData| r = Some(f(l));
518
519 self.with_borrow_mut(f);
520
521 r.unwrap()
522 }
523}
524
525#[derive(Debug, Clone, PartialEq, Eq)]
527pub enum CaptureFilter {
528 None,
530
531 All,
533 ContextVars {
535 exclude: ContextValueSet,
537 },
538 ContextLocals {
540 exclude: ContextValueSet,
542 },
543
544 Include(ContextValueSet),
546
547 Exclude(ContextValueSet),
549}
550impl CaptureFilter {
551 pub const fn context_vars() -> Self {
553 Self::ContextVars {
554 exclude: ContextValueSet::new(),
555 }
556 }
557
558 pub const fn context_locals() -> Self {
560 Self::ContextLocals {
561 exclude: ContextValueSet::new(),
562 }
563 }
564
565 pub fn app_only() -> Self {
567 let mut set = ContextValueSet::new();
568 set.insert_app();
569 Self::Include(set)
570 }
571}
572
573pub trait ContextLocalKeyProvider {
577 fn context_local_key(&'static self) -> AppLocalId;
579}
580impl<T: Send + Sync + 'static> ContextLocalKeyProvider for ContextLocal<T> {
581 fn context_local_key(&'static self) -> AppLocalId {
582 self.id()
583 }
584}
585
586#[allow(clippy::exhaustive_structs)]
590pub struct TracingDispatcherContext;
591
592impl ContextLocalKeyProvider for TracingDispatcherContext {
593 fn context_local_key(&'static self) -> AppLocalId {
594 static ID: bool = true;
595 AppLocalId(&ID as *const bool as *const () as usize)
596 }
597}
598
599#[derive(Default, Clone, PartialEq, Eq)]
601pub struct ContextValueSet(LocalSet);
602impl ContextValueSet {
603 pub const fn new() -> Self {
605 Self(new_local_set())
606 }
607
608 pub fn insert(&mut self, value: &'static impl ContextLocalKeyProvider) -> bool {
610 self.0.insert(value.context_local_key())
611 }
612
613 pub fn remove(&mut self, value: &'static impl ContextLocalKeyProvider) -> bool {
615 self.0.remove(&value.context_local_key())
616 }
617
618 pub fn contains(&self, value: &'static impl ContextLocalKeyProvider) -> bool {
620 self.0.contains(&value.context_local_key())
621 }
622
623 pub fn len(&self) -> usize {
625 self.0.len()
626 }
627
628 pub fn is_empty(&self) -> bool {
630 self.0.is_empty()
631 }
632
633 pub fn insert_all(&mut self, other: &Self) {
635 self.0.extend(other.0.iter().copied());
636 }
637
638 pub fn remove_all(&mut self, other: &Self) {
640 for o in other.0.iter() {
641 self.0.remove(o);
642 }
643 }
644
645 pub fn insert_app(&mut self) -> bool {
647 let inserted_app = self.0.insert(AppId::local_id());
648 static TRACING: TracingDispatcherContext = TracingDispatcherContext;
649 self.insert(&TRACING) || inserted_app
650 }
651}
652impl fmt::Debug for ContextValueSet {
653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654 f.debug_struct("ContextValueSet").field("len()", &self.len()).finish()
655 }
656}