1use std::{
56 any::TypeId,
57 cell::{RefCell, UnsafeCell},
58 fmt::Debug,
59 marker::PhantomData,
60 ops::{Deref, DerefMut},
61 rc::Rc,
62};
63
64use ahash::AHashMap;
65use ustr::Ustr;
66
67use super::Actor;
68
69pub struct ActorRef<T: Actor> {
73 actor_rc: Rc<UnsafeCell<dyn Actor>>,
74 _marker: PhantomData<T>,
75}
76
77impl<T: Actor> Debug for ActorRef<T> {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.debug_struct(stringify!(ActorRef))
80 .field("actor_id", &self.deref().id())
81 .finish()
82 }
83}
84
85impl<T: Actor> Deref for ActorRef<T> {
86 type Target = T;
87
88 fn deref(&self) -> &Self::Target {
89 unsafe { &*(self.actor_rc.get() as *const T) }
91 }
92}
93
94impl<T: Actor> DerefMut for ActorRef<T> {
95 fn deref_mut(&mut self) -> &mut Self::Target {
96 unsafe { &mut *self.actor_rc.get().cast::<T>() }
98 }
99}
100
101thread_local! {
102 static ACTOR_REGISTRY: ActorRegistry = ActorRegistry::new();
103}
104
105pub struct ActorRegistry {
107 actors: RefCell<AHashMap<Ustr, Registration>>,
108}
109
110impl Debug for ActorRegistry {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 let actors_ref = self.actors.borrow();
113 let keys: Vec<&Ustr> = actors_ref.keys().collect();
114 f.debug_struct(stringify!(ActorRegistry))
115 .field("actors", &keys)
116 .finish()
117 }
118}
119
120impl Default for ActorRegistry {
121 fn default() -> Self {
122 Self::new()
123 }
124}
125
126impl ActorRegistry {
127 pub fn new() -> Self {
128 Self {
129 actors: RefCell::new(AHashMap::new()),
130 }
131 }
132
133 pub fn insert(&self, id: Ustr, actor: Rc<UnsafeCell<dyn Actor>>) {
134 let mut actors = self.actors.borrow_mut();
135 if actors.contains_key(&id) {
136 log::warn!("Replacing existing actor with id: {id}");
137 }
138 let previous = actors.insert(
139 id,
140 Registration {
141 actor,
142 identity: Rc::new(()),
143 },
144 );
145 drop(actors);
146 drop(previous);
147 }
148
149 pub fn get(&self, id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
150 self.actors
151 .borrow()
152 .get(id)
153 .map(|entry| entry.actor.clone())
154 }
155
156 pub fn len(&self) -> usize {
158 self.actors.borrow().len()
159 }
160
161 pub fn is_empty(&self) -> bool {
163 self.actors.borrow().is_empty()
164 }
165
166 pub fn remove(&self, id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
168 self.actors.borrow_mut().remove(id).map(|entry| entry.actor)
169 }
170
171 pub fn contains(&self, id: &Ustr) -> bool {
173 self.actors.borrow().contains_key(id)
174 }
175}
176
177pub fn with_actor_registry<R>(f: impl FnOnce(&ActorRegistry) -> R) -> R {
178 ACTOR_REGISTRY.with(f)
179}
180
181pub fn register_actor<T>(actor: T) -> Rc<UnsafeCell<T>>
183where
184 T: Actor + 'static,
185{
186 let actor_id = actor.id();
187 let actor_ref = Rc::new(UnsafeCell::new(actor));
188
189 let actor_trait_ref: Rc<UnsafeCell<dyn Actor>> = actor_ref.clone();
191 with_actor_registry(|registry| registry.insert(actor_id, actor_trait_ref));
192
193 actor_ref
194}
195
196pub fn get_actor(id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
197 with_actor_registry(|registry| registry.get(id))
198}
199
200pub fn deregister_actor(id: &Ustr) {
205 with_actor_registry(|registry| registry.remove(id));
206}
207
208#[must_use]
218pub fn get_actor_unchecked<T: Actor>(id: &Ustr) -> ActorRef<T> {
219 let actor_rc = with_actor_registry(|registry| registry.get(id))
220 .unwrap_or_else(|| panic!("Actor for {id} not found"));
221
222 match actor_ref_from_rc(actor_rc) {
223 Ok(actor_ref) => actor_ref,
224 Err(ActorRefError {
225 expected_type,
226 actual_type,
227 }) => {
228 panic!(
229 "Actor type mismatch for '{id}': expected {expected_type:?}, found {actual_type:?}"
230 )
231 }
232 }
233}
234
235#[must_use]
239pub fn try_get_actor_unchecked<T: Actor>(id: &Ustr) -> Option<ActorRef<T>> {
240 let actor_rc = with_actor_registry(|registry| registry.get(id))?;
241 actor_ref_from_rc(actor_rc).ok()
242}
243
244#[derive(Debug)]
245struct ActorRefError {
246 expected_type: TypeId,
247 actual_type: TypeId,
248}
249
250fn actor_ref_from_rc<T: Actor>(
251 actor_rc: Rc<UnsafeCell<dyn Actor>>,
252) -> Result<ActorRef<T>, ActorRefError> {
253 let actor_ref = unsafe { &*actor_rc.get() };
255 let actual_type = actor_ref.as_any().type_id();
256 let expected_type = TypeId::of::<T>();
257
258 if actual_type != expected_type {
259 return Err(ActorRefError {
260 expected_type,
261 actual_type,
262 });
263 }
264
265 Ok(ActorRef {
266 actor_rc,
267 _marker: PhantomData,
268 })
269}
270
271pub fn actor_exists(id: &Ustr) -> bool {
273 with_actor_registry(|registry| registry.contains(id))
274}
275
276pub fn actor_count() -> usize {
278 with_actor_registry(ActorRegistry::len)
279}
280
281#[derive(Clone)]
282struct Registration {
283 actor: Rc<UnsafeCell<dyn Actor>>,
284 identity: Rc<()>,
285}
286
287#[allow(
288 dead_code,
289 reason = "registration-bound admission remains inactive until runtime integration"
290)]
291pub(super) fn reserve_actor<T: Actor, E: 'static>(
292 id: Ustr,
293 heap_bytes: usize,
294) -> Option<ActorAdmission<T, E>> {
295 let registration = ACTOR_REGISTRY
296 .try_with(|registry| registry.actors.borrow().get(&id).cloned())
297 .ok()
298 .flatten()?;
299 let admission = super::dispatch::reserve(heap_bytes)?;
300 Some(ActorAdmission {
301 id,
302 registration,
303 admission,
304 })
305}
306
307#[allow(
308 dead_code,
309 reason = "registration-bound admission remains inactive until runtime integration"
310)]
311pub(super) struct ActorAdmission<T: Actor, E> {
312 id: Ustr,
313 registration: Registration,
314 admission: super::dispatch::Admission<ActorDelivery<T, E>>,
315}
316
317#[allow(
318 dead_code,
319 reason = "registration-bound admission remains inactive until runtime integration"
320)]
321impl<T: Actor, E: 'static> ActorAdmission<T, E> {
322 pub(super) fn commit(self, event: E, handler: fn(&mut T, &E)) {
323 self.admission.commit(
324 ActorDelivery {
325 id: self.id,
326 registration: self.registration,
327 event,
328 handler,
329 },
330 ActorDelivery::run,
331 );
332 }
333}
334
335struct ActorDelivery<T: Actor, E> {
336 id: Ustr,
337 registration: Registration,
338 event: E,
339 handler: fn(&mut T, &E),
340}
341
342impl<T: Actor, E> ActorDelivery<T, E> {
343 fn run(&mut self) -> bool {
344 let current =
345 ACTOR_REGISTRY
346 .try_with(|registry| {
347 registry.actors.borrow().get(&self.id).is_some_and(|entry| {
348 Rc::ptr_eq(&entry.identity, &self.registration.identity)
349 })
350 })
351 .unwrap_or(false);
352
353 if !current {
354 return true;
355 }
356
357 match super::access::ActorGuard::acquire(self.registration.actor.clone()) {
358 Ok(mut actor) => {
359 (self.handler)(&mut actor, &self.event);
360 true
361 }
362 Err(super::access::ActorAccessError::Busy) => false,
363 Err(_) => {
364 super::dispatch::record_failure(super::dispatch::DispatchError::InvalidDestination);
365 true
366 }
367 }
368 }
369}
370
371#[cfg(test)]
372pub fn clear_actor_registry() {
374 let actors = with_actor_registry(|registry| std::mem::take(&mut *registry.actors.borrow_mut()));
375 drop(actors);
376}
377
378#[cfg(test)]
379mod tests {
380 use std::any::Any;
381
382 use rstest::rstest;
383
384 use super::*;
385
386 #[derive(Debug)]
387 struct TestActor {
388 id: Ustr,
389 value: i32,
390 }
391
392 impl Actor for TestActor {
393 fn id(&self) -> Ustr {
394 self.id
395 }
396 fn handle(&mut self, _msg: &dyn Any) {}
397 fn as_any(&self) -> &dyn Any {
398 self
399 }
400 }
401
402 #[rstest]
403 fn owned_delivery_waits_for_access_and_cancels_same_allocation_registration() {
404 super::super::dispatch::clear().unwrap();
405 clear_actor_registry();
406 let id = Ustr::from("owned-registration");
407 let allocation = register_actor(TestActor { id, value: 11 });
408 let guard =
409 super::super::access::ActorGuard::<TestActor>::acquire(allocation.clone()).unwrap();
410 reserve_actor::<TestActor, _>(id, 0)
411 .unwrap()
412 .commit(23, |actor, value| actor.value = *value);
413 assert_eq!(super::super::dispatch::drain(1).unwrap().delivered, 0);
414 drop(guard);
415 assert_eq!(super::super::dispatch::drain(1).unwrap().delivered, 1);
416 assert_eq!(get_actor_unchecked::<TestActor>(&id).value, 23);
417 reserve_actor::<TestActor, _>(id, 0)
418 .unwrap()
419 .commit(37, |actor, value| actor.value = *value);
420 deregister_actor(&id);
421 with_actor_registry(|registry| registry.insert(id, allocation));
422 assert_eq!(super::super::dispatch::drain(1).unwrap().delivered, 1);
423 assert_eq!(get_actor_unchecked::<TestActor>(&id).value, 23);
424 super::super::dispatch::clear().unwrap();
425 }
426
427 #[rstest]
428 fn owned_delivery_retries_busy_allocation() {
429 super::super::dispatch::clear().unwrap();
430 clear_actor_registry();
431 let id = Ustr::from("busy-delivery");
432 let allocation = register_actor(TestActor { id, value: 11 });
433 let registration = with_actor_registry(|registry| registry.actors.borrow()[&id].clone());
434 let mut delivery = ActorDelivery {
435 id,
436 registration,
437 event: 23,
438 handler: |actor: &mut TestActor, value: &i32| actor.value = *value,
439 };
440 let guard = super::super::access::ActorGuard::<TestActor>::acquire(allocation).unwrap();
441 let busy = delivery.run();
442 assert!(!busy);
443 assert_eq!(guard.value, 11);
444 assert_eq!(super::super::dispatch::failure(), None);
445 drop(guard);
446 let delivered = delivery.run();
447 assert!(delivered);
448 assert_eq!(get_actor_unchecked::<TestActor>(&id).value, 23);
449 assert_eq!(super::super::dispatch::failure(), None);
450 clear_actor_registry();
451 }
452
453 #[rstest]
454 fn owned_delivery_rejects_wrong_actor_type() {
455 #[derive(Debug)]
456 struct OtherActor;
457
458 impl Actor for OtherActor {
459 fn id(&self) -> Ustr {
460 Ustr::from("wrong-delivery-type")
461 }
462 fn handle(&mut self, _msg: &dyn Any) {}
463 fn as_any(&self) -> &dyn Any {
464 self
465 }
466 }
467
468 super::super::dispatch::clear().unwrap();
469 clear_actor_registry();
470 let id = OtherActor.id();
471 register_actor(OtherActor);
472 reserve_actor::<TestActor, _>(id, 0)
473 .unwrap()
474 .commit(23, |_, _| panic!("wrong-type handler must not run"));
475 let result = super::super::dispatch::drain(1);
476 assert_eq!(
477 result,
478 Err(super::super::dispatch::DispatchError::InvalidDestination)
479 );
480 assert_eq!(
481 super::super::dispatch::failure(),
482 Some(super::super::dispatch::DispatchError::InvalidDestination)
483 );
484 super::super::dispatch::clear().unwrap();
485 clear_actor_registry();
486 }
487
488 #[rstest]
489 fn actor_reservation_rejects_after_registry_teardown() {
490 struct Probe;
491
492 impl Drop for Probe {
493 fn drop(&mut self) {
494 assert!(ACTOR_REGISTRY.try_with(|_| ()).is_err());
495 assert!(reserve_actor::<TestActor, ()>(Ustr::from("teardown"), 0).is_none());
496 }
497 }
498
499 thread_local! {
500 static PROBE: Probe = const { Probe };
501 }
502
503 std::thread::spawn(|| {
504 PROBE.with(|_| ());
505 register_actor(TestActor {
506 id: Ustr::from("teardown"),
507 value: 11,
508 });
509 })
510 .join()
511 .unwrap();
512 }
513
514 #[rstest]
515 fn test_register_and_get_actor() {
516 clear_actor_registry();
517
518 let id = Ustr::from("test-actor");
519 let actor = TestActor { id, value: 42 };
520 register_actor(actor);
521
522 let actor_ref = get_actor_unchecked::<TestActor>(&id);
523 assert_eq!(actor_ref.value, 42);
524 }
525
526 #[rstest]
527 fn test_mutation_through_reference() {
528 clear_actor_registry();
529
530 let id = Ustr::from("test-actor-mut");
531 let actor = TestActor { id, value: 0 };
532 register_actor(actor);
533
534 let mut actor_ref = get_actor_unchecked::<TestActor>(&id);
535 actor_ref.value = 999;
536 drop(actor_ref);
537
538 let actor_ref2 = get_actor_unchecked::<TestActor>(&id);
539 assert_eq!(actor_ref2.value, 999);
540 }
541
542 #[rstest]
543 fn test_try_get_returns_none_for_missing() {
544 clear_actor_registry();
545
546 let id = Ustr::from("nonexistent");
547 let result = try_get_actor_unchecked::<TestActor>(&id);
548 assert!(result.is_none());
549 }
550
551 #[rstest]
552 fn test_try_get_returns_none_for_wrong_type() {
553 #[derive(Debug)]
554 struct OtherActor {
555 id: Ustr,
556 }
557
558 impl Actor for OtherActor {
559 fn id(&self) -> Ustr {
560 self.id
561 }
562 fn handle(&mut self, _msg: &dyn Any) {}
563 fn as_any(&self) -> &dyn Any {
564 self
565 }
566 }
567
568 clear_actor_registry();
569
570 let id = Ustr::from("other-actor");
571 let actor = OtherActor { id };
572 register_actor(actor);
573
574 let result = try_get_actor_unchecked::<TestActor>(&id);
575 assert!(result.is_none());
576 }
577
578 #[rstest]
579 fn test_registry_is_thread_local() {
580 clear_actor_registry();
581
582 let id = Ustr::from("thread-local-actor");
583 let actor = TestActor { id, value: 42 };
584 register_actor(actor);
585
586 assert!(actor_exists(&id));
587 assert_eq!(actor_count(), 1);
588
589 let visible_on_other_thread = std::thread::spawn(move || {
590 (actor_exists(&id), actor_count())
592 })
593 .join()
594 .unwrap();
595
596 assert!(!visible_on_other_thread.0);
597 assert_eq!(visible_on_other_thread.1, 0);
598 }
599
600 #[rstest]
601 fn test_actor_ref_survives_registry_removal() {
602 clear_actor_registry();
603
604 let id = Ustr::from("removable-actor");
605 let actor = TestActor { id, value: 7 };
606 register_actor(actor);
607 assert_eq!(actor_count(), 1);
608
609 let mut guard = get_actor_unchecked::<TestActor>(&id);
610
611 with_actor_registry(|registry| {
612 registry.remove(&id);
613 });
614 assert!(!actor_exists(&id));
615 assert_eq!(actor_count(), 0);
616
617 assert_eq!(guard.value, 7);
618 guard.value = 99;
619 assert_eq!(guard.value, 99);
620 }
621
622 #[rstest]
623 fn test_deregister_actor_removes_only_requested_actor_and_retains_guard() {
624 clear_actor_registry();
625
626 let removed_id = Ustr::from("removed-actor");
627 let retained_id = Ustr::from("retained-actor");
628 register_actor(TestActor {
629 id: removed_id,
630 value: 7,
631 });
632 register_actor(TestActor {
633 id: retained_id,
634 value: 11,
635 });
636 let removed_guard = get_actor_unchecked::<TestActor>(&removed_id);
637
638 deregister_actor(&removed_id);
639
640 assert!(!actor_exists(&removed_id));
641 assert!(actor_exists(&retained_id));
642 assert_eq!(actor_count(), 1);
643 assert_eq!(removed_guard.value, 7);
644 assert_eq!(get_actor_unchecked::<TestActor>(&retained_id).value, 11);
645 }
646
647 #[rstest]
648 fn test_actor_ref_survives_same_id_replacement() {
649 clear_actor_registry();
650
651 let id = Ustr::from("replaceable-actor");
652 let actor_a = TestActor { id, value: 1 };
653 register_actor(actor_a);
654
655 let guard_a = get_actor_unchecked::<TestActor>(&id);
656 assert_eq!(guard_a.value, 1);
657
658 let actor_b = TestActor { id, value: 2 };
659 register_actor(actor_b);
660
661 assert_eq!(guard_a.value, 1);
663
664 let guard_b = get_actor_unchecked::<TestActor>(&id);
666 assert_eq!(guard_b.value, 2);
667 assert_eq!(actor_count(), 1);
668 }
669
670 #[should_panic(expected = "Actor type mismatch")]
671 #[rstest]
672 fn test_get_actor_unchecked_panics_on_type_mismatch() {
673 #[derive(Debug)]
674 struct OtherActor {
675 id: Ustr,
676 }
677
678 impl Actor for OtherActor {
679 fn id(&self) -> Ustr {
680 self.id
681 }
682 fn handle(&mut self, _msg: &dyn Any) {}
683 fn as_any(&self) -> &dyn Any {
684 self
685 }
686 }
687
688 clear_actor_registry();
689
690 let id = Ustr::from("typed-actor");
691 let actor = OtherActor { id };
692 register_actor(actor);
693
694 let _guard = get_actor_unchecked::<TestActor>(&id);
695 }
696}