1#![allow(unused)]
4
5use std::collections::HashMap;
6use std::mem;
7use std::sync::{Arc, Condvar, Mutex};
8
9use cljrs_gc::GcPtr;
10use cljrs_reader::Form;
11
12use crate::TypeHint;
13use crate::Value;
14
15#[cfg(all(feature = "no-gc", debug_assertions))]
30pub(crate) fn value_gcptr_is_static(value: &Value) -> bool {
31 use crate::value::MapValue;
32 use crate::value::SetValue;
33 match value {
34 Value::Nil
36 | Value::Bool(_)
37 | Value::Long(_)
38 | Value::Double(_)
39 | Value::Char(_)
40 | Value::Uuid(_) => true,
41 Value::Resource(_) | Value::SharedAtom(_) | Value::ByteBlob(_) => true,
43 Value::BigInt(p) => p.is_static_alloc(),
45 Value::BigDecimal(p) => p.is_static_alloc(),
46 Value::Ratio(p) => p.is_static_alloc(),
47 Value::Str(p) => p.is_static_alloc(),
48 Value::Pattern(p) => p.is_static_alloc(),
49 Value::Matcher(p) => p.is_static_alloc(),
50 Value::Symbol(p) => p.is_static_alloc(),
51 Value::Keyword(p) => p.is_static_alloc(),
52 Value::List(p) => p.is_static_alloc(),
53 Value::Vector(p) => p.is_static_alloc(),
54 Value::Queue(p) => p.is_static_alloc(),
55 Value::Map(m) => match m {
56 MapValue::Array(p) => p.is_static_alloc(),
57 MapValue::Hash(p) => p.is_static_alloc(),
58 MapValue::Sorted(p) => p.is_static_alloc(),
59 },
60 Value::Set(s) => match s {
61 SetValue::Hash(p) => p.is_static_alloc(),
62 SetValue::Sorted(p) => p.is_static_alloc(),
63 },
64 Value::NativeFunction(p) => p.is_static_alloc(),
65 Value::Fn(p) | Value::Macro(p) => p.is_static_alloc(),
66 Value::BoundFn(p) => p.is_static_alloc(),
67 Value::Var(p) => p.is_static_alloc(),
68 Value::Atom(p) => p.is_static_alloc(),
69 Value::Namespace(p) => p.is_static_alloc(),
70 Value::LazySeq(p) => p.is_static_alloc(),
71 Value::Cons(p) => p.is_static_alloc(),
72 Value::Protocol(p) => p.is_static_alloc(),
73 Value::ProtocolFn(p) => p.is_static_alloc(),
74 Value::MultiFn(p) => p.is_static_alloc(),
75 Value::Volatile(p) => p.is_static_alloc(),
76 Value::Delay(p) => p.is_static_alloc(),
77 Value::Promise(p) => p.is_static_alloc(),
78 Value::Future(p) => p.is_static_alloc(),
79 Value::Agent(p) => p.is_static_alloc(),
80 Value::TypeInstance(p) => p.is_static_alloc(),
81 Value::ObjectArray(p) => p.is_static_alloc(),
82 Value::NativeObject(p) => p.is_static_alloc(),
83 Value::Error(p) => p.is_static_alloc(),
84 Value::TransientMap(p) => p.is_static_alloc(),
85 Value::TransientVector(p) => p.is_static_alloc(),
86 Value::TransientSet(p) => p.is_static_alloc(),
87 Value::BooleanArray(_)
89 | Value::ByteArray(_)
90 | Value::ShortArray(_)
91 | Value::IntArray(_)
92 | Value::LongArray(_)
93 | Value::FloatArray(_)
94 | Value::DoubleArray(_)
95 | Value::CharArray(_) => true,
96 Value::Reduced(inner) | Value::WithMeta(inner, _) => value_gcptr_is_static(inner),
98 }
99}
100
101pub type MethodMap = HashMap<Arc<str>, Value>;
105
106#[derive(Debug)]
108pub struct Protocol {
109 pub name: Arc<str>,
110 pub ns: Arc<str>,
111 pub methods: Vec<ProtocolMethod>,
112 pub impls: Mutex<HashMap<Arc<str>, MethodMap>>,
114 pub extend_via_metadata: bool,
118}
119
120impl Protocol {
121 pub fn new(
122 name: Arc<str>,
123 ns: Arc<str>,
124 methods: Vec<ProtocolMethod>,
125 extend_via_metadata: bool,
126 ) -> Self {
127 Self {
128 name,
129 ns,
130 methods,
131 impls: Mutex::new(HashMap::new()),
132 extend_via_metadata,
133 }
134 }
135}
136
137static PROTOCOL_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
146
147pub fn protocol_generation() -> u64 {
149 PROTOCOL_GENERATION.load(std::sync::atomic::Ordering::Acquire)
150}
151
152pub fn bump_protocol_generation() {
155 PROTOCOL_GENERATION.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
156}
157
158impl cljrs_gc::Trace for Protocol {
159 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
160 {
161 let impls = self.impls.lock().unwrap();
162 for method_map in impls.values() {
163 for v in method_map.values() {
164 v.trace(visitor);
165 }
166 }
167 }
168 }
169}
170
171#[derive(Debug, Clone)]
173pub struct ProtocolMethod {
174 pub name: Arc<str>,
175 pub min_arity: usize,
176 pub variadic: bool,
177}
178
179impl cljrs_gc::Trace for ProtocolMethod {
180 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
181}
182
183#[derive(Debug)]
187pub struct ProtocolFn {
188 pub protocol: GcPtr<Protocol>,
189 pub method_name: Arc<str>,
190 pub min_arity: usize,
191 pub variadic: bool,
192}
193
194impl cljrs_gc::Trace for ProtocolFn {
195 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
196 use cljrs_gc::GcVisitor as _;
197 visitor.visit(&self.protocol);
198 }
199}
200
201#[derive(Debug)]
205pub struct MultiFn {
206 pub name: Arc<str>,
207 pub dispatch_fn: Value,
208 pub methods: Mutex<HashMap<String, Value>>,
210 pub dispatch_vals: Mutex<HashMap<String, Value>>,
212 pub prefers: Mutex<HashMap<String, Vec<String>>>,
214 pub preference_vals: Mutex<HashMap<String, Value>>,
217 pub default_dispatch: String,
219 method_generation: std::sync::atomic::AtomicU64,
220 method_cache: Mutex<MultiFnMethodCache>,
221}
222
223#[derive(Debug, Default)]
224struct MultiFnMethodCache {
225 hierarchy_generation: u64,
226 method_generation: u64,
227 methods: HashMap<String, String>,
228}
229
230impl MultiFn {
231 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
232 Self {
233 name,
234 dispatch_fn,
235 methods: Mutex::new(HashMap::new()),
236 dispatch_vals: Mutex::new(HashMap::new()),
237 prefers: Mutex::new(HashMap::new()),
238 preference_vals: Mutex::new(HashMap::new()),
239 default_dispatch,
240 method_generation: std::sync::atomic::AtomicU64::new(0),
241 method_cache: Mutex::new(MultiFnMethodCache::default()),
242 }
243 }
244
245 pub fn method_generation(&self) -> u64 {
247 self.method_generation
248 .load(std::sync::atomic::Ordering::Acquire)
249 }
250
251 pub fn bump_method_generation(&self) {
253 self.method_generation
254 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
255 }
256
257 pub fn cached_method(
260 &self,
261 dispatch_key: &str,
262 hierarchy_generation: u64,
263 method_generation: u64,
264 ) -> Option<String> {
265 let mut cache = self.method_cache.lock().unwrap();
266 if cache.hierarchy_generation != hierarchy_generation
267 || cache.method_generation != method_generation
268 {
269 cache.hierarchy_generation = hierarchy_generation;
270 cache.method_generation = method_generation;
271 cache.methods.clear();
272 }
273 cache.methods.get(dispatch_key).cloned()
274 }
275
276 pub fn cache_method(
280 &self,
281 dispatch_key: String,
282 method_key: String,
283 hierarchy_generation: u64,
284 method_generation: u64,
285 ) {
286 let mut cache = self.method_cache.lock().unwrap();
287 if cache.hierarchy_generation != hierarchy_generation
288 || cache.method_generation != method_generation
289 {
290 cache.hierarchy_generation = hierarchy_generation;
291 cache.method_generation = method_generation;
292 cache.methods.clear();
293 }
294 cache.methods.insert(dispatch_key, method_key);
295 }
296
297 pub fn cached_method_count(&self) -> usize {
299 self.method_cache.lock().unwrap().methods.len()
300 }
301}
302
303impl cljrs_gc::Trace for MultiFn {
304 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
305 self.dispatch_fn.trace(visitor);
306 {
307 let methods = self.methods.lock().unwrap();
308 for v in methods.values() {
309 v.trace(visitor);
310 }
311 }
312 {
313 let dispatch_vals = self.dispatch_vals.lock().unwrap();
314 for v in dispatch_vals.values() {
315 v.trace(visitor);
316 }
317 }
318 {
319 let preference_vals = self.preference_vals.lock().unwrap();
320 for v in preference_vals.values() {
321 v.trace(visitor);
322 }
323 }
324 }
325}
326
327#[derive(Debug)]
354pub struct Var {
355 pub namespace: Arc<str>,
356 pub name: Arc<str>,
357 pub value: Mutex<Option<Value>>,
358 pub shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
361 pub is_macro: bool,
362 pub meta: Mutex<Option<Value>>,
364 pub watches: Mutex<Vec<(Value, Value)>>,
365 binding_generation: std::sync::atomic::AtomicU64,
366}
367
368impl Var {
369 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
370 Self {
371 namespace: namespace.into(),
372 name: name.into(),
373 value: Mutex::new(None),
374 shared_root: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
375 is_macro: false,
376 meta: Mutex::new(None),
377 watches: Mutex::new(Vec::new()),
378 binding_generation: std::sync::atomic::AtomicU64::new(0),
379 }
380 }
381
382 pub fn from_shared_root(
389 namespace: impl Into<Arc<str>>,
390 name: impl Into<Arc<str>>,
391 is_macro: bool,
392 shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
393 ) -> Self {
394 let local = shared_root
395 .load()
396 .as_ref()
397 .as_ref()
398 .map(crate::shared::demote);
399 Self {
400 namespace: namespace.into(),
401 name: name.into(),
402 value: Mutex::new(local),
403 shared_root,
404 is_macro,
405 meta: Mutex::new(None),
406 watches: Mutex::new(Vec::new()),
407 binding_generation: std::sync::atomic::AtomicU64::new(0),
408 }
409 }
410
411 pub fn is_bound(&self) -> bool {
412 self.value.lock().unwrap().is_some()
413 }
414
415 pub fn deref(&self) -> Option<Value> {
416 self.value.lock().unwrap().clone()
417 }
418
419 pub fn binding_generation(&self) -> u64 {
422 self.binding_generation
423 .load(std::sync::atomic::Ordering::Acquire)
424 }
425
426 pub fn deref_shared(&self) -> Option<Value> {
431 self.shared_root
432 .load()
433 .as_ref()
434 .as_ref()
435 .map(crate::shared::demote)
436 }
437
438 pub fn bind(&self, v: Value) {
439 #[cfg(all(feature = "no-gc", debug_assertions))]
443 debug_assert!(
444 value_gcptr_is_static(&v),
445 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
446 indicate a missing StaticCtxGuard around the value expression",
447 self.namespace,
448 self.name
449 );
450 let v = crate::publish::publish_value(v);
455 let prev = {
461 let mut slot = self.value.lock().unwrap();
462 slot.replace(v.clone())
463 };
464 let shared = crate::shared::promote(&v).ok();
470 self.shared_root.store(Arc::new(shared));
471 self.binding_generation
472 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
473 if let Some(prev) = prev {
474 crate::jit_hooks::notify_var_rebind(&prev, &v);
475 }
476 }
477
478 pub fn get_meta(&self) -> Option<Value> {
479 self.meta.lock().unwrap().clone()
480 }
481
482 pub fn set_meta(&self, m: Value) {
483 *self.meta.lock().unwrap() = Some(m);
484 }
485
486 pub fn full_name(&self) -> String {
487 format!("{}/{}", self.namespace, self.name)
488 }
489}
490
491impl cljrs_gc::Trace for Var {
492 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
493 {
494 let value = self.value.lock().unwrap();
495 if let Some(v) = value.as_ref() {
496 v.trace(visitor);
497 }
498 }
499 {
500 let meta = self.meta.lock().unwrap();
501 if let Some(m) = meta.as_ref() {
502 m.trace(visitor);
503 }
504 }
505 {
506 let watches = self.watches.lock().unwrap();
507 for (key, f) in watches.iter() {
508 key.trace(visitor);
509 f.trace(visitor);
510 }
511 }
512 }
513}
514
515#[derive(Debug)]
519pub struct Atom {
520 pub value: Mutex<Value>,
521 pub meta: Mutex<Option<Value>>,
522 pub validator: Mutex<Option<Value>>,
523 pub watches: Mutex<Vec<(Value, Value)>>,
524}
525
526impl Atom {
527 pub fn new(v: Value) -> Self {
528 let v = crate::publish::publish_value(v);
531 Self {
532 value: Mutex::new(v),
533 meta: Mutex::new(None),
534 validator: Mutex::new(None),
535 watches: Mutex::new(Vec::new()),
536 }
537 }
538
539 pub fn deref(&self) -> Value {
540 self.value.lock().unwrap().clone()
541 }
542
543 pub fn reset(&self, v: Value) -> Value {
544 #[cfg(all(feature = "no-gc", debug_assertions))]
546 debug_assert!(
547 value_gcptr_is_static(&v),
548 "no-gc: Atom::reset() received a region-local value — the new-value \
549 expression must be computed inside a StaticCtxGuard (i.e. inside \
550 the swap! / reset! call) so it is allocated in the static arena"
551 );
552 let v = crate::publish::publish_value(v);
554 let mut guard = self.value.lock().unwrap();
555 *guard = v.clone();
556 v
557 }
558
559 pub fn get_meta(&self) -> Option<Value> {
560 self.meta.lock().unwrap().clone()
561 }
562
563 pub fn set_meta(&self, m: Option<Value>) {
564 *self.meta.lock().unwrap() = m;
565 }
566
567 pub fn get_validator(&self) -> Option<Value> {
568 self.validator.lock().unwrap().clone()
569 }
570
571 pub fn set_validator(&self, vf: Option<Value>) {
572 *self.validator.lock().unwrap() = vf;
573 }
574}
575
576impl cljrs_gc::Trace for Atom {
577 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
578 {
579 let value = self.value.lock().unwrap();
580 value.trace(visitor);
581 }
582 {
583 let meta = self.meta.lock().unwrap();
584 if let Some(m) = meta.as_ref() {
585 m.trace(visitor);
586 }
587 }
588 {
589 let validator = self.validator.lock().unwrap();
590 if let Some(vf) = validator.as_ref() {
591 vf.trace(visitor);
592 }
593 }
594 {
595 let watches = self.watches.lock().unwrap();
596 for (key, f) in watches.iter() {
597 key.trace(visitor);
598 f.trace(visitor);
599 }
600 }
601 }
602}
603
604#[derive(Debug, Clone, Default)]
610pub struct ReferClojureFilter {
611 pub only: Option<std::collections::HashSet<Arc<str>>>,
613 pub exclude: std::collections::HashSet<Arc<str>>,
615 pub rename: HashMap<Arc<str>, Arc<str>>,
619}
620
621impl ReferClojureFilter {
622 pub fn local_name(&self, name: &Arc<str>) -> Option<Arc<str>> {
625 if self.exclude.contains(name) {
626 return None;
627 }
628 if let Some(only) = &self.only
629 && !only.contains(name)
630 {
631 return None;
632 }
633 Some(
634 self.rename
635 .get(name)
636 .cloned()
637 .unwrap_or_else(|| name.clone()),
638 )
639 }
640}
641
642#[derive(Debug)]
644pub struct Namespace {
645 pub name: Arc<str>,
646 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
648 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
650 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
652 pub source_file: Mutex<Option<Arc<str>>>,
656 pub git_repo_root: Mutex<Option<Arc<str>>>,
658 pub is_versioned: bool,
661 pub meta: Mutex<Option<Value>>,
663 pub refer_clojure_filter: Mutex<Option<ReferClojureFilter>>,
666}
667
668impl Namespace {
669 pub fn new(name: impl Into<Arc<str>>) -> Self {
670 Self {
671 name: name.into(),
672 interns: Mutex::new(HashMap::new()),
673 refers: Mutex::new(HashMap::new()),
674 aliases: Mutex::new(HashMap::new()),
675 source_file: Mutex::new(None),
676 git_repo_root: Mutex::new(None),
677 is_versioned: false,
678 meta: Mutex::new(None),
679 refer_clojure_filter: Mutex::new(None),
680 }
681 }
682
683 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
685 Self {
686 is_versioned: true,
687 ..Self::new(name)
688 }
689 }
690
691 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
693 *self.source_file.lock().unwrap() = Some(Arc::from(file));
694 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
695 }
696
697 pub fn get_meta(&self) -> Option<Value> {
698 self.meta.lock().unwrap().clone()
699 }
700
701 pub fn set_meta(&self, m: Value) {
702 *self.meta.lock().unwrap() = Some(m);
703 }
704}
705
706impl cljrs_gc::Trace for Namespace {
707 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
708 use cljrs_gc::GcVisitor as _;
709 {
710 let interns = self.interns.lock().unwrap();
711 for var in interns.values() {
712 visitor.visit(var);
713 }
714 }
715 {
716 let refers = self.refers.lock().unwrap();
717 for var in refers.values() {
718 visitor.visit(var);
719 }
720 }
721 {
722 let meta = self.meta.lock().unwrap();
723 if let Some(m) = meta.as_ref() {
724 m.trace(visitor);
725 }
726 }
727 }
728}
729
730pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
736
737pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
740
741#[derive(Clone, Debug)]
742pub enum Arity {
743 Fixed(usize),
744 Variadic { min: usize },
745}
746
747pub struct NativeFn {
748 pub name: Arc<str>,
749 pub arity: Arity,
750 pub func: NativeFnFunc,
751}
752
753impl NativeFn {
754 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
756 Self {
757 name: name.into(),
758 arity,
759 func: Arc::new(func),
760 }
761 }
762
763 pub fn with_closure(
765 name: impl Into<Arc<str>>,
766 arity: Arity,
767 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
768 ) -> Self {
769 Self {
770 name: name.into(),
771 arity,
772 func: Arc::new(func),
773 }
774 }
775}
776
777impl std::fmt::Debug for NativeFn {
778 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
779 f.debug_struct("NativeFn")
780 .field("name", &self.name)
781 .field("arity", &self.arity)
782 .field("func", &"<fn>")
783 .finish()
784 }
785}
786
787impl cljrs_gc::Trace for NativeFn {
788 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
789}
790
791#[derive(Debug, Clone)]
795pub struct CljxFnArity {
796 pub params: Vec<Arc<str>>,
799 pub rest_param: Option<Arc<str>>,
801 pub body: Vec<Form>,
803 pub destructure_params: Vec<(usize, Form)>,
807 pub destructure_rest: Option<Form>,
809 pub ir_arity_id: u64,
811 pub param_hints: Vec<Option<TypeHint>>,
815 pub rest_hint: Option<TypeHint>,
818}
819
820impl CljxFnArity {
821 pub fn heap_size(&self) -> usize {
823 self.params.capacity() * mem::size_of::<Arc<str>>()
825 + self.body.capacity() * mem::size_of::<Form>()
827 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
828 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
830 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
831 + self.destructure_rest.as_ref()
833 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
834 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
836 }
837}
838
839#[derive(Debug, Clone)]
843pub struct CljxFn {
844 pub name: Option<Arc<str>>,
845 pub arities: Vec<CljxFnArity>,
846 pub closed_over_names: Vec<Arc<str>>,
848 pub closed_over_vals: Vec<Value>,
850 pub is_macro: bool,
852 pub is_async: bool,
857 pub defining_ns: Arc<str>,
859 pub self_ptr: Option<GcPtr<CljxFn>>,
864}
865
866impl CljxFn {
867 pub fn new(
868 name: Option<Arc<str>>,
869 arities: Vec<CljxFnArity>,
870 closed_over_names: Vec<Arc<str>>,
871 closed_over_vals: Vec<Value>,
872 is_macro: bool,
873 defining_ns: Arc<str>,
874 ) -> Self {
875 Self {
876 name,
877 arities,
878 closed_over_names,
879 closed_over_vals,
880 is_macro,
881 is_async: false,
882 defining_ns,
883 self_ptr: None,
884 }
885 }
886}
887
888impl cljrs_gc::Trace for CljxFn {
889 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
890 use cljrs_gc::GcVisitor as _;
891 for v in &self.closed_over_vals {
892 v.trace(visitor);
893 }
894 if let Some(ref p) = self.self_ptr {
895 visitor.visit(p);
896 }
897 }
898
899 fn gc_size_extra(&self) -> usize {
900 self.arities.capacity() * mem::size_of::<CljxFnArity>()
902 + self
903 .arities
904 .iter()
905 .map(CljxFnArity::heap_size)
906 .sum::<usize>()
907 }
908}
909
910#[derive(Debug)]
917pub struct BoundFn {
918 pub wrapped: Value,
920 pub captured_bindings: HashMap<usize, Value>,
922}
923
924impl cljrs_gc::Trace for BoundFn {
925 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
926 self.wrapped.trace(visitor);
927 for val in self.captured_bindings.values() {
928 val.trace(visitor);
929 }
930 }
931
932 fn gc_size_extra(&self) -> usize {
933 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
935 }
936}
937
938pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
942 fn force(&self) -> Result<Value, String>;
943}
944
945pub enum LazySeqState {
947 Pending(Box<dyn Thunk>),
949 Forced(Value),
951 Error(String),
953}
954
955pub struct LazySeq {
957 pub state: Mutex<LazySeqState>,
958}
959
960impl LazySeq {
961 pub fn new(thunk: Box<dyn Thunk>) -> Self {
962 Self {
963 state: Mutex::new(LazySeqState::Pending(thunk)),
964 }
965 }
966
967 pub fn realize(&self) -> Value {
970 let thunk = {
971 let mut guard = self.state.lock().unwrap();
972 match &*guard {
973 LazySeqState::Forced(v) => return v.clone(),
974 LazySeqState::Error(_) => return Value::Nil,
975 LazySeqState::Pending(_) => {}
976 }
977 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
979 let LazySeqState::Pending(thunk) = prev else {
980 unreachable!("state was not Pending")
981 };
982 thunk
983 };
985 match thunk.force() {
988 Ok(result) => {
989 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
990 result
991 }
992 Err(msg) => {
993 *self.state.lock().unwrap() = LazySeqState::Error(msg);
994 Value::Nil
995 }
996 }
997 }
998
999 pub fn error(&self) -> Option<String> {
1001 let guard = self.state.lock().unwrap();
1002 if let LazySeqState::Error(e) = &*guard {
1003 Some(e.clone())
1004 } else {
1005 None
1006 }
1007 }
1008}
1009
1010impl std::fmt::Debug for LazySeq {
1011 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1012 write!(f, "LazySeq(...)")
1013 }
1014}
1015
1016impl cljrs_gc::Trace for LazySeq {
1017 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1018 {
1021 let state = self.state.lock().unwrap();
1022 match &*state {
1023 LazySeqState::Pending(thunk) => thunk.trace(visitor),
1024 LazySeqState::Forced(v) => v.trace(visitor),
1025 LazySeqState::Error(_) => {}
1026 }
1027 }
1028 }
1029}
1030
1031#[derive(Debug, Clone)]
1038pub struct CljxCons {
1039 pub head: Value,
1040 pub tail: Value,
1041}
1042
1043impl cljrs_gc::Trace for CljxCons {
1044 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1045 self.head.trace(visitor);
1046 self.tail.trace(visitor);
1047 }
1048}
1049
1050pub struct Volatile {
1054 pub value: Mutex<Value>,
1055}
1056
1057impl Volatile {
1058 pub fn new(v: Value) -> Self {
1059 let v = crate::publish::publish_value(v);
1061 Self {
1062 value: Mutex::new(v),
1063 }
1064 }
1065
1066 pub fn deref(&self) -> Value {
1067 self.value.lock().unwrap().clone()
1068 }
1069
1070 pub fn reset(&self, v: Value) -> Value {
1071 #[cfg(all(feature = "no-gc", debug_assertions))]
1073 debug_assert!(
1074 value_gcptr_is_static(&v),
1075 "no-gc: Volatile::reset() received a region-local value — ensure the \
1076 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
1077 );
1078 let v = crate::publish::publish_value(v);
1080 *self.value.lock().unwrap() = v.clone();
1081 v
1082 }
1083}
1084
1085impl std::fmt::Debug for Volatile {
1086 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1087 write!(f, "Volatile")
1088 }
1089}
1090
1091impl cljrs_gc::Trace for Volatile {
1092 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1093 {
1094 let value = self.value.lock().unwrap();
1095 value.trace(visitor);
1096 }
1097 }
1098}
1099
1100pub enum DelayState {
1104 Pending(Box<dyn Thunk>),
1105 Forced(Value),
1106}
1107
1108pub struct Delay {
1110 pub state: Mutex<DelayState>,
1111}
1112
1113impl Delay {
1114 pub fn new(thunk: Box<dyn Thunk>) -> Self {
1115 Self {
1116 state: Mutex::new(DelayState::Pending(thunk)),
1117 }
1118 }
1119
1120 pub fn force(&self) -> Result<Value, String> {
1123 let thunk = {
1124 let mut guard = self.state.lock().unwrap();
1125 if let DelayState::Forced(v) = &*guard {
1126 return Ok(v.clone());
1127 }
1128 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
1129 let DelayState::Pending(thunk) = prev else {
1130 unreachable!("state was not Pending")
1131 };
1132 thunk
1133 };
1135 let result = thunk.force()?;
1138 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
1139 Ok(result)
1140 }
1141
1142 pub fn is_realized(&self) -> bool {
1144 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
1145 }
1146}
1147
1148impl std::fmt::Debug for Delay {
1149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1150 write!(f, "Delay")
1151 }
1152}
1153
1154impl cljrs_gc::Trace for Delay {
1155 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1156 {
1159 let state = self.state.lock().unwrap();
1160 match &*state {
1161 DelayState::Pending(thunk) => thunk.trace(visitor),
1162 DelayState::Forced(v) => v.trace(visitor),
1163 }
1164 }
1165 }
1166}
1167
1168pub struct CljxPromise {
1172 pub value: Mutex<Option<Value>>,
1173 pub cond: Condvar,
1174}
1175
1176impl CljxPromise {
1177 pub fn new() -> Self {
1178 Self {
1179 value: Mutex::new(None),
1180 cond: Condvar::new(),
1181 }
1182 }
1183
1184 pub fn deliver(&self, v: Value) {
1186 let v = crate::publish::publish_value(v);
1189 let mut guard = self.value.lock().unwrap();
1190 if guard.is_none() {
1191 *guard = Some(v);
1192 self.cond.notify_all();
1193 }
1194 }
1195
1196 pub fn deref_blocking(&self) -> Value {
1198 let mut guard = self.value.lock().unwrap();
1199 while guard.is_none() {
1200 guard = self.cond.wait(guard).unwrap();
1201 }
1202 guard.as_ref().unwrap().clone()
1203 }
1204
1205 pub fn is_realized(&self) -> bool {
1207 self.value.lock().unwrap().is_some()
1208 }
1209}
1210
1211impl Default for CljxPromise {
1212 fn default() -> Self {
1213 Self::new()
1214 }
1215}
1216
1217impl std::fmt::Debug for CljxPromise {
1218 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1219 write!(f, "Promise")
1220 }
1221}
1222
1223impl cljrs_gc::Trace for CljxPromise {
1224 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1225 {
1226 let value = self.value.lock().unwrap();
1227 if let Some(v) = value.as_ref() {
1228 v.trace(visitor);
1229 }
1230 }
1231 }
1232}
1233
1234pub enum FutureState {
1238 Running,
1239 Done(Value),
1240 Failed(Value),
1244 GasExhausted,
1247 Cancelled,
1248}
1249
1250pub const FUTURE_CANCELLED_MSG: &str = "future was cancelled";
1252
1253pub struct CljxFuture {
1255 pub state: Mutex<FutureState>,
1256 pub cond: Condvar,
1257 observed: std::sync::atomic::AtomicBool,
1261}
1262
1263impl CljxFuture {
1264 pub fn new() -> Self {
1265 Self {
1266 state: Mutex::new(FutureState::Running),
1267 cond: Condvar::new(),
1268 observed: std::sync::atomic::AtomicBool::new(false),
1269 }
1270 }
1271
1272 pub fn is_done(&self) -> bool {
1274 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1275 }
1276
1277 pub fn is_cancelled(&self) -> bool {
1279 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1280 }
1281
1282 pub fn cancel(&self) -> bool {
1287 let mut state = self.state.lock().unwrap();
1288 if matches!(&*state, FutureState::Running) {
1289 *state = FutureState::Cancelled;
1290 self.cond.notify_all();
1291 true
1292 } else {
1293 false
1294 }
1295 }
1296
1297 pub fn cancelled_error() -> Value {
1304 Value::Error(GcPtr::new(crate::ExceptionInfo::new(
1305 crate::ValueError::Other(FUTURE_CANCELLED_MSG.to_string()),
1306 FUTURE_CANCELLED_MSG.to_string(),
1307 None,
1308 None,
1309 )))
1310 }
1311
1312 pub fn mark_observed(&self) {
1316 self.observed
1317 .store(true, std::sync::atomic::Ordering::Relaxed);
1318 }
1319}
1320
1321impl Drop for CljxFuture {
1322 fn drop(&mut self) {
1323 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1334 && let Ok(state) = self.state.lock()
1335 && matches!(&*state, FutureState::Failed(_))
1336 {
1337 eprintln!(
1338 "[clojurust warning] a failed future was discarded without its error \
1339 being observed (no await/deref); the thrown exception was lost"
1340 );
1341 }
1342 }
1343}
1344
1345impl Default for CljxFuture {
1346 fn default() -> Self {
1347 Self::new()
1348 }
1349}
1350
1351impl std::fmt::Debug for CljxFuture {
1352 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1353 write!(f, "Future")
1354 }
1355}
1356
1357impl cljrs_gc::Trace for CljxFuture {
1358 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1359 {
1360 let state = self.state.lock().unwrap();
1361 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1364 v.trace(visitor);
1365 }
1366 }
1367 }
1368}
1369
1370pub struct Agent {
1374 pub state: Arc<Mutex<Value>>,
1376 pub error: Arc<Mutex<Option<Value>>>,
1378 pub watches: Mutex<Vec<(Value, Value)>>,
1379}
1380
1381impl Agent {
1382 pub fn get_state(&self) -> Value {
1383 self.state.lock().unwrap().clone()
1384 }
1385
1386 pub fn get_error(&self) -> Option<Value> {
1387 self.error.lock().unwrap().clone()
1388 }
1389
1390 pub fn clear_error(&self) {
1391 *self.error.lock().unwrap() = None;
1392 }
1393}
1394
1395impl std::fmt::Debug for Agent {
1396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1397 write!(f, "Agent")
1398 }
1399}
1400
1401impl cljrs_gc::Trace for Agent {
1402 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1403 {
1404 let state = self.state.lock().unwrap();
1405 state.trace(visitor);
1406 }
1407 {
1408 let error = self.error.lock().unwrap();
1409 if let Some(e) = error.as_ref() {
1410 e.trace(visitor);
1411 }
1412 }
1413 {
1414 let watches = self.watches.lock().unwrap();
1415 for (key, f) in watches.iter() {
1416 key.trace(visitor);
1417 f.trace(visitor);
1418 }
1419 }
1420 }
1421}
1422
1423#[cfg(test)]
1426mod var_tests {
1427 use super::*;
1428 use crate::shared::SharedValue;
1429
1430 #[test]
1431 fn bind_promotable_mirrors_shared_root() {
1432 let var = Var::new("user", "x");
1433 assert!(var.shared_root.load().is_none());
1434 var.bind(Value::Long(7));
1435 assert!(matches!(
1436 var.shared_root.load().as_ref().as_ref(),
1437 Some(SharedValue::Long(7))
1438 ));
1439 assert_eq!(var.deref(), Some(Value::Long(7)));
1440 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1441 }
1442
1443 #[test]
1444 fn bind_nonpromotable_clears_shared_root() {
1445 let var = Var::new("user", "f");
1446 var.bind(Value::Long(1));
1447 assert!(var.shared_root.load().is_some());
1448 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1451 Ok(Value::Nil)
1452 })));
1453 var.bind(f);
1454 assert!(var.shared_root.load().is_none());
1455 assert!(var.is_bound());
1456 assert_eq!(var.deref_shared(), None);
1457 }
1458
1459 #[test]
1460 fn from_shared_root_seeds_local_slot() {
1461 let src = Var::new("user", "y");
1462 src.bind(Value::Long(99));
1463 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1464 assert_eq!(recv.deref(), Some(Value::Long(99)));
1465 src.bind(Value::Long(100));
1467 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1468 }
1469}