Skip to main content

flare_core_runtime/task/
manager.rs

1//! 任务管理器实现
2//!
3//! 负责管理所有任务的生命周期,包括启动、停止、依赖管理等
4
5use super::{Task, TaskResult, TaskState};
6use crate::config::RuntimeConfig;
7use crate::error::RuntimeError;
8use crate::state::StateTracker;
9use crate::utils::topological_sort;
10use std::collections::HashMap;
11use std::sync::Arc;
12use std::time::Duration;
13use tokio::sync::oneshot;
14use tokio::task::JoinSet;
15use tracing::{debug, error, info, warn};
16
17/// 任务管理器
18///
19/// 负责管理所有任务的生命周期
20///
21/// # 功能
22///
23/// - 任务注册和管理
24/// - 依赖排序和启动
25/// - 优雅停机
26/// - 状态追踪
27///
28/// # 示例
29///
30/// ```rust,ignore
31/// use flare_core_runtime::task::TaskManager;
32/// use flare_core_runtime::task::SpawnTask;
33///
34/// let manager = TaskManager::new();
35///
36/// // 添加任务
37/// manager.add_task(Box::new(SpawnTask::new("task-1", async { Ok(()) })));
38///
39/// // 启动所有任务
40/// let (join_set, shutdown_txs) = manager.start_all().await?;
41///
42/// // 等待停机信号
43/// // ...
44///
45/// // 停止所有任务
46/// manager.stop_all(join_set, shutdown_txs).await;
47/// ```
48pub struct TaskManager {
49    /// 任务列表
50    tasks: Vec<Box<dyn Task>>,
51    /// 状态追踪器
52    state_tracker: Arc<StateTracker>,
53    /// 配置
54    config: RuntimeConfig,
55}
56
57impl TaskManager {
58    /// 创建新的任务管理器
59    pub fn new() -> Self {
60        Self {
61            tasks: Vec::new(),
62            state_tracker: Arc::new(StateTracker::new()),
63            config: RuntimeConfig::default(),
64        }
65    }
66
67    /// 创建新的任务管理器(带配置)
68    pub fn with_config(config: RuntimeConfig) -> Self {
69        Self {
70            tasks: Vec::new(),
71            state_tracker: Arc::new(StateTracker::new()),
72            config,
73        }
74    }
75
76    /// 添加任务
77    pub fn add_task(&mut self, task: Box<dyn Task>) {
78        debug!(task_name = %task.name(), "Adding task to manager");
79        self.tasks.push(task);
80    }
81
82    /// 获取任务数量
83    pub fn task_count(&self) -> usize {
84        self.tasks.len()
85    }
86
87    /// 获取状态追踪器
88    pub fn state_tracker(&self) -> Arc<StateTracker> {
89        Arc::clone(&self.state_tracker)
90    }
91
92    /// 启动所有任务
93    ///
94    /// # 返回
95    ///
96    /// - `join_set` - 任务 JoinSet,用于等待任务完成
97    /// - `shutdown_txs` - shutdown 信号发送器列表
98    ///
99    /// # 错误
100    ///
101    /// - 循环依赖
102    /// - 缺失依赖
103    pub async fn start_all(
104        &mut self,
105    ) -> Result<(JoinSet<TaskResult>, Vec<oneshot::Sender<()>>), RuntimeError> {
106        info!(task_count = self.tasks.len(), "Starting all tasks");
107
108        // 1. 拓扑排序,确定启动顺序
109        let sorted_tasks = self.sort_tasks()?;
110
111        // 2. 注册所有任务到状态追踪器
112        for task in &sorted_tasks {
113            self.state_tracker
114                .register_task(task.name(), TaskState::Pending)
115                .await;
116        }
117
118        // 3. 启动任务
119        let mut join_set = JoinSet::new();
120        let mut shutdown_txs = Vec::new();
121
122        for task in sorted_tasks {
123            let task_name = task.name().to_string();
124            let (shutdown_tx, shutdown_rx) = oneshot::channel();
125            shutdown_txs.push(shutdown_tx);
126
127            // 更新状态为 Starting
128            self.state_tracker
129                .update_state(&task_name, TaskState::Starting)
130                .await;
131
132            // 启动任务
133            let state_tracker = Arc::clone(&self.state_tracker);
134            let task_future = task.run(shutdown_rx);
135
136            join_set.spawn(async move {
137                // 更新状态为 Running
138                state_tracker
139                    .update_state(&task_name, TaskState::Running)
140                    .await;
141
142                // 执行任务
143                let result = task_future.await;
144
145                // 更新状态
146                match &result {
147                    Ok(_) => {
148                        debug!(task_name = %task_name, "Task completed");
149                        state_tracker
150                            .update_state(&task_name, TaskState::Stopped)
151                            .await;
152                    }
153                    Err(e) => {
154                        error!(task_name = %task_name, error = %e, "❌ Task failed");
155                        state_tracker
156                            .update_state_with_error(&task_name, TaskState::Failed, e.to_string())
157                            .await;
158                    }
159                }
160
161                result
162            });
163        }
164
165        info!("All tasks started");
166        Ok((join_set, shutdown_txs))
167    }
168
169    /// 停止所有任务
170    ///
171    /// # 参数
172    ///
173    /// * `join_set` - 任务 JoinSet
174    /// * `shutdown_txs` - shutdown 信号发送器列表
175    pub async fn stop_all(
176        &self,
177        mut join_set: JoinSet<TaskResult>,
178        shutdown_txs: Vec<oneshot::Sender<()>>,
179    ) {
180        info!("Stopping all tasks");
181
182        // 1. 发送 shutdown 信号
183        for tx in shutdown_txs {
184            let _ = tx.send(());
185        }
186
187        // 2. 等待所有任务关闭
188        match tokio::time::timeout(self.config.shutdown_timeout, async {
189            while let Some(result) = join_set.join_next().await {
190                match result {
191                    Ok(Ok(_)) => {
192                        debug!("Task completed gracefully");
193                    }
194                    Ok(Err(e)) => {
195                        warn!("Task completed with error: {}", e);
196                    }
197                    Err(e) => {
198                        warn!("Task join error: {}", e);
199                    }
200                }
201            }
202        })
203        .await
204        {
205            Ok(_) => {
206                info!("All tasks completed");
207            }
208            Err(_) => {
209                warn!("Tasks shutdown timeout, forcing exit");
210                join_set.abort_all();
211            }
212        }
213    }
214
215    /// 等待所有任务就绪
216    pub async fn wait_for_ready(&self) -> Result<(), RuntimeError> {
217        if !self.config.task_startup.enable_ready_check {
218            debug!("Task ready check is disabled, skipping");
219            return Ok(());
220        }
221
222        info!("Waiting for all tasks to be ready");
223
224        // 等待所有任务状态变为 Running
225        let timeout = self.config.task_startup.ready_check_timeout;
226        let start = std::time::Instant::now();
227
228        loop {
229            if self.state_tracker.all_ready().await {
230                info!("✅ All tasks are ready");
231                return Ok(());
232            }
233
234            if start.elapsed() > timeout {
235                return Err(RuntimeError::StartupTimeout {
236                    name: "all tasks".to_string(),
237                    timeout,
238                });
239            }
240
241            tokio::time::sleep(Duration::from_millis(100)).await;
242        }
243    }
244
245    /// 拓扑排序任务
246    fn sort_tasks(&mut self) -> Result<Vec<Box<dyn Task>>, RuntimeError> {
247        // 构建任务依赖列表
248        let items: Vec<(String, Vec<String>)> = self
249            .tasks
250            .iter()
251            .map(|task| (task.name().to_string(), task.dependencies()))
252            .collect();
253
254        // 拓扑排序
255        let sorted_names = topological_sort(items)
256            .map_err(|cycle| RuntimeError::CircularDependency { tasks: cycle })?;
257
258        // 按拓扑名顺序取出任务:禁止用「原始下标 + swap_remove」——每次 swap_remove 都会打乱下标,导致越界 panic。
259        let mut by_name: HashMap<String, Box<dyn Task>> = HashMap::new();
260        for task in self.tasks.drain(..) {
261            by_name.insert(task.name().to_string(), task);
262        }
263
264        let mut sorted_tasks = Vec::with_capacity(sorted_names.len());
265        for name in sorted_names {
266            if let Some(task) = by_name.remove(&name) {
267                sorted_tasks.push(task);
268            }
269        }
270        for (_, task) in by_name {
271            warn!(
272                task_name = %task.name(),
273                "Task missing from topological order output, appending at end"
274            );
275            sorted_tasks.push(task);
276        }
277
278        Ok(sorted_tasks)
279    }
280}
281
282impl Default for TaskManager {
283    fn default() -> Self {
284        Self::new()
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291    use crate::task::SpawnTask;
292
293    #[tokio::test]
294    async fn test_task_manager_new() {
295        let manager = TaskManager::new();
296        assert_eq!(manager.task_count(), 0);
297    }
298
299    #[tokio::test]
300    async fn test_task_manager_add_task() {
301        let mut manager = TaskManager::new();
302        manager.add_task(Box::new(SpawnTask::new("task-1", async { Ok(()) })));
303        assert_eq!(manager.task_count(), 1);
304    }
305
306    #[tokio::test]
307    async fn test_task_manager_state_tracker() {
308        let manager = TaskManager::new();
309        let _tracker = manager.state_tracker();
310    }
311
312    /// 回归:多任务且无依赖时,拓扑序与插入序可能不一致,旧实现用 swap_remove(原下标) 会 panic。
313    #[tokio::test]
314    async fn test_task_manager_start_all_three_independent_no_panic() {
315        let mut manager = TaskManager::new();
316        manager.add_task(Box::new(SpawnTask::new("conversation-grpc", async {
317            tokio::time::sleep(std::time::Duration::from_millis(5)).await;
318            Ok(())
319        })));
320        manager.add_task(Box::new(SpawnTask::new("read-receipt-consumer", async {
321            tokio::time::sleep(std::time::Duration::from_millis(5)).await;
322            Ok(())
323        })));
324        manager.add_task(Box::new(SpawnTask::new(
325            "conversation-ensure-consumer",
326            async {
327                tokio::time::sleep(std::time::Duration::from_millis(5)).await;
328                Ok(())
329            },
330        )));
331
332        let (mut join_set, shutdown_txs) = manager.start_all().await.expect("start_all");
333        for tx in shutdown_txs {
334            let _ = tx.send(());
335        }
336        while join_set.join_next().await.is_some() {}
337    }
338}