1#![allow(unused)]
4
5use crate::parking::Event;
6use std::collections::HashMap;
7use std::mem;
8use std::rc::Rc;
9use std::sync::{Arc, Condvar, Mutex};
10use std::time::{Duration, Instant};
11
12use cljrs_gc::GcPtr;
13use cljrs_gc::mutator::TrackedMutex;
14use cljrs_reader::Form;
15
16use crate::TypeHint;
17use crate::Value;
18
19#[cfg(all(feature = "no-gc", debug_assertions))]
34pub(crate) fn value_gcptr_is_static(value: &Value) -> bool {
35 use crate::value::MapValue;
36 use crate::value::SetValue;
37 match value {
38 Value::Nil
40 | Value::Bool(_)
41 | Value::Long(_)
42 | Value::Double(_)
43 | Value::Char(_)
44 | Value::Uuid(_) => true,
45 Value::Resource(_) | Value::SharedAtom(_) | Value::ByteBlob(_) => true,
47 Value::BigInt(p) => p.is_static_alloc(),
49 Value::BigDecimal(p) => p.is_static_alloc(),
50 Value::Ratio(p) => p.is_static_alloc(),
51 Value::Str(p) => p.is_static_alloc(),
52 Value::Pattern(p) => p.is_static_alloc(),
53 Value::Matcher(p) => p.is_static_alloc(),
54 Value::Symbol(p) => p.is_static_alloc(),
55 Value::Keyword(p) => p.is_static_alloc(),
56 Value::List(p) => p.is_static_alloc(),
57 Value::Vector(p) => p.is_static_alloc(),
58 Value::Queue(p) => p.is_static_alloc(),
59 Value::Map(m) => match m {
60 MapValue::Array(p) => p.is_static_alloc(),
61 MapValue::Hash(p) => p.is_static_alloc(),
62 MapValue::Sorted(p) => p.is_static_alloc(),
63 },
64 Value::Set(s) => match s {
65 SetValue::Hash(p) => p.is_static_alloc(),
66 SetValue::Sorted(p) => p.is_static_alloc(),
67 },
68 Value::NativeFunction(p) => p.is_static_alloc(),
69 Value::Fn(p) | Value::Macro(p) => p.is_static_alloc(),
70 Value::BoundFn(p) => p.is_static_alloc(),
71 Value::Var(p) => p.is_static_alloc(),
72 Value::Atom(p) => p.is_static_alloc(),
73 Value::Namespace(p) => p.is_static_alloc(),
74 Value::LazySeq(p) => p.is_static_alloc(),
75 Value::Cons(p) => p.is_static_alloc(),
76 Value::Protocol(p) => p.is_static_alloc(),
77 Value::ProtocolFn(p) => p.is_static_alloc(),
78 Value::MultiFn(p) => p.is_static_alloc(),
79 Value::Volatile(p) => p.is_static_alloc(),
80 Value::Delay(p) => p.is_static_alloc(),
81 Value::Promise(p) => p.is_static_alloc(),
82 Value::Future(p) => p.is_static_alloc(),
83 Value::Agent(p) => p.is_static_alloc(),
84 Value::TypeInstance(p) => p.is_static_alloc(),
85 Value::ObjectArray(p) => p.is_static_alloc(),
86 Value::NativeObject(p) => p.is_static_alloc(),
87 Value::Error(p) => p.is_static_alloc(),
88 Value::TransientMap(p) => p.is_static_alloc(),
89 Value::TransientVector(p) => p.is_static_alloc(),
90 Value::TransientSet(p) => p.is_static_alloc(),
91 Value::BooleanArray(_)
93 | Value::ByteArray(_)
94 | Value::ShortArray(_)
95 | Value::IntArray(_)
96 | Value::LongArray(_)
97 | Value::FloatArray(_)
98 | Value::DoubleArray(_)
99 | Value::CharArray(_) => true,
100 Value::Reduced(inner) | Value::WithMeta(inner, _) => value_gcptr_is_static(inner),
102 }
103}
104
105pub type MethodMap = HashMap<Arc<str>, Value>;
109
110#[derive(Debug)]
112pub struct Protocol {
113 pub name: Arc<str>,
114 pub ns: Arc<str>,
115 pub methods: Vec<ProtocolMethod>,
116 pub impls: TrackedMutex<HashMap<Arc<str>, MethodMap>>,
118 pub extend_via_metadata: bool,
122}
123
124impl Protocol {
125 pub fn new(
126 name: Arc<str>,
127 ns: Arc<str>,
128 methods: Vec<ProtocolMethod>,
129 extend_via_metadata: bool,
130 ) -> Self {
131 Self {
132 name,
133 ns,
134 methods,
135 impls: TrackedMutex::new(HashMap::new()),
136 extend_via_metadata,
137 }
138 }
139}
140
141static PROTOCOL_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
150
151pub fn protocol_generation() -> u64 {
153 PROTOCOL_GENERATION.load(std::sync::atomic::Ordering::Acquire)
154}
155
156pub fn bump_protocol_generation() {
159 PROTOCOL_GENERATION.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
160}
161
162impl cljrs_gc::Trace for Protocol {
163 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
164 {
165 let impls = self.impls.lock().unwrap();
166 for method_map in impls.values() {
167 for v in method_map.values() {
168 v.trace(visitor);
169 }
170 }
171 }
172 }
173}
174
175#[derive(Debug, Clone)]
177pub struct ProtocolMethod {
178 pub name: Arc<str>,
179 pub min_arity: usize,
180 pub variadic: bool,
181}
182
183impl cljrs_gc::Trace for ProtocolMethod {
184 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
185}
186
187#[derive(Debug)]
191pub struct ProtocolFn {
192 pub protocol: GcPtr<Protocol>,
193 pub method_name: Arc<str>,
194 pub min_arity: usize,
195 pub variadic: bool,
196}
197
198impl cljrs_gc::Trace for ProtocolFn {
199 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
200 use cljrs_gc::GcVisitor as _;
201 visitor.visit(&self.protocol);
202 }
203}
204
205#[derive(Debug)]
209pub struct MultiFn {
210 pub name: Arc<str>,
211 pub dispatch_fn: Value,
212 pub methods: TrackedMutex<HashMap<String, Value>>,
214 pub dispatch_vals: TrackedMutex<HashMap<String, Value>>,
216 pub prefers: TrackedMutex<HashMap<String, Vec<String>>>,
218 pub preference_vals: TrackedMutex<HashMap<String, Value>>,
221 pub default_dispatch: String,
223 method_generation: std::sync::atomic::AtomicU64,
224 method_cache: TrackedMutex<MultiFnMethodCache>,
225}
226
227#[derive(Debug, Default)]
228struct MultiFnMethodCache {
229 hierarchy_generation: u64,
230 method_generation: u64,
231 methods: HashMap<String, String>,
232}
233
234impl MultiFn {
235 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
236 Self {
237 name,
238 dispatch_fn,
239 methods: TrackedMutex::new(HashMap::new()),
240 dispatch_vals: TrackedMutex::new(HashMap::new()),
241 prefers: TrackedMutex::new(HashMap::new()),
242 preference_vals: TrackedMutex::new(HashMap::new()),
243 default_dispatch,
244 method_generation: std::sync::atomic::AtomicU64::new(0),
245 method_cache: TrackedMutex::new(MultiFnMethodCache::default()),
246 }
247 }
248
249 pub fn method_generation(&self) -> u64 {
251 self.method_generation
252 .load(std::sync::atomic::Ordering::Acquire)
253 }
254
255 pub fn bump_method_generation(&self) {
257 self.method_generation
258 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
259 }
260
261 pub fn cached_method(
264 &self,
265 dispatch_key: &str,
266 hierarchy_generation: u64,
267 method_generation: u64,
268 ) -> Option<String> {
269 let mut cache = self.method_cache.lock().unwrap();
270 if cache.hierarchy_generation != hierarchy_generation
271 || cache.method_generation != method_generation
272 {
273 cache.hierarchy_generation = hierarchy_generation;
274 cache.method_generation = method_generation;
275 cache.methods.clear();
276 }
277 cache.methods.get(dispatch_key).cloned()
278 }
279
280 pub fn cache_method(
284 &self,
285 dispatch_key: String,
286 method_key: String,
287 hierarchy_generation: u64,
288 method_generation: u64,
289 ) {
290 let mut cache = self.method_cache.lock().unwrap();
291 if cache.hierarchy_generation != hierarchy_generation
292 || cache.method_generation != method_generation
293 {
294 cache.hierarchy_generation = hierarchy_generation;
295 cache.method_generation = method_generation;
296 cache.methods.clear();
297 }
298 cache.methods.insert(dispatch_key, method_key);
299 }
300
301 pub fn cached_method_count(&self) -> usize {
303 self.method_cache.lock().unwrap().methods.len()
304 }
305}
306
307impl cljrs_gc::Trace for MultiFn {
308 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
309 self.dispatch_fn.trace(visitor);
310 {
311 let methods = self.methods.lock().unwrap();
312 for v in methods.values() {
313 v.trace(visitor);
314 }
315 }
316 {
317 let dispatch_vals = self.dispatch_vals.lock().unwrap();
318 for v in dispatch_vals.values() {
319 v.trace(visitor);
320 }
321 }
322 {
323 let preference_vals = self.preference_vals.lock().unwrap();
324 for v in preference_vals.values() {
325 v.trace(visitor);
326 }
327 }
328 }
329}
330
331#[derive(Debug)]
358pub struct Var {
359 pub namespace: Arc<str>,
360 pub name: Arc<str>,
361 pub value: TrackedMutex<Option<Value>>,
362 pub shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
365 pub is_macro: bool,
366 pub meta: TrackedMutex<Option<Value>>,
368 pub watches: TrackedMutex<Vec<(Value, Value)>>,
369 binding_generation: std::sync::atomic::AtomicU64,
370}
371
372impl Var {
373 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
374 Self {
375 namespace: namespace.into(),
376 name: name.into(),
377 value: TrackedMutex::new(None),
378 shared_root: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
379 is_macro: false,
380 meta: TrackedMutex::new(None),
381 watches: TrackedMutex::new(Vec::new()),
382 binding_generation: std::sync::atomic::AtomicU64::new(0),
383 }
384 }
385
386 pub fn from_shared_root(
393 namespace: impl Into<Arc<str>>,
394 name: impl Into<Arc<str>>,
395 is_macro: bool,
396 shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
397 ) -> Self {
398 let local = shared_root
399 .load()
400 .as_ref()
401 .as_ref()
402 .map(crate::shared::demote);
403 Self {
404 namespace: namespace.into(),
405 name: name.into(),
406 value: TrackedMutex::new(local),
407 shared_root,
408 is_macro,
409 meta: TrackedMutex::new(None),
410 watches: TrackedMutex::new(Vec::new()),
411 binding_generation: std::sync::atomic::AtomicU64::new(0),
412 }
413 }
414
415 pub fn is_bound(&self) -> bool {
416 self.value.lock().unwrap().is_some()
417 }
418
419 pub fn deref(&self) -> Option<Value> {
420 self.value.lock().unwrap().clone()
421 }
422
423 pub fn binding_generation(&self) -> u64 {
426 self.binding_generation
427 .load(std::sync::atomic::Ordering::Acquire)
428 }
429
430 pub fn deref_shared(&self) -> Option<Value> {
435 self.shared_root
436 .load()
437 .as_ref()
438 .as_ref()
439 .map(crate::shared::demote)
440 }
441
442 pub fn bind(&self, v: Value) {
443 #[cfg(all(feature = "no-gc", debug_assertions))]
447 debug_assert!(
448 value_gcptr_is_static(&v),
449 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
450 indicate a missing StaticCtxGuard around the value expression",
451 self.namespace,
452 self.name
453 );
454 let v = crate::publish::publish_value(v);
459 let prev = {
465 let mut slot = self.value.lock().unwrap();
466 slot.replace(v.clone())
467 };
468 let shared = crate::shared::promote(&v).ok();
474 self.shared_root.store(Arc::new(shared));
475 self.binding_generation
476 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
477 if let Some(prev) = prev {
478 crate::jit_hooks::notify_var_rebind(&prev, &v);
479 }
480 }
481
482 pub fn get_meta(&self) -> Option<Value> {
483 self.meta.lock().unwrap().clone()
484 }
485
486 pub fn set_meta(&self, m: Value) {
487 *self.meta.lock().unwrap() = Some(m);
488 }
489
490 pub fn full_name(&self) -> String {
491 format!("{}/{}", self.namespace, self.name)
492 }
493}
494
495impl cljrs_gc::Trace for Var {
496 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
497 {
498 let value = self.value.lock().unwrap();
499 if let Some(v) = value.as_ref() {
500 v.trace(visitor);
501 }
502 }
503 {
504 let meta = self.meta.lock().unwrap();
505 if let Some(m) = meta.as_ref() {
506 m.trace(visitor);
507 }
508 }
509 {
510 let watches = self.watches.lock().unwrap();
511 for (key, f) in watches.iter() {
512 key.trace(visitor);
513 f.trace(visitor);
514 }
515 }
516 }
517}
518
519#[derive(Debug)]
523pub struct Atom {
524 pub value: TrackedMutex<Value>,
525 pub meta: TrackedMutex<Option<Value>>,
526 pub validator: TrackedMutex<Option<Value>>,
527 pub watches: TrackedMutex<Vec<(Value, Value)>>,
528}
529
530impl Atom {
531 pub fn new(v: Value) -> Self {
532 let v = crate::publish::publish_value(v);
535 Self {
536 value: TrackedMutex::new(v),
537 meta: TrackedMutex::new(None),
538 validator: TrackedMutex::new(None),
539 watches: TrackedMutex::new(Vec::new()),
540 }
541 }
542
543 pub fn deref(&self) -> Value {
544 self.value.lock().unwrap().clone()
545 }
546
547 pub fn reset(&self, v: Value) -> Value {
548 #[cfg(all(feature = "no-gc", debug_assertions))]
550 debug_assert!(
551 value_gcptr_is_static(&v),
552 "no-gc: Atom::reset() received a region-local value — the new-value \
553 expression must be computed inside a StaticCtxGuard (i.e. inside \
554 the swap! / reset! call) so it is allocated in the static arena"
555 );
556 let v = crate::publish::publish_value(v);
558 let mut guard = self.value.lock().unwrap();
559 *guard = v.clone();
560 v
561 }
562
563 pub fn get_meta(&self) -> Option<Value> {
564 self.meta.lock().unwrap().clone()
565 }
566
567 pub fn set_meta(&self, m: Option<Value>) {
568 *self.meta.lock().unwrap() = m;
569 }
570
571 pub fn get_validator(&self) -> Option<Value> {
572 self.validator.lock().unwrap().clone()
573 }
574
575 pub fn set_validator(&self, vf: Option<Value>) {
576 *self.validator.lock().unwrap() = vf;
577 }
578}
579
580impl cljrs_gc::Trace for Atom {
581 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
582 {
583 let value = self.value.lock().unwrap();
584 value.trace(visitor);
585 }
586 {
587 let meta = self.meta.lock().unwrap();
588 if let Some(m) = meta.as_ref() {
589 m.trace(visitor);
590 }
591 }
592 {
593 let validator = self.validator.lock().unwrap();
594 if let Some(vf) = validator.as_ref() {
595 vf.trace(visitor);
596 }
597 }
598 {
599 let watches = self.watches.lock().unwrap();
600 for (key, f) in watches.iter() {
601 key.trace(visitor);
602 f.trace(visitor);
603 }
604 }
605 }
606}
607
608#[derive(Debug, Clone, Default)]
614pub struct ReferClojureFilter {
615 pub only: Option<std::collections::HashSet<Arc<str>>>,
617 pub exclude: std::collections::HashSet<Arc<str>>,
619 pub rename: HashMap<Arc<str>, Arc<str>>,
623}
624
625impl ReferClojureFilter {
626 pub fn local_name(&self, name: &Arc<str>) -> Option<Arc<str>> {
629 if self.exclude.contains(name) {
630 return None;
631 }
632 if let Some(only) = &self.only
633 && !only.contains(name)
634 {
635 return None;
636 }
637 Some(
638 self.rename
639 .get(name)
640 .cloned()
641 .unwrap_or_else(|| name.clone()),
642 )
643 }
644}
645
646#[derive(Debug)]
648pub struct Namespace {
649 pub name: Arc<str>,
650 pub interns: TrackedMutex<HashMap<Arc<str>, GcPtr<Var>>>,
652 pub refers: TrackedMutex<HashMap<Arc<str>, GcPtr<Var>>>,
654 pub aliases: TrackedMutex<HashMap<Arc<str>, Arc<str>>>,
656 pub source_file: TrackedMutex<Option<Arc<str>>>,
660 pub git_repo_root: TrackedMutex<Option<Arc<str>>>,
662 pub is_versioned: bool,
665 pub meta: TrackedMutex<Option<Value>>,
667 pub refer_clojure_filter: TrackedMutex<Option<ReferClojureFilter>>,
670}
671
672impl Namespace {
673 pub fn new(name: impl Into<Arc<str>>) -> Self {
674 Self {
675 name: name.into(),
676 interns: TrackedMutex::new(HashMap::new()),
677 refers: TrackedMutex::new(HashMap::new()),
678 aliases: TrackedMutex::new(HashMap::new()),
679 source_file: TrackedMutex::new(None),
680 git_repo_root: TrackedMutex::new(None),
681 is_versioned: false,
682 meta: TrackedMutex::new(None),
683 refer_clojure_filter: TrackedMutex::new(None),
684 }
685 }
686
687 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
689 Self {
690 is_versioned: true,
691 ..Self::new(name)
692 }
693 }
694
695 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
697 *self.source_file.lock().unwrap() = Some(Arc::from(file));
698 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
699 }
700
701 pub fn get_meta(&self) -> Option<Value> {
702 self.meta.lock().unwrap().clone()
703 }
704
705 pub fn set_meta(&self, m: Value) {
706 *self.meta.lock().unwrap() = Some(m);
707 }
708}
709
710impl cljrs_gc::Trace for Namespace {
711 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
712 use cljrs_gc::GcVisitor as _;
713 {
714 let interns = self.interns.lock().unwrap();
715 for var in interns.values() {
716 visitor.visit(var);
717 }
718 }
719 {
720 let refers = self.refers.lock().unwrap();
721 for var in refers.values() {
722 visitor.visit(var);
723 }
724 }
725 {
726 let meta = self.meta.lock().unwrap();
727 if let Some(m) = meta.as_ref() {
728 m.trace(visitor);
729 }
730 }
731 }
732}
733
734pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
740
741pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
744
745#[derive(Clone, Debug)]
746pub enum Arity {
747 Fixed(usize),
748 Variadic { min: usize },
749}
750
751pub struct NativeFn {
752 pub name: Arc<str>,
753 pub arity: Arity,
754 pub func: NativeFnFunc,
755 pub blocking_func: Option<NativeFnFunc>,
757}
758
759impl NativeFn {
760 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
762 Self {
763 name: name.into(),
764 arity,
765 blocking_func: None,
766 func: Arc::new(move |args| {
767 crate::realization::catch(|| func(args))
768 .unwrap_or_else(|message| Err(crate::ValueError::Other(message)))
769 }),
770 }
771 }
772
773 pub fn with_closure(
775 name: impl Into<Arc<str>>,
776 arity: Arity,
777 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
778 ) -> Self {
779 Self {
780 name: name.into(),
781 arity,
782 blocking_func: None,
783 func: Arc::new(move |args| {
784 crate::realization::catch(|| func(args))
785 .unwrap_or_else(|message| Err(crate::ValueError::Other(message)))
786 }),
787 }
788 }
789
790 pub fn with_blocking(
793 name: impl Into<Arc<str>>,
794 arity: Arity,
795 func: impl Fn(&[Value]) -> crate::ValueResult<Value> + 'static,
796 blocking_func: impl Fn(&[Value]) -> crate::ValueResult<Value> + 'static,
797 ) -> Self {
798 let mut native = Self::with_closure(name, arity, func);
799 native.blocking_func = Some(Arc::new(move |args| {
800 crate::realization::catch(|| blocking_func(args))
801 .unwrap_or_else(|message| Err(crate::ValueError::Other(message)))
802 }));
803 native
804 }
805
806 pub fn call_blocking(&self, args: &[Value]) -> crate::ValueResult<Value> {
808 let expected = match self.arity {
809 Arity::Fixed(n) if args.len() != n => Some(n.to_string()),
810 Arity::Variadic { min } if args.len() < min => Some(format!("{min}+")),
811 _ => None,
812 };
813 if let Some(expected) = expected {
814 return Err(crate::ValueError::ArityError {
815 name: self.name.to_string(),
816 expected,
817 got: args.len(),
818 });
819 }
820 let adapter = self.blocking_func.as_ref().ok_or_else(|| {
821 crate::ValueError::Other(format!(
822 "blocking cannot offload {}: this function has no owned-work adapter",
823 self.name
824 ))
825 })?;
826 adapter(args)
827 }
828}
829
830impl std::fmt::Debug for NativeFn {
831 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
832 f.debug_struct("NativeFn")
833 .field("name", &self.name)
834 .field("arity", &self.arity)
835 .field("func", &"<fn>")
836 .finish()
837 }
838}
839
840impl cljrs_gc::Trace for NativeFn {
841 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
842}
843
844#[derive(Debug, Clone)]
848pub struct CljxFnArity {
849 pub params: Vec<Arc<str>>,
852 pub rest_param: Option<Arc<str>>,
854 pub body: Vec<Form>,
856 pub destructure_params: Vec<(usize, Form)>,
860 pub destructure_rest: Option<Form>,
862 pub ir_arity_id: u64,
864 pub param_hints: Vec<Option<TypeHint>>,
868 pub rest_hint: Option<TypeHint>,
871}
872
873impl CljxFnArity {
874 pub fn heap_size(&self) -> usize {
876 self.params.capacity() * mem::size_of::<Arc<str>>()
878 + self.body.capacity() * mem::size_of::<Form>()
880 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
881 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
883 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
884 + self.destructure_rest.as_ref()
886 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
887 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
889 }
890}
891
892#[derive(Debug, Clone)]
896pub struct CljxFn {
897 pub name: Option<Arc<str>>,
898 pub arities: Vec<CljxFnArity>,
899 pub closed_over_names: Vec<Arc<str>>,
901 pub closed_over_vals: Vec<Value>,
903 pub is_macro: bool,
905 pub is_async: bool,
910 pub defining_ns: Arc<str>,
912 pub self_ptr: Option<GcPtr<CljxFn>>,
917}
918
919impl CljxFn {
920 pub fn new(
921 name: Option<Arc<str>>,
922 arities: Vec<CljxFnArity>,
923 closed_over_names: Vec<Arc<str>>,
924 closed_over_vals: Vec<Value>,
925 is_macro: bool,
926 defining_ns: Arc<str>,
927 ) -> Self {
928 Self {
929 name,
930 arities,
931 closed_over_names,
932 closed_over_vals,
933 is_macro,
934 is_async: false,
935 defining_ns,
936 self_ptr: None,
937 }
938 }
939}
940
941impl cljrs_gc::Trace for CljxFn {
942 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
943 use cljrs_gc::GcVisitor as _;
944 for v in &self.closed_over_vals {
945 v.trace(visitor);
946 }
947 if let Some(ref p) = self.self_ptr {
948 visitor.visit(p);
949 }
950 }
951
952 fn gc_size_extra(&self) -> usize {
953 self.arities.capacity() * mem::size_of::<CljxFnArity>()
955 + self
956 .arities
957 .iter()
958 .map(CljxFnArity::heap_size)
959 .sum::<usize>()
960 }
961}
962
963#[derive(Debug)]
970pub struct BoundFn {
971 pub wrapped: Value,
973 pub captured_bindings: HashMap<usize, Value>,
975}
976
977impl cljrs_gc::Trace for BoundFn {
978 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
979 self.wrapped.trace(visitor);
980 for val in self.captured_bindings.values() {
981 val.trace(visitor);
982 }
983 }
984
985 fn gc_size_extra(&self) -> usize {
986 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
988 }
989}
990
991pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
995 fn force(&self) -> Result<Value, String>;
996}
997
998#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1000pub struct Realizer {
1001 thread: std::thread::ThreadId,
1002 fiber: Option<u64>,
1003}
1004impl Realizer {
1005 pub fn current() -> Self {
1006 Self {
1007 thread: std::thread::current().id(),
1008 fiber: crate::parking::current(),
1009 }
1010 }
1011
1012 pub fn ensure_can_wait(self) -> Result<(), String> {
1015 if self.thread == std::thread::current().id()
1016 && self.fiber.is_some()
1017 && crate::parking::current().is_none()
1018 {
1019 Err("cannot block the isolate thread waiting for a parked fiber. Run this code inside a fiber.".into())
1020 } else {
1021 Ok(())
1022 }
1023 }
1024}
1025
1026struct RealizationGuard<'a> {
1028 lazy: Option<&'a LazySeq>,
1029 delay: Option<&'a Delay>,
1030}
1031impl Drop for RealizationGuard<'_> {
1032 fn drop(&mut self) {
1033 if let Some(cell) = self.lazy {
1034 let mut state = cell.state.lock().unwrap();
1035 if let LazySeqState::Realizing { thunk, .. } = &*state {
1036 *state = LazySeqState::Pending(thunk.clone());
1037 }
1038 drop(state);
1039 cell.ready.notify_all();
1040 }
1041 if let Some(cell) = self.delay {
1042 let mut state = cell.state.lock().unwrap();
1043 if let DelayState::Realizing { thunk, .. } = &*state {
1044 *state = DelayState::Pending(thunk.clone());
1045 }
1046 drop(state);
1047 cell.ready.notify_all();
1048 }
1049 }
1050}
1051
1052pub enum LazySeqState {
1054 Pending(Rc<dyn Thunk>),
1056 Realizing {
1057 owner: Realizer,
1058 thunk: Rc<dyn Thunk>,
1059 },
1060 Forced(Value),
1062 Error(String),
1064}
1065
1066pub struct LazySeq {
1068 ready: Event,
1069 pub state: TrackedMutex<LazySeqState>,
1070}
1071
1072impl LazySeq {
1073 pub fn new(thunk: Box<dyn Thunk>) -> Self {
1074 Self {
1075 state: TrackedMutex::new(LazySeqState::Pending(Rc::from(thunk))),
1076 ready: Event::default(),
1077 }
1078 }
1079
1080 pub fn realize(&self) -> Value {
1085 match self.try_realize() {
1086 Ok(value) => value,
1087 Err(error) => {
1088 assert!(self.error().is_some(), "{error}");
1091 Value::Nil
1092 }
1093 }
1094 }
1095
1096 pub fn try_realize(&self) -> Result<Value, String> {
1099 let owner = Realizer::current();
1100 let thunk = loop {
1101 let listener = self.ready.listen();
1102 let mut state = self.state.lock().unwrap();
1103 match &*state {
1104 LazySeqState::Forced(v) => return Ok(v.clone()),
1105 LazySeqState::Error(error) => return Err(error.clone()),
1106 LazySeqState::Realizing { owner: active, .. } => {
1107 if *active == owner {
1108 return Ok(Value::Nil);
1109 }
1110 active.ensure_can_wait()?;
1111 drop(state);
1112 listener.wait(None);
1113 continue;
1114 }
1115 LazySeqState::Pending(_) => {}
1116 }
1117 let LazySeqState::Pending(thunk) =
1118 mem::replace(&mut *state, LazySeqState::Forced(Value::Nil))
1119 else {
1120 unreachable!()
1121 };
1122 *state = LazySeqState::Realizing {
1123 owner,
1124 thunk: thunk.clone(),
1125 };
1126 break thunk;
1127 };
1128 let mut guard = RealizationGuard {
1129 lazy: Some(self),
1130 delay: None,
1131 };
1132 let result = match thunk.force() {
1133 Ok(result) => {
1134 let result = crate::publish::publish_value(result);
1135 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
1136 Ok(result)
1137 }
1138 Err(msg) => {
1139 *self.state.lock().unwrap() = LazySeqState::Error(msg.clone());
1140 Err(msg)
1141 }
1142 };
1143 guard.lazy = None;
1144 self.ready.notify_all();
1145 result
1146 }
1147
1148 pub fn error(&self) -> Option<String> {
1150 let guard = self.state.lock().unwrap();
1151 if let LazySeqState::Error(e) = &*guard {
1152 Some(e.clone())
1153 } else {
1154 None
1155 }
1156 }
1157}
1158
1159impl std::fmt::Debug for LazySeq {
1160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1161 write!(f, "LazySeq(...)")
1162 }
1163}
1164
1165impl cljrs_gc::Trace for LazySeq {
1166 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1167 {
1170 let state = self.state.lock().unwrap();
1171 match &*state {
1172 LazySeqState::Pending(thunk) => thunk.trace(visitor),
1173 LazySeqState::Realizing { thunk, .. } => thunk.trace(visitor),
1174 LazySeqState::Forced(v) => v.trace(visitor),
1175 LazySeqState::Error(_) => {}
1176 }
1177 }
1178 }
1179}
1180
1181#[derive(Debug, Clone)]
1188pub struct CljxCons {
1189 pub head: Value,
1190 pub tail: Value,
1191}
1192
1193impl cljrs_gc::Trace for CljxCons {
1194 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1195 self.head.trace(visitor);
1196 self.tail.trace(visitor);
1197 }
1198}
1199
1200pub struct Volatile {
1204 pub value: TrackedMutex<Value>,
1205}
1206
1207impl Volatile {
1208 pub fn new(v: Value) -> Self {
1209 let v = crate::publish::publish_value(v);
1211 Self {
1212 value: TrackedMutex::new(v),
1213 }
1214 }
1215
1216 pub fn deref(&self) -> Value {
1217 self.value.lock().unwrap().clone()
1218 }
1219
1220 pub fn reset(&self, v: Value) -> Value {
1221 #[cfg(all(feature = "no-gc", debug_assertions))]
1223 debug_assert!(
1224 value_gcptr_is_static(&v),
1225 "no-gc: Volatile::reset() received a region-local value — ensure the \
1226 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
1227 );
1228 let v = crate::publish::publish_value(v);
1230 *self.value.lock().unwrap() = v.clone();
1231 v
1232 }
1233}
1234
1235impl std::fmt::Debug for Volatile {
1236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1237 write!(f, "Volatile")
1238 }
1239}
1240
1241impl cljrs_gc::Trace for Volatile {
1242 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1243 {
1244 let value = self.value.lock().unwrap();
1245 value.trace(visitor);
1246 }
1247 }
1248}
1249
1250pub enum DelayState {
1254 Error(String),
1255 Pending(Rc<dyn Thunk>),
1256 Realizing {
1257 owner: Realizer,
1258 thunk: Rc<dyn Thunk>,
1259 },
1260 Forced(Value),
1261}
1262
1263pub struct Delay {
1265 ready: Event,
1266 pub state: TrackedMutex<DelayState>,
1267}
1268
1269impl Delay {
1270 pub fn new(thunk: Box<dyn Thunk>) -> Self {
1271 Self {
1272 state: TrackedMutex::new(DelayState::Pending(Rc::from(thunk))),
1273 ready: Event::default(),
1274 }
1275 }
1276
1277 pub fn force(&self) -> Result<Value, String> {
1280 let owner = Realizer::current();
1281 let thunk = loop {
1282 let listener = self.ready.listen();
1283 let mut state = self.state.lock().unwrap();
1284 match &*state {
1285 DelayState::Forced(v) => return Ok(v.clone()),
1286 DelayState::Error(e) => return Err(e.clone()),
1287 DelayState::Realizing { owner: active, .. } => {
1288 if *active == owner {
1289 return Err("recursive delay realization".into());
1290 }
1291 active.ensure_can_wait()?;
1292 drop(state);
1293 listener.wait(None);
1294 continue;
1295 }
1296 DelayState::Pending(_) => {}
1297 }
1298 let DelayState::Pending(thunk) =
1299 mem::replace(&mut *state, DelayState::Forced(Value::Nil))
1300 else {
1301 unreachable!()
1302 };
1303 *state = DelayState::Realizing {
1304 owner,
1305 thunk: thunk.clone(),
1306 };
1307 break thunk;
1308 };
1309 let mut guard = RealizationGuard {
1310 lazy: None,
1311 delay: Some(self),
1312 };
1313 let result = thunk.force().map(crate::publish::publish_value);
1314 *self.state.lock().unwrap() = match &result {
1315 Ok(value) => DelayState::Forced(value.clone()),
1316 Err(error) => DelayState::Error(error.clone()),
1317 };
1318 guard.delay = None;
1319 self.ready.notify_all();
1320 result
1321 }
1322
1323 pub fn is_realized(&self) -> bool {
1325 matches!(
1326 &*self.state.lock().unwrap(),
1327 DelayState::Forced(_) | DelayState::Error(_)
1328 )
1329 }
1330}
1331
1332impl std::fmt::Debug for Delay {
1333 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1334 write!(f, "Delay")
1335 }
1336}
1337
1338impl cljrs_gc::Trace for Delay {
1339 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1340 {
1343 let state = self.state.lock().unwrap();
1344 match &*state {
1345 DelayState::Pending(thunk) => thunk.trace(visitor),
1346 DelayState::Realizing { thunk, .. } => thunk.trace(visitor),
1347 DelayState::Error(_) => {}
1348 DelayState::Forced(v) => v.trace(visitor),
1349 }
1350 }
1351 }
1352}
1353
1354pub struct CljxPromise {
1358 pub ready: Event,
1359 pub value: Mutex<Option<Value>>,
1360 pub cond: Condvar,
1361}
1362
1363impl CljxPromise {
1364 pub fn new() -> Self {
1365 Self {
1366 value: Mutex::new(None),
1367 cond: Condvar::new(),
1368 ready: Event::default(),
1369 }
1370 }
1371
1372 pub fn deliver(&self, v: Value) {
1374 let v = crate::publish::publish_value(v);
1377 let mut guard = self.value.lock().unwrap();
1378 if guard.is_none() {
1379 *guard = Some(v);
1380 self.cond.notify_all();
1381 drop(guard);
1382 self.ready.notify_all();
1383 }
1384 }
1385
1386 pub fn deref_blocking(&self) -> Value {
1388 self.wait_value(None).unwrap()
1389 }
1390
1391 pub fn wait_value(&self, timeout: Option<Duration>) -> Option<Value> {
1392 let deadline = timeout.map(|d| Instant::now() + d);
1393 loop {
1394 let listener = self.ready.listen();
1395 if let Some(value) = self.value.lock().unwrap().clone() {
1396 return Some(value);
1397 }
1398 if !listener.wait(deadline.map(|d| d.saturating_duration_since(Instant::now()))) {
1399 return self.value.lock().unwrap().clone();
1400 }
1401 }
1402 }
1403
1404 pub fn is_realized(&self) -> bool {
1406 self.value.lock().unwrap().is_some()
1407 }
1408}
1409
1410impl Default for CljxPromise {
1411 fn default() -> Self {
1412 Self::new()
1413 }
1414}
1415
1416impl std::fmt::Debug for CljxPromise {
1417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1418 write!(f, "Promise")
1419 }
1420}
1421
1422impl cljrs_gc::Trace for CljxPromise {
1423 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1424 {
1425 let value = self.value.lock().unwrap();
1426 if let Some(v) = value.as_ref() {
1427 v.trace(visitor);
1428 }
1429 }
1430 }
1431}
1432
1433pub enum FutureState {
1437 Running,
1438 Done(Value),
1439 Failed(Value),
1443 GasExhausted,
1446 MemoryLimitExceeded(cljrs_gc::MemoryLimitExceeded),
1449 Cancelled,
1450}
1451
1452pub const FUTURE_CANCELLED_MSG: &str = "future was cancelled";
1454
1455pub struct CljxFuture {
1457 pub ready: Event,
1458 pub state: Mutex<FutureState>,
1459 pub cond: Condvar,
1460 observed: std::sync::atomic::AtomicBool,
1464}
1465
1466impl CljxFuture {
1467 pub fn new() -> Self {
1468 Self {
1469 state: Mutex::new(FutureState::Running),
1470 cond: Condvar::new(),
1471 ready: Event::default(),
1472 observed: std::sync::atomic::AtomicBool::new(false),
1473 }
1474 }
1475
1476 pub fn wait_value(&self, timeout: Option<Duration>) -> crate::ValueResult<Option<Value>> {
1478 let deadline = timeout.map(|d| Instant::now() + d);
1479 loop {
1480 let listener = self.ready.listen();
1481 if let Some(result) = self.settled_value() {
1482 return result.map(Some);
1483 }
1484 if !listener.wait(deadline.map(|d| d.saturating_duration_since(Instant::now()))) {
1485 return self.settled_value().transpose();
1486 }
1487 }
1488 }
1489
1490 pub fn settled_value(&self) -> Option<crate::ValueResult<Value>> {
1491 use crate::ValueError;
1492 let state = self.state.lock().unwrap();
1493 let value = match &*state {
1494 FutureState::Running => return None,
1495 FutureState::Done(v) => Ok(v.clone()),
1496 FutureState::Failed(v) => Err(ValueError::Thrown(v.clone())),
1497 FutureState::GasExhausted => Err(ValueError::GasExhausted),
1498 FutureState::MemoryLimitExceeded(f) => Err(ValueError::MemoryLimitExceeded(*f)),
1499 FutureState::Cancelled => {
1500 drop(state);
1501 return Some(Err(ValueError::Thrown(Self::cancelled_error())));
1502 }
1503 };
1504 self.mark_observed();
1505 Some(value)
1506 }
1507
1508 pub fn is_done(&self) -> bool {
1510 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1511 }
1512
1513 pub fn is_cancelled(&self) -> bool {
1515 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1516 }
1517
1518 pub fn cancel(&self) -> bool {
1523 let mut state = self.state.lock().unwrap();
1524 if matches!(&*state, FutureState::Running) {
1525 *state = FutureState::Cancelled;
1526 self.cond.notify_all();
1527 drop(state);
1528 self.ready.notify_all();
1529 true
1530 } else {
1531 false
1532 }
1533 }
1534
1535 pub fn cancelled_error() -> Value {
1542 Value::Error(GcPtr::new(crate::ExceptionInfo::new(
1543 crate::ValueError::Other(FUTURE_CANCELLED_MSG.to_string()),
1544 FUTURE_CANCELLED_MSG.to_string(),
1545 None,
1546 None,
1547 )))
1548 }
1549
1550 pub fn mark_observed(&self) {
1554 self.observed
1555 .store(true, std::sync::atomic::Ordering::Relaxed);
1556 }
1557}
1558
1559impl Drop for CljxFuture {
1560 fn drop(&mut self) {
1561 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1572 && let Ok(state) = self.state.lock()
1573 && matches!(&*state, FutureState::Failed(_))
1574 {
1575 eprintln!(
1576 "[clojurust warning] a failed future was discarded without its error \
1577 being observed (no await/deref); the thrown exception was lost"
1578 );
1579 }
1580 }
1581}
1582
1583impl Default for CljxFuture {
1584 fn default() -> Self {
1585 Self::new()
1586 }
1587}
1588
1589impl std::fmt::Debug for CljxFuture {
1590 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1591 write!(f, "Future")
1592 }
1593}
1594
1595impl cljrs_gc::Trace for CljxFuture {
1596 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1597 {
1598 let state = self.state.lock().unwrap();
1599 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1602 v.trace(visitor);
1603 }
1604 }
1605 }
1606}
1607
1608pub struct Agent {
1612 pub state: Arc<Mutex<Value>>,
1614 pub error: Arc<Mutex<Option<Value>>>,
1616 pub watches: TrackedMutex<Vec<(Value, Value)>>,
1617}
1618
1619impl Agent {
1620 pub fn get_state(&self) -> Value {
1621 self.state.lock().unwrap().clone()
1622 }
1623
1624 pub fn get_error(&self) -> Option<Value> {
1625 self.error.lock().unwrap().clone()
1626 }
1627
1628 pub fn clear_error(&self) {
1629 *self.error.lock().unwrap() = None;
1630 }
1631}
1632
1633impl std::fmt::Debug for Agent {
1634 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1635 write!(f, "Agent")
1636 }
1637}
1638
1639impl cljrs_gc::Trace for Agent {
1640 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1641 {
1642 let state = self.state.lock().unwrap();
1643 state.trace(visitor);
1644 }
1645 {
1646 let error = self.error.lock().unwrap();
1647 if let Some(e) = error.as_ref() {
1648 e.trace(visitor);
1649 }
1650 }
1651 {
1652 let watches = self.watches.lock().unwrap();
1653 for (key, f) in watches.iter() {
1654 key.trace(visitor);
1655 f.trace(visitor);
1656 }
1657 }
1658 }
1659}
1660
1661#[cfg(test)]
1664mod var_tests {
1665 use super::*;
1666 use crate::shared::SharedValue;
1667
1668 #[test]
1669 fn bind_promotable_mirrors_shared_root() {
1670 let var = Var::new("user", "x");
1671 assert!(var.shared_root.load().is_none());
1672 var.bind(Value::Long(7));
1673 assert!(matches!(
1674 var.shared_root.load().as_ref().as_ref(),
1675 Some(SharedValue::Long(7))
1676 ));
1677 assert_eq!(var.deref(), Some(Value::Long(7)));
1678 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1679 }
1680
1681 #[test]
1682 fn bind_nonpromotable_clears_shared_root() {
1683 let var = Var::new("user", "f");
1684 var.bind(Value::Long(1));
1685 assert!(var.shared_root.load().is_some());
1686 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1689 Ok(Value::Nil)
1690 })));
1691 var.bind(f);
1692 assert!(var.shared_root.load().is_none());
1693 assert!(var.is_bound());
1694 assert_eq!(var.deref_shared(), None);
1695 }
1696
1697 #[test]
1698 fn from_shared_root_seeds_local_slot() {
1699 let src = Var::new("user", "y");
1700 src.bind(Value::Long(99));
1701 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1702 assert_eq!(recv.deref(), Some(Value::Long(99)));
1703 src.bind(Value::Long(100));
1705 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1706 }
1707}