Skip to main content

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}