cljrs_runtime/env/
gc_roots.rs1#[cfg(not(feature = "no-gc"))]
2use crate::env::dynamics;
3use crate::env::env::Env;
4use crate::env::env::GlobalEnv;
5use crate::env::error::{EvalError, EvalResult};
6use std::cell::RefCell;
7
8type StwReclaimHook = Box<dyn Fn() + Send + Sync + 'static>;
19static STW_RECLAIM_HOOKS: std::sync::RwLock<Vec<StwReclaimHook>> =
20 std::sync::RwLock::new(Vec::new());
21
22pub fn set_stw_reclaim_hook(f: impl Fn() + Send + Sync + 'static) {
28 STW_RECLAIM_HOOKS.write().unwrap().push(Box::new(f));
29}
30
31#[cfg(not(feature = "no-gc"))]
33fn run_stw_reclaim() {
34 for hook in STW_RECLAIM_HOOKS.read().unwrap().iter() {
35 hook();
36 }
37}
38
39thread_local! {
52 static ENV_ROOTS: RefCell<Vec<*const Env>> = const { RefCell::new(Vec::new()) };
53 static VALUE_ROOTS: RefCell<Vec<(*const cljrs_value::Value, usize)>> =
58 const { RefCell::new(Vec::new()) };
59 static OPTION_VALUE_ROOTS: RefCell<Vec<(*const Option<cljrs_value::Value>, usize)>> =
63 const { RefCell::new(Vec::new()) };
64}
65cljrs_gc::mutator_slot!("cljrs-runtime/ENV_ROOTS", refcell ENV_ROOTS: Vec<*const Env>, trace trace_saved_envs);
66cljrs_gc::mutator_slot!("cljrs-runtime/VALUE_ROOTS", refcell VALUE_ROOTS: Vec<(*const cljrs_value::Value, usize)>, trace trace_saved_values);
67cljrs_gc::mutator_slot!(
68 "cljrs-runtime/OPTION_VALUE_ROOTS",
69 refcell OPTION_VALUE_ROOTS: Vec<(*const Option<cljrs_value::Value>, usize)>, trace trace_saved_options
70);
71
72fn trace_saved_envs(roots: &Vec<*const Env>, visitor: &mut cljrs_gc::MarkVisitor) {
75 for &env in roots {
76 trace_env_roots(unsafe { &*env }, visitor);
77 }
78}
79fn trace_saved_values(
80 roots: &Vec<(*const cljrs_value::Value, usize)>,
81 visitor: &mut cljrs_gc::MarkVisitor,
82) {
83 use cljrs_gc::Trace;
84 for &(ptr, len) in roots {
85 for value in unsafe { std::slice::from_raw_parts(ptr, len) } {
86 value.trace(visitor);
87 }
88 }
89}
90fn trace_saved_options(
91 roots: &Vec<(*const Option<cljrs_value::Value>, usize)>,
92 visitor: &mut cljrs_gc::MarkVisitor,
93) {
94 use cljrs_gc::Trace;
95 for &(ptr, len) in roots {
96 for value in unsafe { std::slice::from_raw_parts(ptr, len) }
97 .iter()
98 .flatten()
99 {
100 value.trace(visitor);
101 }
102 }
103}
104
105pub struct EnvRootGuard;
107
108impl Drop for EnvRootGuard {
109 fn drop(&mut self) {
110 ENV_ROOTS.with(|roots| {
111 roots.borrow_mut().pop();
112 });
113 }
114}
115
116pub struct ValueRootGuard {
122 entry: Option<(*const cljrs_value::Value, usize)>,
123}
124
125impl Drop for ValueRootGuard {
126 fn drop(&mut self) {
127 if let Some(entry) = self.entry {
128 VALUE_ROOTS.with(|roots| {
129 let mut roots = roots.borrow_mut();
130 if let Some(i) = roots.iter().rposition(|e| *e == entry) {
131 roots.remove(i);
132 }
133 });
134 }
135 }
136}
137
138pub fn push_env_root(env: &Env) -> EnvRootGuard {
141 ENV_ROOTS.with(|roots| {
142 roots.borrow_mut().push(env as *const Env);
143 });
144 EnvRootGuard
145}
146
147pub fn root_value(val: &cljrs_value::Value) -> ValueRootGuard {
149 let entry = (val as *const cljrs_value::Value, 1);
150 VALUE_ROOTS.with(|roots| roots.borrow_mut().push(entry));
151 ValueRootGuard { entry: Some(entry) }
152}
153
154pub fn root_values(vals: &[cljrs_value::Value]) -> ValueRootGuard {
156 if vals.is_empty() {
157 return ValueRootGuard { entry: None };
158 }
159 let entry = (vals.as_ptr(), vals.len());
160 VALUE_ROOTS.with(|roots| roots.borrow_mut().push(entry));
161 ValueRootGuard { entry: Some(entry) }
162}
163
164pub struct OptionValueRootGuard {
166 pushed: bool,
167}
168
169impl Drop for OptionValueRootGuard {
170 fn drop(&mut self) {
171 if self.pushed {
172 OPTION_VALUE_ROOTS.with(|roots| {
173 roots.borrow_mut().pop();
174 });
175 }
176 }
177}
178
179pub fn root_option_values(vals: &[Option<cljrs_value::Value>]) -> OptionValueRootGuard {
185 if vals.is_empty() {
186 return OptionValueRootGuard { pushed: false };
187 }
188 OPTION_VALUE_ROOTS.with(|roots| {
189 roots.borrow_mut().push((vals.as_ptr(), vals.len()));
190 });
191 OptionValueRootGuard { pushed: true }
192}
193
194#[cfg(feature = "no-gc")]
202pub fn force_collect(_env: &Env) {}
203
204#[cfg(not(feature = "no-gc"))]
205pub fn force_collect(env: &Env) {
206 let Some(_stw_guard) = cljrs_gc::begin_stw() else {
207 cljrs_gc::safepoint();
209 return;
210 };
211
212 cljrs_gc::HEAP.collect(|visitor| {
213 cljrs_gc::HEAP.trace_registered_roots(visitor);
214 trace_env_roots(env, visitor);
215 trace_thread_env_roots(visitor);
216 trace_value_roots(visitor);
217 trace_option_value_roots(visitor);
218 dynamics::trace_current(visitor);
219 crate::env::monitor::trace_current(visitor);
220 cljrs_value::collections::trace_snapshots(visitor);
221 crate::env::taps::trace_roots(visitor);
222 cljrs_gc::trace_thread_alloc_roots(visitor);
223 });
224 run_stw_reclaim();
226}
227
228#[cfg(feature = "no-gc")]
238pub fn gc_safepoint(_env: &Env) -> EvalResult<()> {
239 check_memory_fault()
240}
241
242#[cfg(not(feature = "no-gc"))]
243pub fn gc_safepoint(env: &Env) -> EvalResult<()> {
244 if !cljrs_gc::governor::runtime_fault_possible()
246 && !cljrs_gc::gc_requested()
247 && !cljrs_gc::CONFIG_CANCELLATION.in_progress()
248 {
249 return Ok(());
250 }
251 collect_at_safepoint(env);
252 check_memory_fault()
253}
254
255pub fn check_memory_fault() -> EvalResult<()> {
258 match cljrs_gc::resolve_runtime_fault() {
259 Some(fault) => Err(EvalError::MemoryLimitExceeded(fault)),
260 None => Ok(()),
261 }
262}
263
264#[cfg(not(feature = "no-gc"))]
265fn collect_at_safepoint(env: &Env) {
266 if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
267 return;
268 }
269
270 if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
272 cljrs_gc::safepoint();
273 return;
274 }
275
276 if !cljrs_gc::take_gc_request() {
278 cljrs_gc::safepoint();
280 return;
281 }
282
283 let Some(_stw_guard) = cljrs_gc::begin_stw() else {
285 cljrs_gc::safepoint();
287 return;
288 };
289
290 cljrs_gc::HEAP.collect(|visitor| {
293 cljrs_gc::HEAP.trace_registered_roots(visitor);
295 trace_env_roots(env, visitor);
297 trace_thread_env_roots(visitor);
299 trace_value_roots(visitor);
301 trace_option_value_roots(visitor);
303 dynamics::trace_current(visitor);
305 crate::env::monitor::trace_current(visitor);
306 cljrs_value::collections::trace_snapshots(visitor);
307 crate::env::taps::trace_roots(visitor);
309 cljrs_gc::trace_thread_alloc_roots(visitor);
311 });
312 run_stw_reclaim();
314 }
316
317fn trace_env_roots(env: &Env, visitor: &mut cljrs_gc::MarkVisitor) {
321 use cljrs_gc::Trace;
322 for frame in &env.frames {
324 for (_name, val) in &frame.bindings {
325 val.trace(visitor);
326 }
327 }
328 trace_globals(&env.globals, visitor);
331}
332
333#[cfg(not(feature = "no-gc"))]
335fn trace_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
336 use cljrs_gc::Trace;
337 VALUE_ROOTS.with(|roots| {
338 for &(ptr, count) in roots.borrow().iter() {
339 let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
343 for val in slice {
344 val.trace(visitor);
345 }
346 }
347 });
348}
349
350#[cfg(not(feature = "no-gc"))]
354fn trace_option_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
355 use cljrs_gc::Trace;
356 OPTION_VALUE_ROOTS.with(|roots| {
357 for &(ptr, count) in roots.borrow().iter() {
358 let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
361 for val in slice.iter().flatten() {
362 val.trace(visitor);
363 }
364 }
365 });
366}
367
368#[cfg(not(feature = "no-gc"))]
370fn trace_thread_env_roots(visitor: &mut cljrs_gc::MarkVisitor) {
371 use cljrs_gc::Trace;
372 ENV_ROOTS.with(|roots| {
373 for env_ptr in roots.borrow().iter() {
374 let env = unsafe { &**env_ptr };
377 for frame in &env.frames {
378 for (_name, val) in &frame.bindings {
379 val.trace(visitor);
380 }
381 }
382 }
383 });
384}
385
386pub(crate) fn trace_globals(globals: &GlobalEnv, visitor: &mut cljrs_gc::MarkVisitor) {
388 use cljrs_gc::{GcVisitor as _, Trace};
389 let namespaces = globals.namespaces.read().unwrap();
390 for ns_ptr in namespaces.values() {
391 visitor.visit(ns_ptr);
392 }
393 drop(namespaces);
394 let version_cache = globals.version_cache.lock().unwrap();
397 for val in version_cache.values() {
398 val.trace(visitor);
399 }
400}
401
402#[cfg(feature = "no-gc")]
411pub fn async_gc_collect() {}
412
413#[cfg(not(feature = "no-gc"))]
414pub fn async_gc_collect() {
415 if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
416 return;
417 }
418 if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
419 cljrs_gc::safepoint();
420 return;
421 }
422 if !cljrs_gc::take_gc_request() {
423 cljrs_gc::safepoint();
424 return;
425 }
426 let Some(_stw_guard) = cljrs_gc::begin_stw() else {
427 cljrs_gc::safepoint();
428 return;
429 };
430 cljrs_gc::HEAP.collect(|visitor| {
431 cljrs_gc::HEAP.trace_registered_roots(visitor);
432 trace_thread_env_roots(visitor);
433 trace_value_roots(visitor);
434 trace_option_value_roots(visitor);
435 dynamics::trace_current(visitor);
436 crate::env::monitor::trace_current(visitor);
437 cljrs_value::collections::trace_snapshots(visitor);
438 crate::env::taps::trace_roots(visitor);
439 cljrs_gc::trace_thread_alloc_roots(visitor);
440 });
441 run_stw_reclaim();
443}