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, Clone, Default)]
511pub struct ReferClojureFilter {
512 pub only: Option<std::collections::HashSet<Arc<str>>>,
514 pub exclude: std::collections::HashSet<Arc<str>>,
516 pub rename: HashMap<Arc<str>, Arc<str>>,
520}
521
522impl ReferClojureFilter {
523 pub fn local_name(&self, name: &Arc<str>) -> Option<Arc<str>> {
526 if self.exclude.contains(name) {
527 return None;
528 }
529 if let Some(only) = &self.only
530 && !only.contains(name)
531 {
532 return None;
533 }
534 Some(
535 self.rename
536 .get(name)
537 .cloned()
538 .unwrap_or_else(|| name.clone()),
539 )
540 }
541}
542
543#[derive(Debug)]
545pub struct Namespace {
546 pub name: Arc<str>,
547 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
549 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
551 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
553 pub source_file: Mutex<Option<Arc<str>>>,
557 pub git_repo_root: Mutex<Option<Arc<str>>>,
559 pub is_versioned: bool,
562 pub meta: Mutex<Option<Value>>,
564 pub refer_clojure_filter: Mutex<Option<ReferClojureFilter>>,
567}
568
569impl Namespace {
570 pub fn new(name: impl Into<Arc<str>>) -> Self {
571 Self {
572 name: name.into(),
573 interns: Mutex::new(HashMap::new()),
574 refers: Mutex::new(HashMap::new()),
575 aliases: Mutex::new(HashMap::new()),
576 source_file: Mutex::new(None),
577 git_repo_root: Mutex::new(None),
578 is_versioned: false,
579 meta: Mutex::new(None),
580 refer_clojure_filter: Mutex::new(None),
581 }
582 }
583
584 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
586 Self {
587 is_versioned: true,
588 ..Self::new(name)
589 }
590 }
591
592 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
594 *self.source_file.lock().unwrap() = Some(Arc::from(file));
595 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
596 }
597
598 pub fn get_meta(&self) -> Option<Value> {
599 self.meta.lock().unwrap().clone()
600 }
601
602 pub fn set_meta(&self, m: Value) {
603 *self.meta.lock().unwrap() = Some(m);
604 }
605}
606
607impl cljrs_gc::Trace for Namespace {
608 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
609 use cljrs_gc::GcVisitor as _;
610 {
611 let interns = self.interns.lock().unwrap();
612 for var in interns.values() {
613 visitor.visit(var);
614 }
615 }
616 {
617 let refers = self.refers.lock().unwrap();
618 for var in refers.values() {
619 visitor.visit(var);
620 }
621 }
622 {
623 let meta = self.meta.lock().unwrap();
624 if let Some(m) = meta.as_ref() {
625 m.trace(visitor);
626 }
627 }
628 }
629}
630
631pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
637
638pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
641
642#[derive(Clone, Debug)]
643pub enum Arity {
644 Fixed(usize),
645 Variadic { min: usize },
646}
647
648pub struct NativeFn {
649 pub name: Arc<str>,
650 pub arity: Arity,
651 pub func: NativeFnFunc,
652}
653
654impl NativeFn {
655 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
657 Self {
658 name: name.into(),
659 arity,
660 func: Arc::new(func),
661 }
662 }
663
664 pub fn with_closure(
666 name: impl Into<Arc<str>>,
667 arity: Arity,
668 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
669 ) -> Self {
670 Self {
671 name: name.into(),
672 arity,
673 func: Arc::new(func),
674 }
675 }
676}
677
678impl std::fmt::Debug for NativeFn {
679 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
680 f.debug_struct("NativeFn")
681 .field("name", &self.name)
682 .field("arity", &self.arity)
683 .field("func", &"<fn>")
684 .finish()
685 }
686}
687
688impl cljrs_gc::Trace for NativeFn {
689 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
690}
691
692#[derive(Debug, Clone)]
696pub struct CljxFnArity {
697 pub params: Vec<Arc<str>>,
700 pub rest_param: Option<Arc<str>>,
702 pub body: Vec<Form>,
704 pub destructure_params: Vec<(usize, Form)>,
708 pub destructure_rest: Option<Form>,
710 pub ir_arity_id: u64,
712 pub param_hints: Vec<Option<TypeHint>>,
716 pub rest_hint: Option<TypeHint>,
719}
720
721impl CljxFnArity {
722 pub fn heap_size(&self) -> usize {
724 self.params.capacity() * mem::size_of::<Arc<str>>()
726 + self.body.capacity() * mem::size_of::<Form>()
728 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
729 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
731 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
732 + self.destructure_rest.as_ref()
734 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
735 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
737 }
738}
739
740#[derive(Debug, Clone)]
744pub struct CljxFn {
745 pub name: Option<Arc<str>>,
746 pub arities: Vec<CljxFnArity>,
747 pub closed_over_names: Vec<Arc<str>>,
749 pub closed_over_vals: Vec<Value>,
751 pub is_macro: bool,
753 pub is_async: bool,
758 pub defining_ns: Arc<str>,
760 pub self_ptr: Option<GcPtr<CljxFn>>,
765}
766
767impl CljxFn {
768 pub fn new(
769 name: Option<Arc<str>>,
770 arities: Vec<CljxFnArity>,
771 closed_over_names: Vec<Arc<str>>,
772 closed_over_vals: Vec<Value>,
773 is_macro: bool,
774 defining_ns: Arc<str>,
775 ) -> Self {
776 Self {
777 name,
778 arities,
779 closed_over_names,
780 closed_over_vals,
781 is_macro,
782 is_async: false,
783 defining_ns,
784 self_ptr: None,
785 }
786 }
787}
788
789impl cljrs_gc::Trace for CljxFn {
790 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
791 use cljrs_gc::GcVisitor as _;
792 for v in &self.closed_over_vals {
793 v.trace(visitor);
794 }
795 if let Some(ref p) = self.self_ptr {
796 visitor.visit(p);
797 }
798 }
799
800 fn gc_size_extra(&self) -> usize {
801 self.arities.capacity() * mem::size_of::<CljxFnArity>()
803 + self
804 .arities
805 .iter()
806 .map(CljxFnArity::heap_size)
807 .sum::<usize>()
808 }
809}
810
811#[derive(Debug)]
818pub struct BoundFn {
819 pub wrapped: Value,
821 pub captured_bindings: HashMap<usize, Value>,
823}
824
825impl cljrs_gc::Trace for BoundFn {
826 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
827 self.wrapped.trace(visitor);
828 for val in self.captured_bindings.values() {
829 val.trace(visitor);
830 }
831 }
832
833 fn gc_size_extra(&self) -> usize {
834 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
836 }
837}
838
839pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
843 fn force(&self) -> Result<Value, String>;
844}
845
846pub enum LazySeqState {
848 Pending(Box<dyn Thunk>),
850 Forced(Value),
852 Error(String),
854}
855
856pub struct LazySeq {
858 pub state: Mutex<LazySeqState>,
859}
860
861impl LazySeq {
862 pub fn new(thunk: Box<dyn Thunk>) -> Self {
863 Self {
864 state: Mutex::new(LazySeqState::Pending(thunk)),
865 }
866 }
867
868 pub fn realize(&self) -> Value {
871 let thunk = {
872 let mut guard = self.state.lock().unwrap();
873 match &*guard {
874 LazySeqState::Forced(v) => return v.clone(),
875 LazySeqState::Error(_) => return Value::Nil,
876 LazySeqState::Pending(_) => {}
877 }
878 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
880 let LazySeqState::Pending(thunk) = prev else {
881 unreachable!("state was not Pending")
882 };
883 thunk
884 };
886 match thunk.force() {
889 Ok(result) => {
890 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
891 result
892 }
893 Err(msg) => {
894 *self.state.lock().unwrap() = LazySeqState::Error(msg);
895 Value::Nil
896 }
897 }
898 }
899
900 pub fn error(&self) -> Option<String> {
902 let guard = self.state.lock().unwrap();
903 if let LazySeqState::Error(e) = &*guard {
904 Some(e.clone())
905 } else {
906 None
907 }
908 }
909}
910
911impl std::fmt::Debug for LazySeq {
912 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
913 write!(f, "LazySeq(...)")
914 }
915}
916
917impl cljrs_gc::Trace for LazySeq {
918 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
919 {
922 let state = self.state.lock().unwrap();
923 match &*state {
924 LazySeqState::Pending(thunk) => thunk.trace(visitor),
925 LazySeqState::Forced(v) => v.trace(visitor),
926 LazySeqState::Error(_) => {}
927 }
928 }
929 }
930}
931
932#[derive(Debug, Clone)]
939pub struct CljxCons {
940 pub head: Value,
941 pub tail: Value,
942}
943
944impl cljrs_gc::Trace for CljxCons {
945 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
946 self.head.trace(visitor);
947 self.tail.trace(visitor);
948 }
949}
950
951pub struct Volatile {
955 pub value: Mutex<Value>,
956}
957
958impl Volatile {
959 pub fn new(v: Value) -> Self {
960 let v = crate::publish::publish_value(v);
962 Self {
963 value: Mutex::new(v),
964 }
965 }
966
967 pub fn deref(&self) -> Value {
968 self.value.lock().unwrap().clone()
969 }
970
971 pub fn reset(&self, v: Value) -> Value {
972 #[cfg(all(feature = "no-gc", debug_assertions))]
974 debug_assert!(
975 value_gcptr_is_static(&v),
976 "no-gc: Volatile::reset() received a region-local value — ensure the \
977 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
978 );
979 let v = crate::publish::publish_value(v);
981 *self.value.lock().unwrap() = v.clone();
982 v
983 }
984}
985
986impl std::fmt::Debug for Volatile {
987 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
988 write!(f, "Volatile")
989 }
990}
991
992impl cljrs_gc::Trace for Volatile {
993 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
994 {
995 let value = self.value.lock().unwrap();
996 value.trace(visitor);
997 }
998 }
999}
1000
1001pub enum DelayState {
1005 Pending(Box<dyn Thunk>),
1006 Forced(Value),
1007}
1008
1009pub struct Delay {
1011 pub state: Mutex<DelayState>,
1012}
1013
1014impl Delay {
1015 pub fn new(thunk: Box<dyn Thunk>) -> Self {
1016 Self {
1017 state: Mutex::new(DelayState::Pending(thunk)),
1018 }
1019 }
1020
1021 pub fn force(&self) -> Result<Value, String> {
1024 let thunk = {
1025 let mut guard = self.state.lock().unwrap();
1026 if let DelayState::Forced(v) = &*guard {
1027 return Ok(v.clone());
1028 }
1029 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
1030 let DelayState::Pending(thunk) = prev else {
1031 unreachable!("state was not Pending")
1032 };
1033 thunk
1034 };
1036 let result = thunk.force()?;
1039 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
1040 Ok(result)
1041 }
1042
1043 pub fn is_realized(&self) -> bool {
1045 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
1046 }
1047}
1048
1049impl std::fmt::Debug for Delay {
1050 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1051 write!(f, "Delay")
1052 }
1053}
1054
1055impl cljrs_gc::Trace for Delay {
1056 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1057 {
1060 let state = self.state.lock().unwrap();
1061 match &*state {
1062 DelayState::Pending(thunk) => thunk.trace(visitor),
1063 DelayState::Forced(v) => v.trace(visitor),
1064 }
1065 }
1066 }
1067}
1068
1069pub struct CljxPromise {
1073 pub value: Mutex<Option<Value>>,
1074 pub cond: Condvar,
1075}
1076
1077impl CljxPromise {
1078 pub fn new() -> Self {
1079 Self {
1080 value: Mutex::new(None),
1081 cond: Condvar::new(),
1082 }
1083 }
1084
1085 pub fn deliver(&self, v: Value) {
1087 let v = crate::publish::publish_value(v);
1090 let mut guard = self.value.lock().unwrap();
1091 if guard.is_none() {
1092 *guard = Some(v);
1093 self.cond.notify_all();
1094 }
1095 }
1096
1097 pub fn deref_blocking(&self) -> Value {
1099 let mut guard = self.value.lock().unwrap();
1100 while guard.is_none() {
1101 guard = self.cond.wait(guard).unwrap();
1102 }
1103 guard.as_ref().unwrap().clone()
1104 }
1105
1106 pub fn is_realized(&self) -> bool {
1108 self.value.lock().unwrap().is_some()
1109 }
1110}
1111
1112impl Default for CljxPromise {
1113 fn default() -> Self {
1114 Self::new()
1115 }
1116}
1117
1118impl std::fmt::Debug for CljxPromise {
1119 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1120 write!(f, "Promise")
1121 }
1122}
1123
1124impl cljrs_gc::Trace for CljxPromise {
1125 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1126 {
1127 let value = self.value.lock().unwrap();
1128 if let Some(v) = value.as_ref() {
1129 v.trace(visitor);
1130 }
1131 }
1132 }
1133}
1134
1135pub enum FutureState {
1139 Running,
1140 Done(Value),
1141 Failed(Value),
1145 GasExhausted,
1148 Cancelled,
1149}
1150
1151pub struct CljxFuture {
1153 pub state: Mutex<FutureState>,
1154 pub cond: Condvar,
1155 observed: std::sync::atomic::AtomicBool,
1159}
1160
1161impl CljxFuture {
1162 pub fn new() -> Self {
1163 Self {
1164 state: Mutex::new(FutureState::Running),
1165 cond: Condvar::new(),
1166 observed: std::sync::atomic::AtomicBool::new(false),
1167 }
1168 }
1169
1170 pub fn is_done(&self) -> bool {
1172 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1173 }
1174
1175 pub fn is_cancelled(&self) -> bool {
1177 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1178 }
1179
1180 pub fn mark_observed(&self) {
1184 self.observed
1185 .store(true, std::sync::atomic::Ordering::Relaxed);
1186 }
1187}
1188
1189impl Drop for CljxFuture {
1190 fn drop(&mut self) {
1191 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1202 && let Ok(state) = self.state.lock()
1203 && matches!(&*state, FutureState::Failed(_))
1204 {
1205 eprintln!(
1206 "[clojurust warning] a failed future was discarded without its error \
1207 being observed (no await/deref); the thrown exception was lost"
1208 );
1209 }
1210 }
1211}
1212
1213impl Default for CljxFuture {
1214 fn default() -> Self {
1215 Self::new()
1216 }
1217}
1218
1219impl std::fmt::Debug for CljxFuture {
1220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1221 write!(f, "Future")
1222 }
1223}
1224
1225impl cljrs_gc::Trace for CljxFuture {
1226 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1227 {
1228 let state = self.state.lock().unwrap();
1229 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1232 v.trace(visitor);
1233 }
1234 }
1235 }
1236}
1237
1238pub struct Agent {
1242 pub state: Arc<Mutex<Value>>,
1244 pub error: Arc<Mutex<Option<Value>>>,
1246 pub watches: Mutex<Vec<(Value, Value)>>,
1247}
1248
1249impl Agent {
1250 pub fn get_state(&self) -> Value {
1251 self.state.lock().unwrap().clone()
1252 }
1253
1254 pub fn get_error(&self) -> Option<Value> {
1255 self.error.lock().unwrap().clone()
1256 }
1257
1258 pub fn clear_error(&self) {
1259 *self.error.lock().unwrap() = None;
1260 }
1261}
1262
1263impl std::fmt::Debug for Agent {
1264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1265 write!(f, "Agent")
1266 }
1267}
1268
1269impl cljrs_gc::Trace for Agent {
1270 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1271 {
1272 let state = self.state.lock().unwrap();
1273 state.trace(visitor);
1274 }
1275 {
1276 let error = self.error.lock().unwrap();
1277 if let Some(e) = error.as_ref() {
1278 e.trace(visitor);
1279 }
1280 }
1281 {
1282 let watches = self.watches.lock().unwrap();
1283 for (key, f) in watches.iter() {
1284 key.trace(visitor);
1285 f.trace(visitor);
1286 }
1287 }
1288 }
1289}
1290
1291#[cfg(test)]
1294mod var_tests {
1295 use super::*;
1296 use crate::shared::SharedValue;
1297
1298 #[test]
1299 fn bind_promotable_mirrors_shared_root() {
1300 let var = Var::new("user", "x");
1301 assert!(var.shared_root.load().is_none());
1302 var.bind(Value::Long(7));
1303 assert!(matches!(
1304 var.shared_root.load().as_ref().as_ref(),
1305 Some(SharedValue::Long(7))
1306 ));
1307 assert_eq!(var.deref(), Some(Value::Long(7)));
1308 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1309 }
1310
1311 #[test]
1312 fn bind_nonpromotable_clears_shared_root() {
1313 let var = Var::new("user", "f");
1314 var.bind(Value::Long(1));
1315 assert!(var.shared_root.load().is_some());
1316 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1319 Ok(Value::Nil)
1320 })));
1321 var.bind(f);
1322 assert!(var.shared_root.load().is_none());
1323 assert!(var.is_bound());
1324 assert_eq!(var.deref_shared(), None);
1325 }
1326
1327 #[test]
1328 fn from_shared_root_seeds_local_slot() {
1329 let src = Var::new("user", "y");
1330 src.bind(Value::Long(99));
1331 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1332 assert_eq!(recv.deref(), Some(Value::Long(99)));
1333 src.bind(Value::Long(100));
1335 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1336 }
1337}