1#![allow(unused)]
4
5use crate::parking::Event;
6use std::collections::HashMap;
7use std::mem;
8use std::rc::Rc;
9use std::sync::{Arc, Condvar, Mutex};
10use std::time::{Duration, Instant};
11
12use cljrs_gc::GcPtr;
13use cljrs_gc::mutator::TrackedMutex;
14use cljrs_reader::Form;
15
16use crate::TypeHint;
17use crate::Value;
18
19pub type MethodMap = HashMap<Arc<str>, Value>;
23
24#[cfg(feature = "no-gc")]
25type ImplementationOwnership = HashMap<Arc<str>, HashMap<Arc<str>, crate::publish::ValueLease>>;
26
27#[derive(Debug)]
29pub struct Protocol {
30 pub name: Arc<str>,
31 pub ns: Arc<str>,
32 pub methods: Vec<ProtocolMethod>,
33 pub impls: TrackedMutex<HashMap<Arc<str>, MethodMap>>,
35 #[cfg(feature = "no-gc")]
36 impl_ownership: std::cell::RefCell<ImplementationOwnership>,
37 pub extend_via_metadata: bool,
41}
42
43impl Protocol {
44 pub fn new(
45 name: Arc<str>,
46 ns: Arc<str>,
47 methods: Vec<ProtocolMethod>,
48 extend_via_metadata: bool,
49 ) -> Self {
50 Self {
51 name,
52 ns,
53 methods,
54 impls: TrackedMutex::new(HashMap::new()),
55 #[cfg(feature = "no-gc")]
56 impl_ownership: Default::default(),
57 extend_via_metadata,
58 }
59 }
60}
61
62impl Protocol {
63 pub fn set_implementation(&self, tag: Arc<str>, method: Arc<str>, value: Value) {
65 #[cfg(feature = "no-gc")]
66 let (value, lease) = crate::publish::prepare_for_owner(
67 value,
68 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
69 );
70 #[cfg(not(feature = "no-gc"))]
71 let value = crate::publish::publish_value(value);
72 self.impls
73 .lock()
74 .unwrap()
75 .entry(tag.clone())
76 .or_default()
77 .insert(method.clone(), value);
78 #[cfg(feature = "no-gc")]
79 {
80 let previous = self
81 .impl_ownership
82 .borrow_mut()
83 .entry(tag)
84 .or_default()
85 .insert(method, lease);
86 drop(previous);
87 }
88 }
89 pub fn protect_implementation(&self, tag: &str, method: &str) {
91 #[cfg(feature = "no-gc")]
92 if let Some(lease) = self
93 .impl_ownership
94 .borrow()
95 .get(tag)
96 .and_then(|methods| methods.get(method))
97 {
98 lease.protect_read();
99 }
100 #[cfg(not(feature = "no-gc"))]
101 let _ = (tag, method);
102 }
103}
104
105static PROTOCOL_GENERATION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
114
115pub fn protocol_generation() -> u64 {
117 PROTOCOL_GENERATION.load(std::sync::atomic::Ordering::Acquire)
118}
119
120pub fn bump_protocol_generation() {
123 PROTOCOL_GENERATION.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
124}
125
126impl Drop for Protocol {
127 fn drop(&mut self) {
128 crate::publish::clear_slots(self as *const _ as usize);
129 }
130}
131
132impl cljrs_gc::Trace for Protocol {
133 #[cfg(feature = "no-gc")]
134 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {
135 }
137 #[cfg(not(feature = "no-gc"))]
138 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
139 {
140 let impls = self.impls.lock().unwrap();
141 for method_map in impls.values() {
142 for v in method_map.values() {
143 v.trace(visitor);
144 }
145 }
146 }
147 }
148}
149
150#[derive(Debug, Clone)]
152pub struct ProtocolMethod {
153 pub name: Arc<str>,
154 pub min_arity: usize,
155 pub variadic: bool,
156}
157
158impl cljrs_gc::Trace for ProtocolMethod {
159 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
160}
161
162#[derive(Debug)]
166pub struct ProtocolFn {
167 pub protocol: GcPtr<Protocol>,
168 pub method_name: Arc<str>,
169 pub min_arity: usize,
170 pub variadic: bool,
171}
172
173impl cljrs_gc::Trace for ProtocolFn {
174 #[cfg(feature = "no-gc")]
175 fn immutable_children(&self) -> bool {
176 true
177 }
178 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
179 use cljrs_gc::GcVisitor as _;
180 visitor.visit(&self.protocol);
181 }
182}
183
184#[derive(Debug)]
188pub struct MultiFn {
189 pub name: Arc<str>,
190 pub dispatch_fn: Value,
191 pub methods: TrackedMutex<HashMap<String, Value>>,
193 pub dispatch_vals: TrackedMutex<HashMap<String, Value>>,
195 pub prefers: TrackedMutex<HashMap<String, Vec<String>>>,
197 pub preference_vals: TrackedMutex<HashMap<String, Value>>,
200 pub default_dispatch: String,
202 method_generation: std::sync::atomic::AtomicU64,
203 method_cache: TrackedMutex<MultiFnMethodCache>,
204 #[cfg(feature = "no-gc")]
205 method_ownership: std::cell::RefCell<HashMap<String, crate::publish::ValueLease>>,
206}
207
208#[derive(Debug, Default)]
209struct MultiFnMethodCache {
210 hierarchy_generation: u64,
211 method_generation: u64,
212 methods: HashMap<String, String>,
213}
214
215impl MultiFn {
216 pub fn new(name: Arc<str>, dispatch_fn: Value, default_dispatch: String) -> Self {
217 Self {
218 name,
219 dispatch_fn,
220 methods: TrackedMutex::new(HashMap::new()),
221 dispatch_vals: TrackedMutex::new(HashMap::new()),
222 prefers: TrackedMutex::new(HashMap::new()),
223 preference_vals: TrackedMutex::new(HashMap::new()),
224 default_dispatch,
225 method_generation: std::sync::atomic::AtomicU64::new(0),
226 method_cache: TrackedMutex::new(MultiFnMethodCache::default()),
227 #[cfg(feature = "no-gc")]
228 method_ownership: Default::default(),
229 }
230 }
231
232 pub fn method(&self, key: &str) -> Option<Value> {
234 let value = self.methods.lock().unwrap().get(key).cloned();
235 #[cfg(feature = "no-gc")]
236 if let Some(lease) = self.method_ownership.borrow().get(key) {
237 lease.protect_read();
238 }
239 value
240 }
241
242 pub fn set_method(&self, key: String, value: Value) {
244 #[cfg(feature = "no-gc")]
245 let (value, lease) = crate::publish::prepare_for_owner(
246 value,
247 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
248 );
249 #[cfg(not(feature = "no-gc"))]
250 let value = crate::publish::publish_value(value);
251 self.methods.lock().unwrap().insert(key.clone(), value);
252 #[cfg(feature = "no-gc")]
253 {
254 let previous = self.method_ownership.borrow_mut().insert(key, lease);
255 drop(previous);
256 }
257 }
258 pub fn remove_method(&self, key: &str) -> bool {
260 let removed = self.methods.lock().unwrap().remove(key).is_some();
261 #[cfg(feature = "no-gc")]
262 {
263 let previous = self.method_ownership.borrow_mut().remove(key);
264 drop(previous);
265 }
266 removed
267 }
268
269 pub fn method_generation(&self) -> u64 {
271 self.method_generation
272 .load(std::sync::atomic::Ordering::Acquire)
273 }
274
275 pub fn bump_method_generation(&self) {
277 self.method_generation
278 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
279 }
280
281 pub fn cached_method(
284 &self,
285 dispatch_key: &str,
286 hierarchy_generation: u64,
287 method_generation: u64,
288 ) -> Option<String> {
289 let mut cache = self.method_cache.lock().unwrap();
290 if cache.hierarchy_generation != hierarchy_generation
291 || cache.method_generation != method_generation
292 {
293 cache.hierarchy_generation = hierarchy_generation;
294 cache.method_generation = method_generation;
295 cache.methods.clear();
296 }
297 cache.methods.get(dispatch_key).cloned()
298 }
299
300 pub fn cache_method(
304 &self,
305 dispatch_key: String,
306 method_key: String,
307 hierarchy_generation: u64,
308 method_generation: u64,
309 ) {
310 let mut cache = self.method_cache.lock().unwrap();
311 if cache.hierarchy_generation != hierarchy_generation
312 || cache.method_generation != method_generation
313 {
314 cache.hierarchy_generation = hierarchy_generation;
315 cache.method_generation = method_generation;
316 cache.methods.clear();
317 }
318 cache.methods.insert(dispatch_key, method_key);
319 }
320
321 pub fn cached_method_count(&self) -> usize {
323 self.method_cache.lock().unwrap().methods.len()
324 }
325}
326
327impl Drop for MultiFn {
328 fn drop(&mut self) {
329 crate::publish::clear_slots(self as *const _ as usize);
330 }
331}
332
333impl cljrs_gc::Trace for MultiFn {
334 #[cfg(feature = "no-gc")]
335 fn init_ownership(&self) {
336 let methods = self.methods.lock().unwrap().clone();
337 for (key, value) in methods {
338 self.set_method(key, value);
339 }
340 for (prefix, table) in [
341 ("dispatch", &self.dispatch_vals),
342 ("prefer", &self.preference_vals),
343 ] {
344 for (key, value) in table.lock().unwrap().iter() {
345 let lease = cljrs_gc::region::RegionLease::for_value(value);
346 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
347 self as *const _ as usize,
348 format!("{prefix}:{key}"),
349 &lease,
350 );
351 }
352 }
353 }
354 #[cfg(feature = "no-gc")]
355 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
356 self.dispatch_fn.trace(visitor);
357 }
358 #[cfg(not(feature = "no-gc"))]
359 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
360 self.dispatch_fn.trace(visitor);
361 {
362 let methods = self.methods.lock().unwrap();
363 for v in methods.values() {
364 v.trace(visitor);
365 }
366 }
367 {
368 let dispatch_vals = self.dispatch_vals.lock().unwrap();
369 for v in dispatch_vals.values() {
370 v.trace(visitor);
371 }
372 }
373 {
374 let preference_vals = self.preference_vals.lock().unwrap();
375 for v in preference_vals.values() {
376 v.trace(visitor);
377 }
378 }
379 }
380}
381
382#[cfg(feature = "no-gc")]
383fn prepare_optional(
384 value: Option<Value>,
385 address: usize,
386) -> (Option<Value>, crate::publish::ValueLease) {
387 match value {
388 Some(value) => {
389 let (value, lease) = crate::publish::prepare_for_owner(
390 value,
391 &cljrs_gc::region::AllocationOwner::for_address(address),
392 );
393 (Some(value), lease)
394 }
395 None => (None, Default::default()),
396 }
397}
398fn edit_watches(
399 watches: &TrackedMutex<Vec<(Value, Value)>>,
400 added: Option<(Value, Value)>,
401 removed: Option<&Value>,
402) {
403 let mut watches = watches.lock().unwrap();
404 if let Some((key, callback)) = added {
405 if let Some(entry) = watches.iter_mut().find(|(k, _)| k == &key) {
406 entry.1 = callback;
407 } else {
408 watches.push((key, callback));
409 }
410 }
411 if let Some(key) = removed {
412 watches.retain(|(k, _)| k != key);
413 }
414}
415
416#[cfg(feature = "no-gc")]
417fn refresh_watch_lease(
418 watches: &TrackedMutex<Vec<(Value, Value)>>,
419 cached: &std::cell::RefCell<crate::publish::ValueLease>,
420 address: usize,
421) {
422 let values: Vec<Value> = watches
423 .lock()
424 .unwrap()
425 .iter()
426 .flat_map(|(key, callback)| [key.clone(), callback.clone()])
427 .collect();
428 let lease = crate::publish::ValueLease::for_values(
429 &values,
430 &cljrs_gc::region::AllocationOwner::for_address(address),
431 );
432 *cached.borrow_mut() = lease;
433}
434
435#[derive(Debug)]
462pub struct Var {
463 #[cfg(feature = "no-gc")]
464 read_epoch: std::cell::Cell<u64>,
465 pub namespace: Arc<str>,
466 pub name: Arc<str>,
467 pub value: TrackedMutex<Option<Value>>,
468 #[cfg(feature = "no-gc")]
469 value_lease: std::cell::RefCell<crate::publish::ValueLease>,
470 pub shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
473 pub is_macro: bool,
474 pub meta: TrackedMutex<Option<Value>>,
476 #[cfg(feature = "no-gc")]
477 meta_lease: std::cell::RefCell<crate::publish::ValueLease>,
478 #[cfg(feature = "no-gc")]
479 watch_lease: std::cell::RefCell<crate::publish::ValueLease>,
480 pub watches: TrackedMutex<Vec<(Value, Value)>>,
481 binding_generation: std::sync::atomic::AtomicU64,
482}
483
484impl Var {
485 pub fn new(namespace: impl Into<Arc<str>>, name: impl Into<Arc<str>>) -> Self {
486 Self {
487 namespace: namespace.into(),
488 name: name.into(),
489 value: TrackedMutex::new(None),
490 #[cfg(feature = "no-gc")]
491 read_epoch: Default::default(),
492 #[cfg(feature = "no-gc")]
493 value_lease: Default::default(),
494 shared_root: Arc::new(arc_swap::ArcSwap::new(Arc::new(None))),
495 is_macro: false,
496 meta: TrackedMutex::new(None),
497 #[cfg(feature = "no-gc")]
498 meta_lease: Default::default(),
499 #[cfg(feature = "no-gc")]
500 watch_lease: Default::default(),
501 watches: TrackedMutex::new(Vec::new()),
502 binding_generation: std::sync::atomic::AtomicU64::new(0),
503 }
504 }
505
506 pub fn from_shared_root(
513 namespace: impl Into<Arc<str>>,
514 name: impl Into<Arc<str>>,
515 is_macro: bool,
516 shared_root: Arc<arc_swap::ArcSwap<Option<crate::shared::SharedValue>>>,
517 ) -> Self {
518 let local = shared_root
519 .load()
520 .as_ref()
521 .as_ref()
522 .map(crate::shared::demote);
523 #[cfg(feature = "no-gc")]
524 let (local, lease) = match local {
525 Some(value) => {
526 let (value, lease) = crate::publish::prepare_definition(
527 value,
528 &cljrs_gc::region::AllocationOwner::current(),
529 );
530 (Some(value), lease)
531 }
532 None => (None, Default::default()),
533 };
534 Self {
535 namespace: namespace.into(),
536 name: name.into(),
537 value: TrackedMutex::new(local),
538 #[cfg(feature = "no-gc")]
539 read_epoch: Default::default(),
540 #[cfg(feature = "no-gc")]
541 value_lease: std::cell::RefCell::new(lease),
542 shared_root,
543 is_macro,
544 meta: TrackedMutex::new(None),
545 #[cfg(feature = "no-gc")]
546 meta_lease: Default::default(),
547 #[cfg(feature = "no-gc")]
548 watch_lease: Default::default(),
549 watches: TrackedMutex::new(Vec::new()),
550 binding_generation: std::sync::atomic::AtomicU64::new(0),
551 }
552 }
553
554 pub fn is_bound(&self) -> bool {
555 self.value.lock().unwrap().is_some()
556 }
557
558 pub fn snapshot(&self) -> Option<crate::publish::ValueSnapshot> {
559 self.value
560 .lock()
561 .unwrap()
562 .clone()
563 .map(|value| crate::publish::ValueSnapshot {
564 value,
565 #[cfg(feature = "no-gc")]
566 lease: self.value_lease.borrow().snapshot(),
567 })
568 }
569 pub fn deref(&self) -> Option<Value> {
570 let value = self.value.lock().unwrap().clone();
571 #[cfg(feature = "no-gc")]
572 {
573 let epoch = cljrs_gc::region::ReadScope::epoch();
574 if epoch == 0 || self.read_epoch.replace(epoch) != epoch {
575 self.value_lease.borrow().protect_read();
576 }
577 }
578 value
579 }
580
581 pub fn binding_generation(&self) -> u64 {
584 self.binding_generation
585 .load(std::sync::atomic::Ordering::Acquire)
586 }
587
588 pub fn deref_shared(&self) -> Option<Value> {
593 self.shared_root
594 .load()
595 .as_ref()
596 .as_ref()
597 .map(crate::shared::demote)
598 }
599
600 pub fn bind(&self, v: Value) {
601 #[cfg(feature = "no-gc")]
602 self.read_epoch.set(0);
603 #[cfg(not(feature = "no-gc"))]
604 let v = crate::publish::publish_value(v);
605 #[cfg(feature = "no-gc")]
606 let previous_lease = self.value_lease.borrow().snapshot();
607 #[cfg(feature = "no-gc")]
608 let (v, lease) = crate::publish::prepare_definition(
609 v,
610 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
611 );
612 let prev = {
618 let mut slot = self.value.lock().unwrap();
619 slot.replace(v.clone())
620 };
621 #[cfg(feature = "no-gc")]
622 {
623 *self.value_lease.borrow_mut() = lease;
624 }
625 let shared = crate::shared::promote(&v).ok();
631 self.shared_root.store(Arc::new(shared));
632 self.binding_generation
633 .fetch_add(1, std::sync::atomic::Ordering::AcqRel);
634 if let Some(prev) = prev {
635 crate::jit_hooks::notify_var_rebind(&prev, &v);
636 }
637 #[cfg(feature = "no-gc")]
638 {
639 drop(previous_lease);
640 }
641 }
642
643 pub fn add_watch(&self, key: Value, callback: Value) {
644 edit_watches(&self.watches, Some((key, callback)), None);
645 #[cfg(feature = "no-gc")]
646 self.refresh_watch_lease();
647 }
648 pub fn remove_watch(&self, key: &Value) {
649 edit_watches(&self.watches, None, Some(key));
650 #[cfg(feature = "no-gc")]
651 self.refresh_watch_lease();
652 }
653 #[cfg(feature = "no-gc")]
654 fn refresh_watch_lease(&self) {
655 refresh_watch_lease(&self.watches, &self.watch_lease, self as *const _ as usize);
656 }
657 pub fn protect_watches(&self) {
658 #[cfg(feature = "no-gc")]
659 self.watch_lease.borrow().protect_read();
660 }
661
662 pub fn get_meta(&self) -> Option<Value> {
663 #[cfg(feature = "no-gc")]
664 self.meta_lease.borrow().protect_read();
665 self.meta.lock().unwrap().clone()
666 }
667
668 pub fn set_meta(&self, m: Value) {
669 #[cfg(not(feature = "no-gc"))]
670 let m = crate::publish::publish_for_owner(m, self as *const _ as usize);
671 #[cfg(feature = "no-gc")]
672 let (m, lease) = crate::publish::prepare_definition(
673 m,
674 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
675 );
676 *self.meta.lock().unwrap() = Some(m);
677 #[cfg(feature = "no-gc")]
678 {
679 *self.meta_lease.borrow_mut() = lease;
680 }
681 }
682
683 pub fn full_name(&self) -> String {
684 format!("{}/{}", self.namespace, self.name)
685 }
686}
687
688impl cljrs_gc::Trace for Var {
689 #[cfg(feature = "no-gc")]
690 fn init_ownership(&self) {
691 let owner = cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize);
692 let lease = self.value_lease.borrow().reparent(&owner);
693 let old = self.value_lease.replace(lease);
694 drop(old);
695 }
696
697 #[cfg(feature = "no-gc")]
698 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
699 #[cfg(not(feature = "no-gc"))]
700 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
701 #[cfg(not(feature = "no-gc"))]
702 {
703 let value = self.value.lock().unwrap();
704 if let Some(v) = value.as_ref() {
705 v.trace(visitor);
706 }
707 }
708 {
709 let meta = self.meta.lock().unwrap();
710 if let Some(m) = meta.as_ref() {
711 m.trace(visitor);
712 }
713 }
714 {
715 let watches = self.watches.lock().unwrap();
716 for (key, f) in watches.iter() {
717 key.trace(visitor);
718 f.trace(visitor);
719 }
720 }
721 }
722}
723
724#[derive(Debug)]
728pub struct Atom {
729 pub value: TrackedMutex<Value>,
730 #[cfg(feature = "no-gc")]
731 value_lease: std::cell::RefCell<crate::publish::ValueLease>,
732 pub meta: TrackedMutex<Option<Value>>,
733 #[cfg(feature = "no-gc")]
734 meta_lease: std::cell::RefCell<crate::publish::ValueLease>,
735 #[cfg(feature = "no-gc")]
736 watch_lease: std::cell::RefCell<crate::publish::ValueLease>,
737 pub validator: TrackedMutex<Option<Value>>,
738 #[cfg(feature = "no-gc")]
739 validator_lease: std::cell::RefCell<crate::publish::ValueLease>,
740 pub watches: TrackedMutex<Vec<(Value, Value)>>,
741}
742
743impl Atom {
744 pub fn new(v: Value) -> Self {
745 #[cfg(feature = "no-gc")]
748 let (v, lease) =
749 crate::publish::prepare_definition(v, &cljrs_gc::region::AllocationOwner::current());
750 #[cfg(not(feature = "no-gc"))]
751 let v = crate::publish::publish_value(v);
752 Self {
753 value: TrackedMutex::new(v),
754 #[cfg(feature = "no-gc")]
755 value_lease: std::cell::RefCell::new(lease),
756 meta: TrackedMutex::new(None),
757 #[cfg(feature = "no-gc")]
758 meta_lease: Default::default(),
759 #[cfg(feature = "no-gc")]
760 watch_lease: Default::default(),
761 validator: TrackedMutex::new(None),
762 #[cfg(feature = "no-gc")]
763 validator_lease: Default::default(),
764 watches: TrackedMutex::new(Vec::new()),
765 }
766 }
767
768 pub fn snapshot(&self) -> crate::publish::ValueSnapshot {
769 crate::publish::ValueSnapshot {
770 value: self.value.lock().unwrap().clone(),
771 #[cfg(feature = "no-gc")]
772 lease: self.value_lease.borrow().snapshot(),
773 }
774 }
775
776 pub fn deref(&self) -> Value {
777 let snapshot = self.snapshot();
778 snapshot.protect_current();
779 snapshot.value
780 }
781
782 pub fn reset(&self, v: Value) -> Value {
783 #[cfg(not(feature = "no-gc"))]
784 let v = crate::publish::publish_value(v);
785 #[cfg(feature = "no-gc")]
786 let (v, lease) = crate::publish::prepare_for_owner(
787 v,
788 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
789 );
790 #[cfg(feature = "no-gc")]
791 lease.publish_return();
792 *self.value.lock().unwrap() = v.clone();
793 #[cfg(feature = "no-gc")]
794 {
795 *self.value_lease.borrow_mut() = lease;
796 }
797 v
798 }
799
800 pub fn compare_and_set(&self, expected: &Value, value: Value) -> bool {
803 let matches_expected = |current: &Value| {
806 matches!((current, expected), (Value::Vector(a), Value::Vector(b)) if GcPtr::ptr_eq(a, b))
807 || current == expected
808 };
809 if !matches_expected(&self.value.lock().unwrap()) {
810 return false;
811 }
812 #[cfg(not(feature = "no-gc"))]
813 let value = crate::publish::publish_value(value);
814 #[cfg(feature = "no-gc")]
815 let (value, lease) = crate::publish::prepare_for_owner(
816 value,
817 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
818 );
819 let mut guard = self.value.lock().unwrap();
820 if !matches_expected(&guard) {
821 return false;
822 }
823 *guard = value;
824 drop(guard);
825 #[cfg(feature = "no-gc")]
826 {
827 *self.value_lease.borrow_mut() = lease;
828 }
829 true
830 }
831
832 pub fn add_watch(&self, key: Value, callback: Value) {
833 edit_watches(&self.watches, Some((key, callback)), None);
834 #[cfg(feature = "no-gc")]
835 self.refresh_watch_lease();
836 }
837 pub fn remove_watch(&self, key: &Value) {
838 edit_watches(&self.watches, None, Some(key));
839 #[cfg(feature = "no-gc")]
840 self.refresh_watch_lease();
841 }
842 #[cfg(feature = "no-gc")]
843 fn refresh_watch_lease(&self) {
844 refresh_watch_lease(&self.watches, &self.watch_lease, self as *const _ as usize);
845 }
846 pub fn protect_watches(&self) {
847 #[cfg(feature = "no-gc")]
848 self.watch_lease.borrow().protect_read();
849 }
850
851 pub fn get_meta(&self) -> Option<Value> {
852 #[cfg(feature = "no-gc")]
853 self.meta_lease.borrow().protect_read();
854 self.meta.lock().unwrap().clone()
855 }
856
857 pub fn set_meta(&self, m: Option<Value>) {
858 #[cfg(feature = "no-gc")]
859 let (m, lease) = prepare_optional(m, self as *const _ as usize);
860 *self.meta.lock().unwrap() = m;
861 #[cfg(feature = "no-gc")]
862 {
863 *self.meta_lease.borrow_mut() = lease;
864 }
865 }
866
867 pub fn get_validator(&self) -> Option<Value> {
868 #[cfg(feature = "no-gc")]
869 self.validator_lease.borrow().protect_read();
870 self.validator.lock().unwrap().clone()
871 }
872
873 pub fn set_validator(&self, vf: Option<Value>) {
874 #[cfg(feature = "no-gc")]
875 let (vf, lease) = prepare_optional(vf, self as *const _ as usize);
876 *self.validator.lock().unwrap() = vf;
877 #[cfg(feature = "no-gc")]
878 {
879 *self.validator_lease.borrow_mut() = lease;
880 }
881 }
882}
883
884impl cljrs_gc::Trace for Atom {
885 #[cfg(feature = "no-gc")]
886 fn init_ownership(&self) {
887 let owner = cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize);
888 let lease = self.value_lease.borrow().reparent(&owner);
889 let old = self.value_lease.replace(lease);
890 drop(old);
891 }
892 #[cfg(feature = "no-gc")]
893 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
894 #[cfg(not(feature = "no-gc"))]
895 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
896 #[cfg(not(feature = "no-gc"))]
897 {
898 let value = self.value.lock().unwrap();
899 value.trace(visitor);
900 }
901 {
902 let meta = self.meta.lock().unwrap();
903 if let Some(m) = meta.as_ref() {
904 m.trace(visitor);
905 }
906 }
907 {
908 let validator = self.validator.lock().unwrap();
909 if let Some(vf) = validator.as_ref() {
910 vf.trace(visitor);
911 }
912 }
913 {
914 let watches = self.watches.lock().unwrap();
915 for (key, f) in watches.iter() {
916 key.trace(visitor);
917 f.trace(visitor);
918 }
919 }
920 }
921}
922
923#[derive(Debug, Clone, Default)]
929pub struct ReferClojureFilter {
930 pub only: Option<std::collections::HashSet<Arc<str>>>,
932 pub exclude: std::collections::HashSet<Arc<str>>,
934 pub rename: HashMap<Arc<str>, Arc<str>>,
938}
939
940impl ReferClojureFilter {
941 pub fn local_name(&self, name: &Arc<str>) -> Option<Arc<str>> {
944 if self.exclude.contains(name) {
945 return None;
946 }
947 if let Some(only) = &self.only
948 && !only.contains(name)
949 {
950 return None;
951 }
952 Some(
953 self.rename
954 .get(name)
955 .cloned()
956 .unwrap_or_else(|| name.clone()),
957 )
958 }
959}
960
961#[derive(Debug)]
963pub struct Namespace {
964 pub name: Arc<str>,
965 pub interns: TrackedMutex<HashMap<Arc<str>, GcPtr<Var>>>,
967 pub refers: TrackedMutex<HashMap<Arc<str>, GcPtr<Var>>>,
969 pub aliases: TrackedMutex<HashMap<Arc<str>, Arc<str>>>,
971 pub source_file: TrackedMutex<Option<Arc<str>>>,
975 pub git_repo_root: TrackedMutex<Option<Arc<str>>>,
977 pub is_versioned: bool,
980 pub meta: TrackedMutex<Option<Value>>,
982 pub refer_clojure_filter: TrackedMutex<Option<ReferClojureFilter>>,
985}
986
987impl Namespace {
988 pub fn new(name: impl Into<Arc<str>>) -> Self {
989 Self {
990 name: name.into(),
991 interns: TrackedMutex::new(HashMap::new()),
992 refers: TrackedMutex::new(HashMap::new()),
993 aliases: TrackedMutex::new(HashMap::new()),
994 source_file: TrackedMutex::new(None),
995 git_repo_root: TrackedMutex::new(None),
996 is_versioned: false,
997 meta: TrackedMutex::new(None),
998 refer_clojure_filter: TrackedMutex::new(None),
999 }
1000 }
1001
1002 pub fn new_versioned(name: impl Into<Arc<str>>) -> Self {
1004 let mut namespace = Self::new(name);
1005 namespace.is_versioned = true;
1006 namespace
1007 }
1008
1009 pub fn set_source_location(&self, file: &str, repo_root: Option<&str>) {
1011 *self.source_file.lock().unwrap() = Some(Arc::from(file));
1012 *self.git_repo_root.lock().unwrap() = repo_root.map(Arc::from);
1013 }
1014
1015 pub fn get_meta(&self) -> Option<Value> {
1016 crate::publish::read_slot(self as *const _ as usize, 0);
1017 self.meta.lock().unwrap().clone()
1018 }
1019
1020 pub fn set_meta(&self, m: Value) {
1021 let m = crate::publish::publish_for_owner(m, self as *const _ as usize);
1022 *self.meta.lock().unwrap() = Some(m);
1023 }
1024}
1025
1026impl Drop for Namespace {
1027 fn drop(&mut self) {
1028 crate::publish::clear_slots(self as *const _ as usize);
1029 }
1030}
1031
1032impl cljrs_gc::Trace for Namespace {
1033 #[cfg(feature = "no-gc")]
1034 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {
1035 }
1037 #[cfg(not(feature = "no-gc"))]
1038 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1039 use cljrs_gc::GcVisitor as _;
1040 {
1041 let interns = self.interns.lock().unwrap();
1042 for var in interns.values() {
1043 visitor.visit(var);
1044 }
1045 }
1046 {
1047 let refers = self.refers.lock().unwrap();
1048 for var in refers.values() {
1049 visitor.visit(var);
1050 }
1051 }
1052 {
1053 let meta = self.meta.lock().unwrap();
1054 if let Some(m) = meta.as_ref() {
1055 m.trace(visitor);
1056 }
1057 }
1058 }
1059}
1060
1061pub type NativeFnPtr = fn(&[Value]) -> crate::error::ValueResult<Value>;
1067
1068pub type NativeFnFunc = Arc<dyn Fn(&[Value]) -> crate::error::ValueResult<Value>>;
1071
1072#[derive(Clone, Debug)]
1073pub enum Arity {
1074 Fixed(usize),
1075 Variadic { min: usize },
1076}
1077
1078pub struct NativeFn {
1079 pub name: Arc<str>,
1080 pub diagnostic_id: cljrs_gc::fiber::NativeDiagnosticId,
1082 pub arity: Arity,
1083 pub func: NativeFnFunc,
1084 pub captures: Arc<[Value]>,
1086 pub blocking_func: Option<NativeFnFunc>,
1088}
1089
1090impl NativeFn {
1091 pub fn new(name: impl Into<Arc<str>>, arity: Arity, func: NativeFnPtr) -> Self {
1093 Self {
1094 name: name.into(),
1095 arity,
1096 blocking_func: None,
1097 captures: Default::default(),
1098 diagnostic_id: Default::default(),
1099 func: Arc::new(move |args| {
1100 crate::parking::catch_cancelled(|| {
1101 crate::realization::catch(|| func(args))
1102 .unwrap_or_else(|message| Err(crate::ValueError::Other(message)))
1103 })?
1104 }),
1105 }
1106 }
1107
1108 pub fn with_closure(
1111 name: impl Into<Arc<str>>,
1112 arity: Arity,
1113 func: impl Fn(&[Value]) -> crate::error::ValueResult<Value> + 'static,
1114 ) -> Self {
1115 Self {
1116 name: name.into(),
1117 arity,
1118 blocking_func: None,
1119 captures: Default::default(),
1120 diagnostic_id: Default::default(),
1121 func: Arc::new(move |args| {
1122 crate::parking::catch_cancelled(|| {
1123 crate::realization::catch(|| func(args))
1124 .unwrap_or_else(|message| Err(crate::ValueError::Other(message)))
1125 })?
1126 }),
1127 }
1128 }
1129
1130 pub fn with_captures(
1134 name: impl Into<Arc<str>>,
1135 arity: Arity,
1136 captures: Vec<Value>,
1137 func: impl Fn(&[Value], &[Value]) -> crate::ValueResult<Value> + 'static,
1138 ) -> Self {
1139 let captures: Arc<[Value]> = captures.into();
1140 let shared = captures.clone();
1141 let mut native = Self::with_closure(name, arity, move |args| func(&shared, args));
1142 native.captures = captures;
1143 native
1144 }
1145
1146 pub fn with_blocking(
1149 name: impl Into<Arc<str>>,
1150 arity: Arity,
1151 func: impl Fn(&[Value]) -> crate::ValueResult<Value> + 'static,
1152 blocking_func: impl Fn(&[Value]) -> crate::ValueResult<Value> + 'static,
1153 ) -> Self {
1154 let mut native = Self::with_closure(name, arity, func);
1155 native.blocking_func = Some(Arc::new(move |args| {
1156 crate::parking::catch_cancelled(|| {
1157 crate::realization::catch(|| blocking_func(args))
1158 .unwrap_or_else(|message| Err(crate::ValueError::Other(message)))
1159 })?
1160 }));
1161 native
1162 }
1163
1164 pub fn call_blocking(&self, args: &[Value]) -> crate::ValueResult<Value> {
1166 let expected = match self.arity {
1167 Arity::Fixed(n) if args.len() != n => Some(n.to_string()),
1168 Arity::Variadic { min } if args.len() < min => Some(format!("{min}+")),
1169 _ => None,
1170 };
1171 if let Some(expected) = expected {
1172 return Err(crate::ValueError::ArityError {
1173 name: self.name.to_string(),
1174 expected,
1175 got: args.len(),
1176 });
1177 }
1178 let adapter = self.blocking_func.as_ref().ok_or_else(|| {
1179 crate::ValueError::Other(format!(
1180 "blocking cannot offload {}: this function has no owned-work adapter",
1181 self.name
1182 ))
1183 })?;
1184 adapter(args)
1185 }
1186}
1187
1188impl std::fmt::Debug for NativeFn {
1189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1190 f.debug_struct("NativeFn")
1191 .field("name", &self.name)
1192 .field("arity", &self.arity)
1193 .field("func", &"<fn>")
1194 .finish()
1195 }
1196}
1197
1198impl cljrs_gc::Trace for NativeFn {
1199 #[cfg(feature = "no-gc")]
1200 fn immutable_children(&self) -> bool {
1201 true
1202 }
1203 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1204 for value in self.captures.iter() {
1205 value.trace(visitor);
1206 }
1207 }
1208}
1209
1210#[derive(Debug, Clone)]
1214pub struct CljxFnArity {
1215 pub params: Vec<Arc<str>>,
1218 pub rest_param: Option<Arc<str>>,
1220 pub body: Arc<[Form]>,
1224 pub destructure_params: Vec<(usize, Form)>,
1228 pub destructure_rest: Option<Form>,
1230 pub ir_arity_id: u64,
1232 pub param_hints: Vec<Option<TypeHint>>,
1236 pub rest_hint: Option<TypeHint>,
1239}
1240
1241impl CljxFnArity {
1242 pub fn heap_size(&self) -> usize {
1244 self.params.capacity() * mem::size_of::<Arc<str>>()
1246 + self.body.len() * mem::size_of::<Form>()
1248 + self.body.iter().map(|f| f.heap_size()).sum::<usize>()
1249 + self.destructure_params.capacity() * mem::size_of::<(usize, Form)>()
1251 + self.destructure_params.iter().map(|(_, f)| f.heap_size()).sum::<usize>()
1252 + self.destructure_rest.as_ref()
1254 .map_or(0, |f| mem::size_of::<Form>() + f.heap_size())
1255 + self.param_hints.capacity() * mem::size_of::<Option<TypeHint>>()
1257 }
1258}
1259
1260#[derive(Debug, Clone)]
1264pub struct CljxFn {
1265 pub name: Option<Arc<str>>,
1266 pub arities: Vec<CljxFnArity>,
1267 pub closed_over_names: Vec<Arc<str>>,
1269 pub closed_over_vals: Vec<Value>,
1271 pub is_macro: bool,
1273 pub is_async: bool,
1278 pub defining_ns: Arc<str>,
1280 pub self_ptr: Option<GcPtr<CljxFn>>,
1285}
1286
1287impl CljxFn {
1288 pub fn new(
1289 name: Option<Arc<str>>,
1290 arities: Vec<CljxFnArity>,
1291 closed_over_names: Vec<Arc<str>>,
1292 closed_over_vals: Vec<Value>,
1293 is_macro: bool,
1294 defining_ns: Arc<str>,
1295 ) -> Self {
1296 Self {
1297 name,
1298 arities,
1299 closed_over_names,
1300 closed_over_vals,
1301 is_macro,
1302 is_async: false,
1303 defining_ns,
1304 self_ptr: None,
1305 }
1306 }
1307}
1308
1309impl cljrs_gc::Trace for CljxFn {
1310 #[cfg(feature = "no-gc")]
1311 fn immutable_children(&self) -> bool {
1312 true
1313 }
1314 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1315 use cljrs_gc::GcVisitor as _;
1316 for v in &self.closed_over_vals {
1317 v.trace(visitor);
1318 }
1319 if let Some(ref p) = self.self_ptr {
1320 visitor.visit(p);
1321 }
1322 }
1323
1324 fn gc_size_extra(&self) -> usize {
1325 self.arities.capacity() * mem::size_of::<CljxFnArity>()
1327 + self
1328 .arities
1329 .iter()
1330 .map(CljxFnArity::heap_size)
1331 .sum::<usize>()
1332 }
1333}
1334
1335#[derive(Debug)]
1342pub struct BoundFn {
1343 pub wrapped: Value,
1345 pub captured_bindings: HashMap<usize, Value>,
1347}
1348
1349impl cljrs_gc::Trace for BoundFn {
1350 #[cfg(feature = "no-gc")]
1351 fn immutable_children(&self) -> bool {
1352 true
1353 }
1354 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1355 self.wrapped.trace(visitor);
1356 for val in self.captured_bindings.values() {
1357 val.trace(visitor);
1358 }
1359 }
1360
1361 fn gc_size_extra(&self) -> usize {
1362 self.captured_bindings.capacity() * (1 + mem::size_of::<usize>() + mem::size_of::<Value>())
1364 }
1365}
1366
1367pub trait Thunk: std::fmt::Debug + cljrs_gc::Trace {
1371 fn force(&self) -> crate::ValueResult<Value>;
1375}
1376
1377fn thunk_error_message(error: crate::ValueError) -> String {
1379 match error {
1380 crate::ValueError::Other(message) => message,
1381 other => other.to_string(),
1382 }
1383}
1384
1385#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1387pub struct Realizer {
1388 thread: std::thread::ThreadId,
1389 fiber: Option<u64>,
1390}
1391impl Realizer {
1392 pub fn current() -> Self {
1393 Self {
1394 thread: std::thread::current().id(),
1395 fiber: crate::parking::current(),
1396 }
1397 }
1398
1399 pub fn ensure_can_wait(self) -> Result<(), String> {
1402 if self.thread == std::thread::current().id()
1403 && self.fiber.is_some()
1404 && crate::parking::current().is_none()
1405 {
1406 Err("cannot block the isolate thread waiting for a parked fiber. Run this code inside a fiber.".into())
1407 } else {
1408 Ok(())
1409 }
1410 }
1411}
1412
1413struct RealizationGuard<'a> {
1415 lazy: Option<&'a LazySeq>,
1416 delay: Option<&'a Delay>,
1417}
1418impl Drop for RealizationGuard<'_> {
1419 fn drop(&mut self) {
1420 if let Some(cell) = self.lazy {
1421 let mut state = cell.state.lock().unwrap();
1422 if let LazySeqState::Realizing { thunk, .. } = &*state {
1423 *state = LazySeqState::Pending(thunk.clone());
1424 }
1425 drop(state);
1426 cell.ready.notify_all();
1427 }
1428 if let Some(cell) = self.delay {
1429 let mut state = cell.state.lock().unwrap();
1430 if let DelayState::Realizing { thunk, .. } = &*state {
1431 *state = DelayState::Pending(thunk.clone());
1432 }
1433 drop(state);
1434 cell.ready.notify_all();
1435 }
1436 }
1437}
1438
1439pub enum LazySeqState {
1441 Pending(Rc<dyn Thunk>),
1443 Realizing {
1444 owner: Realizer,
1445 thunk: Rc<dyn Thunk>,
1446 },
1447 Forced(Value),
1449 Error(String),
1451}
1452
1453pub struct LazySeq {
1455 ready: Event,
1456 pub state: TrackedMutex<LazySeqState>,
1457}
1458
1459impl LazySeq {
1460 pub fn new(thunk: Box<dyn Thunk>) -> Self {
1461 Self {
1462 state: TrackedMutex::new(LazySeqState::Pending(Rc::from(thunk))),
1463 ready: Event::default(),
1464 }
1465 }
1466
1467 pub fn realize(&self) -> Value {
1472 match self.try_realize() {
1473 Ok(value) => value,
1474 Err(crate::ValueError::Cancelled) => crate::parking::resume_cancelled(),
1476 Err(error) => {
1477 assert!(self.error().is_some(), "{error}");
1480 Value::Nil
1481 }
1482 }
1483 }
1484
1485 pub fn try_realize(&self) -> crate::ValueResult<Value> {
1490 let owner = Realizer::current();
1491 let thunk = loop {
1492 let listener = self.ready.listen();
1493 let mut state = self.state.lock().unwrap();
1494 match &*state {
1495 LazySeqState::Forced(v) => {
1496 let value = v.clone();
1497 drop(state);
1498 crate::publish::read_slot(self as *const _ as usize, 0);
1499 return Ok(value);
1500 }
1501 LazySeqState::Error(error) => return Err(crate::ValueError::Other(error.clone())),
1502 LazySeqState::Realizing { owner: active, .. } => {
1503 if *active == owner {
1504 return Ok(Value::Nil);
1505 }
1506 active.ensure_can_wait().map_err(crate::ValueError::Other)?;
1507 drop(state);
1508 listener.wait(None);
1509 continue;
1510 }
1511 LazySeqState::Pending(_) => {}
1512 }
1513 let LazySeqState::Pending(thunk) =
1514 mem::replace(&mut *state, LazySeqState::Forced(Value::Nil))
1515 else {
1516 unreachable!()
1517 };
1518 *state = LazySeqState::Realizing {
1519 owner,
1520 thunk: thunk.clone(),
1521 };
1522 break thunk;
1523 };
1524 let mut guard = RealizationGuard {
1525 lazy: Some(self),
1526 delay: None,
1527 };
1528 let result = match thunk.force() {
1529 Ok(result) => {
1530 let result = crate::publish::publish_for_owner(result, self as *const _ as usize);
1531 *self.state.lock().unwrap() = LazySeqState::Forced(result.clone());
1532 Ok(result)
1533 }
1534 Err(crate::ValueError::Cancelled) => {
1537 drop(thunk);
1538 drop(guard);
1539 return Err(crate::ValueError::Cancelled);
1540 }
1541 Err(error) => {
1542 let message = thunk_error_message(error);
1543 *self.state.lock().unwrap() = LazySeqState::Error(message.clone());
1544 Err(crate::ValueError::Other(message))
1545 }
1546 };
1547 drop(thunk);
1548 #[cfg(feature = "no-gc")]
1549 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
1550 self as *const _ as usize,
1551 usize::MAX,
1552 &Default::default(),
1553 );
1554 guard.lazy = None;
1555 if result.is_ok() {
1556 crate::publish::read_slot(self as *const _ as usize, 0);
1557 }
1558 self.ready.notify_all();
1559 result
1560 }
1561
1562 pub fn error(&self) -> Option<String> {
1564 let guard = self.state.lock().unwrap();
1565 if let LazySeqState::Error(e) = &*guard {
1566 Some(e.clone())
1567 } else {
1568 None
1569 }
1570 }
1571}
1572
1573impl std::fmt::Debug for LazySeq {
1574 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1575 write!(f, "LazySeq(...)")
1576 }
1577}
1578
1579impl Drop for LazySeq {
1580 fn drop(&mut self) {
1581 crate::publish::clear_slots(self as *const _ as usize);
1582 }
1583}
1584impl cljrs_gc::Trace for LazySeq {
1585 #[cfg(feature = "no-gc")]
1586 fn init_ownership(&self) {
1587 let state = self.state.lock().unwrap();
1588 if let LazySeqState::Pending(thunk) = &*state {
1589 struct ThunkRef<'a>(&'a dyn Thunk);
1590 impl cljrs_gc::Trace for ThunkRef<'_> {
1591 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1592 self.0.trace(visitor);
1593 }
1594 }
1595 let lease = cljrs_gc::region::RegionLease::for_value(&ThunkRef(thunk.as_ref()));
1596 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
1597 self as *const _ as usize,
1598 usize::MAX,
1599 &lease,
1600 );
1601 }
1602 }
1603 #[cfg(feature = "no-gc")]
1604 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
1605 #[cfg(not(feature = "no-gc"))]
1606 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1607 {
1610 let state = self.state.lock().unwrap();
1611 match &*state {
1612 LazySeqState::Pending(thunk) => thunk.trace(visitor),
1613 LazySeqState::Realizing { thunk, .. } => thunk.trace(visitor),
1614 LazySeqState::Forced(v) => v.trace(visitor),
1615 LazySeqState::Error(_) => {}
1616 }
1617 }
1618 }
1619}
1620
1621#[derive(Debug, Clone)]
1628pub struct CljxCons {
1629 pub head: Value,
1630 pub tail: Value,
1631}
1632
1633impl cljrs_gc::Trace for CljxCons {
1634 #[cfg(feature = "no-gc")]
1635 fn immutable_children(&self) -> bool {
1636 true
1637 }
1638 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1639 self.head.trace(visitor);
1640 self.tail.trace(visitor);
1641 }
1642}
1643
1644pub struct Volatile {
1648 pub value: TrackedMutex<Value>,
1649 #[cfg(feature = "no-gc")]
1650 value_lease: std::cell::RefCell<crate::publish::ValueLease>,
1651}
1652
1653impl Volatile {
1654 pub fn new(v: Value) -> Self {
1655 #[cfg(feature = "no-gc")]
1657 let (v, lease) =
1658 crate::publish::prepare_for_owner(v, &cljrs_gc::region::AllocationOwner::current());
1659 #[cfg(not(feature = "no-gc"))]
1660 let v = crate::publish::publish_value(v);
1661 Self {
1662 value: TrackedMutex::new(v),
1663 #[cfg(feature = "no-gc")]
1664 value_lease: std::cell::RefCell::new(lease),
1665 }
1666 }
1667
1668 pub fn snapshot(&self) -> crate::publish::ValueSnapshot {
1669 crate::publish::ValueSnapshot {
1670 value: self.value.lock().unwrap().clone(),
1671 #[cfg(feature = "no-gc")]
1672 lease: self.value_lease.borrow().snapshot(),
1673 }
1674 }
1675
1676 pub fn deref(&self) -> Value {
1677 let snapshot = self.snapshot();
1678 snapshot.protect_current();
1679 snapshot.value
1680 }
1681
1682 pub fn reset(&self, v: Value) -> Value {
1683 #[cfg(not(feature = "no-gc"))]
1684 let v = crate::publish::publish_value(v);
1685 #[cfg(feature = "no-gc")]
1686 let (v, lease) = crate::publish::prepare_for_owner(
1687 v,
1688 &cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize),
1689 );
1690 #[cfg(feature = "no-gc")]
1691 lease.publish_return();
1692 *self.value.lock().unwrap() = v.clone();
1693 #[cfg(feature = "no-gc")]
1694 {
1695 *self.value_lease.borrow_mut() = lease;
1696 }
1697 v
1698 }
1699}
1700
1701impl std::fmt::Debug for Volatile {
1702 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1703 write!(f, "Volatile")
1704 }
1705}
1706
1707impl cljrs_gc::Trace for Volatile {
1708 #[cfg(feature = "no-gc")]
1709 fn init_ownership(&self) {
1710 let owner = cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize);
1711 let lease = self.value_lease.borrow().reparent(&owner);
1712 let old = self.value_lease.replace(lease);
1713 drop(old);
1714 }
1715
1716 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1717 #[cfg(not(feature = "no-gc"))]
1718 {
1719 let value = self.value.lock().unwrap();
1720 value.trace(visitor);
1721 }
1722 }
1723}
1724
1725pub enum DelayState {
1729 Error(String),
1730 Pending(Rc<dyn Thunk>),
1731 Realizing {
1732 owner: Realizer,
1733 thunk: Rc<dyn Thunk>,
1734 },
1735 Forced(Value),
1736}
1737
1738pub struct Delay {
1740 ready: Event,
1741 pub state: TrackedMutex<DelayState>,
1742}
1743
1744impl Delay {
1745 pub fn new(thunk: Box<dyn Thunk>) -> Self {
1746 Self {
1747 state: TrackedMutex::new(DelayState::Pending(Rc::from(thunk))),
1748 ready: Event::default(),
1749 }
1750 }
1751
1752 pub fn force(&self) -> crate::ValueResult<Value> {
1755 let owner = Realizer::current();
1756 let thunk = loop {
1757 let listener = self.ready.listen();
1758 let mut state = self.state.lock().unwrap();
1759 match &*state {
1760 DelayState::Forced(v) => {
1761 let value = v.clone();
1762 drop(state);
1763 crate::publish::read_slot(self as *const _ as usize, 0);
1764 return Ok(value);
1765 }
1766 DelayState::Error(e) => return Err(crate::ValueError::Other(e.clone())),
1767 DelayState::Realizing { owner: active, .. } => {
1768 if *active == owner {
1769 return Err(crate::ValueError::Other(
1770 "recursive delay realization".into(),
1771 ));
1772 }
1773 active.ensure_can_wait().map_err(crate::ValueError::Other)?;
1774 drop(state);
1775 listener.wait(None);
1776 continue;
1777 }
1778 DelayState::Pending(_) => {}
1779 }
1780 let DelayState::Pending(thunk) =
1781 mem::replace(&mut *state, DelayState::Forced(Value::Nil))
1782 else {
1783 unreachable!()
1784 };
1785 *state = DelayState::Realizing {
1786 owner,
1787 thunk: thunk.clone(),
1788 };
1789 break thunk;
1790 };
1791 let mut guard = RealizationGuard {
1792 lazy: None,
1793 delay: Some(self),
1794 };
1795 let result = match thunk.force() {
1796 Ok(value) => Ok(crate::publish::publish_for_owner(
1797 value,
1798 self as *const _ as usize,
1799 )),
1800 Err(crate::ValueError::Cancelled) => {
1802 drop(thunk);
1803 drop(guard);
1804 return Err(crate::ValueError::Cancelled);
1805 }
1806 Err(error) => Err(thunk_error_message(error)),
1807 };
1808 *self.state.lock().unwrap() = match &result {
1809 Ok(value) => DelayState::Forced(value.clone()),
1810 Err(error) => DelayState::Error(error.clone()),
1811 };
1812 let result = result.map_err(crate::ValueError::Other);
1813 drop(thunk);
1814 #[cfg(feature = "no-gc")]
1815 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
1816 self as *const _ as usize,
1817 usize::MAX,
1818 &Default::default(),
1819 );
1820 guard.delay = None;
1821 if result.is_ok() {
1822 crate::publish::read_slot(self as *const _ as usize, 0);
1823 }
1824 self.ready.notify_all();
1825 result
1826 }
1827
1828 pub fn is_realized(&self) -> bool {
1830 matches!(
1831 &*self.state.lock().unwrap(),
1832 DelayState::Forced(_) | DelayState::Error(_)
1833 )
1834 }
1835}
1836
1837impl std::fmt::Debug for Delay {
1838 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1839 write!(f, "Delay")
1840 }
1841}
1842
1843impl Drop for Delay {
1844 fn drop(&mut self) {
1845 crate::publish::clear_slots(self as *const _ as usize);
1846 }
1847}
1848impl cljrs_gc::Trace for Delay {
1849 #[cfg(feature = "no-gc")]
1850 fn init_ownership(&self) {
1851 let state = self.state.lock().unwrap();
1852 if let DelayState::Pending(thunk) = &*state {
1853 struct ThunkRef<'a>(&'a dyn Thunk);
1854 impl cljrs_gc::Trace for ThunkRef<'_> {
1855 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1856 self.0.trace(visitor);
1857 }
1858 }
1859 let lease = cljrs_gc::region::RegionLease::for_value(&ThunkRef(thunk.as_ref()));
1860 cljrs_gc::region::AllocationOwner::for_address(self as *const _ as usize).set_slot(
1861 self as *const _ as usize,
1862 usize::MAX,
1863 &lease,
1864 );
1865 }
1866 }
1867 #[cfg(feature = "no-gc")]
1868 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
1869 #[cfg(not(feature = "no-gc"))]
1870 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1871 {
1874 let state = self.state.lock().unwrap();
1875 match &*state {
1876 DelayState::Pending(thunk) => thunk.trace(visitor),
1877 DelayState::Realizing { thunk, .. } => thunk.trace(visitor),
1878 DelayState::Error(_) => {}
1879 DelayState::Forced(v) => v.trace(visitor),
1880 }
1881 }
1882 }
1883}
1884
1885pub struct CljxPromise {
1889 pub ready: Event,
1890 pub value: Mutex<Option<Value>>,
1891 pub cond: Condvar,
1892}
1893
1894impl CljxPromise {
1895 pub fn new() -> Self {
1896 Self {
1897 value: Mutex::new(None),
1898 cond: Condvar::new(),
1899 ready: Event::default(),
1900 }
1901 }
1902
1903 pub fn deliver(&self, v: Value) {
1905 if self.value.lock().unwrap().is_some() {
1906 return;
1907 }
1908 let v = crate::publish::publish_for_owner(v, self as *const _ as usize);
1911 let mut guard = self.value.lock().unwrap();
1912 if guard.is_none() {
1913 *guard = Some(v);
1914 self.cond.notify_all();
1915 drop(guard);
1916 self.ready.notify_all();
1917 }
1918 }
1919
1920 pub fn deref_blocking(&self) -> Value {
1922 self.wait_value(None).unwrap()
1923 }
1924
1925 pub fn wait_value(&self, timeout: Option<Duration>) -> Option<Value> {
1926 let deadline = timeout.map(|d| Instant::now() + d);
1927 loop {
1928 let listener = self.ready.listen();
1929 let value = self.value.lock().unwrap().clone();
1930 if let Some(value) = value {
1931 crate::publish::read_slot(self as *const _ as usize, 0);
1932 return Some(value);
1933 }
1934 if !listener.wait(deadline.map(|d| d.saturating_duration_since(Instant::now()))) {
1935 return self.value.lock().unwrap().clone();
1936 }
1937 }
1938 }
1939
1940 pub fn is_realized(&self) -> bool {
1942 self.value.lock().unwrap().is_some()
1943 }
1944}
1945
1946impl Drop for CljxPromise {
1947 fn drop(&mut self) {
1948 crate::publish::clear_slots(self as *const _ as usize);
1949 }
1950}
1951
1952impl Default for CljxPromise {
1953 fn default() -> Self {
1954 Self::new()
1955 }
1956}
1957
1958impl std::fmt::Debug for CljxPromise {
1959 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1960 write!(f, "Promise")
1961 }
1962}
1963
1964impl cljrs_gc::Trace for CljxPromise {
1965 #[cfg(feature = "no-gc")]
1966 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
1967 #[cfg(not(feature = "no-gc"))]
1968 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
1969 {
1970 let value = self.value.lock().unwrap();
1971 if let Some(v) = value.as_ref() {
1972 v.trace(visitor);
1973 }
1974 }
1975 }
1976}
1977
1978pub enum FutureState {
1982 Running,
1983 Done(Value),
1984 Failed(Value),
1988 GasExhausted,
1991 Interrupted,
1993 MemoryLimitExceeded(cljrs_gc::MemoryLimitExceeded),
1996 Cancelled,
1997}
1998
1999pub const FUTURE_CANCELLED_MSG: &str = "future was cancelled";
2001
2002pub struct CljxFuture {
2004 pub ready: Event,
2005 pub state: Mutex<FutureState>,
2006 pub cond: Condvar,
2007 executor_thread: Option<std::thread::ThreadId>,
2008 observed: std::sync::atomic::AtomicBool,
2012}
2013
2014impl CljxFuture {
2015 pub fn new() -> Self {
2016 Self {
2017 state: Mutex::new(FutureState::Running),
2018 cond: Condvar::new(),
2019 ready: Event::default(),
2020 executor_thread: None,
2021 observed: std::sync::atomic::AtomicBool::new(false),
2022 }
2023 }
2024
2025 pub fn new_local() -> Self {
2027 let mut future = Self::new();
2028 future.executor_thread = Some(std::thread::current().id());
2029 future
2030 }
2031
2032 pub fn wait_value(&self, timeout: Option<Duration>) -> crate::ValueResult<Option<Value>> {
2034 let deadline = timeout.map(|d| Instant::now() + d);
2035 loop {
2036 let listener = self.ready.listen();
2037 if let Some(result) = self.settled_value() {
2038 return result.map(Some);
2039 }
2040 if deadline.is_some_and(|deadline| deadline <= Instant::now()) {
2041 return self.settled_value().transpose();
2042 }
2043 if crate::parking::current().is_none()
2044 && self.executor_thread == Some(std::thread::current().id())
2045 {
2046 return Err(crate::ValueError::Other(
2047 "cannot wait for an isolate future outside a fiber; use await in async code or fiber::run_root for synchronous evaluation".into(),
2048 ));
2049 }
2050 if !listener.wait(deadline.map(|d| d.saturating_duration_since(Instant::now()))) {
2051 return self.settled_value().transpose();
2052 }
2053 }
2054 }
2055
2056 pub fn settled_value(&self) -> Option<crate::ValueResult<Value>> {
2057 use crate::ValueError;
2058 let state = self.state.lock().unwrap();
2059 let value = match &*state {
2060 FutureState::Running => return None,
2061 FutureState::Done(v) => Ok(v.clone()),
2062 FutureState::Failed(v) => Err(crate::ValueError::Thrown(v.clone())),
2063 FutureState::GasExhausted => Err(crate::ValueError::GasExhausted),
2064 FutureState::MemoryLimitExceeded(f) => Err(crate::ValueError::MemoryLimitExceeded(*f)),
2065 FutureState::Cancelled | FutureState::Interrupted => {
2069 drop(state);
2070 return Some(Err(crate::ValueError::Thrown(Self::cancelled_error())));
2071 }
2072 };
2073 self.mark_observed();
2074 drop(state);
2075 #[cfg(feature = "no-gc")]
2076 match &value {
2077 Ok(value) | Err(crate::ValueError::Thrown(value)) => {
2078 cljrs_gc::region::RegionLease::for_value(value).protect_read()
2079 }
2080 _ => {}
2081 }
2082 Some(value)
2083 }
2084
2085 pub fn is_done(&self) -> bool {
2087 !matches!(&*self.state.lock().unwrap(), FutureState::Running)
2088 }
2089
2090 pub fn is_cancelled(&self) -> bool {
2092 matches!(&*self.state.lock().unwrap(), FutureState::Cancelled)
2093 }
2094
2095 pub fn cancel(&self) -> bool {
2100 let mut state = self.state.lock().unwrap();
2101 if matches!(&*state, FutureState::Running) {
2102 *state = FutureState::Cancelled;
2103 self.cond.notify_all();
2104 drop(state);
2105 self.ready.notify_all();
2106 true
2107 } else {
2108 false
2109 }
2110 }
2111
2112 pub fn cancelled_error() -> Value {
2119 Value::Error(GcPtr::new(crate::ExceptionInfo::new(
2120 crate::ValueError::Other(FUTURE_CANCELLED_MSG.to_string()),
2121 FUTURE_CANCELLED_MSG.to_string(),
2122 None,
2123 None,
2124 )))
2125 }
2126
2127 pub fn mark_observed(&self) {
2131 self.observed
2132 .store(true, std::sync::atomic::Ordering::Relaxed);
2133 }
2134}
2135
2136impl Drop for CljxFuture {
2137 fn drop(&mut self) {
2138 if !self.observed.load(std::sync::atomic::Ordering::Relaxed)
2149 && let Ok(state) = self.state.lock()
2150 {
2151 match &*state {
2152 FutureState::Failed(_)
2153 | FutureState::GasExhausted
2154 | FutureState::MemoryLimitExceeded(_) => eprintln!(
2155 "[clojurust warning] a failed future was discarded without its error \
2156 being observed (no await/deref); the failure was lost"
2157 ),
2158 FutureState::Interrupted if warn_interrupted_futures() => eprintln!(
2161 "[clojurust warning] an interrupted future was discarded without \
2162 being observed (no await/deref)"
2163 ),
2164 _ => {}
2165 }
2166 }
2167 crate::publish::clear_slots(self as *const _ as usize);
2168 }
2169}
2170
2171fn warn_interrupted_futures() -> bool {
2176 static WARN: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
2177 *WARN.get_or_init(|| {
2178 std::env::var("CLJRS_WARN_INTERRUPTED_FUTURES").is_ok_and(|value| {
2179 matches!(
2180 value.trim().to_ascii_lowercase().as_str(),
2181 "1" | "true" | "yes"
2182 )
2183 })
2184 })
2185}
2186
2187impl Default for CljxFuture {
2188 fn default() -> Self {
2189 Self::new()
2190 }
2191}
2192
2193impl std::fmt::Debug for CljxFuture {
2194 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2195 write!(f, "Future")
2196 }
2197}
2198
2199impl cljrs_gc::Trace for CljxFuture {
2200 #[cfg(feature = "no-gc")]
2201 fn trace(&self, _: &mut cljrs_gc::MarkVisitor) {}
2202 #[cfg(not(feature = "no-gc"))]
2203 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
2204 {
2205 let state = self.state.lock().unwrap();
2206 if let FutureState::Done(v) | FutureState::Failed(v) = &*state {
2209 v.trace(visitor);
2210 }
2211 }
2212 }
2213}
2214
2215pub struct Agent {
2219 pub state: Arc<Mutex<Value>>,
2221 pub error: Arc<Mutex<Option<Value>>>,
2223 pub watches: TrackedMutex<Vec<(Value, Value)>>,
2224}
2225
2226impl Agent {
2227 pub fn get_state(&self) -> Value {
2228 self.state.lock().unwrap().clone()
2229 }
2230
2231 pub fn get_error(&self) -> Option<Value> {
2232 self.error.lock().unwrap().clone()
2233 }
2234
2235 pub fn clear_error(&self) {
2236 *self.error.lock().unwrap() = None;
2237 }
2238}
2239
2240impl std::fmt::Debug for Agent {
2241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2242 write!(f, "Agent")
2243 }
2244}
2245
2246impl cljrs_gc::Trace for Agent {
2247 fn trace(&self, visitor: &mut cljrs_gc::MarkVisitor) {
2248 {
2249 let state = self.state.lock().unwrap();
2250 state.trace(visitor);
2251 }
2252 {
2253 let error = self.error.lock().unwrap();
2254 if let Some(e) = error.as_ref() {
2255 e.trace(visitor);
2256 }
2257 }
2258 {
2259 let watches = self.watches.lock().unwrap();
2260 for (key, f) in watches.iter() {
2261 key.trace(visitor);
2262 f.trace(visitor);
2263 }
2264 }
2265 }
2266}
2267
2268#[cfg(test)]
2271mod var_tests {
2272 use super::*;
2273 use crate::shared::SharedValue;
2274
2275 #[test]
2276 fn native_captures_share_one_immutable_allocation() {
2277 let native = NativeFn::with_captures(
2278 "capture",
2279 Arity::Fixed(0),
2280 vec![Value::Long(7)],
2281 |captures, _| Ok(captures[0].clone()),
2282 );
2283 assert_eq!(Arc::strong_count(&native.captures), 2);
2284 assert_eq!((native.func)(&[]).unwrap(), Value::Long(7));
2285 }
2286
2287 #[test]
2288 fn bind_promotable_mirrors_shared_root() {
2289 let var = Var::new("user", "x");
2290 assert!(var.shared_root.load().is_none());
2291 var.bind(Value::Long(7));
2292 assert!(matches!(
2293 var.shared_root.load().as_ref().as_ref(),
2294 Some(SharedValue::Long(7))
2295 ));
2296 assert_eq!(var.deref(), Some(Value::Long(7)));
2297 assert_eq!(var.deref_shared(), Some(Value::Long(7)));
2298 }
2299
2300 #[test]
2301 fn bind_nonpromotable_clears_shared_root() {
2302 let var = Var::new("user", "f");
2303 var.bind(Value::Long(1));
2304 assert!(var.shared_root.load().is_some());
2305 let f = Value::NativeFunction(GcPtr::new(NativeFn::new("f", Arity::Fixed(0), |_| {
2308 Ok(Value::Nil)
2309 })));
2310 var.bind(f);
2311 assert!(var.shared_root.load().is_none());
2312 assert!(var.is_bound());
2313 assert_eq!(var.deref_shared(), None);
2314 }
2315
2316 #[test]
2317 fn from_shared_root_seeds_local_slot() {
2318 let src = Var::new("user", "y");
2319 src.bind(Value::Long(99));
2320 let recv = Var::from_shared_root("user", "y", false, src.shared_root.clone());
2321 assert_eq!(recv.deref(), Some(Value::Long(99)));
2322 src.bind(Value::Long(100));
2324 assert_eq!(recv.deref_shared(), Some(Value::Long(100)));
2325 }
2326}