1mod barrier;
2mod block_allocation;
3mod gc_work;
4pub(super) mod global;
5mod mature_evac;
6pub(super) mod mutator;
7
8use std::sync::atomic::{AtomicBool, AtomicUsize};
9use std::sync::Arc;
10
11pub use self::global::LXR;
12
13use atomic::Atomic;
14use atomic::Ordering;
15use spin::Lazy;
16type RwLock<T> = spin::rwlock::RwLock<T>;
17
18static NUM_CONCURRENT_TRACING_PACKETS: AtomicUsize = AtomicUsize::new(0);
21static DISABLE_LASY_DEC_FOR_CURRENT_GC: AtomicBool = AtomicBool::new(false);
22static NO_EVAC: AtomicBool = AtomicBool::new(false);
23
24const LAZY_DECREMENTS: bool = !cfg!(feature = "lxr_no_lazy");
28
29const NURSERY_EVACUATION: bool = !cfg!(feature = "lxr_no_nursery_evac");
31
32pub(crate) const MATURE_EVACUATION: bool = !cfg!(feature = "lxr_no_mature_evac");
34
35const RC_STOP_PERCENT: usize = 15;
37
38const TRACE_THRESHOLD: usize = 20;
40
41const CYCLE_TRIGGER_THRESHOLD: usize = 1024;
43
44fn concurrent_marking_packets_drained() -> bool {
45 NUM_CONCURRENT_TRACING_PACKETS.load(Ordering::SeqCst) == 0
46}
47
48fn disable_lasy_dec_for_current_gc() -> bool {
49 DISABLE_LASY_DEC_FOR_CURRENT_GC.load(Ordering::SeqCst)
50}
51
52struct LazySweepingJobsCounter {
55 decs_counter: Option<Arc<AtomicUsize>>,
56 counter: Arc<AtomicUsize>,
57}
58impl LazySweepingJobsCounter {
59 pub fn new_decs() -> Self {
60 let lazy_sweeping_jobs = LAZY_SWEEPING_JOBS.read();
61 let decs_counter = lazy_sweeping_jobs.curr_decs_counter.as_ref().unwrap();
62 decs_counter.fetch_add(1, Ordering::SeqCst);
63 let counter = lazy_sweeping_jobs.curr_counter.as_ref().unwrap();
64 counter.fetch_add(1, Ordering::SeqCst);
65 Self {
66 decs_counter: Some(decs_counter.clone()),
67 counter: counter.clone(),
68 }
69 }
70
71 #[allow(clippy::should_implement_trait)]
72 pub fn clone(&self) -> Self {
73 self.counter.fetch_add(1, Ordering::SeqCst);
74 Self {
75 decs_counter: None,
76 counter: self.counter.clone(),
77 }
78 }
79
80 pub fn clone_with_decs(&self) -> Self {
81 self.decs_counter
82 .as_ref()
83 .unwrap()
84 .fetch_add(1, Ordering::SeqCst);
85 self.counter.fetch_add(1, Ordering::SeqCst);
86 Self {
87 decs_counter: self.decs_counter.clone(),
88 counter: self.counter.clone(),
89 }
90 }
91}
92
93impl Drop for LazySweepingJobsCounter {
94 fn drop(&mut self) {
95 let lazy_sweeping_jobs = LAZY_SWEEPING_JOBS.read();
96 if let Some(decs) = self.decs_counter.as_ref() {
97 if decs.fetch_sub(1, Ordering::SeqCst) == 1 {
98 let f = lazy_sweeping_jobs.end_of_decs.as_ref().unwrap();
99 f(self.clone())
100 }
101 }
102 if self.counter.fetch_sub(1, Ordering::SeqCst) == 1 {
103 if let Some(f) = lazy_sweeping_jobs.end_of_lazy.as_ref() {
104 f()
105 }
106 }
107 }
108}
109
110struct LazySweepingJobs {
111 prev_decs_counter: Option<Arc<AtomicUsize>>,
112 curr_decs_counter: Option<Arc<AtomicUsize>>,
113 prev_counter: Option<Arc<AtomicUsize>>,
114 curr_counter: Option<Arc<AtomicUsize>>,
115 pub end_of_decs: Option<Box<dyn Send + Sync + Fn(LazySweepingJobsCounter)>>,
116 pub end_of_lazy: Option<Box<dyn Send + Sync + Fn()>>,
117}
118
119impl LazySweepingJobs {
120 fn new() -> Self {
121 Self {
122 prev_decs_counter: None,
123 curr_decs_counter: None,
124 prev_counter: None,
125 curr_counter: None,
126 end_of_decs: None,
127 end_of_lazy: None,
128 }
129 }
130
131 pub fn all_finished() -> bool {
132 LAZY_SWEEPING_JOBS
133 .read()
134 .prev_counter
135 .as_ref()
136 .map(|c| c.load(Ordering::SeqCst))
137 .unwrap_or(0)
138 == 0
139 }
140
141 pub fn swap(&mut self) {
142 self.prev_decs_counter = self.curr_decs_counter.take();
143 self.curr_decs_counter = Some(Arc::new(AtomicUsize::new(0)));
144 self.prev_counter = self.curr_counter.take();
145 self.curr_counter = Some(Arc::new(AtomicUsize::new(0)));
146 }
147}
148
149static LAZY_SWEEPING_JOBS: Lazy<RwLock<LazySweepingJobs>> =
150 Lazy::new(|| RwLock::new(LazySweepingJobs::new()));
151
152static SURVIVAL_RATIO_PREDICTOR: SurvivalRatioPredictor = SurvivalRatioPredictor {
153 alloc_vol: AtomicUsize::new(0),
154 copy_promote_vol: AtomicUsize::new(0),
155 prev_copy_promote_ratio: Atomic::new(0.01),
156 promote_vol: AtomicUsize::new(0),
157 prev_promote_ratio: Atomic::new(0.01),
158};
159
160struct SurvivalRatioPredictor {
162 alloc_vol: AtomicUsize,
164 copy_promote_vol: AtomicUsize,
166 prev_copy_promote_ratio: Atomic<f64>,
168 promote_vol: AtomicUsize,
170 prev_promote_ratio: Atomic<f64>,
172}
173
174impl SurvivalRatioPredictor {
175 pub fn set_alloc_size(&self, size: usize) {
176 assert_eq!(self.alloc_vol.load(Ordering::SeqCst), 0);
177 self.alloc_vol.store(size, Ordering::SeqCst);
178 }
179
180 pub fn copy_promote_ratio(&self) -> f64 {
182 self.prev_copy_promote_ratio.load(Ordering::Relaxed)
183 }
184
185 pub fn promote_ratio(&self) -> f64 {
187 self.prev_promote_ratio.load(Ordering::Relaxed)
188 }
189
190 pub fn update_ratios(&self) {
191 let alloc_vol = self.alloc_vol.swap(0, Ordering::SeqCst);
192 let copy_promote_vol = self.copy_promote_vol.swap(0, Ordering::SeqCst);
193 let promote_vol = self.promote_vol.swap(0, Ordering::SeqCst);
194 if alloc_vol == 0 {
195 return;
196 }
197 let smooth = |prev_ratio: &Atomic<f64>, vol: usize| {
198 let curr = f64::min(vol as f64 / alloc_vol as f64, 1.0);
199 let prev = prev_ratio.load(Ordering::SeqCst);
200 prev_ratio.store(f64::min((curr * 3f64 + prev) / 4f64, 1.0), Ordering::SeqCst);
201 };
202 smooth(&self.prev_copy_promote_ratio, copy_promote_vol);
203 smooth(&self.prev_promote_ratio, promote_vol);
204 }
205}
206
207struct SurvivalRatioPredictorLocal {
208 copy_promote_vol: AtomicUsize,
209 promote_vol: AtomicUsize,
210}
211
212impl Default for SurvivalRatioPredictorLocal {
213 fn default() -> Self {
214 Self {
215 copy_promote_vol: AtomicUsize::new(0),
216 promote_vol: AtomicUsize::new(0),
217 }
218 }
219}
220
221impl SurvivalRatioPredictorLocal {
222 pub fn record_promotion(&self, size: usize, copied: bool) {
223 self.promote_vol.fetch_add(size, Ordering::Relaxed);
224 if copied {
225 self.copy_promote_vol.fetch_add(size, Ordering::Relaxed);
226 }
227 }
228
229 pub fn sync(&self) {
230 SURVIVAL_RATIO_PREDICTOR.copy_promote_vol.fetch_add(
231 self.copy_promote_vol.load(Ordering::Relaxed),
232 Ordering::Relaxed,
233 );
234 SURVIVAL_RATIO_PREDICTOR
235 .promote_vol
236 .fetch_add(self.promote_vol.load(Ordering::Relaxed), Ordering::Relaxed);
237 }
238}
239
240static MATURE_LIVE_PREDICTOR: MatureLivePredictor = MatureLivePredictor {
241 live_pages: Atomic::new(0f64),
242};
243
244struct MatureLivePredictor {
245 live_pages: Atomic<f64>,
246}
247
248impl MatureLivePredictor {
249 pub fn live_pages(&self) -> f64 {
250 self.live_pages.load(Ordering::Relaxed)
251 }
252
253 pub fn update(&self, live_pages: usize) -> f64 {
254 let prev = self.live_pages.load(Ordering::Relaxed);
256 let curr = live_pages as f64;
257 let weight = 3f64;
258 let next = (weight * curr + prev) / (weight + 1f64);
259 self.live_pages.store(next, Ordering::Relaxed);
262 next
263 }
264}