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
228pub fn gc_safepoint(env: &Env) -> EvalResult<()> {
238 fiber_safepoint(env)?;
239 #[cfg(not(feature = "no-gc"))]
240 collect_if_needed(env);
241 check_memory_fault()
242}
243
244fn fiber_safepoint(env: &Env) -> EvalResult<()> {
245 cljrs_gc::fiber::safepoint_with(|yield_now| {
246 let _env_root = push_env_root(env);
247 yield_now()
248 })
249 .map_err(|error| match error {
250 cljrs_gc::fiber::SafepointError::StackOverflow(message) => EvalError::Runtime(message),
251 cljrs_gc::fiber::SafepointError::Cancelled => EvalError::Cancelled,
252 })
253}
254
255#[cfg(not(feature = "no-gc"))]
256fn collect_if_needed(env: &Env) {
257 if !cljrs_gc::governor::runtime_fault_possible()
259 && !cljrs_gc::gc_requested()
260 && !cljrs_gc::CONFIG_CANCELLATION.in_progress()
261 {
262 return;
263 }
264 collect_at_safepoint(env);
265}
266
267pub fn check_memory_fault() -> EvalResult<()> {
270 match cljrs_gc::resolve_runtime_fault() {
271 Some(fault) => Err(EvalError::MemoryLimitExceeded(fault)),
272 None => Ok(()),
273 }
274}
275
276#[cfg(not(feature = "no-gc"))]
277fn collect_at_safepoint(env: &Env) {
278 if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
279 return;
280 }
281
282 if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
284 cljrs_gc::safepoint();
285 return;
286 }
287
288 if !cljrs_gc::take_gc_request() {
290 cljrs_gc::safepoint();
292 return;
293 }
294
295 let Some(_stw_guard) = cljrs_gc::begin_stw() else {
297 cljrs_gc::safepoint();
299 return;
300 };
301
302 cljrs_gc::HEAP.collect(|visitor| {
305 cljrs_gc::HEAP.trace_registered_roots(visitor);
307 trace_env_roots(env, visitor);
309 trace_thread_env_roots(visitor);
311 trace_value_roots(visitor);
313 trace_option_value_roots(visitor);
315 dynamics::trace_current(visitor);
317 crate::env::monitor::trace_current(visitor);
318 cljrs_value::collections::trace_snapshots(visitor);
319 crate::env::taps::trace_roots(visitor);
321 cljrs_gc::trace_thread_alloc_roots(visitor);
323 });
324 run_stw_reclaim();
326 }
328
329fn trace_env_roots(env: &Env, visitor: &mut cljrs_gc::MarkVisitor) {
333 use cljrs_gc::Trace;
334 for frame in &env.frames {
336 for (_name, val) in &frame.bindings {
337 val.trace(visitor);
338 }
339 }
340 trace_globals(&env.globals, visitor);
343}
344
345#[cfg(not(feature = "no-gc"))]
347fn trace_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
348 use cljrs_gc::Trace;
349 VALUE_ROOTS.with(|roots| {
350 for &(ptr, count) in roots.borrow().iter() {
351 let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
355 for val in slice {
356 val.trace(visitor);
357 }
358 }
359 });
360}
361
362#[cfg(not(feature = "no-gc"))]
366fn trace_option_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
367 use cljrs_gc::Trace;
368 OPTION_VALUE_ROOTS.with(|roots| {
369 for &(ptr, count) in roots.borrow().iter() {
370 let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
373 for val in slice.iter().flatten() {
374 val.trace(visitor);
375 }
376 }
377 });
378}
379
380#[cfg(not(feature = "no-gc"))]
382fn trace_thread_env_roots(visitor: &mut cljrs_gc::MarkVisitor) {
383 use cljrs_gc::Trace;
384 ENV_ROOTS.with(|roots| {
385 for env_ptr in roots.borrow().iter() {
386 let env = unsafe { &**env_ptr };
389 for frame in &env.frames {
390 for (_name, val) in &frame.bindings {
391 val.trace(visitor);
392 }
393 }
394 }
395 });
396}
397
398pub(crate) fn trace_globals(globals: &GlobalEnv, visitor: &mut cljrs_gc::MarkVisitor) {
400 use cljrs_gc::{GcVisitor as _, Trace};
401 let namespaces = globals.namespaces.read().unwrap();
402 for ns_ptr in namespaces.values() {
403 visitor.visit(ns_ptr);
404 }
405 drop(namespaces);
406 let version_cache = globals.version_cache.lock().unwrap();
409 for val in version_cache.values() {
410 val.trace(visitor);
411 }
412}
413
414#[cfg(feature = "no-gc")]
423pub fn async_gc_collect() {}
424
425#[cfg(not(feature = "no-gc"))]
426pub fn async_gc_collect() {
427 if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
428 return;
429 }
430 if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
431 cljrs_gc::safepoint();
432 return;
433 }
434 if !cljrs_gc::take_gc_request() {
435 cljrs_gc::safepoint();
436 return;
437 }
438 let Some(_stw_guard) = cljrs_gc::begin_stw() else {
439 cljrs_gc::safepoint();
440 return;
441 };
442 cljrs_gc::HEAP.collect(|visitor| {
443 cljrs_gc::HEAP.trace_registered_roots(visitor);
444 trace_thread_env_roots(visitor);
445 trace_value_roots(visitor);
446 trace_option_value_roots(visitor);
447 dynamics::trace_current(visitor);
448 crate::env::monitor::trace_current(visitor);
449 cljrs_value::collections::trace_snapshots(visitor);
450 crate::env::taps::trace_roots(visitor);
451 cljrs_gc::trace_thread_alloc_roots(visitor);
452 });
453 run_stw_reclaim();
455}