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 prefers: Mutex<HashMap<String, Vec<String>>>,
212 pub default_dispatch: String,
214}
215
216impl MultiFn {
217 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
218 Self {
219 name,
220 dispatch_fn,
221 methods: Mutex::new(HashMap::new()),
222 prefers: Mutex::new(HashMap::new()),
223 default_dispatch,
224 }
225 }
226}
227
228impl cljrs_gc::Trace for MultiFn {
229 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
230 self.dispatch_fn.trace(visitor);
231 {
232 let methods = self.methods.lock().unwrap();
233 for v in methods.values() {
234 v.trace(visitor);
235 }
236 }
237 }
238}
239
240#[derive(Debug)]
267pub struct Var {
268 pub namespace: Arc<str>,
269 pub name: Arc<str>,
270 pub value: Mutex<Option<Value>>,
271 pub shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
274 pub is_macro: bool,
275 pub meta: Mutex<Option<Value>>,
277 pub watches: Mutex<Vec<(Value, Value)>>,
278}
279
280impl Var {
281 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
282 Self {
283 namespace: namespace.into(),
284 name: name.into(),
285 value: Mutex::new(None),
286 shared_root: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
287 is_macro: false,
288 meta: Mutex::new(None),
289 watches: Mutex::new(Vec::new()),
290 }
291 }
292
293 pub fn from_shared_root(
300 namespace: impl Into<Arc<str>>,
301 name: impl Into<Arc<str>>,
302 is_macro: bool,
303 shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
304 ) -> Self {
305 let local = shared_root
306 .load()
307 .as_ref()
308 .as_ref()
309 .map(crate::shared::demote);
310 Self {
311 namespace: namespace.into(),
312 name: name.into(),
313 value: Mutex::new(local),
314 shared_root,
315 is_macro,
316 meta: Mutex::new(None),
317 watches: Mutex::new(Vec::new()),
318 }
319 }
320
321 pub fn is_bound(&self) -> bool {
322 self.value.lock().unwrap().is_some()
323 }
324
325 pub fn deref(&self) -> Option<Value> {
326 self.value.lock().unwrap().clone()
327 }
328
329 pub fn deref_shared(&self) -> Option<Value> {
334 self.shared_root
335 .load()
336 .as_ref()
337 .as_ref()
338 .map(crate::shared::demote)
339 }
340
341 pub fn bind(&self, v: Value) {
342 #[cfg(all(feature = "no-gc", debug_assertions))]
346 debug_assert!(
347 value_gcptr_is_static(&v),
348 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
349 indicate a missing StaticCtxGuard around the value expression",
350 self.namespace,
351 self.name
352 );
353 let v = crate::publish::publish_value(v);
358 let prev = {
364 let mut slot = self.value.lock().unwrap();
365 slot.replace(v.clone())
366 };
367 let shared = crate::shared::promote(&v).ok();
373 self.shared_root.store(Arc::new(shared));
374 if let Some(prev) = prev {
375 crate::jit_hooks::notify_var_rebind(&prev, &v);
376 }
377 }
378
379 pub fn get_meta(&self) -> Option<Value> {
380 self.meta.lock().unwrap().clone()
381 }
382
383 pub fn set_meta(&self, m: Value) {
384 *self.meta.lock().unwrap() = Some(m);
385 }
386
387 pub fn full_name(&self) -> String {
388 format!("{}/{}", self.namespace, self.name)
389 }
390}
391
392impl cljrs_gc::Trace for Var {
393 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
394 {
395 let value = self.value.lock().unwrap();
396 if let Some(v) = value.as_ref() {
397 v.trace(visitor);
398 }
399 }
400 {
401 let meta = self.meta.lock().unwrap();
402 if let Some(m) = meta.as_ref() {
403 m.trace(visitor);
404 }
405 }
406 {
407 let watches = self.watches.lock().unwrap();
408 for (key, f) in watches.iter() {
409 key.trace(visitor);
410 f.trace(visitor);
411 }
412 }
413 }
414}
415
416#[derive(Debug)]
420pub struct Atom {
421 pub value: Mutex<Value>,
422 pub meta: Mutex<Option<Value>>,
423 pub validator: Mutex<Option<Value>>,
424 pub watches: Mutex<Vec<(Value, Value)>>,
425}
426
427impl Atom {
428 pub fn new(v: Value) -> Self {
429 let v = crate::publish::publish_value(v);
432 Self {
433 value: Mutex::new(v),
434 meta: Mutex::new(None),
435 validator: Mutex::new(None),
436 watches: Mutex::new(Vec::new()),
437 }
438 }
439
440 pub fn deref(&self) -> Value {
441 self.value.lock().unwrap().clone()
442 }
443
444 pub fn reset(&self, v: Value) -> Value {
445 #[cfg(all(feature = "no-gc", debug_assertions))]
447 debug_assert!(
448 value_gcptr_is_static(&v),
449 "no-gc: Atom::reset() received a region-local value — the new-value \
450 expression must be computed inside a StaticCtxGuard (i.e. inside \
451 the swap! / reset! call) so it is allocated in the static arena"
452 );
453 let v = crate::publish::publish_value(v);
455 let mut guard = self.value.lock().unwrap();
456 *guard = v.clone();
457 v
458 }
459
460 pub fn get_meta(&self) -> Option<Value> {
461 self.meta.lock().unwrap().clone()
462 }
463
464 pub fn set_meta(&self, m: Option<Value>) {
465 *self.meta.lock().unwrap() = m;
466 }
467
468 pub fn get_validator(&self) -> Option<Value> {
469 self.validator.lock().unwrap().clone()
470 }
471
472 pub fn set_validator(&self, vf: Option<Value>) {
473 *self.validator.lock().unwrap() = vf;
474 }
475}
476
477impl cljrs_gc::Trace for Atom {
478 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
479 {
480 let value = self.value.lock().unwrap();
481 value.trace(visitor);
482 }
483 {
484 let meta = self.meta.lock().unwrap();
485 if let Some(m) = meta.as_ref() {
486 m.trace(visitor);
487 }
488 }
489 {
490 let validator = self.validator.lock().unwrap();
491 if let Some(vf) = validator.as_ref() {
492 vf.trace(visitor);
493 }
494 }
495 {
496 let watches = self.watches.lock().unwrap();
497 for (key, f) in watches.iter() {
498 key.trace(visitor);
499 f.trace(visitor);
500 }
501 }
502 }
503}
504
505#[derive(Debug)]
509pub struct Namespace {
510 pub name: Arc<str>,
511 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
513 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
515 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
517 pub source_file: Mutex<Option<Arc<str>>>,
521 pub git_repo_root: Mutex<Option<Arc<str>>>,
523 pub is_versioned: bool,
526 pub meta: Mutex<Option<Value>>,
528}
529
530impl Namespace {
531 pub fn new(name: impl Into<Arc<str>>) -> Self {
532 Self {
533 name: name.into(),
534 interns: Mutex::new(HashMap::new()),
535 refers: Mutex::new(HashMap::new()),
536 aliases: Mutex::new(HashMap::new()),
537 source_file: Mutex::new(None),
538 git_repo_root: Mutex::new(None),
539 is_versioned: false,
540 meta: Mutex::new(None),
541 }
542 }
543
544 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
546 Self {
547 is_versioned: true,
548 ..Self::new(name)
549 }
550 }
551
552 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
554 *self.source_file.lock().unwrap() = Some(Arc::from(file));
555 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
556 }
557
558 pub fn get_meta(&self) -> Option<Value> {
559 self.meta.lock().unwrap().clone()
560 }
561
562 pub fn set_meta(&self, m: Value) {
563 *self.meta.lock().unwrap() = Some(m);
564 }
565}
566
567impl cljrs_gc::Trace for Namespace {
568 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
569 use cljrs_gc::GcVisitor as _;
570 {
571 let interns = self.interns.lock().unwrap();
572 for var in interns.values() {
573 visitor.visit(var);
574 }
575 }
576 {
577 let refers = self.refers.lock().unwrap();
578 for var in refers.values() {
579 visitor.visit(var);
580 }
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}
590
591pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
597
598pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
601
602#[derive(Clone, Debug)]
603pub enum Arity {
604 Fixed(usize),
605 Variadic { min: usize },
606}
607
608pub struct NativeFn {
609 pub name: Arc<str>,
610 pub arity: Arity,
611 pub func: NativeFnFunc,
612}
613
614impl NativeFn {
615 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
617 Self {
618 name: name.into(),
619 arity,
620 func: Arc::new(func),
621 }
622 }
623
624 pub fn with_closure(
626 name: impl Into<Arc<str>>,
627 arity: Arity,
628 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
629 ) -> Self {
630 Self {
631 name: name.into(),
632 arity,
633 func: Arc::new(func),
634 }
635 }
636}
637
638impl std::fmt::Debug for NativeFn {
639 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
640 f.debug_struct("NativeFn")
641 .field("name", &self.name)
642 .field("arity", &self.arity)
643 .field("func", &"<fn>")
644 .finish()
645 }
646}
647
648impl cljrs_gc::Trace for NativeFn {
649 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
650}
651
652#[derive(Debug, Clone)]
656pub struct CljxFnArity {
657 pub params: Vec<Arc<str>>,
660 pub rest_param: Option<Arc<str>>,
662 pub body: Vec<Form>,
664 pub destructure_params: Vec<(usize, Form)>,
668 pub destructure_rest: Option<Form>,
670 pub ir_arity_id: u64,
672 pub param_hints: Vec<Option<TypeHint>>,
676 pub rest_hint: Option<TypeHint>,
679}
680
681impl CljxFnArity {
682 pub fn heap_size(&self) -> usize {
684 self.params.capacity() * mem::size_of::<Arc<str>>()
686 + self.body.capacity() * mem::size_of::<Form>()
688 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
689 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
691 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
692 + self.destructure_rest.as_ref()
694 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
695 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
697 }
698}
699
700#[derive(Debug, Clone)]
704pub struct CljxFn {
705 pub name: Option<Arc<str>>,
706 pub arities: Vec<CljxFnArity>,
707 pub closed_over_names: Vec<Arc<str>>,
709 pub closed_over_vals: Vec<Value>,
711 pub is_macro: bool,
713 pub is_async: bool,
718 pub defining_ns: Arc<str>,
720 pub self_ptr: Option<GcPtr<CljxFn>>,
725}
726
727impl CljxFn {
728 pub fn new(
729 name: Option<Arc<str>>,
730 arities: Vec<CljxFnArity>,
731 closed_over_names: Vec<Arc<str>>,
732 closed_over_vals: Vec<Value>,
733 is_macro: bool,
734 defining_ns: Arc<str>,
735 ) -> Self {
736 Self {
737 name,
738 arities,
739 closed_over_names,
740 closed_over_vals,
741 is_macro,
742 is_async: false,
743 defining_ns,
744 self_ptr: None,
745 }
746 }
747}
748
749impl cljrs_gc::Trace for CljxFn {
750 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
751 use cljrs_gc::GcVisitor as _;
752 for v in &self.closed_over_vals {
753 v.trace(visitor);
754 }
755 if let Some(ref p) = self.self_ptr {
756 visitor.visit(p);
757 }
758 }
759
760 fn gc_size_extra(&self) -> usize {
761 self.arities.capacity() * mem::size_of::<CljxFnArity>()
763 + self
764 .arities
765 .iter()
766 .map(CljxFnArity::heap_size)
767 .sum::<usize>()
768 }
769}
770
771#[derive(Debug)]
778pub struct BoundFn {
779 pub wrapped: Value,
781 pub captured_bindings: HashMap<usize, Value>,
783}
784
785impl cljrs_gc::Trace for BoundFn {
786 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
787 self.wrapped.trace(visitor);
788 for val in self.captured_bindings.values() {
789 val.trace(visitor);
790 }
791 }
792
793 fn gc_size_extra(&self) -> usize {
794 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
796 }
797}
798
799pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
803 fn force(&self) -> Result<Value, String>;
804}
805
806pub enum LazySeqState {
808 Pending(Box<dyn Thunk>),
810 Forced(Value),
812 Error(String),
814}
815
816pub struct LazySeq {
818 pub state: Mutex<LazySeqState>,
819}
820
821impl LazySeq {
822 pub fn new(thunk: Box<dyn Thunk>) -> Self {
823 Self {
824 state: Mutex::new(LazySeqState::Pending(thunk)),
825 }
826 }
827
828 pub fn realize(&self) -> Value {
831 let thunk = {
832 let mut guard = self.state.lock().unwrap();
833 match &*guard {
834 LazySeqState::Forced(v) => return v.clone(),
835 LazySeqState::Error(_) => return Value::Nil,
836 LazySeqState::Pending(_) => {}
837 }
838 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
840 let LazySeqState::Pending(thunk) = prev else {
841 unreachable!("state was not Pending")
842 };
843 thunk
844 };
846 match thunk.force() {
849 Ok(result) => {
850 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
851 result
852 }
853 Err(msg) => {
854 *self.state.lock().unwrap() = LazySeqState::Error(msg);
855 Value::Nil
856 }
857 }
858 }
859
860 pub fn error(&self) -> Option<String> {
862 let guard = self.state.lock().unwrap();
863 if let LazySeqState::Error(e) = &*guard {
864 Some(e.clone())
865 } else {
866 None
867 }
868 }
869}
870
871impl std::fmt::Debug for LazySeq {
872 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
873 write!(f, "LazySeq(...)")
874 }
875}
876
877impl cljrs_gc::Trace for LazySeq {
878 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
879 {
882 let state = self.state.lock().unwrap();
883 match &*state {
884 LazySeqState::Pending(thunk) => thunk.trace(visitor),
885 LazySeqState::Forced(v) => v.trace(visitor),
886 LazySeqState::Error(_) => {}
887 }
888 }
889 }
890}
891
892#[derive(Debug, Clone)]
899pub struct CljxCons {
900 pub head: Value,
901 pub tail: Value,
902}
903
904impl cljrs_gc::Trace for CljxCons {
905 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
906 self.head.trace(visitor);
907 self.tail.trace(visitor);
908 }
909}
910
911pub struct Volatile {
915 pub value: Mutex<Value>,
916}
917
918impl Volatile {
919 pub fn new(v: Value) -> Self {
920 let v = crate::publish::publish_value(v);
922 Self {
923 value: Mutex::new(v),
924 }
925 }
926
927 pub fn deref(&self) -> Value {
928 self.value.lock().unwrap().clone()
929 }
930
931 pub fn reset(&self, v: Value) -> Value {
932 #[cfg(all(feature = "no-gc", debug_assertions))]
934 debug_assert!(
935 value_gcptr_is_static(&v),
936 "no-gc: Volatile::reset() received a region-local value — ensure the \
937 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
938 );
939 let v = crate::publish::publish_value(v);
941 *self.value.lock().unwrap() = v.clone();
942 v
943 }
944}
945
946impl std::fmt::Debug for Volatile {
947 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
948 write!(f, "Volatile")
949 }
950}
951
952impl cljrs_gc::Trace for Volatile {
953 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
954 {
955 let value = self.value.lock().unwrap();
956 value.trace(visitor);
957 }
958 }
959}
960
961pub enum DelayState {
965 Pending(Box<dyn Thunk>),
966 Forced(Value),
967}
968
969pub struct Delay {
971 pub state: Mutex<DelayState>,
972}
973
974impl Delay {
975 pub fn new(thunk: Box<dyn Thunk>) -> Self {
976 Self {
977 state: Mutex::new(DelayState::Pending(thunk)),
978 }
979 }
980
981 pub fn force(&self) -> Result<Value, String> {
984 let thunk = {
985 let mut guard = self.state.lock().unwrap();
986 if let DelayState::Forced(v) = &*guard {
987 return Ok(v.clone());
988 }
989 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
990 let DelayState::Pending(thunk) = prev else {
991 unreachable!("state was not Pending")
992 };
993 thunk
994 };
996 let result = thunk.force()?;
999 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
1000 Ok(result)
1001 }
1002
1003 pub fn is_realized(&self) -> bool {
1005 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
1006 }
1007}
1008
1009impl std::fmt::Debug for Delay {
1010 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1011 write!(f, "Delay")
1012 }
1013}
1014
1015impl cljrs_gc::Trace for Delay {
1016 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1017 {
1020 let state = self.state.lock().unwrap();
1021 match &*state {
1022 DelayState::Pending(thunk) => thunk.trace(visitor),
1023 DelayState::Forced(v) => v.trace(visitor),
1024 }
1025 }
1026 }
1027}
1028
1029pub struct CljxPromise {
1033 pub value: Mutex<Option<Value>>,
1034 pub cond: Condvar,
1035}
1036
1037impl CljxPromise {
1038 pub fn new() -> Self {
1039 Self {
1040 value: Mutex::new(None),
1041 cond: Condvar::new(),
1042 }
1043 }
1044
1045 pub fn deliver(&self, v: Value) {
1047 let v = crate::publish::publish_value(v);
1050 let mut guard = self.value.lock().unwrap();
1051 if guard.is_none() {
1052 *guard = Some(v);
1053 self.cond.notify_all();
1054 }
1055 }
1056
1057 pub fn deref_blocking(&self) -> Value {
1059 let mut guard = self.value.lock().unwrap();
1060 while guard.is_none() {
1061 guard = self.cond.wait(guard).unwrap();
1062 }
1063 guard.as_ref().unwrap().clone()
1064 }
1065
1066 pub fn is_realized(&self) -> bool {
1068 self.value.lock().unwrap().is_some()
1069 }
1070}
1071
1072impl Default for CljxPromise {
1073 fn default() -> Self {
1074 Self::new()
1075 }
1076}
1077
1078impl std::fmt::Debug for CljxPromise {
1079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1080 write!(f, "Promise")
1081 }
1082}
1083
1084impl cljrs_gc::Trace for CljxPromise {
1085 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1086 {
1087 let value = self.value.lock().unwrap();
1088 if let Some(v) = value.as_ref() {
1089 v.trace(visitor);
1090 }
1091 }
1092 }
1093}
1094
1095pub enum FutureState {
1099 Running,
1100 Done(Value),
1101 Failed(Value),
1105 GasExhausted,
1108 Cancelled,
1109}
1110
1111pub struct CljxFuture {
1113 pub state: Mutex<FutureState>,
1114 pub cond: Condvar,
1115 observed: std::sync::atomic::AtomicBool,
1119}
1120
1121impl CljxFuture {
1122 pub fn new() -> Self {
1123 Self {
1124 state: Mutex::new(FutureState::Running),
1125 cond: Condvar::new(),
1126 observed: std::sync::atomic::AtomicBool::new(false),
1127 }
1128 }
1129
1130 pub fn is_done(&self) -> bool {
1132 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1133 }
1134
1135 pub fn is_cancelled(&self) -> bool {
1137 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1138 }
1139
1140 pub fn mark_observed(&self) {
1144 self.observed
1145 .store(true, std::sync::atomic::Ordering::Relaxed);
1146 }
1147}
1148
1149impl Drop for CljxFuture {
1150 fn drop(&mut self) {
1151 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1162 && let Ok(state) = self.state.lock()
1163 && matches!(&*state, FutureState::Failed(_))
1164 {
1165 eprintln!(
1166 "[clojurust warning] a failed future was discarded without its error \
1167 being observed (no await/deref); the thrown exception was lost"
1168 );
1169 }
1170 }
1171}
1172
1173impl Default for CljxFuture {
1174 fn default() -> Self {
1175 Self::new()
1176 }
1177}
1178
1179impl std::fmt::Debug for CljxFuture {
1180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1181 write!(f, "Future")
1182 }
1183}
1184
1185impl cljrs_gc::Trace for CljxFuture {
1186 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1187 {
1188 let state = self.state.lock().unwrap();
1189 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1192 v.trace(visitor);
1193 }
1194 }
1195 }
1196}
1197
1198pub struct Agent {
1202 pub state: Arc<Mutex<Value>>,
1204 pub error: Arc<Mutex<Option<Value>>>,
1206 pub watches: Mutex<Vec<(Value, Value)>>,
1207}
1208
1209impl Agent {
1210 pub fn get_state(&self) -> Value {
1211 self.state.lock().unwrap().clone()
1212 }
1213
1214 pub fn get_error(&self) -> Option<Value> {
1215 self.error.lock().unwrap().clone()
1216 }
1217
1218 pub fn clear_error(&self) {
1219 *self.error.lock().unwrap() = None;
1220 }
1221}
1222
1223impl std::fmt::Debug for Agent {
1224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1225 write!(f, "Agent")
1226 }
1227}
1228
1229impl cljrs_gc::Trace for Agent {
1230 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1231 {
1232 let state = self.state.lock().unwrap();
1233 state.trace(visitor);
1234 }
1235 {
1236 let error = self.error.lock().unwrap();
1237 if let Some(e) = error.as_ref() {
1238 e.trace(visitor);
1239 }
1240 }
1241 {
1242 let watches = self.watches.lock().unwrap();
1243 for (key, f) in watches.iter() {
1244 key.trace(visitor);
1245 f.trace(visitor);
1246 }
1247 }
1248 }
1249}
1250
1251#[cfg(test)]
1254mod var_tests {
1255 use super::*;
1256 use crate::shared::SharedValue;
1257
1258 #[test]
1259 fn bind_promotable_mirrors_shared_root() {
1260 let var = Var::new("user", "x");
1261 assert!(var.shared_root.load().is_none());
1262 var.bind(Value::Long(7));
1263 assert!(matches!(
1264 var.shared_root.load().as_ref().as_ref(),
1265 Some(SharedValue::Long(7))
1266 ));
1267 assert_eq!(var.deref(), Some(Value::Long(7)));
1268 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1269 }
1270
1271 #[test]
1272 fn bind_nonpromotable_clears_shared_root() {
1273 let var = Var::new("user", "f");
1274 var.bind(Value::Long(1));
1275 assert!(var.shared_root.load().is_some());
1276 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1279 Ok(Value::Nil)
1280 })));
1281 var.bind(f);
1282 assert!(var.shared_root.load().is_none());
1283 assert!(var.is_bound());
1284 assert_eq!(var.deref_shared(), None);
1285 }
1286
1287 #[test]
1288 fn from_shared_root_seeds_local_slot() {
1289 let src = Var::new("user", "y");
1290 src.bind(Value::Long(99));
1291 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1292 assert_eq!(recv.deref(), Some(Value::Long(99)));
1293 src.bind(Value::Long(100));
1295 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1296 }
1297}