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(_) => 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 *self.value.lock().unwrap() = Some(v);
255 }
256
257 pub fn get_meta(&self) -> Option<Value> {
258 self.meta.lock().unwrap().clone()
259 }
260
261 pub fn set_meta(&self, m: Value) {
262 *self.meta.lock().unwrap() = Some(m);
263 }
264
265 pub fn full_name(&self) -> String {
266 format!("{}/{}", self.namespace, self.name)
267 }
268}
269
270impl cljrs_gc::Trace for Var {
271 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
272 {
273 let value = self.value.lock().unwrap();
274 if let Some(v) = value.as_ref() {
275 v.trace(visitor);
276 }
277 }
278 {
279 let meta = self.meta.lock().unwrap();
280 if let Some(m) = meta.as_ref() {
281 m.trace(visitor);
282 }
283 }
284 {
285 let watches = self.watches.lock().unwrap();
286 for (key, f) in watches.iter() {
287 key.trace(visitor);
288 f.trace(visitor);
289 }
290 }
291 }
292}
293
294#[derive(Debug)]
298pub struct Atom {
299 pub value: Mutex<Value>,
300 pub meta: Mutex<Option<Value>>,
301 pub validator: Mutex<Option<Value>>,
302 pub watches: Mutex<Vec<(Value, Value)>>,
303}
304
305impl Atom {
306 pub fn new(v: Value) -> Self {
307 Self {
308 value: Mutex::new(v),
309 meta: Mutex::new(None),
310 validator: Mutex::new(None),
311 watches: Mutex::new(Vec::new()),
312 }
313 }
314
315 pub fn deref(&self) -> Value {
316 self.value.lock().unwrap().clone()
317 }
318
319 pub fn reset(&self, v: Value) -> Value {
320 #[cfg(all(feature = "no-gc", debug_assertions))]
322 debug_assert!(
323 value_gcptr_is_static(&v),
324 "no-gc: Atom::reset() received a region-local value — the new-value \
325 expression must be computed inside a StaticCtxGuard (i.e. inside \
326 the swap! / reset! call) so it is allocated in the static arena"
327 );
328 let mut guard = self.value.lock().unwrap();
329 *guard = v.clone();
330 v
331 }
332
333 pub fn get_meta(&self) -> Option<Value> {
334 self.meta.lock().unwrap().clone()
335 }
336
337 pub fn set_meta(&self, m: Option<Value>) {
338 *self.meta.lock().unwrap() = m;
339 }
340
341 pub fn get_validator(&self) -> Option<Value> {
342 self.validator.lock().unwrap().clone()
343 }
344
345 pub fn set_validator(&self, vf: Option<Value>) {
346 *self.validator.lock().unwrap() = vf;
347 }
348}
349
350impl cljrs_gc::Trace for Atom {
351 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
352 {
353 let value = self.value.lock().unwrap();
354 value.trace(visitor);
355 }
356 {
357 let meta = self.meta.lock().unwrap();
358 if let Some(m) = meta.as_ref() {
359 m.trace(visitor);
360 }
361 }
362 {
363 let validator = self.validator.lock().unwrap();
364 if let Some(vf) = validator.as_ref() {
365 vf.trace(visitor);
366 }
367 }
368 {
369 let watches = self.watches.lock().unwrap();
370 for (key, f) in watches.iter() {
371 key.trace(visitor);
372 f.trace(visitor);
373 }
374 }
375 }
376}
377
378#[derive(Debug)]
382pub struct Namespace {
383 pub name: Arc<str>,
384 pub interns: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
386 pub refers: Mutex<HashMap<Arc<str>, GcPtr<Var>>>,
388 pub aliases: Mutex<HashMap<Arc<str>, Arc<str>>>,
390 pub source_file: Mutex<Option<Arc<str>>>,
394 pub git_repo_root: Mutex<Option<Arc<str>>>,
396 pub is_versioned: bool,
399}
400
401impl Namespace {
402 pub fn new(name: impl Into<Arc<str>>) -> Self {
403 Self {
404 name: name.into(),
405 interns: Mutex::new(HashMap::new()),
406 refers: Mutex::new(HashMap::new()),
407 aliases: Mutex::new(HashMap::new()),
408 source_file: Mutex::new(None),
409 git_repo_root: Mutex::new(None),
410 is_versioned: false,
411 }
412 }
413
414 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
416 Self {
417 is_versioned: true,
418 ..Self::new(name)
419 }
420 }
421
422 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
424 *self.source_file.lock().unwrap() = Some(Arc::from(file));
425 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
426 }
427}
428
429impl cljrs_gc::Trace for Namespace {
430 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
431 use cljrs_gc::GcVisitor as _;
432 {
433 let interns = self.interns.lock().unwrap();
434 for var in interns.values() {
435 visitor.visit(var);
436 }
437 }
438 {
439 let refers = self.refers.lock().unwrap();
440 for var in refers.values() {
441 visitor.visit(var);
442 }
443 }
444 }
445}
446
447pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
453
454pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value> + Send + Sync>;
457
458#[derive(Clone, Debug)]
459pub enum Arity {
460 Fixed(usize),
461 Variadic { min: usize },
462}
463
464pub struct NativeFn {
465 pub name: Arc<str>,
466 pub arity: Arity,
467 pub func: NativeFnFunc,
468}
469
470impl NativeFn {
471 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
473 Self {
474 name: name.into(),
475 arity,
476 func: Arc::new(func),
477 }
478 }
479
480 pub fn with_closure(
482 name: impl Into<Arc<str>>,
483 arity: Arity,
484 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + Send + Sync + 'static,
485 ) -> Self {
486 Self {
487 name: name.into(),
488 arity,
489 func: Arc::new(func),
490 }
491 }
492}
493
494impl std::fmt::Debug for NativeFn {
495 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496 f.debug_struct("NativeFn")
497 .field("name", &self.name)
498 .field("arity", &self.arity)
499 .field("func", &"<fn>")
500 .finish()
501 }
502}
503
504impl cljrs_gc::Trace for NativeFn {
505 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
506}
507
508#[derive(Debug, Clone)]
512pub struct CljxFnArity {
513 pub params: Vec<Arc<str>>,
516 pub rest_param: Option<Arc<str>>,
518 pub body: Vec<Form>,
520 pub destructure_params: Vec<(usize, Form)>,
524 pub destructure_rest: Option<Form>,
526 pub ir_arity_id: u64,
528}
529
530impl CljxFnArity {
531 pub fn heap_size(&self) -> usize {
533 self.params.capacity() * mem::size_of::<Arc<str>>()
535 + self.body.capacity() * mem::size_of::<Form>()
537 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
538 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
540 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
541 + self.destructure_rest.as_ref()
543 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
544 }
545}
546
547#[derive(Debug, Clone)]
551pub struct CljxFn {
552 pub name: Option<Arc<str>>,
553 pub arities: Vec<CljxFnArity>,
554 pub closed_over_names: Vec<Arc<str>>,
556 pub closed_over_vals: Vec<Value>,
558 pub is_macro: bool,
560 pub is_async: bool,
565 pub defining_ns: Arc<str>,
567}
568
569impl CljxFn {
570 pub fn new(
571 name: Option<Arc<str>>,
572 arities: Vec<CljxFnArity>,
573 closed_over_names: Vec<Arc<str>>,
574 closed_over_vals: Vec<Value>,
575 is_macro: bool,
576 defining_ns: Arc<str>,
577 ) -> Self {
578 Self {
579 name,
580 arities,
581 closed_over_names,
582 closed_over_vals,
583 is_macro,
584 is_async: false,
585 defining_ns,
586 }
587 }
588}
589
590impl cljrs_gc::Trace for CljxFn {
591 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
592 for v in &self.closed_over_vals {
593 v.trace(visitor);
594 }
595 }
596
597 fn gc_size_extra(&self) -> usize {
598 self.arities.capacity() * mem::size_of::<CljxFnArity>()
600 + self
601 .arities
602 .iter()
603 .map(CljxFnArity::heap_size)
604 .sum::<usize>()
605 }
606}
607
608#[derive(Debug)]
615pub struct BoundFn {
616 pub wrapped: Value,
618 pub captured_bindings: HashMap<usize, Value>,
620}
621
622impl cljrs_gc::Trace for BoundFn {
623 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
624 self.wrapped.trace(visitor);
625 for val in self.captured_bindings.values() {
626 val.trace(visitor);
627 }
628 }
629
630 fn gc_size_extra(&self) -> usize {
631 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
633 }
634}
635
636pub trait Thunk: Send + Sync + std::fmt::Debug + cljrs_gc::Trace {
640 fn force(&self) -> Result<Value, String>;
641}
642
643pub enum LazySeqState {
645 Pending(Box<dyn Thunk>),
647 Forced(Value),
649 Error(String),
651}
652
653pub struct LazySeq {
655 pub state: Mutex<LazySeqState>,
656}
657
658impl LazySeq {
659 pub fn new(thunk: Box<dyn Thunk>) -> Self {
660 Self {
661 state: Mutex::new(LazySeqState::Pending(thunk)),
662 }
663 }
664
665 pub fn realize(&self) -> Value {
668 let thunk = {
669 let mut guard = self.state.lock().unwrap();
670 match &*guard {
671 LazySeqState::Forced(v) => return v.clone(),
672 LazySeqState::Error(_) => return Value::Nil,
673 LazySeqState::Pending(_) => {}
674 }
675 let prev = mem::replace(&mut *guard, LazySeqState::Forced(Value::Nil));
677 let LazySeqState::Pending(thunk) = prev else {
678 unreachable!("state was not Pending")
679 };
680 thunk
681 };
683 match thunk.force() {
686 Ok(result) => {
687 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
688 result
689 }
690 Err(msg) => {
691 *self.state.lock().unwrap() = LazySeqState::Error(msg);
692 Value::Nil
693 }
694 }
695 }
696
697 pub fn error(&self) -> Option<String> {
699 let guard = self.state.lock().unwrap();
700 if let LazySeqState::Error(e) = &*guard {
701 Some(e.clone())
702 } else {
703 None
704 }
705 }
706}
707
708impl std::fmt::Debug for LazySeq {
709 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
710 write!(f, "LazySeq(...)")
711 }
712}
713
714impl cljrs_gc::Trace for LazySeq {
715 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
716 {
719 let state = self.state.lock().unwrap();
720 match &*state {
721 LazySeqState::Pending(thunk) => thunk.trace(visitor),
722 LazySeqState::Forced(v) => v.trace(visitor),
723 LazySeqState::Error(_) => {}
724 }
725 }
726 }
727}
728
729#[derive(Debug, Clone)]
736pub struct CljxCons {
737 pub head: Value,
738 pub tail: Value,
739}
740
741impl cljrs_gc::Trace for CljxCons {
742 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
743 self.head.trace(visitor);
744 self.tail.trace(visitor);
745 }
746}
747
748pub struct Volatile {
752 pub value: Mutex<Value>,
753}
754
755impl Volatile {
756 pub fn new(v: Value) -> Self {
757 Self {
758 value: Mutex::new(v),
759 }
760 }
761
762 pub fn deref(&self) -> Value {
763 self.value.lock().unwrap().clone()
764 }
765
766 pub fn reset(&self, v: Value) -> Value {
767 #[cfg(all(feature = "no-gc", debug_assertions))]
769 debug_assert!(
770 value_gcptr_is_static(&v),
771 "no-gc: Volatile::reset() received a region-local value — ensure the \
772 new-value expression is inside a StaticCtxGuard (vreset! handles this)"
773 );
774 *self.value.lock().unwrap() = v.clone();
775 v
776 }
777}
778
779impl std::fmt::Debug for Volatile {
780 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
781 write!(f, "Volatile")
782 }
783}
784
785impl cljrs_gc::Trace for Volatile {
786 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
787 {
788 let value = self.value.lock().unwrap();
789 value.trace(visitor);
790 }
791 }
792}
793
794pub enum DelayState {
798 Pending(Box<dyn Thunk>),
799 Forced(Value),
800}
801
802pub struct Delay {
804 pub state: Mutex<DelayState>,
805}
806
807impl Delay {
808 pub fn new(thunk: Box<dyn Thunk>) -> Self {
809 Self {
810 state: Mutex::new(DelayState::Pending(thunk)),
811 }
812 }
813
814 pub fn force(&self) -> Result<Value, String> {
817 let thunk = {
818 let mut guard = self.state.lock().unwrap();
819 if let DelayState::Forced(v) = &*guard {
820 return Ok(v.clone());
821 }
822 let prev = mem::replace(&mut *guard, DelayState::Forced(Value::Nil));
823 let DelayState::Pending(thunk) = prev else {
824 unreachable!("state was not Pending")
825 };
826 thunk
827 };
829 let result = thunk.force()?;
832 *self.state.lock().unwrap() = DelayState::Forced(result.clone());
833 Ok(result)
834 }
835
836 pub fn is_realized(&self) -> bool {
838 matches!(&*self.state.lock().unwrap(), DelayState::Forced(_))
839 }
840}
841
842impl std::fmt::Debug for Delay {
843 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
844 write!(f, "Delay")
845 }
846}
847
848impl cljrs_gc::Trace for Delay {
849 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
850 {
853 let state = self.state.lock().unwrap();
854 match &*state {
855 DelayState::Pending(thunk) => thunk.trace(visitor),
856 DelayState::Forced(v) => v.trace(visitor),
857 }
858 }
859 }
860}
861
862pub struct CljxPromise {
866 pub value: Mutex<Option<Value>>,
867 pub cond: Condvar,
868}
869
870impl CljxPromise {
871 pub fn new() -> Self {
872 Self {
873 value: Mutex::new(None),
874 cond: Condvar::new(),
875 }
876 }
877
878 pub fn deliver(&self, v: Value) {
880 let mut guard = self.value.lock().unwrap();
881 if guard.is_none() {
882 *guard = Some(v);
883 self.cond.notify_all();
884 }
885 }
886
887 pub fn deref_blocking(&self) -> Value {
889 let mut guard = self.value.lock().unwrap();
890 while guard.is_none() {
891 guard = self.cond.wait(guard).unwrap();
892 }
893 guard.as_ref().unwrap().clone()
894 }
895
896 pub fn is_realized(&self) -> bool {
898 self.value.lock().unwrap().is_some()
899 }
900}
901
902impl Default for CljxPromise {
903 fn default() -> Self {
904 Self::new()
905 }
906}
907
908impl std::fmt::Debug for CljxPromise {
909 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
910 write!(f, "Promise")
911 }
912}
913
914impl cljrs_gc::Trace for CljxPromise {
915 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
916 {
917 let value = self.value.lock().unwrap();
918 if let Some(v) = value.as_ref() {
919 v.trace(visitor);
920 }
921 }
922 }
923}
924
925pub enum FutureState {
929 Running,
930 Done(Value),
931 Failed(Value),
935 Cancelled,
936}
937
938pub struct CljxFuture {
940 pub state: Mutex<FutureState>,
941 pub cond: Condvar,
942 observed: std::sync::atomic::AtomicBool,
946}
947
948impl CljxFuture {
949 pub fn new() -> Self {
950 Self {
951 state: Mutex::new(FutureState::Running),
952 cond: Condvar::new(),
953 observed: std::sync::atomic::AtomicBool::new(false),
954 }
955 }
956
957 pub fn is_done(&self) -> bool {
959 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
960 }
961
962 pub fn is_cancelled(&self) -> bool {
964 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
965 }
966
967 pub fn mark_observed(&self) {
971 self.observed
972 .store(true, std::sync::atomic::Ordering::Relaxed);
973 }
974}
975
976impl Drop for CljxFuture {
977 fn drop(&mut self) {
978 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
989 && let Ok(state) = self.state.lock()
990 && matches!(&*state, FutureState::Failed(_))
991 {
992 eprintln!(
993 "[clojurust warning] a failed future was discarded without its error \
994 being observed (no await/deref); the thrown exception was lost"
995 );
996 }
997 }
998}
999
1000impl Default for CljxFuture {
1001 fn default() -> Self {
1002 Self::new()
1003 }
1004}
1005
1006impl std::fmt::Debug for CljxFuture {
1007 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1008 write!(f, "Future")
1009 }
1010}
1011
1012impl cljrs_gc::Trace for CljxFuture {
1013 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1014 {
1015 let state = self.state.lock().unwrap();
1016 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
1019 v.trace(visitor);
1020 }
1021 }
1022 }
1023}
1024
1025pub type AgentFn = Box<dyn FnOnce(Value) -> Result<Value, Value> + Send>;
1029
1030pub enum AgentMsg {
1032 Update(AgentFn),
1033 Shutdown,
1034}
1035
1036pub struct Agent {
1038 pub state: Arc<Mutex<Value>>,
1040 pub error: Arc<Mutex<Option<Value>>>,
1042 pub sender: Mutex<std::sync::mpsc::SyncSender<AgentMsg>>,
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}