nautilus_common/actor/
registry.rs1use std::{
58 any::TypeId,
59 cell::{RefCell, UnsafeCell},
60 fmt::Debug,
61 marker::PhantomData,
62 ops::{Deref, DerefMut},
63 rc::Rc,
64};
65
66use ahash::AHashMap;
67use ustr::Ustr;
68
69use super::Actor;
70
71pub struct ActorRef<T: Actor> {
84 actor_rc: Rc<UnsafeCell<dyn Actor>>,
85 _marker: PhantomData<T>,
86}
87
88impl<T: Actor> Debug for ActorRef<T> {
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct(stringify!(ActorRef))
91 .field("actor_id", &self.deref().id())
92 .finish()
93 }
94}
95
96impl<T: Actor> Deref for ActorRef<T> {
97 type Target = T;
98
99 fn deref(&self) -> &Self::Target {
100 unsafe { &*(self.actor_rc.get() as *const T) }
102 }
103}
104
105impl<T: Actor> DerefMut for ActorRef<T> {
106 fn deref_mut(&mut self) -> &mut Self::Target {
107 unsafe { &mut *self.actor_rc.get().cast::<T>() }
109 }
110}
111
112thread_local! {
113 static ACTOR_REGISTRY: ActorRegistry = ActorRegistry::new();
114}
115
116pub struct ActorRegistry {
118 actors: RefCell<AHashMap<Ustr, Rc<UnsafeCell<dyn Actor>>>>,
119}
120
121impl Debug for ActorRegistry {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 let actors_ref = self.actors.borrow();
124 let keys: Vec<&Ustr> = actors_ref.keys().collect();
125 f.debug_struct(stringify!(ActorRegistry))
126 .field("actors", &keys)
127 .finish()
128 }
129}
130
131impl Default for ActorRegistry {
132 fn default() -> Self {
133 Self::new()
134 }
135}
136
137impl ActorRegistry {
138 pub fn new() -> Self {
139 Self {
140 actors: RefCell::new(AHashMap::new()),
141 }
142 }
143
144 pub fn insert(&self, id: Ustr, actor: Rc<UnsafeCell<dyn Actor>>) {
145 let mut actors = self.actors.borrow_mut();
146 if actors.contains_key(&id) {
147 log::warn!("Replacing existing actor with id: {id}");
148 }
149 actors.insert(id, actor);
150 }
151
152 pub fn get(&self, id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
153 self.actors.borrow().get(id).cloned()
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)
169 }
170
171 pub fn contains(&self, id: &Ustr) -> bool {
173 self.actors.borrow().contains_key(id)
174 }
175}
176
177pub fn get_actor_registry() -> &'static ActorRegistry {
178 ACTOR_REGISTRY.with(|registry| unsafe {
179 std::mem::transmute::<&ActorRegistry, &'static ActorRegistry>(registry)
184 })
185}
186
187pub fn register_actor<T>(actor: T) -> Rc<UnsafeCell<T>>
189where
190 T: Actor + 'static,
191{
192 let actor_id = actor.id();
193 let actor_ref = Rc::new(UnsafeCell::new(actor));
194
195 let actor_trait_ref: Rc<UnsafeCell<dyn Actor>> = actor_ref.clone();
197 get_actor_registry().insert(actor_id, actor_trait_ref);
198
199 actor_ref
200}
201
202pub fn get_actor(id: &Ustr) -> Option<Rc<UnsafeCell<dyn Actor>>> {
203 get_actor_registry().get(id)
204}
205
206#[must_use]
224pub fn get_actor_unchecked<T: Actor>(id: &Ustr) -> ActorRef<T> {
225 let registry = get_actor_registry();
226 let actor_rc = registry
227 .get(id)
228 .unwrap_or_else(|| panic!("Actor for {id} not found"));
229
230 let actor_ref = unsafe { &*actor_rc.get() };
232 let actual_type = actor_ref.as_any().type_id();
233 let expected_type = TypeId::of::<T>();
234
235 assert!(
236 actual_type == expected_type,
237 "Actor type mismatch for '{id}': expected {expected_type:?}, found {actual_type:?}"
238 );
239
240 ActorRef {
241 actor_rc,
242 _marker: PhantomData,
243 }
244}
245
246#[must_use]
252pub fn try_get_actor_unchecked<T: Actor>(id: &Ustr) -> Option<ActorRef<T>> {
253 let registry = get_actor_registry();
254 let actor_rc = registry.get(id)?;
255
256 let actor_ref = unsafe { &*actor_rc.get() };
258 let actual_type = actor_ref.as_any().type_id();
259 let expected_type = TypeId::of::<T>();
260
261 if actual_type != expected_type {
262 return None;
263 }
264
265 Some(ActorRef {
266 actor_rc,
267 _marker: PhantomData,
268 })
269}
270
271pub fn actor_exists(id: &Ustr) -> bool {
273 get_actor_registry().contains(id)
274}
275
276pub fn actor_count() -> usize {
278 get_actor_registry().len()
279}
280
281#[cfg(test)]
282pub fn clear_actor_registry() {
284 let registry = get_actor_registry();
285 registry.actors.borrow_mut().clear();
286}
287
288#[cfg(test)]
289mod tests {
290 use std::any::Any;
291
292 use rstest::rstest;
293
294 use super::*;
295
296 #[derive(Debug)]
297 struct TestActor {
298 id: Ustr,
299 value: i32,
300 }
301
302 impl Actor for TestActor {
303 fn id(&self) -> Ustr {
304 self.id
305 }
306 fn handle(&mut self, _msg: &dyn Any) {}
307 fn as_any(&self) -> &dyn Any {
308 self
309 }
310 }
311
312 #[rstest]
313 fn test_register_and_get_actor() {
314 clear_actor_registry();
315
316 let id = Ustr::from("test-actor");
317 let actor = TestActor { id, value: 42 };
318 register_actor(actor);
319
320 let actor_ref = get_actor_unchecked::<TestActor>(&id);
321 assert_eq!(actor_ref.value, 42);
322 }
323
324 #[rstest]
325 fn test_mutation_through_reference() {
326 clear_actor_registry();
327
328 let id = Ustr::from("test-actor-mut");
329 let actor = TestActor { id, value: 0 };
330 register_actor(actor);
331
332 let mut actor_ref = get_actor_unchecked::<TestActor>(&id);
333 actor_ref.value = 999;
334
335 let actor_ref2 = get_actor_unchecked::<TestActor>(&id);
336 assert_eq!(actor_ref2.value, 999);
337 }
338
339 #[rstest]
340 fn test_try_get_returns_none_for_missing() {
341 clear_actor_registry();
342
343 let id = Ustr::from("nonexistent");
344 let result = try_get_actor_unchecked::<TestActor>(&id);
345 assert!(result.is_none());
346 }
347
348 #[rstest]
349 fn test_try_get_returns_none_for_wrong_type() {
350 #[derive(Debug)]
351 struct OtherActor {
352 id: Ustr,
353 }
354
355 impl Actor for OtherActor {
356 fn id(&self) -> Ustr {
357 self.id
358 }
359 fn handle(&mut self, _msg: &dyn Any) {}
360 fn as_any(&self) -> &dyn Any {
361 self
362 }
363 }
364
365 clear_actor_registry();
366
367 let id = Ustr::from("other-actor");
368 let actor = OtherActor { id };
369 register_actor(actor);
370
371 let result = try_get_actor_unchecked::<TestActor>(&id);
372 assert!(result.is_none());
373 }
374
375 #[rstest]
376 fn test_registry_is_thread_local() {
377 clear_actor_registry();
378
379 let id = Ustr::from("thread-local-actor");
380 let actor = TestActor { id, value: 42 };
381 register_actor(actor);
382
383 assert!(actor_exists(&id));
384 assert_eq!(actor_count(), 1);
385
386 let visible_on_other_thread = std::thread::spawn(move || {
387 (actor_exists(&id), actor_count())
389 })
390 .join()
391 .unwrap();
392
393 assert!(!visible_on_other_thread.0);
394 assert_eq!(visible_on_other_thread.1, 0);
395 }
396
397 #[rstest]
398 fn test_actor_ref_survives_registry_removal() {
399 clear_actor_registry();
400
401 let id = Ustr::from("removable-actor");
402 let actor = TestActor { id, value: 7 };
403 register_actor(actor);
404 assert_eq!(actor_count(), 1);
405
406 let mut guard = get_actor_unchecked::<TestActor>(&id);
407
408 get_actor_registry().remove(&id);
409 assert!(!actor_exists(&id));
410 assert_eq!(actor_count(), 0);
411
412 assert_eq!(guard.value, 7);
413 guard.value = 99;
414 assert_eq!(guard.value, 99);
415 }
416
417 #[rstest]
418 fn test_actor_ref_survives_same_id_replacement() {
419 clear_actor_registry();
420
421 let id = Ustr::from("replaceable-actor");
422 let actor_a = TestActor { id, value: 1 };
423 register_actor(actor_a);
424
425 let guard_a = get_actor_unchecked::<TestActor>(&id);
426 assert_eq!(guard_a.value, 1);
427
428 let actor_b = TestActor { id, value: 2 };
429 register_actor(actor_b);
430
431 assert_eq!(guard_a.value, 1);
433
434 let guard_b = get_actor_unchecked::<TestActor>(&id);
436 assert_eq!(guard_b.value, 2);
437 assert_eq!(actor_count(), 1);
438 }
439
440 #[should_panic(expected = "Actor type mismatch")]
441 #[rstest]
442 fn test_get_actor_unchecked_panics_on_type_mismatch() {
443 #[derive(Debug)]
444 struct OtherActor {
445 id: Ustr,
446 }
447
448 impl Actor for OtherActor {
449 fn id(&self) -> Ustr {
450 self.id
451 }
452 fn handle(&mut self, _msg: &dyn Any) {}
453 fn as_any(&self) -> &dyn Any {
454 self
455 }
456 }
457
458 clear_actor_registry();
459
460 let id = Ustr::from("typed-actor");
461 let actor = OtherActor { id };
462 register_actor(actor);
463
464 let _guard = get_actor_unchecked::<TestActor>(&id);
465 }
466}