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