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::Value;
13
14#[cfg(all(feature = "no-gc", debug_assertions))]
29pub(crate) fn value_gcptr_is_static(value: &Value) -> bool {
30 use crate::value::MapValue;
31 use crate::value::SetValue;
32 match value {
33 Value::Nil
35 | Value::Bool(_)
36 | Value::Long(_)
37 | Value::Double(_)
38 | Value::Char(_)
39 | Value::Uuid(_) => true,
40 Value::Resource(_) | Value::SharedAtom(_) | Value::ByteBlob(_) => true,
42 Value::BigInt(p) => p.is_static_alloc(),
44 Value::BigDecimal(p) => p.is_static_alloc(),
45 Value::Ratio(p) => p.is_static_alloc(),
46 Value::Str(p) => p.is_static_alloc(),
47 Value::Pattern(p) => p.is_static_alloc(),
48 Value::Matcher(p) => p.is_static_alloc(),
49 Value::Symbol(p) => p.is_static_alloc(),
50 Value::Keyword(p) => p.is_static_alloc(),
51 Value::List(p) => p.is_static_alloc(),
52 Value::Vector(p) => p.is_static_alloc(),
53 Value::Queue(p) => p.is_static_alloc(),
54 Value::Map(m) => match m {
55 MapValue::Array(p) => p.is_static_alloc(),
56 MapValue::Hash(p) => p.is_static_alloc(),
57 MapValue::Sorted(p) => p.is_static_alloc(),
58 },
59 Value::Set(s) => match s {
60 SetValue::Hash(p) => p.is_static_alloc(),
61 SetValue::Sorted(p) => p.is_static_alloc(),
62 },
63 Value::NativeFunction(p) => p.is_static_alloc(),
64 Value::Fn(p) | Value::Macro(p) => p.is_static_alloc(),
65 Value::BoundFn(p) => p.is_static_alloc(),
66 Value::Var(p) => p.is_static_alloc(),
67 Value::Atom(p) => p.is_static_alloc(),
68 Value::Namespace(p) => p.is_static_alloc(),
69 Value::LazySeq(p) => p.is_static_alloc(),
70 Value::Cons(p) => p.is_static_alloc(),
71 Value::Protocol(p) => p.is_static_alloc(),
72 Value::ProtocolFn(p) => p.is_static_alloc(),
73 Value::MultiFn(p) => p.is_static_alloc(),
74 Value::Volatile(p) => p.is_static_alloc(),
75 Value::Delay(p) => p.is_static_alloc(),
76 Value::Promise(p) => p.is_static_alloc(),
77 Value::Future(p) => p.is_static_alloc(),
78 Value::Agent(p) => p.is_static_alloc(),
79 Value::TypeInstance(p) => p.is_static_alloc(),
80 Value::ObjectArray(p) => p.is_static_alloc(),
81 Value::NativeObject(p) => p.is_static_alloc(),
82 Value::Error(p) => p.is_static_alloc(),
83 Value::TransientMap(p) => p.is_static_alloc(),
84 Value::TransientVector(p) => p.is_static_alloc(),
85 Value::TransientSet(p) => p.is_static_alloc(),
86 Value::BooleanArray(_)
88 | Value::ByteArray(_)
89 | Value::ShortArray(_)
90 | Value::IntArray(_)
91 | Value::LongArray(_)
92 | Value::FloatArray(_)
93 | Value::DoubleArray(_)
94 | Value::CharArray(_) => true,
95 Value::Reduced(inner) | Value::WithMeta(inner, _) => value_gcptr_is_static(inner),
97 }
98}
99
100pub type MethodMap = HashMap<Arc<str>, Value>;
104
105#[derive(Debug)]
107pub struct Protocol {
108 pub name: Arc<str>,
109 pub ns: Arc<str>,
110 pub methods: Vec<ProtocolMethod>,
111 pub impls: Mutex<HashMap<Arc<str>, MethodMap>>,
113}
114
115impl Protocol {
116 pub fn new(name: Arc<str>, ns: Arc<str>, methods: Vec<ProtocolMethod>) -> Self {
117 Self {
118 name,
119 ns,
120 methods,
121 impls: Mutex::new(HashMap::new()),
122 }
123 }
124}
125
126static PROTOCOL_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
135
136pub fn protocol_generation() -> u64 {
138 PROTOCOL_GENERATION.load(std::sync::atomic::Ordering::Acquire)
139}
140
141pub fn bump_protocol_generation() {
144 PROTOCOL_GENERATION.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
145}
146
147impl cljrs_gc::Trace for Protocol {
148 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
149 {
150 let impls = self.impls.lock().unwrap();
151 for method_map in impls.values() {
152 for v in method_map.values() {
153 v.trace(visitor);
154 }
155 }
156 }
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct ProtocolMethod {
163 pub name: Arc<str>,
164 pub min_arity: usize,
165 pub variadic: bool,
166}
167
168impl cljrs_gc::Trace for ProtocolMethod {
169 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
170}
171
172#[derive(Debug)]
176pub struct ProtocolFn {
177 pub protocol: GcPtr<Protocol>,
178 pub method_name: Arc<str>,
179 pub min_arity: usize,
180 pub variadic: bool,
181}
182
183impl cljrs_gc::Trace for ProtocolFn {
184 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
185 use cljrs_gc::GcVisitor as _;
186 visitor.visit(&self.protocol);
187 }
188}
189
190#[derive(Debug)]
194pub struct MultiFn {
195 pub name: Arc<str>,
196 pub dispatch_fn: Value,
197 pub methods: Mutex<HashMap<String, Value>>,
199 pub prefers: Mutex<HashMap<String, Vec<String>>>,
201 pub default_dispatch: String,
203}
204
205impl MultiFn {
206 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
207 Self {
208 name,
209 dispatch_fn,
210 methods: Mutex::new(HashMap::new()),
211 prefers: Mutex::new(HashMap::new()),
212 default_dispatch,
213 }
214 }
215}
216
217impl cljrs_gc::Trace for MultiFn {
218 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
219 self.dispatch_fn.trace(visitor);
220 {
221 let methods = self.methods.lock().unwrap();
222 for v in methods.values() {
223 v.trace(visitor);
224 }
225 }
226 }
227}
228
229#[derive(Debug)]
233pub struct Var {
234 pub namespace: Arc<str>,
235 pub name: Arc<str>,
236 pub value: Mutex<Option<Value>>,
237 pub is_macro: bool,
238 pub meta: Mutex<Option<Value>>,
240 pub watches: Mutex<Vec<(Value, Value)>>,
241}
242
243impl Var {
244 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
245 Self {
246 namespace: namespace.into(),
247 name: name.into(),
248 value: Mutex::new(None),
249 is_macro: false,
250 meta: Mutex::new(None),
251 watches: Mutex::new(Vec::new()),
252 }
253 }
254
255 pub fn is_bound(&self) -> bool {
256 self.value.lock().unwrap().is_some()
257 }
258
259 pub fn deref(&self) -> Option<Value> {
260 self.value.lock().unwrap().clone()
261 }
262
263 pub fn bind(&self, v: Value) {
264 #[cfg(all(feature = "no-gc", debug_assertions))]
268 debug_assert!(
269 value_gcptr_is_static(&v),
270 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
271 indicate a missing StaticCtxGuard around the value expression",
272 self.namespace,
273 self.name
274 );
275 let v = crate::publish::publish_value(v);
280 let prev = {
286 let mut slot = self.value.lock().unwrap();
287 slot.replace(v.clone())
288 };
289 if let Some(prev) = prev {
290 crate::jit_hooks::notify_var_rebind(&prev, &v);
291 }
292 }
293
294 pub fn get_meta(&self) -> Option<Value> {
295 self.meta.lock().unwrap().clone()
296 }
297
298 pub fn set_meta(&self, m: Value) {
299 *self.meta.lock().unwrap() = Some(m);
300 }
301
302 pub fn full_name(&self) -> String {
303 format!("{}/{}", self.namespace, self.name)
304 }
305}
306
307impl cljrs_gc::Trace for Var {
308 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
309 {
310 let value = self.value.lock().unwrap();
311 if let Some(v) = value.as_ref() {
312 v.trace(visitor);
313 }
314 }
315 {
316 let meta = self.meta.lock().unwrap();
317 if let Some(m) = meta.as_ref() {
318 m.trace(visitor);
319 }
320 }
321 {
322 let watches = self.watches.lock().unwrap();
323 for (key, f) in watches.iter() {
324 key.trace(visitor);
325 f.trace(visitor);
326 }
327 }
328 }
329}
330
331#[derive(Debug)]
335pub struct Atom {
336 pub value: Mutex<Value>,
337 pub meta: Mutex<Option<Value>>,
338 pub validator: Mutex<Option<Value>>,
339 pub watches: Mutex<Vec<(Value, Value)>>,
340}
341
342impl Atom {
343 pub fn new(v: Value) -> Self {
344 let v = crate::publish::publish_value(v);
347 Self {
348 value: Mutex::new(v),
349 meta: Mutex::new(None),
350 validator: Mutex::new(None),
351 watches: Mutex::new(Vec::new()),
352 }
353 }
354
355 pub fn deref(&self) -> Value {
356 self.value.lock().unwrap().clone()
357 }
358
359 pub fn reset(&self, v: Value) -> Value {
360 #[cfg(all(feature = "no-gc", debug_assertions))]
362 debug_assert!(
363 value_gcptr_is_static(&v),
364 "no-gc: Atom::reset() received a region-local value — the new-value \
365 expression must be computed inside a StaticCtxGuard (i.e. inside \
366 the swap! / reset! call) so it is allocated in the static arena"
367 );
368 let v = crate::publish::publish_value(v);
370 let mut guard = self.value.lock().unwrap();
371 *guard = v.clone();
372 v
373 }
374
375 pub fn get_meta(&self) -> Option<Value> {
376 self.meta.lock().unwrap().clone()
377 }
378
379 pub fn set_meta(&self, m: Option<Value>) {
380 *self.meta.lock().unwrap() = m;
381 }
382
383 pub fn get_validator(&self) -> Option<Value> {
384 self.validator.lock().unwrap().clone()
385 }
386
387 pub fn set_validator(&self, vf: Option<Value>) {
388 *self.validator.lock().unwrap() = vf;
389 }
390}
391
392impl cljrs_gc::Trace for Atom {
393 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
394 {
395 let value = self.value.lock().unwrap();
396 value.trace(visitor);
397 }
398 {
399 let meta = self.meta.lock().unwrap();
400 if let Some(m) = meta.as_ref() {
401 m.trace(visitor);
402 }
403 }
404 {
405 let validator = self.validator.lock().unwrap();
406 if let Some(vf) = validator.as_ref() {
407 vf.trace(visitor);
408 }
409 }
410 {
411 let watches = self.watches.lock().unwrap();
412 for (key, f) in watches.iter() {
413 key.trace(visitor);
414 f.trace(visitor);
415 }
416 }
417 }
418}
419
420#[derive(Debug)]
424pub struct Namespace {
425 pub name: Arc<str>,
426 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
428 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
430 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
432 pub source_file: Mutex<Option<Arc<str>>>,
436 pub git_repo_root: Mutex<Option<Arc<str>>>,
438 pub is_versioned: bool,
441}
442
443impl Namespace {
444 pub fn new(name: impl Into<Arc<str>>) -> Self {
445 Self {
446 name: name.into(),
447 interns: Mutex::new(HashMap::new()),
448 refers: Mutex::new(HashMap::new()),
449 aliases: Mutex::new(HashMap::new()),
450 source_file: Mutex::new(None),
451 git_repo_root: Mutex::new(None),
452 is_versioned: false,
453 }
454 }
455
456 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
458 Self {
459 is_versioned: true,
460 ..Self::new(name)
461 }
462 }
463
464 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
466 *self.source_file.lock().unwrap() = Some(Arc::from(file));
467 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
468 }
469}
470
471impl cljrs_gc::Trace for Namespace {
472 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
473 use cljrs_gc::GcVisitor as _;
474 {
475 let interns = self.interns.lock().unwrap();
476 for var in interns.values() {
477 visitor.visit(var);
478 }
479 }
480 {
481 let refers = self.refers.lock().unwrap();
482 for var in refers.values() {
483 visitor.visit(var);
484 }
485 }
486 }
487}
488
489pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
495
496pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
499
500#[derive(Clone, Debug)]
501pub enum Arity {
502 Fixed(usize),
503 Variadic { min: usize },
504}
505
506pub struct NativeFn {
507 pub name: Arc<str>,
508 pub arity: Arity,
509 pub func: NativeFnFunc,
510}
511
512impl NativeFn {
513 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
515 Self {
516 name: name.into(),
517 arity,
518 func: Arc::new(func),
519 }
520 }
521
522 pub fn with_closure(
524 name: impl Into<Arc<str>>,
525 arity: Arity,
526 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
527 ) -> Self {
528 Self {
529 name: name.into(),
530 arity,
531 func: Arc::new(func),
532 }
533 }
534}
535
536impl std::fmt::Debug for NativeFn {
537 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538 f.debug_struct("NativeFn")
539 .field("name", &self.name)
540 .field("arity", &self.arity)
541 .field("func", &"<fn>")
542 .finish()
543 }
544}
545
546impl cljrs_gc::Trace for NativeFn {
547 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
548}
549
550#[derive(Debug, Clone)]
554pub struct CljxFnArity {
555 pub params: Vec<Arc<str>>,
558 pub rest_param: Option<Arc<str>>,
560 pub body: Vec<Form>,
562 pub destructure_params: Vec<(usize, Form)>,
566 pub destructure_rest: Option<Form>,
568 pub ir_arity_id: u64,
570}
571
572impl CljxFnArity {
573 pub fn heap_size(&self) -> usize {
575 self.params.capacity() * mem::size_of::<Arc<str>>()
577 + self.body.capacity() * mem::size_of::<Form>()
579 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
580 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
582 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
583 + self.destructure_rest.as_ref()
585 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
586 }
587}
588
589#[derive(Debug, Clone)]
593pub struct CljxFn {
594 pub name: Option<Arc<str>>,
595 pub arities: Vec<CljxFnArity>,
596 pub closed_over_names: Vec<Arc<str>>,
598 pub closed_over_vals: Vec<Value>,
600 pub is_macro: bool,
602 pub is_async: bool,
607 pub defining_ns: Arc<str>,
609}
610
611impl CljxFn {
612 pub fn new(
613 name: Option<Arc<str>>,
614 arities: Vec<CljxFnArity>,
615 closed_over_names: Vec<Arc<str>>,
616 closed_over_vals: Vec<Value>,
617 is_macro: bool,
618 defining_ns: Arc<str>,
619 ) -> Self {
620 Self {
621 name,
622 arities,
623 closed_over_names,
624 closed_over_vals,
625 is_macro,
626 is_async: false,
627 defining_ns,
628 }
629 }
630}
631
632impl cljrs_gc::Trace for CljxFn {
633 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
634 for v in &self.closed_over_vals {
635 v.trace(visitor);
636 }
637 }
638
639 fn gc_size_extra(&self) -> usize {
640 self.arities.capacity() * mem::size_of::<CljxFnArity>()
642 + self
643 .arities
644 .iter()
645 .map(CljxFnArity::heap_size)
646 .sum::<usize>()
647 }
648}
649
650#[derive(Debug)]
657pub struct BoundFn {
658 pub wrapped: Value,
660 pub captured_bindings: HashMap<usize, Value>,
662}
663
664impl cljrs_gc::Trace for BoundFn {
665 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
666 self.wrapped.trace(visitor);
667 for val in self.captured_bindings.values() {
668 val.trace(visitor);
669 }
670 }
671
672 fn gc_size_extra(&self) -> usize {
673 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
675 }
676}
677
678pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
682 fn force(&self) -> Result<Value, String>;
683}
684
685pub enum LazySeqState {
687 Pending(Box<dyn Thunk>),
689 Forced(Value),
691 Error(String),
693}
694
695pub struct LazySeq {
697 pub state: Mutex<LazySeqState>,
698}
699
700impl LazySeq {
701 pub fn new(thunk: Box<dyn Thunk>) -> Self {
702 Self {
703 state: Mutex::new(LazySeqState::Pending(thunk)),
704 }
705 }
706
707 pub fn realize(&self) -> Value {
710 let thunk = {
711 let mut guard = self.state.lock().unwrap();
712 match &*guard {
713 LazySeqState::Forced(v) => return v.clone(),
714 LazySeqState::Error(_) => return Value::Nil,
715 LazySeqState::Pending(_) => {}
716 }
717 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
719 let LazySeqState::Pending(thunk) = prev else {
720 unreachable!("state was not Pending")
721 };
722 thunk
723 };
725 match thunk.force() {
728 Ok(result) => {
729 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
730 result
731 }
732 Err(msg) => {
733 *self.state.lock().unwrap() = LazySeqState::Error(msg);
734 Value::Nil
735 }
736 }
737 }
738
739 pub fn error(&self) -> Option<String> {
741 let guard = self.state.lock().unwrap();
742 if let LazySeqState::Error(e) = &*guard {
743 Some(e.clone())
744 } else {
745 None
746 }
747 }
748}
749
750impl std::fmt::Debug for LazySeq {
751 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
752 write!(f, "LazySeq(...)")
753 }
754}
755
756impl cljrs_gc::Trace for LazySeq {
757 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
758 {
761 let state = self.state.lock().unwrap();
762 match &*state {
763 LazySeqState::Pending(thunk) => thunk.trace(visitor),
764 LazySeqState::Forced(v) => v.trace(visitor),
765 LazySeqState::Error(_) => {}
766 }
767 }
768 }
769}
770
771#[derive(Debug, Clone)]
778pub struct CljxCons {
779 pub head: Value,
780 pub tail: Value,
781}
782
783impl cljrs_gc::Trace for CljxCons {
784 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
785 self.head.trace(visitor);
786 self.tail.trace(visitor);
787 }
788}
789
790pub struct Volatile {
794 pub value: Mutex<Value>,
795}
796
797impl Volatile {
798 pub fn new(v: Value) -> Self {
799 let v = crate::publish::publish_value(v);
801 Self {
802 value: Mutex::new(v),
803 }
804 }
805
806 pub fn deref(&self) -> Value {
807 self.value.lock().unwrap().clone()
808 }
809
810 pub fn reset(&self, v: Value) -> Value {
811 #[cfg(all(feature = "no-gc", debug_assertions))]
813 debug_assert!(
814 value_gcptr_is_static(&v),
815 "no-gc: Volatile::reset() received a region-local value — ensure the \
816 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
817 );
818 let v = crate::publish::publish_value(v);
820 *self.value.lock().unwrap() = v.clone();
821 v
822 }
823}
824
825impl std::fmt::Debug for Volatile {
826 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
827 write!(f, "Volatile")
828 }
829}
830
831impl cljrs_gc::Trace for Volatile {
832 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
833 {
834 let value = self.value.lock().unwrap();
835 value.trace(visitor);
836 }
837 }
838}
839
840pub enum DelayState {
844 Pending(Box<dyn Thunk>),
845 Forced(Value),
846}
847
848pub struct Delay {
850 pub state: Mutex<DelayState>,
851}
852
853impl Delay {
854 pub fn new(thunk: Box<dyn Thunk>) -> Self {
855 Self {
856 state: Mutex::new(DelayState::Pending(thunk)),
857 }
858 }
859
860 pub fn force(&self) -> Result<Value, String> {
863 let thunk = {
864 let mut guard = self.state.lock().unwrap();
865 if let DelayState::Forced(v) = &*guard {
866 return Ok(v.clone());
867 }
868 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
869 let DelayState::Pending(thunk) = prev else {
870 unreachable!("state was not Pending")
871 };
872 thunk
873 };
875 let result = thunk.force()?;
878 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
879 Ok(result)
880 }
881
882 pub fn is_realized(&self) -> bool {
884 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
885 }
886}
887
888impl std::fmt::Debug for Delay {
889 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
890 write!(f, "Delay")
891 }
892}
893
894impl cljrs_gc::Trace for Delay {
895 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
896 {
899 let state = self.state.lock().unwrap();
900 match &*state {
901 DelayState::Pending(thunk) => thunk.trace(visitor),
902 DelayState::Forced(v) => v.trace(visitor),
903 }
904 }
905 }
906}
907
908pub struct CljxPromise {
912 pub value: Mutex<Option<Value>>,
913 pub cond: Condvar,
914}
915
916impl CljxPromise {
917 pub fn new() -> Self {
918 Self {
919 value: Mutex::new(None),
920 cond: Condvar::new(),
921 }
922 }
923
924 pub fn deliver(&self, v: Value) {
926 let v = crate::publish::publish_value(v);
929 let mut guard = self.value.lock().unwrap();
930 if guard.is_none() {
931 *guard = Some(v);
932 self.cond.notify_all();
933 }
934 }
935
936 pub fn deref_blocking(&self) -> Value {
938 let mut guard = self.value.lock().unwrap();
939 while guard.is_none() {
940 guard = self.cond.wait(guard).unwrap();
941 }
942 guard.as_ref().unwrap().clone()
943 }
944
945 pub fn is_realized(&self) -> bool {
947 self.value.lock().unwrap().is_some()
948 }
949}
950
951impl Default for CljxPromise {
952 fn default() -> Self {
953 Self::new()
954 }
955}
956
957impl std::fmt::Debug for CljxPromise {
958 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
959 write!(f, "Promise")
960 }
961}
962
963impl cljrs_gc::Trace for CljxPromise {
964 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
965 {
966 let value = self.value.lock().unwrap();
967 if let Some(v) = value.as_ref() {
968 v.trace(visitor);
969 }
970 }
971 }
972}
973
974pub enum FutureState {
978 Running,
979 Done(Value),
980 Failed(Value),
984 Cancelled,
985}
986
987pub struct CljxFuture {
989 pub state: Mutex<FutureState>,
990 pub cond: Condvar,
991 observed: std::sync::atomic::AtomicBool,
995}
996
997impl CljxFuture {
998 pub fn new() -> Self {
999 Self {
1000 state: Mutex::new(FutureState::Running),
1001 cond: Condvar::new(),
1002 observed: std::sync::atomic::AtomicBool::new(false),
1003 }
1004 }
1005
1006 pub fn is_done(&self) -> bool {
1008 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1009 }
1010
1011 pub fn is_cancelled(&self) -> bool {
1013 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1014 }
1015
1016 pub fn mark_observed(&self) {
1020 self.observed
1021 .store(true, std::sync::atomic::Ordering::Relaxed);
1022 }
1023}
1024
1025impl Drop for CljxFuture {
1026 fn drop(&mut self) {
1027 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1038 && let Ok(state) = self.state.lock()
1039 && matches!(&*state, FutureState::Failed(_))
1040 {
1041 eprintln!(
1042 "[clojurust warning] a failed future was discarded without its error \
1043 being observed (no await/deref); the thrown exception was lost"
1044 );
1045 }
1046 }
1047}
1048
1049impl Default for CljxFuture {
1050 fn default() -> Self {
1051 Self::new()
1052 }
1053}
1054
1055impl std::fmt::Debug for CljxFuture {
1056 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1057 write!(f, "Future")
1058 }
1059}
1060
1061impl cljrs_gc::Trace for CljxFuture {
1062 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1063 {
1064 let state = self.state.lock().unwrap();
1065 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1068 v.trace(visitor);
1069 }
1070 }
1071 }
1072}
1073
1074pub struct Agent {
1078 pub state: Arc<Mutex<Value>>,
1080 pub error: Arc<Mutex<Option<Value>>>,
1082 pub watches: Mutex<Vec<(Value, Value)>>,
1083}
1084
1085impl Agent {
1086 pub fn get_state(&self) -> Value {
1087 self.state.lock().unwrap().clone()
1088 }
1089
1090 pub fn get_error(&self) -> Option<Value> {
1091 self.error.lock().unwrap().clone()
1092 }
1093
1094 pub fn clear_error(&self) {
1095 *self.error.lock().unwrap() = None;
1096 }
1097}
1098
1099impl std::fmt::Debug for Agent {
1100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1101 write!(f, "Agent")
1102 }
1103}
1104
1105impl cljrs_gc::Trace for Agent {
1106 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1107 {
1108 let state = self.state.lock().unwrap();
1109 state.trace(visitor);
1110 }
1111 {
1112 let error = self.error.lock().unwrap();
1113 if let Some(e) = error.as_ref() {
1114 e.trace(visitor);
1115 }
1116 }
1117 {
1118 let watches = self.watches.lock().unwrap();
1119 for (key, f) in watches.iter() {
1120 key.trace(visitor);
1121 f.trace(visitor);
1122 }
1123 }
1124 }
1125}