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}
115
116impl Protocol {
117 pub fn new(name: Arc<str>, ns: Arc<str>, methods: Vec<ProtocolMethod>) -> Self {
118 Self {
119 name,
120 ns,
121 methods,
122 impls: Mutex::new(HashMap::new()),
123 }
124 }
125}
126
127static PROTOCOL_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
136
137pub fn protocol_generation() -> u64 {
139 PROTOCOL_GENERATION.load(std::sync::atomic::Ordering::Acquire)
140}
141
142pub fn bump_protocol_generation() {
145 PROTOCOL_GENERATION.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
146}
147
148impl cljrs_gc::Trace for Protocol {
149 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
150 {
151 let impls = self.impls.lock().unwrap();
152 for method_map in impls.values() {
153 for v in method_map.values() {
154 v.trace(visitor);
155 }
156 }
157 }
158 }
159}
160
161#[derive(Debug, Clone)]
163pub struct ProtocolMethod {
164 pub name: Arc<str>,
165 pub min_arity: usize,
166 pub variadic: bool,
167}
168
169impl cljrs_gc::Trace for ProtocolMethod {
170 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
171}
172
173#[derive(Debug)]
177pub struct ProtocolFn {
178 pub protocol: GcPtr<Protocol>,
179 pub method_name: Arc<str>,
180 pub min_arity: usize,
181 pub variadic: bool,
182}
183
184impl cljrs_gc::Trace for ProtocolFn {
185 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
186 use cljrs_gc::GcVisitor as _;
187 visitor.visit(&self.protocol);
188 }
189}
190
191#[derive(Debug)]
195pub struct MultiFn {
196 pub name: Arc<str>,
197 pub dispatch_fn: Value,
198 pub methods: Mutex<HashMap<String, Value>>,
200 pub prefers: Mutex<HashMap<String, Vec<String>>>,
202 pub default_dispatch: String,
204}
205
206impl MultiFn {
207 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
208 Self {
209 name,
210 dispatch_fn,
211 methods: Mutex::new(HashMap::new()),
212 prefers: Mutex::new(HashMap::new()),
213 default_dispatch,
214 }
215 }
216}
217
218impl cljrs_gc::Trace for MultiFn {
219 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
220 self.dispatch_fn.trace(visitor);
221 {
222 let methods = self.methods.lock().unwrap();
223 for v in methods.values() {
224 v.trace(visitor);
225 }
226 }
227 }
228}
229
230#[derive(Debug)]
257pub struct Var {
258 pub namespace: Arc<str>,
259 pub name: Arc<str>,
260 pub value: Mutex<Option<Value>>,
261 pub shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
264 pub is_macro: bool,
265 pub meta: Mutex<Option<Value>>,
267 pub watches: Mutex<Vec<(Value, Value)>>,
268}
269
270impl Var {
271 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
272 Self {
273 namespace: namespace.into(),
274 name: name.into(),
275 value: Mutex::new(None),
276 shared_root: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
277 is_macro: false,
278 meta: Mutex::new(None),
279 watches: Mutex::new(Vec::new()),
280 }
281 }
282
283 pub fn from_shared_root(
290 namespace: impl Into<Arc<str>>,
291 name: impl Into<Arc<str>>,
292 is_macro: bool,
293 shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
294 ) -> Self {
295 let local = shared_root
296 .load()
297 .as_ref()
298 .as_ref()
299 .map(crate::shared::demote);
300 Self {
301 namespace: namespace.into(),
302 name: name.into(),
303 value: Mutex::new(local),
304 shared_root,
305 is_macro,
306 meta: Mutex::new(None),
307 watches: Mutex::new(Vec::new()),
308 }
309 }
310
311 pub fn is_bound(&self) -> bool {
312 self.value.lock().unwrap().is_some()
313 }
314
315 pub fn deref(&self) -> Option<Value> {
316 self.value.lock().unwrap().clone()
317 }
318
319 pub fn deref_shared(&self) -> Option<Value> {
324 self.shared_root
325 .load()
326 .as_ref()
327 .as_ref()
328 .map(crate::shared::demote)
329 }
330
331 pub fn bind(&self, v: Value) {
332 #[cfg(all(feature = "no-gc", debug_assertions))]
336 debug_assert!(
337 value_gcptr_is_static(&v),
338 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
339 indicate a missing StaticCtxGuard around the value expression",
340 self.namespace,
341 self.name
342 );
343 let v = crate::publish::publish_value(v);
348 let prev = {
354 let mut slot = self.value.lock().unwrap();
355 slot.replace(v.clone())
356 };
357 let shared = crate::shared::promote(&v).ok();
363 self.shared_root.store(Arc::new(shared));
364 if let Some(prev) = prev {
365 crate::jit_hooks::notify_var_rebind(&prev, &v);
366 }
367 }
368
369 pub fn get_meta(&self) -> Option<Value> {
370 self.meta.lock().unwrap().clone()
371 }
372
373 pub fn set_meta(&self, m: Value) {
374 *self.meta.lock().unwrap() = Some(m);
375 }
376
377 pub fn full_name(&self) -> String {
378 format!("{}/{}", self.namespace, self.name)
379 }
380}
381
382impl cljrs_gc::Trace for Var {
383 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
384 {
385 let value = self.value.lock().unwrap();
386 if let Some(v) = value.as_ref() {
387 v.trace(visitor);
388 }
389 }
390 {
391 let meta = self.meta.lock().unwrap();
392 if let Some(m) = meta.as_ref() {
393 m.trace(visitor);
394 }
395 }
396 {
397 let watches = self.watches.lock().unwrap();
398 for (key, f) in watches.iter() {
399 key.trace(visitor);
400 f.trace(visitor);
401 }
402 }
403 }
404}
405
406#[derive(Debug)]
410pub struct Atom {
411 pub value: Mutex<Value>,
412 pub meta: Mutex<Option<Value>>,
413 pub validator: Mutex<Option<Value>>,
414 pub watches: Mutex<Vec<(Value, Value)>>,
415}
416
417impl Atom {
418 pub fn new(v: Value) -> Self {
419 let v = crate::publish::publish_value(v);
422 Self {
423 value: Mutex::new(v),
424 meta: Mutex::new(None),
425 validator: Mutex::new(None),
426 watches: Mutex::new(Vec::new()),
427 }
428 }
429
430 pub fn deref(&self) -> Value {
431 self.value.lock().unwrap().clone()
432 }
433
434 pub fn reset(&self, v: Value) -> Value {
435 #[cfg(all(feature = "no-gc", debug_assertions))]
437 debug_assert!(
438 value_gcptr_is_static(&v),
439 "no-gc: Atom::reset() received a region-local value — the new-value \
440 expression must be computed inside a StaticCtxGuard (i.e. inside \
441 the swap! / reset! call) so it is allocated in the static arena"
442 );
443 let v = crate::publish::publish_value(v);
445 let mut guard = self.value.lock().unwrap();
446 *guard = v.clone();
447 v
448 }
449
450 pub fn get_meta(&self) -> Option<Value> {
451 self.meta.lock().unwrap().clone()
452 }
453
454 pub fn set_meta(&self, m: Option<Value>) {
455 *self.meta.lock().unwrap() = m;
456 }
457
458 pub fn get_validator(&self) -> Option<Value> {
459 self.validator.lock().unwrap().clone()
460 }
461
462 pub fn set_validator(&self, vf: Option<Value>) {
463 *self.validator.lock().unwrap() = vf;
464 }
465}
466
467impl cljrs_gc::Trace for Atom {
468 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
469 {
470 let value = self.value.lock().unwrap();
471 value.trace(visitor);
472 }
473 {
474 let meta = self.meta.lock().unwrap();
475 if let Some(m) = meta.as_ref() {
476 m.trace(visitor);
477 }
478 }
479 {
480 let validator = self.validator.lock().unwrap();
481 if let Some(vf) = validator.as_ref() {
482 vf.trace(visitor);
483 }
484 }
485 {
486 let watches = self.watches.lock().unwrap();
487 for (key, f) in watches.iter() {
488 key.trace(visitor);
489 f.trace(visitor);
490 }
491 }
492 }
493}
494
495#[derive(Debug)]
499pub struct Namespace {
500 pub name: Arc<str>,
501 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
503 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
505 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
507 pub source_file: Mutex<Option<Arc<str>>>,
511 pub git_repo_root: Mutex<Option<Arc<str>>>,
513 pub is_versioned: bool,
516}
517
518impl Namespace {
519 pub fn new(name: impl Into<Arc<str>>) -> Self {
520 Self {
521 name: name.into(),
522 interns: Mutex::new(HashMap::new()),
523 refers: Mutex::new(HashMap::new()),
524 aliases: Mutex::new(HashMap::new()),
525 source_file: Mutex::new(None),
526 git_repo_root: Mutex::new(None),
527 is_versioned: false,
528 }
529 }
530
531 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
533 Self {
534 is_versioned: true,
535 ..Self::new(name)
536 }
537 }
538
539 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
541 *self.source_file.lock().unwrap() = Some(Arc::from(file));
542 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
543 }
544}
545
546impl cljrs_gc::Trace for Namespace {
547 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
548 use cljrs_gc::GcVisitor as _;
549 {
550 let interns = self.interns.lock().unwrap();
551 for var in interns.values() {
552 visitor.visit(var);
553 }
554 }
555 {
556 let refers = self.refers.lock().unwrap();
557 for var in refers.values() {
558 visitor.visit(var);
559 }
560 }
561 }
562}
563
564pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
570
571pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
574
575#[derive(Clone, Debug)]
576pub enum Arity {
577 Fixed(usize),
578 Variadic { min: usize },
579}
580
581pub struct NativeFn {
582 pub name: Arc<str>,
583 pub arity: Arity,
584 pub func: NativeFnFunc,
585}
586
587impl NativeFn {
588 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
590 Self {
591 name: name.into(),
592 arity,
593 func: Arc::new(func),
594 }
595 }
596
597 pub fn with_closure(
599 name: impl Into<Arc<str>>,
600 arity: Arity,
601 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
602 ) -> Self {
603 Self {
604 name: name.into(),
605 arity,
606 func: Arc::new(func),
607 }
608 }
609}
610
611impl std::fmt::Debug for NativeFn {
612 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
613 f.debug_struct("NativeFn")
614 .field("name", &self.name)
615 .field("arity", &self.arity)
616 .field("func", &"<fn>")
617 .finish()
618 }
619}
620
621impl cljrs_gc::Trace for NativeFn {
622 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
623}
624
625#[derive(Debug, Clone)]
629pub struct CljxFnArity {
630 pub params: Vec<Arc<str>>,
633 pub rest_param: Option<Arc<str>>,
635 pub body: Vec<Form>,
637 pub destructure_params: Vec<(usize, Form)>,
641 pub destructure_rest: Option<Form>,
643 pub ir_arity_id: u64,
645 pub param_hints: Vec<Option<TypeHint>>,
649 pub rest_hint: Option<TypeHint>,
652}
653
654impl CljxFnArity {
655 pub fn heap_size(&self) -> usize {
657 self.params.capacity() * mem::size_of::<Arc<str>>()
659 + self.body.capacity() * mem::size_of::<Form>()
661 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
662 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
664 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
665 + self.destructure_rest.as_ref()
667 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
668 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
670 }
671}
672
673#[derive(Debug, Clone)]
677pub struct CljxFn {
678 pub name: Option<Arc<str>>,
679 pub arities: Vec<CljxFnArity>,
680 pub closed_over_names: Vec<Arc<str>>,
682 pub closed_over_vals: Vec<Value>,
684 pub is_macro: bool,
686 pub is_async: bool,
691 pub defining_ns: Arc<str>,
693}
694
695impl CljxFn {
696 pub fn new(
697 name: Option<Arc<str>>,
698 arities: Vec<CljxFnArity>,
699 closed_over_names: Vec<Arc<str>>,
700 closed_over_vals: Vec<Value>,
701 is_macro: bool,
702 defining_ns: Arc<str>,
703 ) -> Self {
704 Self {
705 name,
706 arities,
707 closed_over_names,
708 closed_over_vals,
709 is_macro,
710 is_async: false,
711 defining_ns,
712 }
713 }
714}
715
716impl cljrs_gc::Trace for CljxFn {
717 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
718 for v in &self.closed_over_vals {
719 v.trace(visitor);
720 }
721 }
722
723 fn gc_size_extra(&self) -> usize {
724 self.arities.capacity() * mem::size_of::<CljxFnArity>()
726 + self
727 .arities
728 .iter()
729 .map(CljxFnArity::heap_size)
730 .sum::<usize>()
731 }
732}
733
734#[derive(Debug)]
741pub struct BoundFn {
742 pub wrapped: Value,
744 pub captured_bindings: HashMap<usize, Value>,
746}
747
748impl cljrs_gc::Trace for BoundFn {
749 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
750 self.wrapped.trace(visitor);
751 for val in self.captured_bindings.values() {
752 val.trace(visitor);
753 }
754 }
755
756 fn gc_size_extra(&self) -> usize {
757 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
759 }
760}
761
762pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
766 fn force(&self) -> Result<Value, String>;
767}
768
769pub enum LazySeqState {
771 Pending(Box<dyn Thunk>),
773 Forced(Value),
775 Error(String),
777}
778
779pub struct LazySeq {
781 pub state: Mutex<LazySeqState>,
782}
783
784impl LazySeq {
785 pub fn new(thunk: Box<dyn Thunk>) -> Self {
786 Self {
787 state: Mutex::new(LazySeqState::Pending(thunk)),
788 }
789 }
790
791 pub fn realize(&self) -> Value {
794 let thunk = {
795 let mut guard = self.state.lock().unwrap();
796 match &*guard {
797 LazySeqState::Forced(v) => return v.clone(),
798 LazySeqState::Error(_) => return Value::Nil,
799 LazySeqState::Pending(_) => {}
800 }
801 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
803 let LazySeqState::Pending(thunk) = prev else {
804 unreachable!("state was not Pending")
805 };
806 thunk
807 };
809 match thunk.force() {
812 Ok(result) => {
813 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
814 result
815 }
816 Err(msg) => {
817 *self.state.lock().unwrap() = LazySeqState::Error(msg);
818 Value::Nil
819 }
820 }
821 }
822
823 pub fn error(&self) -> Option<String> {
825 let guard = self.state.lock().unwrap();
826 if let LazySeqState::Error(e) = &*guard {
827 Some(e.clone())
828 } else {
829 None
830 }
831 }
832}
833
834impl std::fmt::Debug for LazySeq {
835 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
836 write!(f, "LazySeq(...)")
837 }
838}
839
840impl cljrs_gc::Trace for LazySeq {
841 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
842 {
845 let state = self.state.lock().unwrap();
846 match &*state {
847 LazySeqState::Pending(thunk) => thunk.trace(visitor),
848 LazySeqState::Forced(v) => v.trace(visitor),
849 LazySeqState::Error(_) => {}
850 }
851 }
852 }
853}
854
855#[derive(Debug, Clone)]
862pub struct CljxCons {
863 pub head: Value,
864 pub tail: Value,
865}
866
867impl cljrs_gc::Trace for CljxCons {
868 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
869 self.head.trace(visitor);
870 self.tail.trace(visitor);
871 }
872}
873
874pub struct Volatile {
878 pub value: Mutex<Value>,
879}
880
881impl Volatile {
882 pub fn new(v: Value) -> Self {
883 let v = crate::publish::publish_value(v);
885 Self {
886 value: Mutex::new(v),
887 }
888 }
889
890 pub fn deref(&self) -> Value {
891 self.value.lock().unwrap().clone()
892 }
893
894 pub fn reset(&self, v: Value) -> Value {
895 #[cfg(all(feature = "no-gc", debug_assertions))]
897 debug_assert!(
898 value_gcptr_is_static(&v),
899 "no-gc: Volatile::reset() received a region-local value — ensure the \
900 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
901 );
902 let v = crate::publish::publish_value(v);
904 *self.value.lock().unwrap() = v.clone();
905 v
906 }
907}
908
909impl std::fmt::Debug for Volatile {
910 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
911 write!(f, "Volatile")
912 }
913}
914
915impl cljrs_gc::Trace for Volatile {
916 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
917 {
918 let value = self.value.lock().unwrap();
919 value.trace(visitor);
920 }
921 }
922}
923
924pub enum DelayState {
928 Pending(Box<dyn Thunk>),
929 Forced(Value),
930}
931
932pub struct Delay {
934 pub state: Mutex<DelayState>,
935}
936
937impl Delay {
938 pub fn new(thunk: Box<dyn Thunk>) -> Self {
939 Self {
940 state: Mutex::new(DelayState::Pending(thunk)),
941 }
942 }
943
944 pub fn force(&self) -> Result<Value, String> {
947 let thunk = {
948 let mut guard = self.state.lock().unwrap();
949 if let DelayState::Forced(v) = &*guard {
950 return Ok(v.clone());
951 }
952 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
953 let DelayState::Pending(thunk) = prev else {
954 unreachable!("state was not Pending")
955 };
956 thunk
957 };
959 let result = thunk.force()?;
962 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
963 Ok(result)
964 }
965
966 pub fn is_realized(&self) -> bool {
968 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
969 }
970}
971
972impl std::fmt::Debug for Delay {
973 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
974 write!(f, "Delay")
975 }
976}
977
978impl cljrs_gc::Trace for Delay {
979 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
980 {
983 let state = self.state.lock().unwrap();
984 match &*state {
985 DelayState::Pending(thunk) => thunk.trace(visitor),
986 DelayState::Forced(v) => v.trace(visitor),
987 }
988 }
989 }
990}
991
992pub struct CljxPromise {
996 pub value: Mutex<Option<Value>>,
997 pub cond: Condvar,
998}
999
1000impl CljxPromise {
1001 pub fn new() -> Self {
1002 Self {
1003 value: Mutex::new(None),
1004 cond: Condvar::new(),
1005 }
1006 }
1007
1008 pub fn deliver(&self, v: Value) {
1010 let v = crate::publish::publish_value(v);
1013 let mut guard = self.value.lock().unwrap();
1014 if guard.is_none() {
1015 *guard = Some(v);
1016 self.cond.notify_all();
1017 }
1018 }
1019
1020 pub fn deref_blocking(&self) -> Value {
1022 let mut guard = self.value.lock().unwrap();
1023 while guard.is_none() {
1024 guard = self.cond.wait(guard).unwrap();
1025 }
1026 guard.as_ref().unwrap().clone()
1027 }
1028
1029 pub fn is_realized(&self) -> bool {
1031 self.value.lock().unwrap().is_some()
1032 }
1033}
1034
1035impl Default for CljxPromise {
1036 fn default() -> Self {
1037 Self::new()
1038 }
1039}
1040
1041impl std::fmt::Debug for CljxPromise {
1042 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1043 write!(f, "Promise")
1044 }
1045}
1046
1047impl cljrs_gc::Trace for CljxPromise {
1048 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1049 {
1050 let value = self.value.lock().unwrap();
1051 if let Some(v) = value.as_ref() {
1052 v.trace(visitor);
1053 }
1054 }
1055 }
1056}
1057
1058pub enum FutureState {
1062 Running,
1063 Done(Value),
1064 Failed(Value),
1068 Cancelled,
1069}
1070
1071pub struct CljxFuture {
1073 pub state: Mutex<FutureState>,
1074 pub cond: Condvar,
1075 observed: std::sync::atomic::AtomicBool,
1079}
1080
1081impl CljxFuture {
1082 pub fn new() -> Self {
1083 Self {
1084 state: Mutex::new(FutureState::Running),
1085 cond: Condvar::new(),
1086 observed: std::sync::atomic::AtomicBool::new(false),
1087 }
1088 }
1089
1090 pub fn is_done(&self) -> bool {
1092 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1093 }
1094
1095 pub fn is_cancelled(&self) -> bool {
1097 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1098 }
1099
1100 pub fn mark_observed(&self) {
1104 self.observed
1105 .store(true, std::sync::atomic::Ordering::Relaxed);
1106 }
1107}
1108
1109impl Drop for CljxFuture {
1110 fn drop(&mut self) {
1111 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1122 && let Ok(state) = self.state.lock()
1123 && matches!(&*state, FutureState::Failed(_))
1124 {
1125 eprintln!(
1126 "[clojurust warning] a failed future was discarded without its error \
1127 being observed (no await/deref); the thrown exception was lost"
1128 );
1129 }
1130 }
1131}
1132
1133impl Default for CljxFuture {
1134 fn default() -> Self {
1135 Self::new()
1136 }
1137}
1138
1139impl std::fmt::Debug for CljxFuture {
1140 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1141 write!(f, "Future")
1142 }
1143}
1144
1145impl cljrs_gc::Trace for CljxFuture {
1146 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1147 {
1148 let state = self.state.lock().unwrap();
1149 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1152 v.trace(visitor);
1153 }
1154 }
1155 }
1156}
1157
1158pub struct Agent {
1162 pub state: Arc<Mutex<Value>>,
1164 pub error: Arc<Mutex<Option<Value>>>,
1166 pub watches: Mutex<Vec<(Value, Value)>>,
1167}
1168
1169impl Agent {
1170 pub fn get_state(&self) -> Value {
1171 self.state.lock().unwrap().clone()
1172 }
1173
1174 pub fn get_error(&self) -> Option<Value> {
1175 self.error.lock().unwrap().clone()
1176 }
1177
1178 pub fn clear_error(&self) {
1179 *self.error.lock().unwrap() = None;
1180 }
1181}
1182
1183impl std::fmt::Debug for Agent {
1184 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1185 write!(f, "Agent")
1186 }
1187}
1188
1189impl cljrs_gc::Trace for Agent {
1190 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1191 {
1192 let state = self.state.lock().unwrap();
1193 state.trace(visitor);
1194 }
1195 {
1196 let error = self.error.lock().unwrap();
1197 if let Some(e) = error.as_ref() {
1198 e.trace(visitor);
1199 }
1200 }
1201 {
1202 let watches = self.watches.lock().unwrap();
1203 for (key, f) in watches.iter() {
1204 key.trace(visitor);
1205 f.trace(visitor);
1206 }
1207 }
1208 }
1209}
1210
1211#[cfg(test)]
1214mod var_tests {
1215 use super::*;
1216 use crate::shared::SharedValue;
1217
1218 #[test]
1219 fn bind_promotable_mirrors_shared_root() {
1220 let var = Var::new("user", "x");
1221 assert!(var.shared_root.load().is_none());
1222 var.bind(Value::Long(7));
1223 assert!(matches!(
1224 var.shared_root.load().as_ref().as_ref(),
1225 Some(SharedValue::Long(7))
1226 ));
1227 assert_eq!(var.deref(), Some(Value::Long(7)));
1228 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1229 }
1230
1231 #[test]
1232 fn bind_nonpromotable_clears_shared_root() {
1233 let var = Var::new("user", "f");
1234 var.bind(Value::Long(1));
1235 assert!(var.shared_root.load().is_some());
1236 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1239 Ok(Value::Nil)
1240 })));
1241 var.bind(f);
1242 assert!(var.shared_root.load().is_none());
1243 assert!(var.is_bound());
1244 assert_eq!(var.deref_shared(), None);
1245 }
1246
1247 #[test]
1248 fn from_shared_root_seeds_local_slot() {
1249 let src = Var::new("user", "y");
1250 src.bind(Value::Long(99));
1251 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1252 assert_eq!(recv.deref(), Some(Value::Long(99)));
1253 src.bind(Value::Long(100));
1255 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1256 }
1257}