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 pub self_ptr: Option<GcPtr<CljxFn>>,
698}
699
700impl CljxFn {
701 pub fn new(
702 name: Option<Arc<str>>,
703 arities: Vec<CljxFnArity>,
704 closed_over_names: Vec<Arc<str>>,
705 closed_over_vals: Vec<Value>,
706 is_macro: bool,
707 defining_ns: Arc<str>,
708 ) -> Self {
709 Self {
710 name,
711 arities,
712 closed_over_names,
713 closed_over_vals,
714 is_macro,
715 is_async: false,
716 defining_ns,
717 self_ptr: None,
718 }
719 }
720}
721
722impl cljrs_gc::Trace for CljxFn {
723 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
724 use cljrs_gc::GcVisitor as _;
725 for v in &self.closed_over_vals {
726 v.trace(visitor);
727 }
728 if let Some(ref p) = self.self_ptr {
729 visitor.visit(p);
730 }
731 }
732
733 fn gc_size_extra(&self) -> usize {
734 self.arities.capacity() * mem::size_of::<CljxFnArity>()
736 + self
737 .arities
738 .iter()
739 .map(CljxFnArity::heap_size)
740 .sum::<usize>()
741 }
742}
743
744#[derive(Debug)]
751pub struct BoundFn {
752 pub wrapped: Value,
754 pub captured_bindings: HashMap<usize, Value>,
756}
757
758impl cljrs_gc::Trace for BoundFn {
759 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
760 self.wrapped.trace(visitor);
761 for val in self.captured_bindings.values() {
762 val.trace(visitor);
763 }
764 }
765
766 fn gc_size_extra(&self) -> usize {
767 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
769 }
770}
771
772pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
776 fn force(&self) -> Result<Value, String>;
777}
778
779pub enum LazySeqState {
781 Pending(Box<dyn Thunk>),
783 Forced(Value),
785 Error(String),
787}
788
789pub struct LazySeq {
791 pub state: Mutex<LazySeqState>,
792}
793
794impl LazySeq {
795 pub fn new(thunk: Box<dyn Thunk>) -> Self {
796 Self {
797 state: Mutex::new(LazySeqState::Pending(thunk)),
798 }
799 }
800
801 pub fn realize(&self) -> Value {
804 let thunk = {
805 let mut guard = self.state.lock().unwrap();
806 match &*guard {
807 LazySeqState::Forced(v) => return v.clone(),
808 LazySeqState::Error(_) => return Value::Nil,
809 LazySeqState::Pending(_) => {}
810 }
811 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
813 let LazySeqState::Pending(thunk) = prev else {
814 unreachable!("state was not Pending")
815 };
816 thunk
817 };
819 match thunk.force() {
822 Ok(result) => {
823 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
824 result
825 }
826 Err(msg) => {
827 *self.state.lock().unwrap() = LazySeqState::Error(msg);
828 Value::Nil
829 }
830 }
831 }
832
833 pub fn error(&self) -> Option<String> {
835 let guard = self.state.lock().unwrap();
836 if let LazySeqState::Error(e) = &*guard {
837 Some(e.clone())
838 } else {
839 None
840 }
841 }
842}
843
844impl std::fmt::Debug for LazySeq {
845 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
846 write!(f, "LazySeq(...)")
847 }
848}
849
850impl cljrs_gc::Trace for LazySeq {
851 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
852 {
855 let state = self.state.lock().unwrap();
856 match &*state {
857 LazySeqState::Pending(thunk) => thunk.trace(visitor),
858 LazySeqState::Forced(v) => v.trace(visitor),
859 LazySeqState::Error(_) => {}
860 }
861 }
862 }
863}
864
865#[derive(Debug, Clone)]
872pub struct CljxCons {
873 pub head: Value,
874 pub tail: Value,
875}
876
877impl cljrs_gc::Trace for CljxCons {
878 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
879 self.head.trace(visitor);
880 self.tail.trace(visitor);
881 }
882}
883
884pub struct Volatile {
888 pub value: Mutex<Value>,
889}
890
891impl Volatile {
892 pub fn new(v: Value) -> Self {
893 let v = crate::publish::publish_value(v);
895 Self {
896 value: Mutex::new(v),
897 }
898 }
899
900 pub fn deref(&self) -> Value {
901 self.value.lock().unwrap().clone()
902 }
903
904 pub fn reset(&self, v: Value) -> Value {
905 #[cfg(all(feature = "no-gc", debug_assertions))]
907 debug_assert!(
908 value_gcptr_is_static(&v),
909 "no-gc: Volatile::reset() received a region-local value — ensure the \
910 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
911 );
912 let v = crate::publish::publish_value(v);
914 *self.value.lock().unwrap() = v.clone();
915 v
916 }
917}
918
919impl std::fmt::Debug for Volatile {
920 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
921 write!(f, "Volatile")
922 }
923}
924
925impl cljrs_gc::Trace for Volatile {
926 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
927 {
928 let value = self.value.lock().unwrap();
929 value.trace(visitor);
930 }
931 }
932}
933
934pub enum DelayState {
938 Pending(Box<dyn Thunk>),
939 Forced(Value),
940}
941
942pub struct Delay {
944 pub state: Mutex<DelayState>,
945}
946
947impl Delay {
948 pub fn new(thunk: Box<dyn Thunk>) -> Self {
949 Self {
950 state: Mutex::new(DelayState::Pending(thunk)),
951 }
952 }
953
954 pub fn force(&self) -> Result<Value, String> {
957 let thunk = {
958 let mut guard = self.state.lock().unwrap();
959 if let DelayState::Forced(v) = &*guard {
960 return Ok(v.clone());
961 }
962 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
963 let DelayState::Pending(thunk) = prev else {
964 unreachable!("state was not Pending")
965 };
966 thunk
967 };
969 let result = thunk.force()?;
972 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
973 Ok(result)
974 }
975
976 pub fn is_realized(&self) -> bool {
978 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
979 }
980}
981
982impl std::fmt::Debug for Delay {
983 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
984 write!(f, "Delay")
985 }
986}
987
988impl cljrs_gc::Trace for Delay {
989 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
990 {
993 let state = self.state.lock().unwrap();
994 match &*state {
995 DelayState::Pending(thunk) => thunk.trace(visitor),
996 DelayState::Forced(v) => v.trace(visitor),
997 }
998 }
999 }
1000}
1001
1002pub struct CljxPromise {
1006 pub value: Mutex<Option<Value>>,
1007 pub cond: Condvar,
1008}
1009
1010impl CljxPromise {
1011 pub fn new() -> Self {
1012 Self {
1013 value: Mutex::new(None),
1014 cond: Condvar::new(),
1015 }
1016 }
1017
1018 pub fn deliver(&self, v: Value) {
1020 let v = crate::publish::publish_value(v);
1023 let mut guard = self.value.lock().unwrap();
1024 if guard.is_none() {
1025 *guard = Some(v);
1026 self.cond.notify_all();
1027 }
1028 }
1029
1030 pub fn deref_blocking(&self) -> Value {
1032 let mut guard = self.value.lock().unwrap();
1033 while guard.is_none() {
1034 guard = self.cond.wait(guard).unwrap();
1035 }
1036 guard.as_ref().unwrap().clone()
1037 }
1038
1039 pub fn is_realized(&self) -> bool {
1041 self.value.lock().unwrap().is_some()
1042 }
1043}
1044
1045impl Default for CljxPromise {
1046 fn default() -> Self {
1047 Self::new()
1048 }
1049}
1050
1051impl std::fmt::Debug for CljxPromise {
1052 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1053 write!(f, "Promise")
1054 }
1055}
1056
1057impl cljrs_gc::Trace for CljxPromise {
1058 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1059 {
1060 let value = self.value.lock().unwrap();
1061 if let Some(v) = value.as_ref() {
1062 v.trace(visitor);
1063 }
1064 }
1065 }
1066}
1067
1068pub enum FutureState {
1072 Running,
1073 Done(Value),
1074 Failed(Value),
1078 Cancelled,
1079}
1080
1081pub struct CljxFuture {
1083 pub state: Mutex<FutureState>,
1084 pub cond: Condvar,
1085 observed: std::sync::atomic::AtomicBool,
1089}
1090
1091impl CljxFuture {
1092 pub fn new() -> Self {
1093 Self {
1094 state: Mutex::new(FutureState::Running),
1095 cond: Condvar::new(),
1096 observed: std::sync::atomic::AtomicBool::new(false),
1097 }
1098 }
1099
1100 pub fn is_done(&self) -> bool {
1102 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1103 }
1104
1105 pub fn is_cancelled(&self) -> bool {
1107 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1108 }
1109
1110 pub fn mark_observed(&self) {
1114 self.observed
1115 .store(true, std::sync::atomic::Ordering::Relaxed);
1116 }
1117}
1118
1119impl Drop for CljxFuture {
1120 fn drop(&mut self) {
1121 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1132 && let Ok(state) = self.state.lock()
1133 && matches!(&*state, FutureState::Failed(_))
1134 {
1135 eprintln!(
1136 "[clojurust warning] a failed future was discarded without its error \
1137 being observed (no await/deref); the thrown exception was lost"
1138 );
1139 }
1140 }
1141}
1142
1143impl Default for CljxFuture {
1144 fn default() -> Self {
1145 Self::new()
1146 }
1147}
1148
1149impl std::fmt::Debug for CljxFuture {
1150 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1151 write!(f, "Future")
1152 }
1153}
1154
1155impl cljrs_gc::Trace for CljxFuture {
1156 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1157 {
1158 let state = self.state.lock().unwrap();
1159 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1162 v.trace(visitor);
1163 }
1164 }
1165 }
1166}
1167
1168pub struct Agent {
1172 pub state: Arc<Mutex<Value>>,
1174 pub error: Arc<Mutex<Option<Value>>>,
1176 pub watches: Mutex<Vec<(Value, Value)>>,
1177}
1178
1179impl Agent {
1180 pub fn get_state(&self) -> Value {
1181 self.state.lock().unwrap().clone()
1182 }
1183
1184 pub fn get_error(&self) -> Option<Value> {
1185 self.error.lock().unwrap().clone()
1186 }
1187
1188 pub fn clear_error(&self) {
1189 *self.error.lock().unwrap() = None;
1190 }
1191}
1192
1193impl std::fmt::Debug for Agent {
1194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1195 write!(f, "Agent")
1196 }
1197}
1198
1199impl cljrs_gc::Trace for Agent {
1200 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1201 {
1202 let state = self.state.lock().unwrap();
1203 state.trace(visitor);
1204 }
1205 {
1206 let error = self.error.lock().unwrap();
1207 if let Some(e) = error.as_ref() {
1208 e.trace(visitor);
1209 }
1210 }
1211 {
1212 let watches = self.watches.lock().unwrap();
1213 for (key, f) in watches.iter() {
1214 key.trace(visitor);
1215 f.trace(visitor);
1216 }
1217 }
1218 }
1219}
1220
1221#[cfg(test)]
1224mod var_tests {
1225 use super::*;
1226 use crate::shared::SharedValue;
1227
1228 #[test]
1229 fn bind_promotable_mirrors_shared_root() {
1230 let var = Var::new("user", "x");
1231 assert!(var.shared_root.load().is_none());
1232 var.bind(Value::Long(7));
1233 assert!(matches!(
1234 var.shared_root.load().as_ref().as_ref(),
1235 Some(SharedValue::Long(7))
1236 ));
1237 assert_eq!(var.deref(), Some(Value::Long(7)));
1238 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1239 }
1240
1241 #[test]
1242 fn bind_nonpromotable_clears_shared_root() {
1243 let var = Var::new("user", "f");
1244 var.bind(Value::Long(1));
1245 assert!(var.shared_root.load().is_some());
1246 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1249 Ok(Value::Nil)
1250 })));
1251 var.bind(f);
1252 assert!(var.shared_root.load().is_none());
1253 assert!(var.is_bound());
1254 assert_eq!(var.deref_shared(), None);
1255 }
1256
1257 #[test]
1258 fn from_shared_root_seeds_local_slot() {
1259 let src = Var::new("user", "y");
1260 src.bind(Value::Long(99));
1261 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1262 assert_eq!(recv.deref(), Some(Value::Long(99)));
1263 src.bind(Value::Long(100));
1265 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1266 }
1267}