1use super::supervision::{supervise, SupervisionParams};
8use super::types::{DepSetupReceivers, PrerequisiteFuture, SupervisionResult, TaskEntry};
9use super::validation::validate_dependencies;
10use crate::contracts::{SupervisedTask, SupervisorEventListener};
11use crate::enums::{ControlMessage, HealthStatus, SupervisorEvent, TaskConfig};
12use crate::error::SupervisorError;
13use crate::persistence::TaskStateStore;
14use std::collections::{HashMap, HashSet};
15use std::fmt;
16use std::future::Future;
17use std::sync::Arc;
18use std::time::Duration;
19use tokio::sync::{broadcast, watch, RwLock, Semaphore};
20use tokio::task::JoinHandle;
21use tracing::{error, info, warn};
22
23#[cfg(feature = "cron")]
24use foxtive_cron::Cron;
25
26pub struct TaskRuntime {
39 pub(super) tasks: HashMap<&'static str, TaskEntry>,
40 pub(super) handles: HashMap<&'static str, JoinHandle<SupervisionResult>>,
41 pub(super) prerequisites: Vec<(&'static str, PrerequisiteFuture)>,
43 pub(super) setup_signals: HashMap<&'static str, watch::Sender<Option<Result<(), String>>>>,
45 pub(super) event_tx: broadcast::Sender<SupervisorEvent>,
47 pub(super) listeners: Vec<Arc<dyn SupervisorEventListener>>,
49 pub(super) state_store: Option<Arc<dyn TaskStateStore>>,
51 pub(super) global_concurrency_limit: Option<Arc<Semaphore>>,
53 pub(crate) task_concurrency_limits: HashMap<&'static str, Arc<Semaphore>>,
55 pub(super) task_configs: HashMap<&'static str, Arc<RwLock<TaskConfig>>>,
57 #[cfg(feature = "cron")]
58 #[allow(dead_code)]
59 pub(super) cron: Option<Arc<tokio::sync::Mutex<Cron>>>,
60}
61
62impl fmt::Debug for TaskRuntime {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("TaskRuntime")
65 .field("tasks_count", &self.tasks.len())
66 .field("handles_count", &self.handles.len())
67 .field("prerequisites_count", &self.prerequisites.len())
68 .finish()
69 }
70}
71
72impl TaskRuntime {
73 pub fn new() -> Self {
75 let (event_tx, _) = broadcast::channel(1024);
76 #[allow(unused_mut)]
77 let mut runtime = Self {
78 tasks: HashMap::new(),
79 handles: HashMap::new(),
80 prerequisites: Vec::new(),
81 setup_signals: HashMap::new(),
82 event_tx,
83 listeners: Vec::new(),
84 state_store: None,
85 global_concurrency_limit: None,
86 task_concurrency_limits: HashMap::new(),
87 task_configs: HashMap::new(),
88 #[cfg(feature = "cron")]
89 cron: None,
90 };
91 runtime
92 }
93
94 pub fn with_global_concurrency_limit(&mut self, limit: usize) -> &mut Self {
101 self.global_concurrency_limit = Some(Arc::new(Semaphore::new(limit)));
102 self
103 }
104
105 pub fn register<T: SupervisedTask + 'static>(&mut self, task: T) -> &mut Self {
112 let (setup_tx, _) = watch::channel(None);
113 let (control_tx, _) = broadcast::channel(10);
114 let id = task.id();
115
116 if let Some(limit) = task.concurrency_limit() {
118 self.task_concurrency_limits.insert(id, Arc::new(Semaphore::new(limit)));
119 }
120
121 let config = TaskConfig::from_task(&task);
123 self.task_configs.insert(id, Arc::new(RwLock::new(config)));
124
125 self.setup_signals.insert(id, setup_tx.clone());
126 self.tasks.insert(
127 id,
128 TaskEntry {
129 task: Arc::new(task),
130 setup_tx,
131 control_tx,
132 },
133 );
134 self
136 }
137
138 pub fn register_many<T: SupervisedTask + 'static>(&mut self, tasks: Vec<T>) -> &mut Self {
140 for task in tasks {
141 self.register(task);
142 }
143 self
144 }
145
146 pub fn register_boxed(&mut self, task: Box<dyn SupervisedTask>) -> &mut Self {
150 let (setup_tx, _) = watch::channel(None);
151 let (control_tx, _) = broadcast::channel(10);
152 let id = task.id();
153
154 if let Some(limit) = task.concurrency_limit() {
155 self.task_concurrency_limits.insert(id, Arc::new(Semaphore::new(limit)));
156 }
157
158 let config = TaskConfig::from_task(task.as_ref());
160 self.task_configs.insert(id, Arc::new(RwLock::new(config)));
161
162 self.setup_signals.insert(id, setup_tx.clone());
163 self.tasks.insert(
164 id,
165 TaskEntry {
166 task: Arc::from(task),
167 setup_tx,
168 control_tx,
169 },
170 );
171 self
173 }
174
175 pub fn register_arc(&mut self, task: Arc<dyn SupervisedTask>) -> &mut Self {
179 let (setup_tx, _) = watch::channel(None);
180 let (control_tx, _) = broadcast::channel(10);
181 let id = task.id();
182
183 if let Some(limit) = task.concurrency_limit() {
184 self.task_concurrency_limits.insert(id, Arc::new(Semaphore::new(limit)));
185 }
186
187 let config = TaskConfig::from_task(task.as_ref());
189 self.task_configs.insert(id, Arc::new(RwLock::new(config)));
190
191 self.setup_signals.insert(id, setup_tx.clone());
192 self.tasks.insert(
193 id,
194 TaskEntry {
195 task,
196 setup_tx,
197 control_tx,
198 },
199 );
200 self
202 }
203
204 pub fn add_task<T: SupervisedTask + 'static>(&mut self, task: T) -> Result<(), SupervisorError> {
214 let id = task.id();
215 if self.tasks.contains_key(id) {
216 return Err(SupervisorError::InternalError(format!("Task {} already exists", id)));
217 }
218
219 self.register(task);
220
221 let tasks_vec: Vec<&TaskEntry> = self.tasks.values().collect();
223 validate_dependencies(&tasks_vec)?;
224
225 let entry = self.tasks.get(id).unwrap();
226
227 let mut dep_receivers = Vec::new();
229 let active_deps = entry.task.active_dependencies();
230 for dep_id in &active_deps {
231 if let Some(sender) = self.setup_signals.get(dep_id) {
232 dep_receivers.push((*dep_id, sender.subscribe()));
233 } else {
234 return Err(SupervisorError::dependency_validation(
235 id,
236 dep_id,
237 crate::error::ValidationError::UnknownTaskId,
238 ));
239 }
240 }
241
242 let task_limit = self.task_concurrency_limits.get(id).cloned();
243 let task_config = self.task_configs.get(id).cloned();
244
245 let params = SupervisionParams {
246 task: entry.task.clone(),
247 setup_tx: entry.setup_tx.clone(),
248 control_rx: entry.control_tx.subscribe(),
249 event_tx: self.event_tx.clone(),
250 dep_receivers,
251 state_store: self.state_store.clone(),
252 global_semaphore: self.global_concurrency_limit.clone(),
253 task_semaphore: task_limit,
254 task_config,
255 };
256
257 let handle = supervise(params);
258 self.handles.insert(id, handle);
259
260 let name = entry.task.name();
262 let _ = self.event_tx.send(SupervisorEvent::TaskRegistered { id: id.to_string(), name });
263
264 Ok(())
265 }
266
267 pub fn restart_task(&self, id: &str) -> Result<(), SupervisorError> {
274 if let Some(entry) = self.tasks.get(id) {
275 let _ = entry.control_tx.send(ControlMessage::Restart);
276 Ok(())
277 } else {
278 Err(SupervisorError::UnknownTask(id.to_string()))
279 }
280 }
281
282 pub fn pause_task(&self, id: &str) -> Result<(), SupervisorError> {
290 if let Some(entry) = self.tasks.get(id) {
291 let _ = entry.control_tx.send(ControlMessage::Pause);
292 Ok(())
293 } else {
294 Err(SupervisorError::UnknownTask(id.to_string()))
295 }
296 }
297
298 pub fn resume_task(&self, id: &str) -> Result<(), SupervisorError> {
306 if let Some(entry) = self.tasks.get(id) {
307 let _ = entry.control_tx.send(ControlMessage::Resume);
308 Ok(())
309 } else {
310 Err(SupervisorError::UnknownTask(id.to_string()))
311 }
312 }
313
314 pub fn reset_circuit_breaker(&self, id: &str) -> Result<(), SupervisorError> {
319 if let Some(entry) = self.tasks.get(id) {
320 let _ = entry.control_tx.send(ControlMessage::ResetCircuitBreaker);
321 Ok(())
322 } else {
323 Err(SupervisorError::UnknownTask(id.to_string()))
324 }
325 }
326
327 pub async fn update_restart_policy(
341 &self,
342 id: &str,
343 new_policy: crate::enums::RestartPolicy,
344 ) -> Result<(), SupervisorError> {
345 let config_lock = self.task_configs.get(id)
346 .ok_or_else(|| SupervisorError::UnknownTask(id.to_string()))?;
347
348 Self::validate_restart_policy(&new_policy)?;
350
351 let mut config = config_lock.write().await;
352 let old_policy = config.restart_policy.clone();
353 config.restart_policy = new_policy.clone();
354
355 if let Some(entry) = self.tasks.get(id) {
357 let name = entry.task.name();
358 let _ = self.event_tx.send(SupervisorEvent::TaskConfigUpdated {
359 id: id.to_string(),
360 name,
361 field: "restart_policy".to_string(),
362 old_value: format!("{:?}", old_policy),
363 new_value: format!("{:?}", new_policy),
364 });
365 }
366
367 info!(task_id = %id, ?old_policy, ?new_policy, "Updated restart policy");
368 Ok(())
369 }
370
371 pub async fn update_backoff_strategy(
383 &self,
384 id: &str,
385 new_strategy: crate::enums::BackoffStrategy,
386 ) -> Result<(), SupervisorError> {
387 let config_lock = self.task_configs.get(id)
388 .ok_or_else(|| SupervisorError::UnknownTask(id.to_string()))?;
389
390 Self::validate_backoff_strategy(&new_strategy)?;
392
393 let mut config = config_lock.write().await;
394 let old_strategy = config.backoff_strategy.clone();
395 config.backoff_strategy = new_strategy.clone();
396
397 if let Some(entry) = self.tasks.get(id) {
399 let name = entry.task.name();
400 let _ = self.event_tx.send(SupervisorEvent::TaskConfigUpdated {
401 id: id.to_string(),
402 name,
403 field: "backoff_strategy".to_string(),
404 old_value: format!("{:?}", old_strategy),
405 new_value: format!("{:?}", new_strategy),
406 });
407 }
408
409 info!(task_id = %id, ?old_strategy, ?new_strategy, "Updated backoff strategy");
410 Ok(())
411 }
412
413 pub async fn set_task_enabled(
425 &self,
426 id: &str,
427 enabled: bool,
428 ) -> Result<(), SupervisorError> {
429 let config_lock = self.task_configs.get(id)
430 .ok_or_else(|| SupervisorError::UnknownTask(id.to_string()))?;
431
432 let mut config = config_lock.write().await;
433 let old_enabled = config.enabled;
434 config.enabled = enabled;
435
436 if let Some(entry) = self.tasks.get(id) {
438 let name = entry.task.name();
439 let _ = self.event_tx.send(SupervisorEvent::TaskConfigUpdated {
440 id: id.to_string(),
441 name,
442 field: "enabled".to_string(),
443 old_value: old_enabled.to_string(),
444 new_value: enabled.to_string(),
445 });
446 }
447
448 info!(task_id = %id, old_enabled = old_enabled, new_enabled = enabled, "Updated task enabled status");
449 Ok(())
450 }
451
452 pub async fn get_task_config(&self, id: &str) -> Option<TaskConfig> {
460 let config_lock = self.task_configs.get(id)?;
461 let config = config_lock.read().await;
462 Some(config.clone())
463 }
464
465 pub async fn is_task_enabled(&self, id: &str) -> bool {
473 if let Some(config_lock) = self.task_configs.get(id) {
474 let config = config_lock.read().await;
475 config.enabled
476 } else {
477 false
478 }
479 }
480
481 pub fn start_group(&mut self, group_id: &str) -> usize {
494 let mut started_count = 0;
495
496 let group_task_ids: Vec<&'static str> = self.tasks.iter()
498 .filter(|(_, entry)| entry.task.group_id() == Some(group_id))
499 .map(|(id, _)| *id)
500 .collect();
501
502 for task_id in group_task_ids {
503 if !self.handles.contains_key(task_id) {
504 if let Err(e) = self.add_task_by_id(task_id) {
506 error!(task_id = %task_id, error = ?e, "Failed to start task in group");
507 } else {
508 started_count += 1;
509 }
510 }
511 }
512
513 info!(group_id = %group_id, started = started_count, "Started task group");
514 started_count
515 }
516
517 pub async fn stop_group(&mut self, group_id: &str) -> usize {
527 let mut stopped_count = 0;
528
529 let group_task_ids: Vec<&'static str> = self.tasks.iter()
531 .filter(|(_, entry)| entry.task.group_id() == Some(group_id))
532 .map(|(id, _)| *id)
533 .collect();
534
535 for task_id in group_task_ids {
536 if let Some(handle) = self.handles.remove(task_id) {
537 if let Some(entry) = self.tasks.get(task_id) {
538 let _ = entry.control_tx.send(ControlMessage::Stop);
539 }
540
541 match tokio::time::timeout(Duration::from_secs(5), handle).await {
543 Ok(_) => stopped_count += 1,
544 Err(_) => {
545 warn!(task_id = %task_id, "Task in group did not stop gracefully, aborting");
546 stopped_count += 1; }
548 }
549 }
550 }
551
552 info!(group_id = %group_id, stopped = stopped_count, "Stopped task group");
553 stopped_count
554 }
555
556 pub fn restart_group(&self, group_id: &str) -> usize {
566 let mut restarted_count = 0;
567
568 for (task_id, entry) in &self.tasks {
569 if entry.task.group_id() == Some(group_id) && self.handles.contains_key(task_id) {
570 let _ = entry.control_tx.send(ControlMessage::Restart);
571 restarted_count += 1;
572 }
573 }
574
575 info!(group_id = %group_id, restarted = restarted_count, "Restarted task group");
576 restarted_count
577 }
578
579 pub fn list_group_tasks(&self, group_id: &str) -> Vec<String> {
587 self.tasks.iter()
588 .filter(|(_, entry)| entry.task.group_id() == Some(group_id))
589 .map(|(id, _)| id.to_string())
590 .collect()
591 }
592
593 pub async fn get_group_health(&self, group_id: &str) -> HealthStatus {
607 let mut has_healthy = false;
608 let mut has_degraded = false;
609 let mut has_unhealthy = false;
610 let mut task_count = 0;
611
612 for entry in self.tasks.values() {
613 if entry.task.group_id() == Some(group_id) {
614 task_count += 1;
615 let health = entry.task.health_check().await;
616 match health {
617 HealthStatus::Healthy => has_healthy = true,
618 HealthStatus::Degraded { .. } => has_degraded = true,
619 HealthStatus::Unhealthy { .. } => has_unhealthy = true,
620 HealthStatus::Unknown => {}, }
622 }
623 }
624
625 if task_count == 0 {
626 return HealthStatus::Unknown;
627 }
628
629 if has_unhealthy {
631 HealthStatus::Unhealthy { reason: "One or more tasks in group are unhealthy".to_string() }
632 } else if has_degraded {
633 HealthStatus::Degraded { reason: "One or more tasks in group are degraded".to_string() }
634 } else if has_healthy {
635 HealthStatus::Healthy
636 } else {
637 HealthStatus::Unknown
638 }
639 }
640
641 pub async fn get_group_health_details(&self, group_id: &str) -> Vec<TaskSummary> {
649 let mut summaries = Vec::new();
650
651 for (task_id, entry) in &self.tasks {
652 if entry.task.group_id() == Some(group_id) {
653 summaries.push(TaskSummary {
654 id: task_id.to_string(),
655 name: entry.task.name(),
656 health: entry.task.health_check().await,
657 });
658 }
659 }
660
661 summaries
662 }
663
664 fn add_task_by_id(&mut self, task_id: &'static str) -> Result<(), SupervisorError> {
666 let entry = self.tasks.get(task_id).unwrap();
667
668 let mut dep_receivers = Vec::new();
670 let active_deps = entry.task.active_dependencies();
671 for dep_id in &active_deps {
672 if let Some(sender) = self.setup_signals.get(dep_id) {
673 dep_receivers.push((*dep_id, sender.subscribe()));
674 } else {
675 return Err(SupervisorError::dependency_validation(
676 task_id,
677 dep_id,
678 crate::error::ValidationError::UnknownTaskId,
679 ));
680 }
681 }
682
683 let task_limit = self.task_concurrency_limits.get(task_id).cloned();
684 let task_config = self.task_configs.get(task_id).cloned();
685
686 let params = SupervisionParams {
687 task: entry.task.clone(),
688 setup_tx: entry.setup_tx.clone(),
689 control_rx: entry.control_tx.subscribe(),
690 event_tx: self.event_tx.clone(),
691 dep_receivers,
692 state_store: self.state_store.clone(),
693 global_semaphore: self.global_concurrency_limit.clone(),
694 task_semaphore: task_limit,
695 task_config,
696 };
697
698 let handle = supervise(params);
699 self.handles.insert(task_id, handle);
700
701 let name = entry.task.name();
702 let _ = self.event_tx.send(SupervisorEvent::TaskRegistered { id: task_id.to_string(), name });
703
704 Ok(())
705 }
706
707 pub async fn remove_task(&mut self, id: &str) -> Result<Option<SupervisionResult>, SupervisorError> {
716 if let Some(entry) = self.tasks.remove(id) {
717 let _ = entry.control_tx.send(ControlMessage::Stop);
718 let name = entry.task.name();
719 self.setup_signals.remove(id);
720 let _ = self.event_tx.send(SupervisorEvent::TaskRemoved { id: id.to_string(), name });
721 if let Some(handle) = self.handles.remove(id) {
722 match handle.await {
723 Ok(res) => Ok(Some(res)),
724 Err(_) => Err(SupervisorError::InternalError(format!("Task {} panicked during removal", id))),
725 }
726 } else {
727 Ok(None)
728 }
729 } else {
730 Err(SupervisorError::UnknownTask(id.to_string()))
731 }
732 }
733
734 pub async fn get_task_info(&self, id: &str) -> Result<TaskSummary, SupervisorError> {
741 if let Some(entry) = self.tasks.get(id) {
742 let health = entry.task.health_check().await;
743 Ok(TaskSummary {
744 id: id.to_string(),
745 name: entry.task.name(),
746 health,
747 })
748 } else {
749 Err(SupervisorError::UnknownTask(id.to_string()))
750 }
751 }
752
753 pub async fn list_tasks(&self) -> Vec<TaskSummary> {
755 let mut summaries = Vec::new();
756 for (id, entry) in &self.tasks {
757 summaries.push(TaskSummary {
758 id: id.to_string(),
759 name: entry.task.name(),
760 health: entry.task.health_check().await,
761 });
762 }
763 summaries
764 }
765
766 pub fn with_state_store(&mut self, store: Arc<dyn TaskStateStore>) -> &mut Self {
772 self.state_store = Some(store);
773 self
774 }
775
776 pub fn subscribe(&self) -> broadcast::Receiver<SupervisorEvent> {
783 self.event_tx.subscribe()
784 }
785
786 pub fn add_listener(&mut self, listener: Arc<dyn SupervisorEventListener>) -> &mut Self {
790 self.listeners.push(listener);
791 self
792 }
793
794 pub fn add_prerequisite(&mut self, name: &'static str, fut: PrerequisiteFuture) -> &mut Self {
800 self.prerequisites.push((name, fut));
801 self
802 }
803
804 pub fn add_prerequisite_fn<F, Fut>(&mut self, name: &'static str, f: F) -> &mut Self
808 where
809 F: FnOnce() -> Fut + Send + 'static,
810 Fut: Future<Output = Result<(), anyhow::Error>> + Send + 'static,
811 {
812 self.add_prerequisite(name, Box::pin(async move { f().await }))
813 }
814
815 pub async fn start_all(&mut self) -> Result<(), SupervisorError> {
825 let mut event_rx = self.event_tx.subscribe();
827 let listeners = self.listeners.clone();
828 tokio::spawn(async move {
829 loop {
830 match event_rx.recv().await {
831 Ok(event) => {
832 for listener in &listeners {
833 listener.on_event(event.clone()).await;
834 }
835 }
836 Err(broadcast::error::RecvError::Lagged(n)) => {
837 warn!(missed_events = n, "Event channel lagged, skipping missed events to prevent listener backlog");
838 continue;
840 }
841 Err(broadcast::error::RecvError::Closed) => {
842 info!("Event channel closed, stopping event distribution");
843 break;
844 }
845 }
846 }
847 });
848
849 if !self.prerequisites.is_empty() {
851 info!("[Supervisor] Running {} prerequisites in parallel...", self.prerequisites.len());
852
853 let prereq_futures: Vec<_> = self.prerequisites.drain(..)
854 .map(|(name, fut)| async move {
855 info!("[Supervisor] Awaiting prerequisite '{name}'...");
856 fut.await.map_err(|e| (name, e))
857 })
858 .collect();
859
860 let results = futures_util::future::try_join_all(prereq_futures).await
861 .map_err(|(name, e)| SupervisorError::prerequisite_failed(name, e))?;
862
863 info!("[Supervisor] All {} prerequisites satisfied", results.len());
864 }
865
866 if self.tasks.is_empty() {
867 warn!("[Supervisor] No tasks registered");
868 return Ok(());
869 }
870
871 let tasks_vec: Vec<&TaskEntry> = self.tasks.values().collect();
873 validate_dependencies(&tasks_vec)?;
874
875 info!(
876 "[Supervisor] Starting {} supervised tasks...",
877 self.tasks.len()
878 );
879
880 let mut sorted_ids: Vec<&'static str> = self.tasks.keys().copied().collect();
882 sorted_ids.sort_by(|a, b| {
883 let task_a = &self.tasks[a].task;
884 let task_b = &self.tasks[b].task;
885 task_b.priority().cmp(&task_a.priority())
886 });
887
888 for id in sorted_ids {
890 let entry = &self.tasks[id];
891 let name = entry.task.name();
893 let _ = self.event_tx.send(SupervisorEvent::TaskRegistered { id: id.to_string(), name });
894
895 let active_deps = entry.task.active_dependencies();
897 let dep_receivers: DepSetupReceivers = active_deps
898 .iter()
899 .map(|dep_id| {
900 let sender = self.setup_signals[dep_id].clone();
901 (*dep_id, sender.subscribe())
902 })
903 .collect();
904
905 let task_limit = self.task_concurrency_limits.get(id).cloned();
906 let task_config = self.task_configs.get(id).cloned();
907
908 let params = SupervisionParams {
909 task: entry.task.clone(),
910 setup_tx: entry.setup_tx.clone(),
911 control_rx: entry.control_tx.subscribe(),
912 event_tx: self.event_tx.clone(),
913 dep_receivers,
914 state_store: self.state_store.clone(),
915 global_semaphore: self.global_concurrency_limit.clone(),
916 task_semaphore: task_limit,
917 task_config,
918 };
919
920 let handle = supervise(params);
921 self.handles.insert(id, handle);
922 }
923
924 info!("[Supervisor] All tasks started");
925 Ok(())
926 }
927
928 pub fn start_one<T: SupervisedTask + 'static>(task: T) -> JoinHandle<SupervisionResult> {
933 let (setup_tx, _) = watch::channel(None);
934 let (control_tx, _) = broadcast::channel(10);
935 let (event_tx, _) = broadcast::channel(1);
936 let params = SupervisionParams {
937 task: Arc::new(task),
938 setup_tx,
939 control_rx: control_tx.subscribe(),
940 event_tx,
941 dep_receivers: vec![],
942 state_store: None,
943 global_semaphore: None,
944 task_semaphore: None,
945 task_config: None,
946 };
947 supervise(params)
948 }
949
950 pub async fn wait_any(&mut self) -> SupervisionResult {
956 if self.handles.is_empty() {
957 warn!("[Supervisor] No tasks to wait for");
958 return SupervisionResult {
959 task_name: "none".to_string(),
960 task_id: "none".to_string(),
961 total_attempts: 0,
962 final_status: crate::enums::SupervisionStatus::ManuallyStopped,
963 };
964 }
965
966 let mut handle_vec: Vec<(&'static str, JoinHandle<SupervisionResult>)> = self.handles.drain().collect();
968
969 use futures_util::future::select_all;
971
972 let mut futures = Vec::new();
973 for (id, handle) in handle_vec.iter_mut() {
974 let id_clone = *id;
975 futures.push(Box::pin(async move {
976 let result = handle.await;
977 (id_clone, result)
978 }));
979 }
980
981 let ((finished_id, result), _idx, _remaining) = select_all(futures).await;
982
983 for (i, (id, handle)) in handle_vec.into_iter().enumerate() {
985 if i != _idx {
986 self.handles.insert(id, handle);
987 }
988 }
989
990 match result {
991 Ok(supervision_result) => {
992 error!(
993 "[Supervisor] Task '{}' (id: {}) terminated: {:?}",
994 supervision_result.task_name,
995 supervision_result.task_id,
996 supervision_result.final_status
997 );
998 supervision_result
999 }
1000 Err(join_err) => {
1001 error!(
1002 "[Supervisor] Task {} panicked: {:?}",
1003 finished_id, join_err
1004 );
1005 SupervisionResult {
1006 task_name: "unknown".to_string(),
1007 task_id: finished_id.to_string(),
1008 total_attempts: 0,
1009 final_status: crate::enums::SupervisionStatus::ManuallyStopped,
1010 }
1011 }
1012 }
1013 }
1014
1015 pub async fn wait_all(&mut self) -> Vec<SupervisionResult> {
1019 let mut results = Vec::new();
1020 while !self.handles.is_empty() {
1021 results.push(self.wait_any().await);
1022 }
1023 results
1024 }
1025
1026 pub async fn shutdown(mut self) {
1033 info!("[Supervisor] Shutting down {} tasks...", self.tasks.len());
1034 let _ = self.event_tx.send(SupervisorEvent::SupervisorShutdownStarted);
1035
1036 let shutdown_order = self.calculate_shutdown_order();
1039
1040 for id in shutdown_order {
1041 if let Some(entry) = self.tasks.get(id) {
1042 let name = entry.task.name();
1043 let timeout = entry.task.shutdown_timeout();
1044 info!(task_id = %id, "Signalling task '{}' to stop...", name);
1045
1046 let _ = entry.control_tx.send(ControlMessage::Stop);
1047
1048 if let Some(handle) = self.handles.remove(id) {
1049 match tokio::time::timeout(timeout, handle).await {
1051 Ok(res) => {
1052 match res {
1053 Ok(_supervision_res) => {
1054 info!(task_id = %id, "Task '{}' supervision completed.", name);
1055 match tokio::time::timeout(timeout, entry.task.on_shutdown()).await {
1057 Ok(_) => info!(task_id = %id, "Task '{}' on_shutdown completed.", name),
1058 Err(_) => warn!(task_id = %id, "Task '{}' on_shutdown timed out after {:?}.", name, timeout),
1059 }
1060 }
1061 Err(e) => error!(task_id = %id, "Task '{}' panicked during shutdown: {:?}", name, e),
1062 }
1063 }
1064 Err(_) => {
1065 warn!(task_id = %id, "Task '{}' did not stop within timeout {:?}. Task will continue in background.", name, timeout);
1066 }
1069 }
1070 }
1071 }
1072 }
1073
1074 for (_id, handle) in self.handles.drain() {
1076 handle.abort();
1077 }
1078
1079 let _ = self.event_tx.send(SupervisorEvent::SupervisorShutdownCompleted);
1080 info!("[Supervisor] All tasks shut down");
1081 }
1082
1083 fn calculate_shutdown_order(&self) -> Vec<&'static str> {
1086 let mut order = Vec::new();
1087 let mut visited = HashSet::new();
1088
1089 let mut dependents: HashMap<&'static str, Vec<&'static str>> = HashMap::new();
1091 for (id, entry) in &self.tasks {
1092 let active_deps = entry.task.active_dependencies();
1093 for dep_id in &active_deps {
1094 dependents.entry(dep_id).or_default().push(*id);
1095 }
1096 }
1097
1098 fn visit(
1100 id: &'static str,
1101 dependents: &HashMap<&'static str, Vec<&'static str>>,
1102 visited: &mut HashSet<&'static str>,
1103 order: &mut Vec<&'static str>,
1104 ) {
1105 if visited.contains(id) {
1106 return;
1107 }
1108
1109 if let Some(deps) = dependents.get(id) {
1111 for dep in deps {
1112 visit(dep, dependents, visited, order);
1113 }
1114 }
1115
1116 visited.insert(id);
1117 order.push(id);
1118 }
1119
1120 for id in self.tasks.keys() {
1121 visit(id, &dependents, &mut visited, &mut order);
1122 }
1123
1124 order
1125 }
1126
1127 pub fn task_count(&self) -> usize {
1129 self.handles.len()
1130 }
1131
1132 fn validate_restart_policy(_policy: &crate::enums::RestartPolicy) -> Result<(), SupervisorError> {
1136 Ok(())
1141 }
1142
1143 fn validate_backoff_strategy(strategy: &crate::enums::BackoffStrategy) -> Result<(), SupervisorError> {
1145 match strategy {
1147 crate::enums::BackoffStrategy::Fixed(duration) => {
1148 if duration.as_secs() > 3600 {
1149 return Err(SupervisorError::InternalError(
1150 "Fixed backoff delay cannot exceed 1 hour".to_string()
1151 ));
1152 }
1153 }
1154 crate::enums::BackoffStrategy::Exponential { initial, max } => {
1155 if initial.as_secs() > 3600 || max.as_secs() > 3600 {
1156 return Err(SupervisorError::InternalError(
1157 "Exponential backoff delays cannot exceed 1 hour".to_string()
1158 ));
1159 }
1160 if initial > max {
1161 return Err(SupervisorError::InternalError(
1162 "Initial backoff cannot be greater than max backoff".to_string()
1163 ));
1164 }
1165 }
1166 crate::enums::BackoffStrategy::Linear { initial, increment: _, max } => {
1167 if initial.as_secs() > 3600 || max.as_secs() > 3600 {
1168 return Err(SupervisorError::InternalError(
1169 "Linear backoff delays cannot exceed 1 hour".to_string()
1170 ));
1171 }
1172 if initial > max {
1173 return Err(SupervisorError::InternalError(
1174 "Initial backoff cannot be greater than max backoff".to_string()
1175 ));
1176 }
1177 }
1178 crate::enums::BackoffStrategy::Fibonacci { initial, max } => {
1179 if initial.as_secs() > 3600 || max.as_secs() > 3600 {
1180 return Err(SupervisorError::InternalError(
1181 "Fibonacci backoff delays cannot exceed 1 hour".to_string()
1182 ));
1183 }
1184 if initial > max {
1185 return Err(SupervisorError::InternalError(
1186 "Initial backoff cannot be greater than max backoff".to_string()
1187 ));
1188 }
1189 }
1190 crate::enums::BackoffStrategy::Custom(_) => {
1191 }
1194 }
1195 Ok(())
1196 }
1197}
1198
1199#[derive(Debug, Clone)]
1201pub struct TaskSummary {
1202 pub id: String,
1203 pub name: String,
1204 pub health: HealthStatus,
1205}
1206
1207impl Default for TaskRuntime {
1208 fn default() -> Self {
1209 Self::new()
1210 }
1211}