reovim_kernel/sched/task.rs
1//! Task abstraction for deferred work units.
2//!
3//! Linux equivalent: `kernel/sched/core.c` `task_struct`
4//!
5//! This module provides the [`Task`] type for encapsulating units of work
6//! to be executed by the scheduler. Tasks are the fundamental unit of
7//! deferred execution in the kernel.
8//!
9//! # Example
10//!
11//! ```
12//! use reovim_kernel::api::v1::*;
13//!
14//! // Create a task with default (normal) priority
15//! let task = Task::new(|| {
16//! println!("Task executed!");
17//! });
18//!
19//! // Create a high-priority task
20//! let urgent = Task::with_priority(Priority::HIGH, || {
21//! println!("Urgent work!");
22//! });
23//! ```
24
25use std::{
26 fmt,
27 sync::atomic::{AtomicU64, Ordering},
28};
29
30/// Unique task identifier.
31///
32/// Task IDs are monotonically increasing and never reused within a session.
33/// This follows the same pattern as [`BufferId`](crate::mm::BufferId) and
34/// [`ScopeId`](crate::ipc::ScopeId).
35///
36/// # Example
37///
38/// ```
39/// use reovim_kernel::api::v1::*;
40///
41/// let id1 = TaskId::new();
42/// let id2 = TaskId::new();
43/// assert_ne!(id1, id2);
44/// assert!(id2.as_u64() > id1.as_u64());
45/// ```
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
47pub struct TaskId(u64);
48
49impl TaskId {
50 /// Create a new unique task ID.
51 ///
52 /// IDs start at 1 and increment monotonically.
53 #[must_use]
54 pub fn new() -> Self {
55 static COUNTER: AtomicU64 = AtomicU64::new(1);
56 Self(COUNTER.fetch_add(1, Ordering::Relaxed))
57 }
58
59 /// Get the raw numeric value.
60 #[inline]
61 #[must_use]
62 pub const fn as_u64(self) -> u64 {
63 self.0
64 }
65
66 /// Create from raw value.
67 ///
68 /// Primarily for testing and deserialization.
69 #[inline]
70 #[must_use]
71 pub const fn from_raw(value: u64) -> Self {
72 Self(value)
73 }
74}
75
76impl Default for TaskId {
77 fn default() -> Self {
78 Self::new()
79 }
80}
81
82// LLVM coverage artifact: closing brace of Display impl marked DA:0
83// despite being exercised by test_task_id_display.
84#[cfg_attr(coverage_nightly, coverage(off))]
85impl fmt::Display for TaskId {
86 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
87 write!(f, "Task({})", self.0)
88 }
89}
90
91/// Task execution priority.
92///
93/// Lower values indicate higher priority (processed sooner).
94/// This follows the convention where 0 is the highest priority.
95///
96/// # Priority Levels
97///
98/// | Level | Value | Use Case |
99/// |-------|-------|----------|
100/// | `CRITICAL` | 0 | Kernel-internal, mode changes |
101/// | `HIGH` | 50 | User input, commands |
102/// | `NORMAL` | 100 | Default for most tasks |
103/// | `LOW` | 200 | Background work |
104/// | `IDLE` | 1000 | Cleanup, logging |
105#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
106pub struct Priority(pub u32);
107
108impl Priority {
109 /// Critical priority - kernel-internal operations, mode changes.
110 pub const CRITICAL: Self = Self(0);
111
112 /// High priority - user input, commands.
113 pub const HIGH: Self = Self(50);
114
115 /// Normal priority - default for most tasks.
116 pub const NORMAL: Self = Self(100);
117
118 /// Low priority - background work, render signals.
119 pub const LOW: Self = Self(200);
120
121 /// Idle priority - cleanup, logging.
122 pub const IDLE: Self = Self(1000);
123
124 /// Create a priority with a custom value.
125 #[inline]
126 #[must_use]
127 pub const fn new(value: u32) -> Self {
128 Self(value)
129 }
130
131 /// Get the raw priority value.
132 #[inline]
133 #[must_use]
134 pub const fn as_u32(self) -> u32 {
135 self.0
136 }
137}
138
139impl Default for Priority {
140 fn default() -> Self {
141 Self::NORMAL
142 }
143}
144
145impl fmt::Display for Priority {
146 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 match *self {
148 Self::CRITICAL => write!(f, "Critical(0)"),
149 Self::HIGH => write!(f, "High(50)"),
150 Self::NORMAL => write!(f, "Normal(100)"),
151 Self::LOW => write!(f, "Low(200)"),
152 Self::IDLE => write!(f, "Idle(1000)"),
153 Self(v) => write!(f, "Priority({v})"),
154 }
155 }
156}
157
158/// Task execution state.
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
160pub enum TaskState {
161 /// Task is waiting to be executed.
162 #[default]
163 Pending,
164
165 /// Task is currently executing.
166 Running,
167
168 /// Task completed successfully.
169 Completed,
170
171 /// Task failed (error or panic).
172 Failed,
173}
174
175impl TaskState {
176 /// Check if the task is still pending.
177 #[inline]
178 #[must_use]
179 pub const fn is_pending(&self) -> bool {
180 matches!(self, Self::Pending)
181 }
182
183 /// Check if the task has finished (completed or failed).
184 #[inline]
185 #[must_use]
186 pub const fn is_finished(&self) -> bool {
187 matches!(self, Self::Completed | Self::Failed)
188 }
189}
190
191impl fmt::Display for TaskState {
192 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193 match self {
194 Self::Pending => write!(f, "Pending"),
195 Self::Running => write!(f, "Running"),
196 Self::Completed => write!(f, "Completed"),
197 Self::Failed => write!(f, "Failed"),
198 }
199 }
200}
201
202/// Type-erased task function.
203///
204/// A boxed closure that can be executed once. Tasks must be `Send` to allow
205/// scheduling from any thread.
206pub type BoxedTask = Box<dyn FnOnce() + Send + 'static>;
207
208/// A unit of deferred work.
209///
210/// Tasks encapsulate a closure to be executed later by the scheduler.
211/// Each task has a unique ID, priority, and execution state.
212///
213/// # Example
214///
215/// ```
216/// use reovim_kernel::api::v1::*;
217///
218/// // Create a high-priority task with a name
219/// let mut task = Task::with_priority(Priority::HIGH, || println!("Hello!"))
220/// .with_name("greeting");
221///
222/// assert_eq!(task.state(), TaskState::Pending);
223/// assert_eq!(task.priority(), Priority::HIGH);
224///
225/// // Execute the task
226/// let result = task.execute();
227/// assert!(result.is_ok());
228/// assert_eq!(task.state(), TaskState::Completed);
229/// ```
230pub struct Task {
231 /// Unique identifier.
232 id: TaskId,
233
234 /// Task priority.
235 priority: Priority,
236
237 /// Current state.
238 state: TaskState,
239
240 /// The work to execute (None after execution).
241 work: Option<BoxedTask>,
242
243 /// Optional name for debugging.
244 name: Option<&'static str>,
245}
246
247impl Task {
248 /// Create a new task with normal priority.
249 #[must_use]
250 pub fn new<F>(work: F) -> Self
251 where
252 F: FnOnce() + Send + 'static,
253 {
254 Self::with_priority(Priority::NORMAL, work)
255 }
256
257 /// Create a task with specific priority.
258 #[must_use]
259 pub fn with_priority<F>(priority: Priority, work: F) -> Self
260 where
261 F: FnOnce() + Send + 'static,
262 {
263 Self {
264 id: TaskId::new(),
265 priority,
266 state: TaskState::Pending,
267 work: Some(Box::new(work)),
268 name: None,
269 }
270 }
271
272 /// Set the task name for debugging.
273 #[must_use]
274 pub const fn with_name(mut self, name: &'static str) -> Self {
275 self.name = Some(name);
276 self
277 }
278
279 /// Get the task ID.
280 #[inline]
281 #[must_use]
282 pub const fn id(&self) -> TaskId {
283 self.id
284 }
285
286 /// Get the task priority.
287 #[inline]
288 #[must_use]
289 pub const fn priority(&self) -> Priority {
290 self.priority
291 }
292
293 /// Get the current state.
294 #[inline]
295 #[must_use]
296 pub const fn state(&self) -> TaskState {
297 self.state
298 }
299
300 /// Get the task name (if set).
301 #[inline]
302 #[must_use]
303 pub const fn name(&self) -> Option<&'static str> {
304 self.name
305 }
306
307 /// Check if the task can still be executed.
308 #[inline]
309 #[must_use]
310 #[cfg_attr(coverage_nightly, coverage(off))]
311 pub const fn is_executable(&self) -> bool {
312 matches!(self.state, TaskState::Pending) && self.work.is_some()
313 }
314
315 /// Execute the task.
316 ///
317 /// Consumes the work closure and transitions state to `Completed`.
318 ///
319 /// # Errors
320 ///
321 /// Returns an error if:
322 /// - Task has already been executed
323 /// - Task was cancelled (work is None)
324 pub fn execute(&mut self) -> Result<(), &'static str> {
325 if self.state != TaskState::Pending {
326 return Err("task already executed or cancelled");
327 }
328
329 let work = self.work.take().ok_or("work closure missing")?;
330
331 self.state = TaskState::Running;
332 work();
333 self.state = TaskState::Completed;
334
335 Ok(())
336 }
337
338 /// Mark the task as failed.
339 ///
340 /// Used by the executor when a panic is caught during execution.
341 pub fn mark_failed(&mut self) {
342 self.work = None;
343 self.state = TaskState::Failed;
344 }
345
346 /// Cancel the task.
347 ///
348 /// Removes the work closure without executing it.
349 pub fn cancel(&mut self) {
350 self.work = None;
351 self.state = TaskState::Failed;
352 }
353}
354
355impl fmt::Debug for Task {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 f.debug_struct("Task")
358 .field("id", &self.id)
359 .field("priority", &self.priority)
360 .field("state", &self.state)
361 .field("name", &self.name)
362 .field("has_work", &self.work.is_some())
363 .finish()
364 }
365}