1 use std::{
2 any::{type_name, TypeId},
3 cell::RefCell,
4 collections::HashMap,
5 hash::BuildHasherDefault,
6 os::raw::c_int,
7 sync::atomic::{AtomicBool, AtomicUsize, Ordering::Relaxed},
8 sync::Arc,
9 };
10
11 use dashmap::DashMap;
12 use once_cell::sync::OnceCell;
13 use rustc_hash::FxHasher;
14
15 use crate::{AllCounts, Counts};
16
17 static ENABLE: AtomicBool = AtomicBool::new(cfg!(feature = "print_at_exit"));
18
19 type GlobalStore = DashMap<TypeId, Arc<Store>, BuildHasherDefault<FxHasher>>;
20
21 #[inline]
global_store() -> &'static GlobalStore22 fn global_store() -> &'static GlobalStore {
23 static MAP: OnceCell<GlobalStore> = OnceCell::new();
24 MAP.get_or_init(|| {
25 if cfg!(feature = "print_at_exit") {
26 extern "C" {
27 fn atexit(f: extern "C" fn()) -> c_int;
28 }
29 extern "C" fn print_at_exit() {
30 eprint!("{}", get_all());
31 }
32 unsafe {
33 atexit(print_at_exit);
34 }
35 }
36
37 GlobalStore::default()
38 })
39 }
40
41 thread_local! {
42 static LOCAL: RefCell<HashMap<TypeId, Arc<Store>, BuildHasherDefault<FxHasher>>> = RefCell::default();
43 }
44
enable(yes: bool)45 pub(crate) fn enable(yes: bool) {
46 ENABLE.store(yes, Relaxed);
47 }
48
49 #[inline]
enabled() -> bool50 fn enabled() -> bool {
51 ENABLE.load(Relaxed)
52 }
53
54 #[inline]
dec<T: 'static>()55 pub(crate) fn dec<T: 'static>() {
56 if enabled() {
57 do_dec(TypeId::of::<T>())
58 }
59 }
60 #[inline(never)]
do_dec(key: TypeId)61 fn do_dec(key: TypeId) {
62 LOCAL.with(|local| {
63 // Fast path: we have needed store in thread local map
64 if let Some(store) = local.borrow().get(&key) {
65 store.dec();
66 return;
67 }
68
69 let global = global_store();
70
71 // Slightly slower: we don't have needed store in our thread local map,
72 // but some other thread has already initialized the needed store in the global map
73 if let Some(store) = global.get(&key) {
74 let store = store.value();
75 local.borrow_mut().insert(key, Arc::clone(store));
76 store.inc();
77 return;
78 }
79
80 // We only decrement counter after incremenrting it, so this line is unreachable
81 })
82 }
83
84 #[inline]
inc<T: 'static>()85 pub(crate) fn inc<T: 'static>() {
86 if enabled() {
87 do_inc(TypeId::of::<T>(), type_name::<T>())
88 }
89 }
90 #[inline(never)]
do_inc(key: TypeId, name: &'static str)91 fn do_inc(key: TypeId, name: &'static str) {
92 LOCAL.with(|local| {
93 // Fast path: we have needed store in thread local map
94 if let Some(store) = local.borrow().get(&key) {
95 store.inc();
96 return;
97 }
98
99 let global = global_store();
100
101 let copy = match global.get(&key) {
102 // Slightly slower path: we don't have needed store in our thread local map,
103 // but some other thread has already initialized the needed store in the global map
104 Some(store) => {
105 let store = store.value();
106 store.inc();
107 Arc::clone(store)
108 }
109 // Slow path: we are the first to initialize both global and local maps
110 None => {
111 let store = global
112 .entry(key)
113 .or_insert_with(|| Arc::new(Store { name, ..Store::default() }))
114 .downgrade();
115 let store = store.value();
116
117 store.inc();
118 Arc::clone(store)
119 }
120 };
121
122 local.borrow_mut().insert(key, copy);
123 });
124 }
125
get<T: 'static>() -> Counts126 pub(crate) fn get<T: 'static>() -> Counts {
127 do_get(TypeId::of::<T>())
128 }
do_get(key: TypeId) -> Counts129 fn do_get(key: TypeId) -> Counts {
130 global_store().entry(key).or_default().value().read()
131 }
132
get_all() -> AllCounts133 pub(crate) fn get_all() -> AllCounts {
134 let mut entries = global_store()
135 .iter()
136 .map(|entry| {
137 let store = entry.value();
138 (store.type_name(), store.read())
139 })
140 .collect::<Vec<_>>();
141 entries.sort_by_key(|(name, _counts)| *name);
142 AllCounts { entries }
143 }
144
145 #[derive(Default)]
146 struct Store {
147 total: AtomicUsize,
148 max_live: AtomicUsize,
149 live: AtomicUsize,
150 name: &'static str,
151 }
152
153 impl Store {
inc(&self)154 fn inc(&self) {
155 self.total.fetch_add(1, Relaxed);
156 let live = self.live.fetch_add(1, Relaxed) + 1;
157 self.max_live.fetch_max(live, Relaxed);
158 }
159
dec(&self)160 fn dec(&self) {
161 self.live.fetch_sub(1, Relaxed);
162 }
163
read(&self) -> Counts164 fn read(&self) -> Counts {
165 Counts {
166 total: self.total.load(Relaxed),
167 max_live: self.max_live.load(Relaxed),
168 live: self.live.load(Relaxed),
169 }
170 }
171
type_name(&self) -> &'static str172 fn type_name(&self) -> &'static str {
173 self.name
174 }
175 }
176