1use std::fmt;
55use std::time::Duration;
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
61pub struct ActorId(pub u32);
62
63impl fmt::Display for ActorId {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(f, "actor:{}", self.0)
66 }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71#[repr(u32)]
72pub enum ActorState {
73 Dormant = 0,
75 Initializing = 1,
77 Active = 2,
79 Draining = 3,
81 Terminated = 4,
83 Failed = 5,
85}
86
87impl ActorState {
88 pub fn from_u32(v: u32) -> Option<Self> {
90 match v {
91 0 => Some(Self::Dormant),
92 1 => Some(Self::Initializing),
93 2 => Some(Self::Active),
94 3 => Some(Self::Draining),
95 4 => Some(Self::Terminated),
96 5 => Some(Self::Failed),
97 _ => None,
98 }
99 }
100
101 pub fn is_alive(self) -> bool {
103 matches!(self, Self::Initializing | Self::Active)
104 }
105}
106
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109pub enum RestartPolicy {
110 Permanent,
112 OneForOne {
114 max_restarts: u32,
116 window: Duration,
118 },
119 OneForAll {
121 max_restarts: u32,
123 window: Duration,
125 },
126 RestForOne {
128 max_restarts: u32,
130 window: Duration,
132 },
133}
134
135impl Default for RestartPolicy {
136 fn default() -> Self {
137 Self::OneForOne {
138 max_restarts: 3,
139 window: Duration::from_secs(60),
140 }
141 }
142}
143
144#[derive(Debug, Clone)]
146pub struct ActorConfig {
147 pub name: String,
149 pub queue_capacity: u32,
151 pub restart_policy: RestartPolicy,
153 pub heartbeat_interval: Duration,
155 pub heartbeat_timeout: Duration,
157 pub initial_state: Option<Vec<u8>>,
159}
160
161impl Default for ActorConfig {
162 fn default() -> Self {
163 Self {
164 name: String::new(),
165 queue_capacity: 1024,
166 restart_policy: RestartPolicy::default(),
167 heartbeat_interval: Duration::from_millis(100),
168 heartbeat_timeout: Duration::from_millis(500),
169 initial_state: None,
170 }
171 }
172}
173
174impl ActorConfig {
175 pub fn named(name: impl Into<String>) -> Self {
177 Self {
178 name: name.into(),
179 ..Default::default()
180 }
181 }
182
183 pub fn with_restart_policy(mut self, policy: RestartPolicy) -> Self {
185 self.restart_policy = policy;
186 self
187 }
188
189 pub fn with_heartbeat(mut self, interval: Duration, timeout: Duration) -> Self {
191 self.heartbeat_interval = interval;
192 self.heartbeat_timeout = timeout;
193 self
194 }
195
196 pub fn with_queue_capacity(mut self, capacity: u32) -> Self {
198 self.queue_capacity = capacity;
199 self
200 }
201}
202
203#[repr(C, align(64))]
208#[derive(Debug, Clone, Copy)]
209pub struct SupervisionEntry {
210 pub actor_id: u32,
212 pub state: u32,
214 pub parent_id: u32,
216 pub restart_count: u32,
218 pub last_heartbeat_ns: u64,
220 pub restart_window_start_ns: u64,
222 pub max_restarts: u32,
224 pub restart_window_ns: u64,
226 pub _pad: [u8; 8],
228}
229
230impl SupervisionEntry {
231 pub fn dormant(actor_id: u32) -> Self {
233 Self {
234 actor_id,
235 state: ActorState::Dormant as u32,
236 parent_id: 0,
237 restart_count: 0,
238 last_heartbeat_ns: 0,
239 restart_window_start_ns: 0,
240 max_restarts: 3,
241 restart_window_ns: 60_000_000_000, _pad: [0; 8],
243 }
244 }
245
246 pub fn is_available(&self) -> bool {
248 self.state == ActorState::Dormant as u32
249 }
250
251 pub fn actor_state(&self) -> ActorState {
253 ActorState::from_u32(self.state).unwrap_or(ActorState::Failed)
254 }
255}
256
257pub struct ActorSupervisor {
262 entries: Vec<SupervisionEntry>,
264 free_list: Vec<u32>,
266 capacity: u32,
268 active_count: u32,
270}
271
272impl ActorSupervisor {
273 pub fn new(grid_size: u32) -> Self {
278 let mut entries = Vec::with_capacity(grid_size as usize);
279 let mut free_list = Vec::with_capacity(grid_size as usize);
280
281 for i in 0..grid_size {
282 entries.push(SupervisionEntry::dormant(i));
283 if i > 0 {
284 free_list.push(i);
286 }
287 }
288
289 Self {
290 entries,
291 free_list,
292 capacity: grid_size - 1, active_count: 0,
294 }
295 }
296
297 pub fn create_actor(
302 &mut self,
303 config: &ActorConfig,
304 parent_id: Option<ActorId>,
305 ) -> Result<ActorId, ActorError> {
306 let slot = self.free_list.pop().ok_or(ActorError::PoolExhausted {
307 capacity: self.capacity,
308 active: self.active_count,
309 })?;
310
311 let entry = &mut self.entries[slot as usize];
312 entry.state = ActorState::Initializing as u32;
313 entry.parent_id = parent_id.map(|p| p.0).unwrap_or(0);
314 entry.restart_count = 0;
315 entry.last_heartbeat_ns = 0;
316
317 match config.restart_policy {
318 RestartPolicy::OneForOne {
319 max_restarts,
320 window,
321 }
322 | RestartPolicy::OneForAll {
323 max_restarts,
324 window,
325 }
326 | RestartPolicy::RestForOne {
327 max_restarts,
328 window,
329 } => {
330 entry.max_restarts = max_restarts;
331 entry.restart_window_ns = window.as_nanos() as u64;
332 }
333 RestartPolicy::Permanent => {
334 entry.max_restarts = 0;
335 entry.restart_window_ns = 0;
336 }
337 }
338
339 self.active_count += 1;
340
341 Ok(ActorId(slot))
342 }
343
344 pub fn activate_actor(&mut self, id: ActorId) -> Result<(), ActorError> {
346 let entry = self
347 .entries
348 .get_mut(id.0 as usize)
349 .ok_or(ActorError::InvalidId(id))?;
350
351 if entry.state != ActorState::Initializing as u32 {
352 return Err(ActorError::InvalidStateTransition {
353 actor: id,
354 from: entry.actor_state(),
355 to: ActorState::Active,
356 });
357 }
358
359 entry.state = ActorState::Active as u32;
360 Ok(())
361 }
362
363 pub fn destroy_actor(&mut self, id: ActorId) -> Result<(), ActorError> {
365 let entry = self
366 .entries
367 .get_mut(id.0 as usize)
368 .ok_or(ActorError::InvalidId(id))?;
369
370 if entry.state == ActorState::Dormant as u32 {
371 return Err(ActorError::InvalidStateTransition {
372 actor: id,
373 from: ActorState::Dormant,
374 to: ActorState::Terminated,
375 });
376 }
377
378 entry.state = ActorState::Dormant as u32;
379 entry.parent_id = 0;
380 entry.restart_count = 0;
381 entry.last_heartbeat_ns = 0;
382
383 self.free_list.push(id.0);
384 self.active_count = self.active_count.saturating_sub(1);
385
386 Ok(())
387 }
388
389 pub fn restart_actor(
393 &mut self,
394 id: ActorId,
395 config: &ActorConfig,
396 ) -> Result<ActorId, ActorError> {
397 let parent_id = {
398 let entry = self
399 .entries
400 .get(id.0 as usize)
401 .ok_or(ActorError::InvalidId(id))?;
402
403 if entry.restart_count >= entry.max_restarts && entry.max_restarts > 0 {
405 return Err(ActorError::MaxRestartsExceeded {
406 actor: id,
407 restarts: entry.restart_count,
408 max: entry.max_restarts,
409 });
410 }
411
412 if entry.parent_id > 0 {
413 Some(ActorId(entry.parent_id))
414 } else {
415 None
416 }
417 };
418
419 let restart_count = self.entries[id.0 as usize].restart_count;
421
422 self.destroy_actor(id)?;
423 let new_id = self.create_actor(config, parent_id)?;
424
425 self.entries[new_id.0 as usize].restart_count = restart_count + 1;
427
428 Ok(new_id)
429 }
430
431 pub fn heartbeat(&mut self, id: ActorId, timestamp_ns: u64) {
433 if let Some(entry) = self.entries.get_mut(id.0 as usize) {
434 entry.last_heartbeat_ns = timestamp_ns;
435 }
436 }
437
438 pub fn check_heartbeats(&self, now_ns: u64, timeout_ns: u64) -> Vec<ActorId> {
442 self.entries
443 .iter()
444 .filter(|e| {
445 e.actor_state().is_alive()
446 && e.last_heartbeat_ns > 0
447 && (now_ns - e.last_heartbeat_ns) > timeout_ns
448 })
449 .map(|e| ActorId(e.actor_id))
450 .collect()
451 }
452
453 pub fn children_of(&self, parent: ActorId) -> Vec<ActorId> {
455 self.entries
456 .iter()
457 .filter(|e| e.parent_id == parent.0 && e.actor_state().is_alive())
458 .map(|e| ActorId(e.actor_id))
459 .collect()
460 }
461
462 pub fn get(&self, id: ActorId) -> Option<&SupervisionEntry> {
464 self.entries.get(id.0 as usize)
465 }
466
467 pub fn active_count(&self) -> u32 {
469 self.active_count
470 }
471
472 pub fn available_count(&self) -> u32 {
474 self.free_list.len() as u32
475 }
476
477 pub fn capacity(&self) -> u32 {
479 self.capacity
480 }
481
482 pub fn entries(&self) -> &[SupervisionEntry] {
484 &self.entries
485 }
486
487 pub fn kill_tree(&mut self, root: ActorId) -> Vec<ActorId> {
494 let mut destroyed = Vec::new();
495 self.kill_tree_recursive(root, &mut destroyed);
496 destroyed
497 }
498
499 fn kill_tree_recursive(&mut self, id: ActorId, destroyed: &mut Vec<ActorId>) {
500 let children = self.children_of(id);
502 for child in children {
503 self.kill_tree_recursive(child, destroyed);
504 }
505
506 if self.destroy_actor(id).is_ok() {
508 destroyed.push(id);
509 }
510 }
511
512 pub fn handle_failure(
516 &mut self,
517 failed_id: ActorId,
518 config: &ActorConfig,
519 ) -> Vec<SupervisionAction> {
520 let mut actions = Vec::new();
521
522 let (parent_id, policy) = {
523 let entry = match self.get(failed_id) {
524 Some(e) => e,
525 None => return actions,
526 };
527 let parent = if entry.parent_id > 0 {
528 Some(ActorId(entry.parent_id))
529 } else {
530 None
531 };
532 (parent, config.restart_policy)
533 };
534
535 if let Some(entry) = self.entries.get_mut(failed_id.0 as usize) {
537 entry.state = ActorState::Failed as u32;
538 }
539 actions.push(SupervisionAction::MarkedFailed(failed_id));
540
541 match policy {
542 RestartPolicy::Permanent => {
543 actions.push(SupervisionAction::Escalated {
545 failed: failed_id,
546 escalated_to: parent_id,
547 });
548 }
549
550 RestartPolicy::OneForOne { .. } => {
551 match self.restart_actor(failed_id, config) {
553 Ok(new_id) => {
554 actions.push(SupervisionAction::Restarted {
555 old_id: failed_id,
556 new_id,
557 });
558 }
559 Err(ActorError::MaxRestartsExceeded { .. }) => {
560 actions.push(SupervisionAction::Escalated {
562 failed: failed_id,
563 escalated_to: parent_id,
564 });
565 }
566 Err(_) => {
567 actions.push(SupervisionAction::Escalated {
568 failed: failed_id,
569 escalated_to: parent_id,
570 });
571 }
572 }
573 }
574
575 RestartPolicy::OneForAll { .. } => {
576 if let Some(parent) = parent_id {
578 let siblings = self.children_of(parent);
579 for sibling in siblings {
580 let _ = self.destroy_actor(sibling);
581 actions.push(SupervisionAction::DestroyedSibling(sibling));
582 }
583 actions.push(SupervisionAction::AllSiblingsDestroyed { parent });
586 }
587 }
588
589 RestartPolicy::RestForOne { .. } => {
590 if let Some(parent) = parent_id {
592 let siblings = self.children_of(parent);
593 let mut found = false;
594 for sibling in siblings {
595 if sibling == failed_id {
596 found = true;
597 }
598 if found {
599 let _ = self.destroy_actor(sibling);
600 actions.push(SupervisionAction::DestroyedSibling(sibling));
601 }
602 }
603 }
604 }
605 }
606
607 actions
608 }
609
610 pub fn depth(&self, id: ActorId) -> u32 {
612 let mut depth = 0;
613 let mut current = id;
614 while let Some(entry) = self.get(current) {
615 if entry.parent_id == 0 {
616 break;
617 }
618 current = ActorId(entry.parent_id);
619 depth += 1;
620 if depth > 100 {
621 break; }
623 }
624 depth
625 }
626
627 pub fn tree_view(&self) -> String {
629 let mut out = String::new();
630 out.push_str("Supervision Tree:\n");
631
632 let roots: Vec<ActorId> = self
634 .entries
635 .iter()
636 .filter(|e| e.parent_id == 0 && e.actor_state().is_alive())
637 .map(|e| ActorId(e.actor_id))
638 .collect();
639
640 for root in roots {
641 self.tree_view_recursive(root, &mut out, 0);
642 }
643
644 out
645 }
646
647 fn tree_view_recursive(&self, id: ActorId, out: &mut String, indent: usize) {
648 if let Some(entry) = self.get(id) {
649 let prefix = " ".repeat(indent);
650 let state = match entry.actor_state() {
651 ActorState::Active => "ACTIVE",
652 ActorState::Dormant => "DORMANT",
653 ActorState::Initializing => "INIT",
654 ActorState::Failed => "FAILED",
655 ActorState::Terminated => "TERM",
656 ActorState::Draining => "DRAIN",
657 };
658 out.push_str(&format!(
659 "{}[{}] actor:{} state={} restarts={} msgs={}\n",
660 prefix,
661 if indent == 0 { "R" } else { "C" },
662 id.0,
663 state,
664 entry.restart_count,
665 entry.last_heartbeat_ns
666 ));
667
668 let children = self.children_of(id);
669 for child in children {
670 self.tree_view_recursive(child, out, indent + 1);
671 }
672 }
673 }
674}
675
676#[derive(Debug, Clone)]
678pub enum SupervisionAction {
679 MarkedFailed(ActorId),
681 Restarted {
683 old_id: ActorId,
685 new_id: ActorId,
687 },
688 DestroyedSibling(ActorId),
690 AllSiblingsDestroyed {
692 parent: ActorId,
694 },
695 Escalated {
697 failed: ActorId,
699 escalated_to: Option<ActorId>,
701 },
702}
703
704#[derive(Debug, Clone)]
706pub enum ActorError {
707 PoolExhausted {
709 capacity: u32,
711 active: u32,
713 },
714 InvalidId(ActorId),
716 InvalidStateTransition {
718 actor: ActorId,
720 from: ActorState,
722 to: ActorState,
724 },
725 MaxRestartsExceeded {
727 actor: ActorId,
729 restarts: u32,
731 max: u32,
733 },
734}
735
736impl fmt::Display for ActorError {
737 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
738 match self {
739 Self::PoolExhausted { capacity, active } => write!(
740 f,
741 "Actor pool exhausted: {}/{} slots active",
742 active, capacity
743 ),
744 Self::InvalidId(id) => write!(f, "Invalid actor ID: {}", id),
745 Self::InvalidStateTransition { actor, from, to } => write!(
746 f,
747 "Invalid state transition for {}: {:?} → {:?}",
748 actor, from, to
749 ),
750 Self::MaxRestartsExceeded {
751 actor,
752 restarts,
753 max,
754 } => write!(
755 f,
756 "Actor {} exceeded max restarts: {}/{}",
757 actor, restarts, max
758 ),
759 }
760 }
761}
762
763impl std::error::Error for ActorError {}
764
765#[cfg(test)]
766mod tests {
767 use super::*;
768
769 #[test]
770 fn test_actor_lifecycle_create_destroy() {
771 let mut supervisor = ActorSupervisor::new(8); assert_eq!(supervisor.capacity(), 7);
774 assert_eq!(supervisor.active_count(), 0);
775 assert_eq!(supervisor.available_count(), 7);
776
777 let config = ActorConfig::named("test-actor");
779 let id = supervisor.create_actor(&config, None).unwrap();
780 assert_eq!(supervisor.active_count(), 1);
781 assert_eq!(supervisor.available_count(), 6);
782
783 supervisor.activate_actor(id).unwrap();
785 let entry = supervisor.get(id).unwrap();
786 assert_eq!(entry.actor_state(), ActorState::Active);
787
788 supervisor.destroy_actor(id).unwrap();
790 assert_eq!(supervisor.active_count(), 0);
791 assert_eq!(supervisor.available_count(), 7);
792 }
793
794 #[test]
795 fn test_actor_parent_child() {
796 let mut supervisor = ActorSupervisor::new(8);
797
798 let parent_config = ActorConfig::named("parent");
799 let parent = supervisor.create_actor(&parent_config, None).unwrap();
800 supervisor.activate_actor(parent).unwrap();
801
802 let child_config = ActorConfig::named("child");
804 let child1 = supervisor
805 .create_actor(&child_config, Some(parent))
806 .unwrap();
807 let child2 = supervisor
808 .create_actor(&child_config, Some(parent))
809 .unwrap();
810 supervisor.activate_actor(child1).unwrap();
811 supervisor.activate_actor(child2).unwrap();
812
813 let children = supervisor.children_of(parent);
814 assert_eq!(children.len(), 2);
815 assert!(children.contains(&child1));
816 assert!(children.contains(&child2));
817 }
818
819 #[test]
820 fn test_actor_restart() {
821 let mut supervisor = ActorSupervisor::new(8);
822 let config =
823 ActorConfig::named("restartable").with_restart_policy(RestartPolicy::OneForOne {
824 max_restarts: 3,
825 window: Duration::from_secs(60),
826 });
827
828 let id = supervisor.create_actor(&config, None).unwrap();
829 supervisor.activate_actor(id).unwrap();
830
831 for i in 0..3 {
833 let new_id = supervisor.restart_actor(id, &config).unwrap();
834 supervisor.activate_actor(new_id).unwrap();
835 let entry = supervisor.get(new_id).unwrap();
836 assert_eq!(entry.restart_count, i + 1);
837 }
838 }
839
840 #[test]
841 fn test_actor_max_restarts_exceeded() {
842 let mut supervisor = ActorSupervisor::new(8);
843 let config = ActorConfig::named("fragile").with_restart_policy(RestartPolicy::OneForOne {
844 max_restarts: 1,
845 window: Duration::from_secs(60),
846 });
847
848 let id = supervisor.create_actor(&config, None).unwrap();
849 supervisor.activate_actor(id).unwrap();
850
851 let new_id = supervisor.restart_actor(id, &config).unwrap();
853 supervisor.activate_actor(new_id).unwrap();
854
855 let result = supervisor.restart_actor(new_id, &config);
857 assert!(matches!(
858 result,
859 Err(ActorError::MaxRestartsExceeded { .. })
860 ));
861 }
862
863 #[test]
864 fn test_pool_exhaustion() {
865 let mut supervisor = ActorSupervisor::new(4); let config = ActorConfig::named("actor");
867
868 for _ in 0..3 {
870 let id = supervisor.create_actor(&config, None).unwrap();
871 supervisor.activate_actor(id).unwrap();
872 }
873
874 let result = supervisor.create_actor(&config, None);
876 assert!(matches!(result, Err(ActorError::PoolExhausted { .. })));
877 }
878
879 #[test]
880 fn test_heartbeat_failure_detection() {
881 let mut supervisor = ActorSupervisor::new(8);
882 let config = ActorConfig::named("monitored");
883
884 let id1 = supervisor.create_actor(&config, None).unwrap();
885 let id2 = supervisor.create_actor(&config, None).unwrap();
886 supervisor.activate_actor(id1).unwrap();
887 supervisor.activate_actor(id2).unwrap();
888
889 supervisor.heartbeat(id1, 1_000_000); supervisor.heartbeat(id2, 1_000_000); let timeout_ns = 500_000_000; supervisor.heartbeat(id1, 400_000_000);
898
899 let failed = supervisor.check_heartbeats(600_000_000, timeout_ns);
901 assert_eq!(failed.len(), 1);
902 assert_eq!(failed[0], id2);
903 }
904
905 #[test]
906 fn test_supervision_entry_size() {
907 assert_eq!(
908 std::mem::size_of::<SupervisionEntry>(),
909 64,
910 "SupervisionEntry must be exactly 64 bytes for GPU cache efficiency"
911 );
912 }
913
914 #[test]
915 fn test_actor_state_roundtrip() {
916 for state in [
917 ActorState::Dormant,
918 ActorState::Initializing,
919 ActorState::Active,
920 ActorState::Draining,
921 ActorState::Terminated,
922 ActorState::Failed,
923 ] {
924 let raw = state as u32;
925 let recovered = ActorState::from_u32(raw).unwrap();
926 assert_eq!(recovered, state);
927 }
928 }
929
930 #[test]
933 fn test_cascading_kill_tree() {
934 let mut sup = ActorSupervisor::new(16);
935 let config = ActorConfig::named("node");
936
937 let root = sup.create_actor(&config, None).unwrap();
939 sup.activate_actor(root).unwrap();
940
941 let child1 = sup.create_actor(&config, Some(root)).unwrap();
942 sup.activate_actor(child1).unwrap();
943
944 let child2 = sup.create_actor(&config, Some(root)).unwrap();
945 sup.activate_actor(child2).unwrap();
946
947 let grandchild1 = sup.create_actor(&config, Some(child1)).unwrap();
948 sup.activate_actor(grandchild1).unwrap();
949
950 assert_eq!(sup.active_count(), 4);
951
952 let destroyed = sup.kill_tree(root);
954 assert_eq!(destroyed.len(), 4);
955 assert_eq!(sup.active_count(), 0);
956 assert_eq!(sup.available_count(), 15); }
958
959 #[test]
960 fn test_handle_failure_one_for_one() {
961 let mut sup = ActorSupervisor::new(8);
962 let config = ActorConfig::named("worker").with_restart_policy(RestartPolicy::OneForOne {
963 max_restarts: 2,
964 window: Duration::from_secs(60),
965 });
966
967 let parent = sup
968 .create_actor(&ActorConfig::named("parent"), None)
969 .unwrap();
970 sup.activate_actor(parent).unwrap();
971
972 let child = sup.create_actor(&config, Some(parent)).unwrap();
973 sup.activate_actor(child).unwrap();
974
975 let actions = sup.handle_failure(child, &config);
977 assert!(actions
978 .iter()
979 .any(|a| matches!(a, SupervisionAction::Restarted { .. })));
980 }
981
982 #[test]
983 fn test_handle_failure_escalation() {
984 let mut sup = ActorSupervisor::new(8);
985 let config = ActorConfig::named("fragile").with_restart_policy(RestartPolicy::Permanent);
986
987 let parent = sup
988 .create_actor(&ActorConfig::named("parent"), None)
989 .unwrap();
990 sup.activate_actor(parent).unwrap();
991
992 let child = sup.create_actor(&config, Some(parent)).unwrap();
993 sup.activate_actor(child).unwrap();
994
995 let actions = sup.handle_failure(child, &config);
997 assert!(actions
998 .iter()
999 .any(|a| matches!(a, SupervisionAction::Escalated { .. })));
1000 }
1001
1002 #[test]
1003 fn test_tree_depth() {
1004 let mut sup = ActorSupervisor::new(16);
1005 let config = ActorConfig::named("node");
1006
1007 let root = sup.create_actor(&config, None).unwrap();
1008 sup.activate_actor(root).unwrap();
1009 assert_eq!(sup.depth(root), 0);
1010
1011 let child = sup.create_actor(&config, Some(root)).unwrap();
1012 sup.activate_actor(child).unwrap();
1013 assert_eq!(sup.depth(child), 1);
1014
1015 let grandchild = sup.create_actor(&config, Some(child)).unwrap();
1016 sup.activate_actor(grandchild).unwrap();
1017 assert_eq!(sup.depth(grandchild), 2);
1018 }
1019
1020 #[test]
1021 fn test_tree_view() {
1022 let mut sup = ActorSupervisor::new(8);
1023 let config = ActorConfig::named("actor");
1024
1025 let root = sup.create_actor(&config, None).unwrap();
1026 sup.activate_actor(root).unwrap();
1027
1028 let child1 = sup.create_actor(&config, Some(root)).unwrap();
1029 sup.activate_actor(child1).unwrap();
1030
1031 let child2 = sup.create_actor(&config, Some(root)).unwrap();
1032 sup.activate_actor(child2).unwrap();
1033
1034 let view = sup.tree_view();
1035 assert!(view.contains("ACTIVE"));
1036 assert!(view.contains("actor:"));
1037 }
1038}