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 v = crate::publish::publish_value(v);
259 let prev = {
265 let mut slot = self.value.lock().unwrap();
266 slot.replace(v.clone())
267 };
268 if let Some(prev) = prev {
269 crate::jit_hooks::notify_var_rebind(&prev, &v);
270 }
271 }
272
273 pub fn get_meta(&self) -> Option<Value> {
274 self.meta.lock().unwrap().clone()
275 }
276
277 pub fn set_meta(&self, m: Value) {
278 *self.meta.lock().unwrap() = Some(m);
279 }
280
281 pub fn full_name(&self) -> String {
282 format!("{}/{}", self.namespace, self.name)
283 }
284}
285
286impl cljrs_gc::Trace for Var {
287 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
288 {
289 let value = self.value.lock().unwrap();
290 if let Some(v) = value.as_ref() {
291 v.trace(visitor);
292 }
293 }
294 {
295 let meta = self.meta.lock().unwrap();
296 if let Some(m) = meta.as_ref() {
297 m.trace(visitor);
298 }
299 }
300 {
301 let watches = self.watches.lock().unwrap();
302 for (key, f) in watches.iter() {
303 key.trace(visitor);
304 f.trace(visitor);
305 }
306 }
307 }
308}
309
310#[derive(Debug)]
314pub struct Atom {
315 pub value: Mutex<Value>,
316 pub meta: Mutex<Option<Value>>,
317 pub validator: Mutex<Option<Value>>,
318 pub watches: Mutex<Vec<(Value, Value)>>,
319}
320
321impl Atom {
322 pub fn new(v: Value) -> Self {
323 let v = crate::publish::publish_value(v);
326 Self {
327 value: Mutex::new(v),
328 meta: Mutex::new(None),
329 validator: Mutex::new(None),
330 watches: Mutex::new(Vec::new()),
331 }
332 }
333
334 pub fn deref(&self) -> Value {
335 self.value.lock().unwrap().clone()
336 }
337
338 pub fn reset(&self, v: Value) -> Value {
339 #[cfg(all(feature = "no-gc", debug_assertions))]
341 debug_assert!(
342 value_gcptr_is_static(&v),
343 "no-gc: Atom::reset() received a region-local value — the new-value \
344 expression must be computed inside a StaticCtxGuard (i.e. inside \
345 the swap! / reset! call) so it is allocated in the static arena"
346 );
347 let v = crate::publish::publish_value(v);
349 let mut guard = self.value.lock().unwrap();
350 *guard = v.clone();
351 v
352 }
353
354 pub fn get_meta(&self) -> Option<Value> {
355 self.meta.lock().unwrap().clone()
356 }
357
358 pub fn set_meta(&self, m: Option<Value>) {
359 *self.meta.lock().unwrap() = m;
360 }
361
362 pub fn get_validator(&self) -> Option<Value> {
363 self.validator.lock().unwrap().clone()
364 }
365
366 pub fn set_validator(&self, vf: Option<Value>) {
367 *self.validator.lock().unwrap() = vf;
368 }
369}
370
371impl cljrs_gc::Trace for Atom {
372 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
373 {
374 let value = self.value.lock().unwrap();
375 value.trace(visitor);
376 }
377 {
378 let meta = self.meta.lock().unwrap();
379 if let Some(m) = meta.as_ref() {
380 m.trace(visitor);
381 }
382 }
383 {
384 let validator = self.validator.lock().unwrap();
385 if let Some(vf) = validator.as_ref() {
386 vf.trace(visitor);
387 }
388 }
389 {
390 let watches = self.watches.lock().unwrap();
391 for (key, f) in watches.iter() {
392 key.trace(visitor);
393 f.trace(visitor);
394 }
395 }
396 }
397}
398
399#[derive(Debug)]
403pub struct Namespace {
404 pub name: Arc<str>,
405 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
407 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
409 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
411 pub source_file: Mutex<Option<Arc<str>>>,
415 pub git_repo_root: Mutex<Option<Arc<str>>>,
417 pub is_versioned: bool,
420}
421
422impl Namespace {
423 pub fn new(name: impl Into<Arc<str>>) -> Self {
424 Self {
425 name: name.into(),
426 interns: Mutex::new(HashMap::new()),
427 refers: Mutex::new(HashMap::new()),
428 aliases: Mutex::new(HashMap::new()),
429 source_file: Mutex::new(None),
430 git_repo_root: Mutex::new(None),
431 is_versioned: false,
432 }
433 }
434
435 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
437 Self {
438 is_versioned: true,
439 ..Self::new(name)
440 }
441 }
442
443 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
445 *self.source_file.lock().unwrap() = Some(Arc::from(file));
446 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
447 }
448}
449
450impl cljrs_gc::Trace for Namespace {
451 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
452 use cljrs_gc::GcVisitor as _;
453 {
454 let interns = self.interns.lock().unwrap();
455 for var in interns.values() {
456 visitor.visit(var);
457 }
458 }
459 {
460 let refers = self.refers.lock().unwrap();
461 for var in refers.values() {
462 visitor.visit(var);
463 }
464 }
465 }
466}
467
468pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
474
475pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
478
479#[derive(Clone, Debug)]
480pub enum Arity {
481 Fixed(usize),
482 Variadic { min: usize },
483}
484
485pub struct NativeFn {
486 pub name: Arc<str>,
487 pub arity: Arity,
488 pub func: NativeFnFunc,
489}
490
491impl NativeFn {
492 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
494 Self {
495 name: name.into(),
496 arity,
497 func: Arc::new(func),
498 }
499 }
500
501 pub fn with_closure(
503 name: impl Into<Arc<str>>,
504 arity: Arity,
505 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
506 ) -> Self {
507 Self {
508 name: name.into(),
509 arity,
510 func: Arc::new(func),
511 }
512 }
513}
514
515impl std::fmt::Debug for NativeFn {
516 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
517 f.debug_struct("NativeFn")
518 .field("name", &self.name)
519 .field("arity", &self.arity)
520 .field("func", &"<fn>")
521 .finish()
522 }
523}
524
525impl cljrs_gc::Trace for NativeFn {
526 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
527}
528
529#[derive(Debug, Clone)]
533pub struct CljxFnArity {
534 pub params: Vec<Arc<str>>,
537 pub rest_param: Option<Arc<str>>,
539 pub body: Vec<Form>,
541 pub destructure_params: Vec<(usize, Form)>,
545 pub destructure_rest: Option<Form>,
547 pub ir_arity_id: u64,
549}
550
551impl CljxFnArity {
552 pub fn heap_size(&self) -> usize {
554 self.params.capacity() * mem::size_of::<Arc<str>>()
556 + self.body.capacity() * mem::size_of::<Form>()
558 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
559 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
561 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
562 + self.destructure_rest.as_ref()
564 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
565 }
566}
567
568#[derive(Debug, Clone)]
572pub struct CljxFn {
573 pub name: Option<Arc<str>>,
574 pub arities: Vec<CljxFnArity>,
575 pub closed_over_names: Vec<Arc<str>>,
577 pub closed_over_vals: Vec<Value>,
579 pub is_macro: bool,
581 pub is_async: bool,
586 pub defining_ns: Arc<str>,
588}
589
590impl CljxFn {
591 pub fn new(
592 name: Option<Arc<str>>,
593 arities: Vec<CljxFnArity>,
594 closed_over_names: Vec<Arc<str>>,
595 closed_over_vals: Vec<Value>,
596 is_macro: bool,
597 defining_ns: Arc<str>,
598 ) -> Self {
599 Self {
600 name,
601 arities,
602 closed_over_names,
603 closed_over_vals,
604 is_macro,
605 is_async: false,
606 defining_ns,
607 }
608 }
609}
610
611impl cljrs_gc::Trace for CljxFn {
612 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
613 for v in &self.closed_over_vals {
614 v.trace(visitor);
615 }
616 }
617
618 fn gc_size_extra(&self) -> usize {
619 self.arities.capacity() * mem::size_of::<CljxFnArity>()
621 + self
622 .arities
623 .iter()
624 .map(CljxFnArity::heap_size)
625 .sum::<usize>()
626 }
627}
628
629#[derive(Debug)]
636pub struct BoundFn {
637 pub wrapped: Value,
639 pub captured_bindings: HashMap<usize, Value>,
641}
642
643impl cljrs_gc::Trace for BoundFn {
644 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
645 self.wrapped.trace(visitor);
646 for val in self.captured_bindings.values() {
647 val.trace(visitor);
648 }
649 }
650
651 fn gc_size_extra(&self) -> usize {
652 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
654 }
655}
656
657pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
661 fn force(&self) -> Result<Value, String>;
662}
663
664pub enum LazySeqState {
666 Pending(Box<dyn Thunk>),
668 Forced(Value),
670 Error(String),
672}
673
674pub struct LazySeq {
676 pub state: Mutex<LazySeqState>,
677}
678
679impl LazySeq {
680 pub fn new(thunk: Box<dyn Thunk>) -> Self {
681 Self {
682 state: Mutex::new(LazySeqState::Pending(thunk)),
683 }
684 }
685
686 pub fn realize(&self) -> Value {
689 let thunk = {
690 let mut guard = self.state.lock().unwrap();
691 match &*guard {
692 LazySeqState::Forced(v) => return v.clone(),
693 LazySeqState::Error(_) => return Value::Nil,
694 LazySeqState::Pending(_) => {}
695 }
696 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
698 let LazySeqState::Pending(thunk) = prev else {
699 unreachable!("state was not Pending")
700 };
701 thunk
702 };
704 match thunk.force() {
707 Ok(result) => {
708 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
709 result
710 }
711 Err(msg) => {
712 *self.state.lock().unwrap() = LazySeqState::Error(msg);
713 Value::Nil
714 }
715 }
716 }
717
718 pub fn error(&self) -> Option<String> {
720 let guard = self.state.lock().unwrap();
721 if let LazySeqState::Error(e) = &*guard {
722 Some(e.clone())
723 } else {
724 None
725 }
726 }
727}
728
729impl std::fmt::Debug for LazySeq {
730 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
731 write!(f, "LazySeq(...)")
732 }
733}
734
735impl cljrs_gc::Trace for LazySeq {
736 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
737 {
740 let state = self.state.lock().unwrap();
741 match &*state {
742 LazySeqState::Pending(thunk) => thunk.trace(visitor),
743 LazySeqState::Forced(v) => v.trace(visitor),
744 LazySeqState::Error(_) => {}
745 }
746 }
747 }
748}
749
750#[derive(Debug, Clone)]
757pub struct CljxCons {
758 pub head: Value,
759 pub tail: Value,
760}
761
762impl cljrs_gc::Trace for CljxCons {
763 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
764 self.head.trace(visitor);
765 self.tail.trace(visitor);
766 }
767}
768
769pub struct Volatile {
773 pub value: Mutex<Value>,
774}
775
776impl Volatile {
777 pub fn new(v: Value) -> Self {
778 let v = crate::publish::publish_value(v);
780 Self {
781 value: Mutex::new(v),
782 }
783 }
784
785 pub fn deref(&self) -> Value {
786 self.value.lock().unwrap().clone()
787 }
788
789 pub fn reset(&self, v: Value) -> Value {
790 #[cfg(all(feature = "no-gc", debug_assertions))]
792 debug_assert!(
793 value_gcptr_is_static(&v),
794 "no-gc: Volatile::reset() received a region-local value — ensure the \
795 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
796 );
797 let v = crate::publish::publish_value(v);
799 *self.value.lock().unwrap() = v.clone();
800 v
801 }
802}
803
804impl std::fmt::Debug for Volatile {
805 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
806 write!(f, "Volatile")
807 }
808}
809
810impl cljrs_gc::Trace for Volatile {
811 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
812 {
813 let value = self.value.lock().unwrap();
814 value.trace(visitor);
815 }
816 }
817}
818
819pub enum DelayState {
823 Pending(Box<dyn Thunk>),
824 Forced(Value),
825}
826
827pub struct Delay {
829 pub state: Mutex<DelayState>,
830}
831
832impl Delay {
833 pub fn new(thunk: Box<dyn Thunk>) -> Self {
834 Self {
835 state: Mutex::new(DelayState::Pending(thunk)),
836 }
837 }
838
839 pub fn force(&self) -> Result<Value, String> {
842 let thunk = {
843 let mut guard = self.state.lock().unwrap();
844 if let DelayState::Forced(v) = &*guard {
845 return Ok(v.clone());
846 }
847 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
848 let DelayState::Pending(thunk) = prev else {
849 unreachable!("state was not Pending")
850 };
851 thunk
852 };
854 let result = thunk.force()?;
857 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
858 Ok(result)
859 }
860
861 pub fn is_realized(&self) -> bool {
863 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
864 }
865}
866
867impl std::fmt::Debug for Delay {
868 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
869 write!(f, "Delay")
870 }
871}
872
873impl cljrs_gc::Trace for Delay {
874 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
875 {
878 let state = self.state.lock().unwrap();
879 match &*state {
880 DelayState::Pending(thunk) => thunk.trace(visitor),
881 DelayState::Forced(v) => v.trace(visitor),
882 }
883 }
884 }
885}
886
887pub struct CljxPromise {
891 pub value: Mutex<Option<Value>>,
892 pub cond: Condvar,
893}
894
895impl CljxPromise {
896 pub fn new() -> Self {
897 Self {
898 value: Mutex::new(None),
899 cond: Condvar::new(),
900 }
901 }
902
903 pub fn deliver(&self, v: Value) {
905 let v = crate::publish::publish_value(v);
908 let mut guard = self.value.lock().unwrap();
909 if guard.is_none() {
910 *guard = Some(v);
911 self.cond.notify_all();
912 }
913 }
914
915 pub fn deref_blocking(&self) -> Value {
917 let mut guard = self.value.lock().unwrap();
918 while guard.is_none() {
919 guard = self.cond.wait(guard).unwrap();
920 }
921 guard.as_ref().unwrap().clone()
922 }
923
924 pub fn is_realized(&self) -> bool {
926 self.value.lock().unwrap().is_some()
927 }
928}
929
930impl Default for CljxPromise {
931 fn default() -> Self {
932 Self::new()
933 }
934}
935
936impl std::fmt::Debug for CljxPromise {
937 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
938 write!(f, "Promise")
939 }
940}
941
942impl cljrs_gc::Trace for CljxPromise {
943 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
944 {
945 let value = self.value.lock().unwrap();
946 if let Some(v) = value.as_ref() {
947 v.trace(visitor);
948 }
949 }
950 }
951}
952
953pub enum FutureState {
957 Running,
958 Done(Value),
959 Failed(Value),
963 Cancelled,
964}
965
966pub struct CljxFuture {
968 pub state: Mutex<FutureState>,
969 pub cond: Condvar,
970 observed: std::sync::atomic::AtomicBool,
974}
975
976impl CljxFuture {
977 pub fn new() -> Self {
978 Self {
979 state: Mutex::new(FutureState::Running),
980 cond: Condvar::new(),
981 observed: std::sync::atomic::AtomicBool::new(false),
982 }
983 }
984
985 pub fn is_done(&self) -> bool {
987 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
988 }
989
990 pub fn is_cancelled(&self) -> bool {
992 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
993 }
994
995 pub fn mark_observed(&self) {
999 self.observed
1000 .store(true, std::sync::atomic::Ordering::Relaxed);
1001 }
1002}
1003
1004impl Drop for CljxFuture {
1005 fn drop(&mut self) {
1006 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
1017 && let Ok(state) = self.state.lock()
1018 && matches!(&*state, FutureState::Failed(_))
1019 {
1020 eprintln!(
1021 "[clojurust warning] a failed future was discarded without its error \
1022 being observed (no await/deref); the thrown exception was lost"
1023 );
1024 }
1025 }
1026}
1027
1028impl Default for CljxFuture {
1029 fn default() -> Self {
1030 Self::new()
1031 }
1032}
1033
1034impl std::fmt::Debug for CljxFuture {
1035 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1036 write!(f, "Future")
1037 }
1038}
1039
1040impl cljrs_gc::Trace for CljxFuture {
1041 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1042 {
1043 let state = self.state.lock().unwrap();
1044 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1047 v.trace(visitor);
1048 }
1049 }
1050 }
1051}
1052
1053pub struct Agent {
1057 pub state: Arc<Mutex<Value>>,
1059 pub error: Arc<Mutex<Option<Value>>>,
1061 pub watches: Mutex<Vec<(Value, Value)>>,
1062}
1063
1064impl Agent {
1065 pub fn get_state(&self) -> Value {
1066 self.state.lock().unwrap().clone()
1067 }
1068
1069 pub fn get_error(&self) -> Option<Value> {
1070 self.error.lock().unwrap().clone()
1071 }
1072
1073 pub fn clear_error(&self) {
1074 *self.error.lock().unwrap() = None;
1075 }
1076}
1077
1078impl std::fmt::Debug for Agent {
1079 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1080 write!(f, "Agent")
1081 }
1082}
1083
1084impl cljrs_gc::Trace for Agent {
1085 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1086 {
1087 let state = self.state.lock().unwrap();
1088 state.trace(visitor);
1089 }
1090 {
1091 let error = self.error.lock().unwrap();
1092 if let Some(e) = error.as_ref() {
1093 e.trace(visitor);
1094 }
1095 }
1096 {
1097 let watches = self.watches.lock().unwrap();
1098 for (key, f) in watches.iter() {
1099 key.trace(visitor);
1100 f.trace(visitor);
1101 }
1102 }
1103 }
1104}