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 pub meta: Mutex<Option<Value>>,
518}
519
520impl Namespace {
521 pub fn new(name: impl Into<Arc<str>>) -> Self {
522 Self {
523 name: name.into(),
524 interns: Mutex::new(HashMap::new()),
525 refers: Mutex::new(HashMap::new()),
526 aliases: Mutex::new(HashMap::new()),
527 source_file: Mutex::new(None),
528 git_repo_root: Mutex::new(None),
529 is_versioned: false,
530 meta: Mutex::new(None),
531 }
532 }
533
534 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
536 Self {
537 is_versioned: true,
538 ..Self::new(name)
539 }
540 }
541
542 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
544 *self.source_file.lock().unwrap() = Some(Arc::from(file));
545 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
546 }
547
548 pub fn get_meta(&self) -> Option<Value> {
549 self.meta.lock().unwrap().clone()
550 }
551
552 pub fn set_meta(&self, m: Value) {
553 *self.meta.lock().unwrap() = Some(m);
554 }
555}
556
557impl cljrs_gc::Trace for Namespace {
558 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
559 use cljrs_gc::GcVisitor as _;
560 {
561 let interns = self.interns.lock().unwrap();
562 for var in interns.values() {
563 visitor.visit(var);
564 }
565 }
566 {
567 let refers = self.refers.lock().unwrap();
568 for var in refers.values() {
569 visitor.visit(var);
570 }
571 }
572 {
573 let meta = self.meta.lock().unwrap();
574 if let Some(m) = meta.as_ref() {
575 m.trace(visitor);
576 }
577 }
578 }
579}
580
581pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
587
588pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
591
592#[derive(Clone, Debug)]
593pub enum Arity {
594 Fixed(usize),
595 Variadic { min: usize },
596}
597
598pub struct NativeFn {
599 pub name: Arc<str>,
600 pub arity: Arity,
601 pub func: NativeFnFunc,
602}
603
604impl NativeFn {
605 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
607 Self {
608 name: name.into(),
609 arity,
610 func: Arc::new(func),
611 }
612 }
613
614 pub fn with_closure(
616 name: impl Into<Arc<str>>,
617 arity: Arity,
618 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
619 ) -> Self {
620 Self {
621 name: name.into(),
622 arity,
623 func: Arc::new(func),
624 }
625 }
626}
627
628impl std::fmt::Debug for NativeFn {
629 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
630 f.debug_struct("NativeFn")
631 .field("name", &self.name)
632 .field("arity", &self.arity)
633 .field("func", &"<fn>")
634 .finish()
635 }
636}
637
638impl cljrs_gc::Trace for NativeFn {
639 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
640}
641
642#[derive(Debug, Clone)]
646pub struct CljxFnArity {
647 pub params: Vec<Arc<str>>,
650 pub rest_param: Option<Arc<str>>,
652 pub body: Vec<Form>,
654 pub destructure_params: Vec<(usize, Form)>,
658 pub destructure_rest: Option<Form>,
660 pub ir_arity_id: u64,
662 pub param_hints: Vec<Option<TypeHint>>,
666 pub rest_hint: Option<TypeHint>,
669}
670
671impl CljxFnArity {
672 pub fn heap_size(&self) -> usize {
674 self.params.capacity() * mem::size_of::<Arc<str>>()
676 + self.body.capacity() * mem::size_of::<Form>()
678 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
679 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
681 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
682 + self.destructure_rest.as_ref()
684 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
685 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
687 }
688}
689
690#[derive(Debug, Clone)]
694pub struct CljxFn {
695 pub name: Option<Arc<str>>,
696 pub arities: Vec<CljxFnArity>,
697 pub closed_over_names: Vec<Arc<str>>,
699 pub closed_over_vals: Vec<Value>,
701 pub is_macro: bool,
703 pub is_async: bool,
708 pub defining_ns: Arc<str>,
710 pub self_ptr: Option<GcPtr<CljxFn>>,
715}
716
717impl CljxFn {
718 pub fn new(
719 name: Option<Arc<str>>,
720 arities: Vec<CljxFnArity>,
721 closed_over_names: Vec<Arc<str>>,
722 closed_over_vals: Vec<Value>,
723 is_macro: bool,
724 defining_ns: Arc<str>,
725 ) -> Self {
726 Self {
727 name,
728 arities,
729 closed_over_names,
730 closed_over_vals,
731 is_macro,
732 is_async: false,
733 defining_ns,
734 self_ptr: None,
735 }
736 }
737}
738
739impl cljrs_gc::Trace for CljxFn {
740 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
741 use cljrs_gc::GcVisitor as _;
742 for v in &self.closed_over_vals {
743 v.trace(visitor);
744 }
745 if let Some(ref p) = self.self_ptr {
746 visitor.visit(p);
747 }
748 }
749
750 fn gc_size_extra(&self) -> usize {
751 self.arities.capacity() * mem::size_of::<CljxFnArity>()
753 + self
754 .arities
755 .iter()
756 .map(CljxFnArity::heap_size)
757 .sum::<usize>()
758 }
759}
760
761#[derive(Debug)]
768pub struct BoundFn {
769 pub wrapped: Value,
771 pub captured_bindings: HashMap<usize, Value>,
773}
774
775impl cljrs_gc::Trace for BoundFn {
776 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
777 self.wrapped.trace(visitor);
778 for val in self.captured_bindings.values() {
779 val.trace(visitor);
780 }
781 }
782
783 fn gc_size_extra(&self) -> usize {
784 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
786 }
787}
788
789pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
793 fn force(&self) -> Result<Value, String>;
794}
795
796pub enum LazySeqState {
798 Pending(Box<dyn Thunk>),
800 Forced(Value),
802 Error(String),
804}
805
806pub struct LazySeq {
808 pub state: Mutex<LazySeqState>,
809}
810
811impl LazySeq {
812 pub fn new(thunk: Box<dyn Thunk>) -> Self {
813 Self {
814 state: Mutex::new(LazySeqState::Pending(thunk)),
815 }
816 }
817
818 pub fn realize(&self) -> Value {
821 let thunk = {
822 let mut guard = self.state.lock().unwrap();
823 match &*guard {
824 LazySeqState::Forced(v) => return v.clone(),
825 LazySeqState::Error(_) => return Value::Nil,
826 LazySeqState::Pending(_) => {}
827 }
828 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
830 let LazySeqState::Pending(thunk) = prev else {
831 unreachable!("state was not Pending")
832 };
833 thunk
834 };
836 match thunk.force() {
839 Ok(result) => {
840 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
841 result
842 }
843 Err(msg) => {
844 *self.state.lock().unwrap() = LazySeqState::Error(msg);
845 Value::Nil
846 }
847 }
848 }
849
850 pub fn error(&self) -> Option<String> {
852 let guard = self.state.lock().unwrap();
853 if let LazySeqState::Error(e) = &*guard {
854 Some(e.clone())
855 } else {
856 None
857 }
858 }
859}
860
861impl std::fmt::Debug for LazySeq {
862 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
863 write!(f, "LazySeq(...)")
864 }
865}
866
867impl cljrs_gc::Trace for LazySeq {
868 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
869 {
872 let state = self.state.lock().unwrap();
873 match &*state {
874 LazySeqState::Pending(thunk) => thunk.trace(visitor),
875 LazySeqState::Forced(v) => v.trace(visitor),
876 LazySeqState::Error(_) => {}
877 }
878 }
879 }
880}
881
882#[derive(Debug, Clone)]
889pub struct CljxCons {
890 pub head: Value,
891 pub tail: Value,
892}
893
894impl cljrs_gc::Trace for CljxCons {
895 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
896 self.head.trace(visitor);
897 self.tail.trace(visitor);
898 }
899}
900
901pub struct Volatile {
905 pub value: Mutex<Value>,
906}
907
908impl Volatile {
909 pub fn new(v: Value) -> Self {
910 let v = crate::publish::publish_value(v);
912 Self {
913 value: Mutex::new(v),
914 }
915 }
916
917 pub fn deref(&self) -> Value {
918 self.value.lock().unwrap().clone()
919 }
920
921 pub fn reset(&self, v: Value) -> Value {
922 #[cfg(all(feature = "no-gc", debug_assertions))]
924 debug_assert!(
925 value_gcptr_is_static(&v),
926 "no-gc: Volatile::reset() received a region-local value — ensure the \
927 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
928 );
929 let v = crate::publish::publish_value(v);
931 *self.value.lock().unwrap() = v.clone();
932 v
933 }
934}
935
936impl std::fmt::Debug for Volatile {
937 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
938 write!(f, "Volatile")
939 }
940}
941
942impl cljrs_gc::Trace for Volatile {
943 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
944 {
945 let value = self.value.lock().unwrap();
946 value.trace(visitor);
947 }
948 }
949}
950
951pub enum DelayState {
955 Pending(Box<dyn Thunk>),
956 Forced(Value),
957}
958
959pub struct Delay {
961 pub state: Mutex<DelayState>,
962}
963
964impl Delay {
965 pub fn new(thunk: Box<dyn Thunk>) -> Self {
966 Self {
967 state: Mutex::new(DelayState::Pending(thunk)),
968 }
969 }
970
971 pub fn force(&self) -> Result<Value, String> {
974 let thunk = {
975 let mut guard = self.state.lock().unwrap();
976 if let DelayState::Forced(v) = &*guard {
977 return Ok(v.clone());
978 }
979 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
980 let DelayState::Pending(thunk) = prev else {
981 unreachable!("state was not Pending")
982 };
983 thunk
984 };
986 let result = thunk.force()?;
989 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
990 Ok(result)
991 }
992
993 pub fn is_realized(&self) -> bool {
995 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
996 }
997}
998
999impl std::fmt::Debug for Delay {
1000 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1001 write!(f, "Delay")
1002 }
1003}
1004
1005impl cljrs_gc::Trace for Delay {
1006 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1007 {
1010 let state = self.state.lock().unwrap();
1011 match &*state {
1012 DelayState::Pending(thunk) => thunk.trace(visitor),
1013 DelayState::Forced(v) => v.trace(visitor),
1014 }
1015 }
1016 }
1017}
1018
1019pub struct CljxPromise {
1023 pub value: Mutex<Option<Value>>,
1024 pub cond: Condvar,
1025}
1026
1027impl CljxPromise {
1028 pub fn new() -> Self {
1029 Self {
1030 value: Mutex::new(None),
1031 cond: Condvar::new(),
1032 }
1033 }
1034
1035 pub fn deliver(&self, v: Value) {
1037 let v = crate::publish::publish_value(v);
1040 let mut guard = self.value.lock().unwrap();
1041 if guard.is_none() {
1042 *guard = Some(v);
1043 self.cond.notify_all();
1044 }
1045 }
1046
1047 pub fn deref_blocking(&self) -> Value {
1049 let mut guard = self.value.lock().unwrap();
1050 while guard.is_none() {
1051 guard = self.cond.wait(guard).unwrap();
1052 }
1053 guard.as_ref().unwrap().clone()
1054 }
1055
1056 pub fn is_realized(&self) -> bool {
1058 self.value.lock().unwrap().is_some()
1059 }
1060}
1061
1062impl Default for CljxPromise {
1063 fn default() -> Self {
1064 Self::new()
1065 }
1066}
1067
1068impl std::fmt::Debug for CljxPromise {
1069 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1070 write!(f, "Promise")
1071 }
1072}
1073
1074impl cljrs_gc::Trace for CljxPromise {
1075 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1076 {
1077 let value = self.value.lock().unwrap();
1078 if let Some(v) = value.as_ref() {
1079 v.trace(visitor);
1080 }
1081 }
1082 }
1083}
1084
1085pub enum FutureState {
1089 Running,
1090 Done(Value),
1091 Failed(Value),
1095 Cancelled,
1096}
1097
1098pub struct CljxFuture {
1100 pub state: Mutex<FutureState>,
1101 pub cond: Condvar,
1102 observed: std::sync::atomic::AtomicBool,
1106}
1107
1108impl CljxFuture {
1109 pub fn new() -> Self {
1110 Self {
1111 state: Mutex::new(FutureState::Running),
1112 cond: Condvar::new(),
1113 observed: std::sync::atomic::AtomicBool::new(false),
1114 }
1115 }
1116
1117 pub fn is_done(&self) -> bool {
1119 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1120 }
1121
1122 pub fn is_cancelled(&self) -> bool {
1124 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1125 }
1126
1127 pub fn mark_observed(&self) {
1131 self.observed
1132 .store(true, std::sync::atomic::Ordering::Relaxed);
1133 }
1134}
1135
1136impl Drop for CljxFuture {
1137 fn drop(&mut self) {
1138 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1149 && let Ok(state) = self.state.lock()
1150 && matches!(&*state, FutureState::Failed(_))
1151 {
1152 eprintln!(
1153 "[clojurust warning] a failed future was discarded without its error \
1154 being observed (no await/deref); the thrown exception was lost"
1155 );
1156 }
1157 }
1158}
1159
1160impl Default for CljxFuture {
1161 fn default() -> Self {
1162 Self::new()
1163 }
1164}
1165
1166impl std::fmt::Debug for CljxFuture {
1167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1168 write!(f, "Future")
1169 }
1170}
1171
1172impl cljrs_gc::Trace for CljxFuture {
1173 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1174 {
1175 let state = self.state.lock().unwrap();
1176 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1179 v.trace(visitor);
1180 }
1181 }
1182 }
1183}
1184
1185pub struct Agent {
1189 pub state: Arc<Mutex<Value>>,
1191 pub error: Arc<Mutex<Option<Value>>>,
1193 pub watches: Mutex<Vec<(Value, Value)>>,
1194}
1195
1196impl Agent {
1197 pub fn get_state(&self) -> Value {
1198 self.state.lock().unwrap().clone()
1199 }
1200
1201 pub fn get_error(&self) -> Option<Value> {
1202 self.error.lock().unwrap().clone()
1203 }
1204
1205 pub fn clear_error(&self) {
1206 *self.error.lock().unwrap() = None;
1207 }
1208}
1209
1210impl std::fmt::Debug for Agent {
1211 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1212 write!(f, "Agent")
1213 }
1214}
1215
1216impl cljrs_gc::Trace for Agent {
1217 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1218 {
1219 let state = self.state.lock().unwrap();
1220 state.trace(visitor);
1221 }
1222 {
1223 let error = self.error.lock().unwrap();
1224 if let Some(e) = error.as_ref() {
1225 e.trace(visitor);
1226 }
1227 }
1228 {
1229 let watches = self.watches.lock().unwrap();
1230 for (key, f) in watches.iter() {
1231 key.trace(visitor);
1232 f.trace(visitor);
1233 }
1234 }
1235 }
1236}
1237
1238#[cfg(test)]
1241mod var_tests {
1242 use super::*;
1243 use crate::shared::SharedValue;
1244
1245 #[test]
1246 fn bind_promotable_mirrors_shared_root() {
1247 let var = Var::new("user", "x");
1248 assert!(var.shared_root.load().is_none());
1249 var.bind(Value::Long(7));
1250 assert!(matches!(
1251 var.shared_root.load().as_ref().as_ref(),
1252 Some(SharedValue::Long(7))
1253 ));
1254 assert_eq!(var.deref(), Some(Value::Long(7)));
1255 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
1256 }
1257
1258 #[test]
1259 fn bind_nonpromotable_clears_shared_root() {
1260 let var = Var::new("user", "f");
1261 var.bind(Value::Long(1));
1262 assert!(var.shared_root.load().is_some());
1263 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
1266 Ok(Value::Nil)
1267 })));
1268 var.bind(f);
1269 assert!(var.shared_root.load().is_none());
1270 assert!(var.is_bound());
1271 assert_eq!(var.deref_shared(), None);
1272 }
1273
1274 #[test]
1275 fn from_shared_root_seeds_local_slot() {
1276 let src = Var::new("user", "y");
1277 src.bind(Value::Long(99));
1278 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
1279 assert_eq!(recv.deref(), Some(Value::Long(99)));
1280 src.bind(Value::Long(100));
1282 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
1283 }
1284}