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)]
234pub struct Var {
235 pub namespace: Arc<str>,
236 pub name: Arc<str>,
237 pub value: Mutex<Option<Value>>,
238 pub is_macro: bool,
239 pub meta: Mutex<Option<Value>>,
241 pub watches: Mutex<Vec<(Value, Value)>>,
242}
243
244impl Var {
245 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
246 Self {
247 namespace: namespace.into(),
248 name: name.into(),
249 value: Mutex::new(None),
250 is_macro: false,
251 meta: Mutex::new(None),
252 watches: Mutex::new(Vec::new()),
253 }
254 }
255
256 pub fn is_bound(&self) -> bool {
257 self.value.lock().unwrap().is_some()
258 }
259
260 pub fn deref(&self) -> Option<Value> {
261 self.value.lock().unwrap().clone()
262 }
263
264 pub fn bind(&self, v: Value) {
265 #[cfg(all(feature = "no-gc", debug_assertions))]
269 debug_assert!(
270 value_gcptr_is_static(&v),
271 "no-gc: Var::bind({}/{}) received a region-local value — store violations \
272 indicate a missing StaticCtxGuard around the value expression",
273 self.namespace,
274 self.name
275 );
276 let v = crate::publish::publish_value(v);
281 let prev = {
287 let mut slot = self.value.lock().unwrap();
288 slot.replace(v.clone())
289 };
290 if let Some(prev) = prev {
291 crate::jit_hooks::notify_var_rebind(&prev, &v);
292 }
293 }
294
295 pub fn get_meta(&self) -> Option<Value> {
296 self.meta.lock().unwrap().clone()
297 }
298
299 pub fn set_meta(&self, m: Value) {
300 *self.meta.lock().unwrap() = Some(m);
301 }
302
303 pub fn full_name(&self) -> String {
304 format!("{}/{}", self.namespace, self.name)
305 }
306}
307
308impl cljrs_gc::Trace for Var {
309 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
310 {
311 let value = self.value.lock().unwrap();
312 if let Some(v) = value.as_ref() {
313 v.trace(visitor);
314 }
315 }
316 {
317 let meta = self.meta.lock().unwrap();
318 if let Some(m) = meta.as_ref() {
319 m.trace(visitor);
320 }
321 }
322 {
323 let watches = self.watches.lock().unwrap();
324 for (key, f) in watches.iter() {
325 key.trace(visitor);
326 f.trace(visitor);
327 }
328 }
329 }
330}
331
332#[derive(Debug)]
336pub struct Atom {
337 pub value: Mutex<Value>,
338 pub meta: Mutex<Option<Value>>,
339 pub validator: Mutex<Option<Value>>,
340 pub watches: Mutex<Vec<(Value, Value)>>,
341}
342
343impl Atom {
344 pub fn new(v: Value) -> Self {
345 let v = crate::publish::publish_value(v);
348 Self {
349 value: Mutex::new(v),
350 meta: Mutex::new(None),
351 validator: Mutex::new(None),
352 watches: Mutex::new(Vec::new()),
353 }
354 }
355
356 pub fn deref(&self) -> Value {
357 self.value.lock().unwrap().clone()
358 }
359
360 pub fn reset(&self, v: Value) -> Value {
361 #[cfg(all(feature = "no-gc", debug_assertions))]
363 debug_assert!(
364 value_gcptr_is_static(&v),
365 "no-gc: Atom::reset() received a region-local value — the new-value \
366 expression must be computed inside a StaticCtxGuard (i.e. inside \
367 the swap! / reset! call) so it is allocated in the static arena"
368 );
369 let v = crate::publish::publish_value(v);
371 let mut guard = self.value.lock().unwrap();
372 *guard = v.clone();
373 v
374 }
375
376 pub fn get_meta(&self) -> Option<Value> {
377 self.meta.lock().unwrap().clone()
378 }
379
380 pub fn set_meta(&self, m: Option<Value>) {
381 *self.meta.lock().unwrap() = m;
382 }
383
384 pub fn get_validator(&self) -> Option<Value> {
385 self.validator.lock().unwrap().clone()
386 }
387
388 pub fn set_validator(&self, vf: Option<Value>) {
389 *self.validator.lock().unwrap() = vf;
390 }
391}
392
393impl cljrs_gc::Trace for Atom {
394 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
395 {
396 let value = self.value.lock().unwrap();
397 value.trace(visitor);
398 }
399 {
400 let meta = self.meta.lock().unwrap();
401 if let Some(m) = meta.as_ref() {
402 m.trace(visitor);
403 }
404 }
405 {
406 let validator = self.validator.lock().unwrap();
407 if let Some(vf) = validator.as_ref() {
408 vf.trace(visitor);
409 }
410 }
411 {
412 let watches = self.watches.lock().unwrap();
413 for (key, f) in watches.iter() {
414 key.trace(visitor);
415 f.trace(visitor);
416 }
417 }
418 }
419}
420
421#[derive(Debug)]
425pub struct Namespace {
426 pub name: Arc<str>,
427 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
429 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
431 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
433 pub source_file: Mutex<Option<Arc<str>>>,
437 pub git_repo_root: Mutex<Option<Arc<str>>>,
439 pub is_versioned: bool,
442}
443
444impl Namespace {
445 pub fn new(name: impl Into<Arc<str>>) -> Self {
446 Self {
447 name: name.into(),
448 interns: Mutex::new(HashMap::new()),
449 refers: Mutex::new(HashMap::new()),
450 aliases: Mutex::new(HashMap::new()),
451 source_file: Mutex::new(None),
452 git_repo_root: Mutex::new(None),
453 is_versioned: false,
454 }
455 }
456
457 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
459 Self {
460 is_versioned: true,
461 ..Self::new(name)
462 }
463 }
464
465 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
467 *self.source_file.lock().unwrap() = Some(Arc::from(file));
468 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
469 }
470}
471
472impl cljrs_gc::Trace for Namespace {
473 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
474 use cljrs_gc::GcVisitor as _;
475 {
476 let interns = self.interns.lock().unwrap();
477 for var in interns.values() {
478 visitor.visit(var);
479 }
480 }
481 {
482 let refers = self.refers.lock().unwrap();
483 for var in refers.values() {
484 visitor.visit(var);
485 }
486 }
487 }
488}
489
490pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
496
497pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
500
501#[derive(Clone, Debug)]
502pub enum Arity {
503 Fixed(usize),
504 Variadic { min: usize },
505}
506
507pub struct NativeFn {
508 pub name: Arc<str>,
509 pub arity: Arity,
510 pub func: NativeFnFunc,
511}
512
513impl NativeFn {
514 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
516 Self {
517 name: name.into(),
518 arity,
519 func: Arc::new(func),
520 }
521 }
522
523 pub fn with_closure(
525 name: impl Into<Arc<str>>,
526 arity: Arity,
527 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
528 ) -> Self {
529 Self {
530 name: name.into(),
531 arity,
532 func: Arc::new(func),
533 }
534 }
535}
536
537impl std::fmt::Debug for NativeFn {
538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
539 f.debug_struct("NativeFn")
540 .field("name", &self.name)
541 .field("arity", &self.arity)
542 .field("func", &"<fn>")
543 .finish()
544 }
545}
546
547impl cljrs_gc::Trace for NativeFn {
548 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
549}
550
551#[derive(Debug, Clone)]
555pub struct CljxFnArity {
556 pub params: Vec<Arc<str>>,
559 pub rest_param: Option<Arc<str>>,
561 pub body: Vec<Form>,
563 pub destructure_params: Vec<(usize, Form)>,
567 pub destructure_rest: Option<Form>,
569 pub ir_arity_id: u64,
571 pub param_hints: Vec<Option<TypeHint>>,
575 pub rest_hint: Option<TypeHint>,
578}
579
580impl CljxFnArity {
581 pub fn heap_size(&self) -> usize {
583 self.params.capacity() * mem::size_of::<Arc<str>>()
585 + self.body.capacity() * mem::size_of::<Form>()
587 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
588 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
590 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
591 + self.destructure_rest.as_ref()
593 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
594 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
596 }
597}
598
599#[derive(Debug, Clone)]
603pub struct CljxFn {
604 pub name: Option<Arc<str>>,
605 pub arities: Vec<CljxFnArity>,
606 pub closed_over_names: Vec<Arc<str>>,
608 pub closed_over_vals: Vec<Value>,
610 pub is_macro: bool,
612 pub is_async: bool,
617 pub defining_ns: Arc<str>,
619}
620
621impl CljxFn {
622 pub fn new(
623 name: Option<Arc<str>>,
624 arities: Vec<CljxFnArity>,
625 closed_over_names: Vec<Arc<str>>,
626 closed_over_vals: Vec<Value>,
627 is_macro: bool,
628 defining_ns: Arc<str>,
629 ) -> Self {
630 Self {
631 name,
632 arities,
633 closed_over_names,
634 closed_over_vals,
635 is_macro,
636 is_async: false,
637 defining_ns,
638 }
639 }
640}
641
642impl cljrs_gc::Trace for CljxFn {
643 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
644 for v in &self.closed_over_vals {
645 v.trace(visitor);
646 }
647 }
648
649 fn gc_size_extra(&self) -> usize {
650 self.arities.capacity() * mem::size_of::<CljxFnArity>()
652 + self
653 .arities
654 .iter()
655 .map(CljxFnArity::heap_size)
656 .sum::<usize>()
657 }
658}
659
660#[derive(Debug)]
667pub struct BoundFn {
668 pub wrapped: Value,
670 pub captured_bindings: HashMap<usize, Value>,
672}
673
674impl cljrs_gc::Trace for BoundFn {
675 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
676 self.wrapped.trace(visitor);
677 for val in self.captured_bindings.values() {
678 val.trace(visitor);
679 }
680 }
681
682 fn gc_size_extra(&self) -> usize {
683 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
685 }
686}
687
688pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
692 fn force(&self) -> Result<Value, String>;
693}
694
695pub enum LazySeqState {
697 Pending(Box<dyn Thunk>),
699 Forced(Value),
701 Error(String),
703}
704
705pub struct LazySeq {
707 pub state: Mutex<LazySeqState>,
708}
709
710impl LazySeq {
711 pub fn new(thunk: Box<dyn Thunk>) -> Self {
712 Self {
713 state: Mutex::new(LazySeqState::Pending(thunk)),
714 }
715 }
716
717 pub fn realize(&self) -> Value {
720 let thunk = {
721 let mut guard = self.state.lock().unwrap();
722 match &*guard {
723 LazySeqState::Forced(v) => return v.clone(),
724 LazySeqState::Error(_) => return Value::Nil,
725 LazySeqState::Pending(_) => {}
726 }
727 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
729 let LazySeqState::Pending(thunk) = prev else {
730 unreachable!("state was not Pending")
731 };
732 thunk
733 };
735 match thunk.force() {
738 Ok(result) => {
739 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
740 result
741 }
742 Err(msg) => {
743 *self.state.lock().unwrap() = LazySeqState::Error(msg);
744 Value::Nil
745 }
746 }
747 }
748
749 pub fn error(&self) -> Option<String> {
751 let guard = self.state.lock().unwrap();
752 if let LazySeqState::Error(e) = &*guard {
753 Some(e.clone())
754 } else {
755 None
756 }
757 }
758}
759
760impl std::fmt::Debug for LazySeq {
761 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
762 write!(f, "LazySeq(...)")
763 }
764}
765
766impl cljrs_gc::Trace for LazySeq {
767 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
768 {
771 let state = self.state.lock().unwrap();
772 match &*state {
773 LazySeqState::Pending(thunk) => thunk.trace(visitor),
774 LazySeqState::Forced(v) => v.trace(visitor),
775 LazySeqState::Error(_) => {}
776 }
777 }
778 }
779}
780
781#[derive(Debug, Clone)]
788pub struct CljxCons {
789 pub head: Value,
790 pub tail: Value,
791}
792
793impl cljrs_gc::Trace for CljxCons {
794 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
795 self.head.trace(visitor);
796 self.tail.trace(visitor);
797 }
798}
799
800pub struct Volatile {
804 pub value: Mutex<Value>,
805}
806
807impl Volatile {
808 pub fn new(v: Value) -> Self {
809 let v = crate::publish::publish_value(v);
811 Self {
812 value: Mutex::new(v),
813 }
814 }
815
816 pub fn deref(&self) -> Value {
817 self.value.lock().unwrap().clone()
818 }
819
820 pub fn reset(&self, v: Value) -> Value {
821 #[cfg(all(feature = "no-gc", debug_assertions))]
823 debug_assert!(
824 value_gcptr_is_static(&v),
825 "no-gc: Volatile::reset() received a region-local value — ensure the \
826 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
827 );
828 let v = crate::publish::publish_value(v);
830 *self.value.lock().unwrap() = v.clone();
831 v
832 }
833}
834
835impl std::fmt::Debug for Volatile {
836 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
837 write!(f, "Volatile")
838 }
839}
840
841impl cljrs_gc::Trace for Volatile {
842 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
843 {
844 let value = self.value.lock().unwrap();
845 value.trace(visitor);
846 }
847 }
848}
849
850pub enum DelayState {
854 Pending(Box<dyn Thunk>),
855 Forced(Value),
856}
857
858pub struct Delay {
860 pub state: Mutex<DelayState>,
861}
862
863impl Delay {
864 pub fn new(thunk: Box<dyn Thunk>) -> Self {
865 Self {
866 state: Mutex::new(DelayState::Pending(thunk)),
867 }
868 }
869
870 pub fn force(&self) -> Result<Value, String> {
873 let thunk = {
874 let mut guard = self.state.lock().unwrap();
875 if let DelayState::Forced(v) = &*guard {
876 return Ok(v.clone());
877 }
878 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
879 let DelayState::Pending(thunk) = prev else {
880 unreachable!("state was not Pending")
881 };
882 thunk
883 };
885 let result = thunk.force()?;
888 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
889 Ok(result)
890 }
891
892 pub fn is_realized(&self) -> bool {
894 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
895 }
896}
897
898impl std::fmt::Debug for Delay {
899 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
900 write!(f, "Delay")
901 }
902}
903
904impl cljrs_gc::Trace for Delay {
905 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
906 {
909 let state = self.state.lock().unwrap();
910 match &*state {
911 DelayState::Pending(thunk) => thunk.trace(visitor),
912 DelayState::Forced(v) => v.trace(visitor),
913 }
914 }
915 }
916}
917
918pub struct CljxPromise {
922 pub value: Mutex<Option<Value>>,
923 pub cond: Condvar,
924}
925
926impl CljxPromise {
927 pub fn new() -> Self {
928 Self {
929 value: Mutex::new(None),
930 cond: Condvar::new(),
931 }
932 }
933
934 pub fn deliver(&self, v: Value) {
936 let v = crate::publish::publish_value(v);
939 let mut guard = self.value.lock().unwrap();
940 if guard.is_none() {
941 *guard = Some(v);
942 self.cond.notify_all();
943 }
944 }
945
946 pub fn deref_blocking(&self) -> Value {
948 let mut guard = self.value.lock().unwrap();
949 while guard.is_none() {
950 guard = self.cond.wait(guard).unwrap();
951 }
952 guard.as_ref().unwrap().clone()
953 }
954
955 pub fn is_realized(&self) -> bool {
957 self.value.lock().unwrap().is_some()
958 }
959}
960
961impl Default for CljxPromise {
962 fn default() -> Self {
963 Self::new()
964 }
965}
966
967impl std::fmt::Debug for CljxPromise {
968 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
969 write!(f, "Promise")
970 }
971}
972
973impl cljrs_gc::Trace for CljxPromise {
974 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
975 {
976 let value = self.value.lock().unwrap();
977 if let Some(v) = value.as_ref() {
978 v.trace(visitor);
979 }
980 }
981 }
982}
983
984pub enum FutureState {
988 Running,
989 Done(Value),
990 Failed(Value),
994 Cancelled,
995}
996
997pub struct CljxFuture {
999 pub state: Mutex<FutureState>,
1000 pub cond: Condvar,
1001 observed: std::sync::atomic::AtomicBool,
1005}
1006
1007impl CljxFuture {
1008 pub fn new() -> Self {
1009 Self {
1010 state: Mutex::new(FutureState::Running),
1011 cond: Condvar::new(),
1012 observed: std::sync::atomic::AtomicBool::new(false),
1013 }
1014 }
1015
1016 pub fn is_done(&self) -> bool {
1018 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
1019 }
1020
1021 pub fn is_cancelled(&self) -> bool {
1023 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
1024 }
1025
1026 pub fn mark_observed(&self) {
1030 self.observed
1031 .store(true, std::sync::atomic::Ordering::Relaxed);
1032 }
1033}
1034
1035impl Drop for CljxFuture {
1036 fn drop(&mut self) {
1037 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1048 && let Ok(state) = self.state.lock()
1049 && matches!(&*state, FutureState::Failed(_))
1050 {
1051 eprintln!(
1052 "[clojurust warning] a failed future was discarded without its error \
1053 being observed (no await/deref); the thrown exception was lost"
1054 );
1055 }
1056 }
1057}
1058
1059impl Default for CljxFuture {
1060 fn default() -> Self {
1061 Self::new()
1062 }
1063}
1064
1065impl std::fmt::Debug for CljxFuture {
1066 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1067 write!(f, "Future")
1068 }
1069}
1070
1071impl cljrs_gc::Trace for CljxFuture {
1072 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1073 {
1074 let state = self.state.lock().unwrap();
1075 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1078 v.trace(visitor);
1079 }
1080 }
1081 }
1082}
1083
1084pub struct Agent {
1088 pub state: Arc<Mutex<Value>>,
1090 pub error: Arc<Mutex<Option<Value>>>,
1092 pub watches: Mutex<Vec<(Value, Value)>>,
1093}
1094
1095impl Agent {
1096 pub fn get_state(&self) -> Value {
1097 self.state.lock().unwrap().clone()
1098 }
1099
1100 pub fn get_error(&self) -> Option<Value> {
1101 self.error.lock().unwrap().clone()
1102 }
1103
1104 pub fn clear_error(&self) {
1105 *self.error.lock().unwrap() = None;
1106 }
1107}
1108
1109impl std::fmt::Debug for Agent {
1110 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1111 write!(f, "Agent")
1112 }
1113}
1114
1115impl cljrs_gc::Trace for Agent {
1116 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1117 {
1118 let state = self.state.lock().unwrap();
1119 state.trace(visitor);
1120 }
1121 {
1122 let error = self.error.lock().unwrap();
1123 if let Some(e) = error.as_ref() {
1124 e.trace(visitor);
1125 }
1126 }
1127 {
1128 let watches = self.watches.lock().unwrap();
1129 for (key, f) in watches.iter() {
1130 key.trace(visitor);
1131 f.trace(visitor);
1132 }
1133 }
1134 }
1135}