Skip to main content

qualia_core_db/inference/ambient_orchestration/
scheduler.rs

1//! Task scheduling: policy-driven task queue and execution history.
2
3use super::*;
4use std::time::Instant;
5
6/// Task scheduler
7pub struct TaskScheduler {
8    scheduling_policy: SchedulingPolicy,
9    task_queue: TaskQueue,
10    execution_history: Vec<TaskExecutionRecord>,
11}
12
13/// Task queue
14pub struct TaskQueue {
15    pending_tasks: Vec<Task>,
16    running_tasks: Vec<Task>,
17    completed_tasks: Vec<Task>,
18}
19
20impl TaskScheduler {
21    /// Create new task scheduler
22    pub fn new() -> Self {
23        Self {
24            scheduling_policy: SchedulingPolicy::Adaptive,
25            task_queue: TaskQueue::new(),
26            execution_history: Vec::new(),
27        }
28    }
29
30    /// Submit task
31    pub fn submit_task(&mut self, task: Task) -> Result<(), AmbientError> {
32        self.task_queue.pending_tasks.push(task);
33        // Sort pending tasks according to the scheduling policy.
34        self.sort_pending();
35        Ok(())
36    }
37
38    /// Sort pending tasks according to the current scheduling policy.
39    fn sort_pending(&mut self) {
40        match self.scheduling_policy {
41            SchedulingPolicy::Fifo => {
42                // FIFO: keep insertion order (no sort needed).
43            }
44            SchedulingPolicy::Priority => {
45                // Priority: highest priority first.
46                self.task_queue
47                    .pending_tasks
48                    .sort_by(|a, b| b.priority.cmp(&a.priority));
49            }
50            SchedulingPolicy::ShortestJobFirst => {
51                // SJF: shortest estimated duration first.
52                self.task_queue
53                    .pending_tasks
54                    .sort_by(|a, b| a.estimated_duration.cmp(&b.estimated_duration));
55            }
56            SchedulingPolicy::Deadline => {
57                // Deadline: earliest deadline first (tasks without deadlines go last).
58                self.task_queue
59                    .pending_tasks
60                    .sort_by(|a, b| match (a.deadline, b.deadline) {
61                        (Some(da), Some(db)) => da.cmp(&db),
62                        (Some(_), None) => std::cmp::Ordering::Less,
63                        (None, Some(_)) => std::cmp::Ordering::Greater,
64                        (None, None) => std::cmp::Ordering::Equal,
65                    });
66            }
67            SchedulingPolicy::Adaptive => {
68                // Adaptive: priority first, then shortest job as tiebreaker.
69                self.task_queue.pending_tasks.sort_by(|a, b| {
70                    b.priority
71                        .cmp(&a.priority)
72                        .then_with(|| a.estimated_duration.cmp(&b.estimated_duration))
73                });
74            }
75        }
76    }
77
78    /// Get pending tasks
79    pub fn get_pending_tasks(&self) -> Vec<Task> {
80        self.task_queue.pending_tasks.clone()
81    }
82
83    pub fn get_pending_tasks_into(&self, out: &mut [TaskHandle]) -> Result<usize, AmbientError> {
84        if out.len() < self.task_queue.pending_tasks.len() {
85            return Err(AmbientError::InsufficientResources(
86                "task output buffer full".to_string(),
87            ));
88        }
89
90        for (index, task) in self.task_queue.pending_tasks.iter().enumerate() {
91            out[index] = TaskHandle {
92                task_id_hash: crate::q_hash(&task.task_id),
93                task_type: task.task_type.clone(),
94                priority: task.priority.clone(),
95                compute_units: task.resource_requirements.compute_units,
96                memory: task.resource_requirements.memory,
97            };
98        }
99
100        Ok(self.task_queue.pending_tasks.len())
101    }
102
103    /// Dispatch the next pending task to a device, moving it to running.
104    /// Returns the dispatched task, or `None` if no tasks are pending.
105    pub fn dispatch_next(&mut self) -> Option<Task> {
106        let task = self.task_queue.pending_tasks.pop()?;
107        self.task_queue.running_tasks.push(task.clone());
108        Some(task)
109    }
110
111    /// Mark a running task as completed, recording it in execution history.
112    pub fn complete_task(
113        &mut self,
114        task_id: &str,
115        device_id: &str,
116        success: bool,
117        usage: ResourceUsage,
118    ) {
119        // Remove from running tasks.
120        if let Some(pos) = self
121            .task_queue
122            .running_tasks
123            .iter()
124            .position(|t| t.task_id == task_id)
125        {
126            let task = self.task_queue.running_tasks.remove(pos);
127            self.task_queue.completed_tasks.push(task.clone());
128
129            // Record in execution history.
130            self.execution_history.push(TaskExecutionRecord {
131                task_id: task.task_id.clone(),
132                device_id: device_id.to_string(),
133                start_time: Instant::now() - task.estimated_duration,
134                end_time: Instant::now(),
135                actual_duration: task.estimated_duration,
136                success,
137                resource_usage: usage,
138            });
139
140            // Trim history.
141            if self.execution_history.len() > 500 {
142                let drop = self.execution_history.len() - 500;
143                self.execution_history.drain(0..drop);
144            }
145            // Trim completed tasks.
146            if self.task_queue.completed_tasks.len() > 200 {
147                let drop = self.task_queue.completed_tasks.len() - 200;
148                self.task_queue.completed_tasks.drain(0..drop);
149            }
150        }
151    }
152
153    /// Get the number of currently running tasks.
154    pub fn running_count(&self) -> usize {
155        self.task_queue.running_tasks.len()
156    }
157
158    /// Get the number of completed tasks.
159    pub fn completed_count(&self) -> usize {
160        self.task_queue.completed_tasks.len()
161    }
162
163    /// Get recent execution history records.
164    pub fn recent_history(&self, n: usize) -> &[TaskExecutionRecord] {
165        let start = self.execution_history.len().saturating_sub(n);
166        &self.execution_history[start..]
167    }
168
169    /// Set the scheduling policy.
170    pub fn set_policy(&mut self, policy: SchedulingPolicy) {
171        self.scheduling_policy = policy;
172        self.sort_pending();
173    }
174}
175
176impl TaskQueue {
177    pub fn new() -> Self {
178        Self {
179            pending_tasks: Vec::new(),
180            running_tasks: Vec::new(),
181            completed_tasks: Vec::new(),
182        }
183    }
184}