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
126impl cljrs_gc::Trace for Protocol {
127 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
128 {
129 let impls = self.impls.lock().unwrap();
130 for method_map in impls.values() {
131 for v in method_map.values() {
132 v.trace(visitor);
133 }
134 }
135 }
136 }
137}
138
139#[derive(Debug, Clone)]
141pub struct ProtocolMethod {
142 pub name: Arc<str>,
143 pub min_arity: usize,
144 pub variadic: bool,
145}
146
147impl cljrs_gc::Trace for ProtocolMethod {
148 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
149}
150
151#[derive(Debug)]
155pub struct ProtocolFn {
156 pub protocol: GcPtr<Protocol>,
157 pub method_name: Arc<str>,
158 pub min_arity: usize,
159 pub variadic: bool,
160}
161
162impl cljrs_gc::Trace for ProtocolFn {
163 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
164 use cljrs_gc::GcVisitor as _;
165 visitor.visit(&self.protocol);
166 }
167}
168
169#[derive(Debug)]
173pub struct MultiFn {
174 pub name: Arc<str>,
175 pub dispatch_fn: Value,
176 pub methods: Mutex<HashMap<String, Value>>,
178 pub prefers: Mutex<HashMap<String, Vec<String>>>,
180 pub default_dispatch: String,
182}
183
184impl MultiFn {
185 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
186 Self {
187 name,
188 dispatch_fn,
189 methods: Mutex::new(HashMap::new()),
190 prefers: Mutex::new(HashMap::new()),
191 default_dispatch,
192 }
193 }
194}
195
196impl cljrs_gc::Trace for MultiFn {
197 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
198 self.dispatch_fn.trace(visitor);
199 {
200 let methods = self.methods.lock().unwrap();
201 for v in methods.values() {
202 v.trace(visitor);
203 }
204 }
205 }
206}
207
208#[derive(Debug)]
212pub struct Var {
213 pub namespace: Arc<str>,
214 pub name: Arc<str>,
215 pub value: Mutex<Option<Value>>,
216 pub is_macro: bool,
217 pub meta: Mutex<Option<Value>>,
219 pub watches: Mutex<Vec<(Value, Value)>>,
220}
221
222impl Var {
223 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
224 Self {
225 namespace: namespace.into(),
226 name: name.into(),
227 value: Mutex::new(None),
228 is_macro: false,
229 meta: Mutex::new(None),
230 watches: Mutex::new(Vec::new()),
231 }
232 }
233
234 pub fn is_bound(&self) -> bool {
235 self.value.lock().unwrap().is_some()
236 }
237
238 pub fn deref(&self) -> Option<Value> {
239 self.value.lock().unwrap().clone()
240 }
241
242 pub fn bind(&self, v: Value) {
243 #[cfg(all(feature = "no-gc", debug_assertions))]
247 debug_assert!(
248 value_gcptr_is_static(&v),
249 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
250 indicate a missing StaticCtxGuard around the value expression",
251 self.namespace,
252 self.name
253 );
254 let prev = {
260 let mut slot = self.value.lock().unwrap();
261 slot.replace(v.clone())
262 };
263 if let Some(prev) = prev {
264 crate::jit_hooks::notify_var_rebind(&prev, &v);
265 }
266 }
267
268 pub fn get_meta(&self) -> Option<Value> {
269 self.meta.lock().unwrap().clone()
270 }
271
272 pub fn set_meta(&self, m: Value) {
273 *self.meta.lock().unwrap() = Some(m);
274 }
275
276 pub fn full_name(&self) -> String {
277 format!("{}/{}", self.namespace, self.name)
278 }
279}
280
281impl cljrs_gc::Trace for Var {
282 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
283 {
284 let value = self.value.lock().unwrap();
285 if let Some(v) = value.as_ref() {
286 v.trace(visitor);
287 }
288 }
289 {
290 let meta = self.meta.lock().unwrap();
291 if let Some(m) = meta.as_ref() {
292 m.trace(visitor);
293 }
294 }
295 {
296 let watches = self.watches.lock().unwrap();
297 for (key, f) in watches.iter() {
298 key.trace(visitor);
299 f.trace(visitor);
300 }
301 }
302 }
303}
304
305#[derive(Debug)]
309pub struct Atom {
310 pub value: Mutex<Value>,
311 pub meta: Mutex<Option<Value>>,
312 pub validator: Mutex<Option<Value>>,
313 pub watches: Mutex<Vec<(Value, Value)>>,
314}
315
316impl Atom {
317 pub fn new(v: Value) -> Self {
318 Self {
319 value: Mutex::new(v),
320 meta: Mutex::new(None),
321 validator: Mutex::new(None),
322 watches: Mutex::new(Vec::new()),
323 }
324 }
325
326 pub fn deref(&self) -> Value {
327 self.value.lock().unwrap().clone()
328 }
329
330 pub fn reset(&self, v: Value) -> Value {
331 #[cfg(all(feature = "no-gc", debug_assertions))]
333 debug_assert!(
334 value_gcptr_is_static(&v),
335 "no-gc: Atom::reset() received a region-local value — the new-value \
336 expression must be computed inside a StaticCtxGuard (i.e. inside \
337 the swap! / reset! call) so it is allocated in the static arena"
338 );
339 let mut guard = self.value.lock().unwrap();
340 *guard = v.clone();
341 v
342 }
343
344 pub fn get_meta(&self) -> Option<Value> {
345 self.meta.lock().unwrap().clone()
346 }
347
348 pub fn set_meta(&self, m: Option<Value>) {
349 *self.meta.lock().unwrap() = m;
350 }
351
352 pub fn get_validator(&self) -> Option<Value> {
353 self.validator.lock().unwrap().clone()
354 }
355
356 pub fn set_validator(&self, vf: Option<Value>) {
357 *self.validator.lock().unwrap() = vf;
358 }
359}
360
361impl cljrs_gc::Trace for Atom {
362 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
363 {
364 let value = self.value.lock().unwrap();
365 value.trace(visitor);
366 }
367 {
368 let meta = self.meta.lock().unwrap();
369 if let Some(m) = meta.as_ref() {
370 m.trace(visitor);
371 }
372 }
373 {
374 let validator = self.validator.lock().unwrap();
375 if let Some(vf) = validator.as_ref() {
376 vf.trace(visitor);
377 }
378 }
379 {
380 let watches = self.watches.lock().unwrap();
381 for (key, f) in watches.iter() {
382 key.trace(visitor);
383 f.trace(visitor);
384 }
385 }
386 }
387}
388
389#[derive(Debug)]
393pub struct Namespace {
394 pub name: Arc<str>,
395 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
397 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
399 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
401 pub source_file: Mutex<Option<Arc<str>>>,
405 pub git_repo_root: Mutex<Option<Arc<str>>>,
407 pub is_versioned: bool,
410}
411
412impl Namespace {
413 pub fn new(name: impl Into<Arc<str>>) -> Self {
414 Self {
415 name: name.into(),
416 interns: Mutex::new(HashMap::new()),
417 refers: Mutex::new(HashMap::new()),
418 aliases: Mutex::new(HashMap::new()),
419 source_file: Mutex::new(None),
420 git_repo_root: Mutex::new(None),
421 is_versioned: false,
422 }
423 }
424
425 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
427 Self {
428 is_versioned: true,
429 ..Self::new(name)
430 }
431 }
432
433 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
435 *self.source_file.lock().unwrap() = Some(Arc::from(file));
436 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
437 }
438}
439
440impl cljrs_gc::Trace for Namespace {
441 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
442 use cljrs_gc::GcVisitor as _;
443 {
444 let interns = self.interns.lock().unwrap();
445 for var in interns.values() {
446 visitor.visit(var);
447 }
448 }
449 {
450 let refers = self.refers.lock().unwrap();
451 for var in refers.values() {
452 visitor.visit(var);
453 }
454 }
455 }
456}
457
458pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
464
465pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
468
469#[derive(Clone, Debug)]
470pub enum Arity {
471 Fixed(usize),
472 Variadic { min: usize },
473}
474
475pub struct NativeFn {
476 pub name: Arc<str>,
477 pub arity: Arity,
478 pub func: NativeFnFunc,
479}
480
481impl NativeFn {
482 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
484 Self {
485 name: name.into(),
486 arity,
487 func: Arc::new(func),
488 }
489 }
490
491 pub fn with_closure(
493 name: impl Into<Arc<str>>,
494 arity: Arity,
495 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
496 ) -> Self {
497 Self {
498 name: name.into(),
499 arity,
500 func: Arc::new(func),
501 }
502 }
503}
504
505impl std::fmt::Debug for NativeFn {
506 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
507 f.debug_struct("NativeFn")
508 .field("name", &self.name)
509 .field("arity", &self.arity)
510 .field("func", &"<fn>")
511 .finish()
512 }
513}
514
515impl cljrs_gc::Trace for NativeFn {
516 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
517}
518
519#[derive(Debug, Clone)]
523pub struct CljxFnArity {
524 pub params: Vec<Arc<str>>,
527 pub rest_param: Option<Arc<str>>,
529 pub body: Vec<Form>,
531 pub destructure_params: Vec<(usize, Form)>,
535 pub destructure_rest: Option<Form>,
537 pub ir_arity_id: u64,
539}
540
541impl CljxFnArity {
542 pub fn heap_size(&self) -> usize {
544 self.params.capacity() * mem::size_of::<Arc<str>>()
546 + self.body.capacity() * mem::size_of::<Form>()
548 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
549 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
551 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
552 + self.destructure_rest.as_ref()
554 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
555 }
556}
557
558#[derive(Debug, Clone)]
562pub struct CljxFn {
563 pub name: Option<Arc<str>>,
564 pub arities: Vec<CljxFnArity>,
565 pub closed_over_names: Vec<Arc<str>>,
567 pub closed_over_vals: Vec<Value>,
569 pub is_macro: bool,
571 pub is_async: bool,
576 pub defining_ns: Arc<str>,
578}
579
580impl CljxFn {
581 pub fn new(
582 name: Option<Arc<str>>,
583 arities: Vec<CljxFnArity>,
584 closed_over_names: Vec<Arc<str>>,
585 closed_over_vals: Vec<Value>,
586 is_macro: bool,
587 defining_ns: Arc<str>,
588 ) -> Self {
589 Self {
590 name,
591 arities,
592 closed_over_names,
593 closed_over_vals,
594 is_macro,
595 is_async: false,
596 defining_ns,
597 }
598 }
599}
600
601impl cljrs_gc::Trace for CljxFn {
602 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
603 for v in &self.closed_over_vals {
604 v.trace(visitor);
605 }
606 }
607
608 fn gc_size_extra(&self) -> usize {
609 self.arities.capacity() * mem::size_of::<CljxFnArity>()
611 + self
612 .arities
613 .iter()
614 .map(CljxFnArity::heap_size)
615 .sum::<usize>()
616 }
617}
618
619#[derive(Debug)]
626pub struct BoundFn {
627 pub wrapped: Value,
629 pub captured_bindings: HashMap<usize, Value>,
631}
632
633impl cljrs_gc::Trace for BoundFn {
634 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
635 self.wrapped.trace(visitor);
636 for val in self.captured_bindings.values() {
637 val.trace(visitor);
638 }
639 }
640
641 fn gc_size_extra(&self) -> usize {
642 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
644 }
645}
646
647pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
651 fn force(&self) -> Result<Value, String>;
652}
653
654pub enum LazySeqState {
656 Pending(Box<dyn Thunk>),
658 Forced(Value),
660 Error(String),
662}
663
664pub struct LazySeq {
666 pub state: Mutex<LazySeqState>,
667}
668
669impl LazySeq {
670 pub fn new(thunk: Box<dyn Thunk>) -> Self {
671 Self {
672 state: Mutex::new(LazySeqState::Pending(thunk)),
673 }
674 }
675
676 pub fn realize(&self) -> Value {
679 let thunk = {
680 let mut guard = self.state.lock().unwrap();
681 match &*guard {
682 LazySeqState::Forced(v) => return v.clone(),
683 LazySeqState::Error(_) => return Value::Nil,
684 LazySeqState::Pending(_) => {}
685 }
686 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
688 let LazySeqState::Pending(thunk) = prev else {
689 unreachable!("state was not Pending")
690 };
691 thunk
692 };
694 match thunk.force() {
697 Ok(result) => {
698 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
699 result
700 }
701 Err(msg) => {
702 *self.state.lock().unwrap() = LazySeqState::Error(msg);
703 Value::Nil
704 }
705 }
706 }
707
708 pub fn error(&self) -> Option<String> {
710 let guard = self.state.lock().unwrap();
711 if let LazySeqState::Error(e) = &*guard {
712 Some(e.clone())
713 } else {
714 None
715 }
716 }
717}
718
719impl std::fmt::Debug for LazySeq {
720 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
721 write!(f, "LazySeq(...)")
722 }
723}
724
725impl cljrs_gc::Trace for LazySeq {
726 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
727 {
730 let state = self.state.lock().unwrap();
731 match &*state {
732 LazySeqState::Pending(thunk) => thunk.trace(visitor),
733 LazySeqState::Forced(v) => v.trace(visitor),
734 LazySeqState::Error(_) => {}
735 }
736 }
737 }
738}
739
740#[derive(Debug, Clone)]
747pub struct CljxCons {
748 pub head: Value,
749 pub tail: Value,
750}
751
752impl cljrs_gc::Trace for CljxCons {
753 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
754 self.head.trace(visitor);
755 self.tail.trace(visitor);
756 }
757}
758
759pub struct Volatile {
763 pub value: Mutex<Value>,
764}
765
766impl Volatile {
767 pub fn new(v: Value) -> Self {
768 Self {
769 value: Mutex::new(v),
770 }
771 }
772
773 pub fn deref(&self) -> Value {
774 self.value.lock().unwrap().clone()
775 }
776
777 pub fn reset(&self, v: Value) -> Value {
778 #[cfg(all(feature = "no-gc", debug_assertions))]
780 debug_assert!(
781 value_gcptr_is_static(&v),
782 "no-gc: Volatile::reset() received a region-local value — ensure the \
783 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
784 );
785 *self.value.lock().unwrap() = v.clone();
786 v
787 }
788}
789
790impl std::fmt::Debug for Volatile {
791 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
792 write!(f, "Volatile")
793 }
794}
795
796impl cljrs_gc::Trace for Volatile {
797 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
798 {
799 let value = self.value.lock().unwrap();
800 value.trace(visitor);
801 }
802 }
803}
804
805pub enum DelayState {
809 Pending(Box<dyn Thunk>),
810 Forced(Value),
811}
812
813pub struct Delay {
815 pub state: Mutex<DelayState>,
816}
817
818impl Delay {
819 pub fn new(thunk: Box<dyn Thunk>) -> Self {
820 Self {
821 state: Mutex::new(DelayState::Pending(thunk)),
822 }
823 }
824
825 pub fn force(&self) -> Result<Value, String> {
828 let thunk = {
829 let mut guard = self.state.lock().unwrap();
830 if let DelayState::Forced(v) = &*guard {
831 return Ok(v.clone());
832 }
833 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
834 let DelayState::Pending(thunk) = prev else {
835 unreachable!("state was not Pending")
836 };
837 thunk
838 };
840 let result = thunk.force()?;
843 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
844 Ok(result)
845 }
846
847 pub fn is_realized(&self) -> bool {
849 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
850 }
851}
852
853impl std::fmt::Debug for Delay {
854 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
855 write!(f, "Delay")
856 }
857}
858
859impl cljrs_gc::Trace for Delay {
860 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
861 {
864 let state = self.state.lock().unwrap();
865 match &*state {
866 DelayState::Pending(thunk) => thunk.trace(visitor),
867 DelayState::Forced(v) => v.trace(visitor),
868 }
869 }
870 }
871}
872
873pub struct CljxPromise {
877 pub value: Mutex<Option<Value>>,
878 pub cond: Condvar,
879}
880
881impl CljxPromise {
882 pub fn new() -> Self {
883 Self {
884 value: Mutex::new(None),
885 cond: Condvar::new(),
886 }
887 }
888
889 pub fn deliver(&self, v: Value) {
891 let mut guard = self.value.lock().unwrap();
892 if guard.is_none() {
893 *guard = Some(v);
894 self.cond.notify_all();
895 }
896 }
897
898 pub fn deref_blocking(&self) -> Value {
900 let mut guard = self.value.lock().unwrap();
901 while guard.is_none() {
902 guard = self.cond.wait(guard).unwrap();
903 }
904 guard.as_ref().unwrap().clone()
905 }
906
907 pub fn is_realized(&self) -> bool {
909 self.value.lock().unwrap().is_some()
910 }
911}
912
913impl Default for CljxPromise {
914 fn default() -> Self {
915 Self::new()
916 }
917}
918
919impl std::fmt::Debug for CljxPromise {
920 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
921 write!(f, "Promise")
922 }
923}
924
925impl cljrs_gc::Trace for CljxPromise {
926 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
927 {
928 let value = self.value.lock().unwrap();
929 if let Some(v) = value.as_ref() {
930 v.trace(visitor);
931 }
932 }
933 }
934}
935
936pub enum FutureState {
940 Running,
941 Done(Value),
942 Failed(Value),
946 Cancelled,
947}
948
949pub struct CljxFuture {
951 pub state: Mutex<FutureState>,
952 pub cond: Condvar,
953 observed: std::sync::atomic::AtomicBool,
957}
958
959impl CljxFuture {
960 pub fn new() -> Self {
961 Self {
962 state: Mutex::new(FutureState::Running),
963 cond: Condvar::new(),
964 observed: std::sync::atomic::AtomicBool::new(false),
965 }
966 }
967
968 pub fn is_done(&self) -> bool {
970 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
971 }
972
973 pub fn is_cancelled(&self) -> bool {
975 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
976 }
977
978 pub fn mark_observed(&self) {
982 self.observed
983 .store(true, std::sync::atomic::Ordering::Relaxed);
984 }
985}
986
987impl Drop for CljxFuture {
988 fn drop(&mut self) {
989 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1000 && let Ok(state) = self.state.lock()
1001 && matches!(&*state, FutureState::Failed(_))
1002 {
1003 eprintln!(
1004 "[clojurust warning] a failed future was discarded without its error \
1005 being observed (no await/deref); the thrown exception was lost"
1006 );
1007 }
1008 }
1009}
1010
1011impl Default for CljxFuture {
1012 fn default() -> Self {
1013 Self::new()
1014 }
1015}
1016
1017impl std::fmt::Debug for CljxFuture {
1018 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1019 write!(f, "Future")
1020 }
1021}
1022
1023impl cljrs_gc::Trace for CljxFuture {
1024 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1025 {
1026 let state = self.state.lock().unwrap();
1027 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1030 v.trace(visitor);
1031 }
1032 }
1033 }
1034}
1035
1036pub struct Agent {
1040 pub state: Arc<Mutex<Value>>,
1042 pub error: Arc<Mutex<Option<Value>>>,
1044 pub watches: Mutex<Vec<(Value, Value)>>,
1045}
1046
1047impl Agent {
1048 pub fn get_state(&self) -> Value {
1049 self.state.lock().unwrap().clone()
1050 }
1051
1052 pub fn get_error(&self) -> Option<Value> {
1053 self.error.lock().unwrap().clone()
1054 }
1055
1056 pub fn clear_error(&self) {
1057 *self.error.lock().unwrap() = None;
1058 }
1059}
1060
1061impl std::fmt::Debug for Agent {
1062 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1063 write!(f, "Agent")
1064 }
1065}
1066
1067impl cljrs_gc::Trace for Agent {
1068 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1069 {
1070 let state = self.state.lock().unwrap();
1071 state.trace(visitor);
1072 }
1073 {
1074 let error = self.error.lock().unwrap();
1075 if let Some(e) = error.as_ref() {
1076 e.trace(visitor);
1077 }
1078 }
1079 {
1080 let watches = self.watches.lock().unwrap();
1081 for (key, f) in watches.iter() {
1082 key.trace(visitor);
1083 f.trace(visitor);
1084 }
1085 }
1086 }
1087}