Skip to main content

ostd/task/
mod.rs

1// SPDX-License-Identifier: MPL-2.0
2//! Tasks are the unit of code execution.
3use vstd::prelude::*;
4
5/* pub mod atomic_mode;
6mod kernel_stack; */
7mod preempt;
8// mod processor;
9pub mod scheduler;
10/* mod utils;
11
12use core::{
13    any::Any,
14    borrow::Borrow,
15    cell::{Cell, SyncUnsafeCell},
16    ops::Deref,
17    ptr::NonNull,
18    sync::atomic::AtomicBool,
19};
20
21use kernel_stack::KernelStack;
22use processor::current_task;*/
23use spin::Once;
24// use utils::ForceSync;
25#[cfg(feature = "irc11")]
26pub use self::preempt::RunningTaskContext;
27pub use self::{
28    preempt::{DisabledPreemptGuard, disable_preempt},
29    /* scheduler::info::{AtomicCpuId, TaskScheduleInfo}, */
30};
31/*
32pub(crate) use crate::arch::task::{context_switch, TaskContext};
33use crate::{cpu::context::UserContext, prelude::*, trap::in_interrupt_context};
34
35static PRE_SCHEDULE_HANDLER: Once<fn()> = Once::new();
36
37static POST_SCHEDULE_HANDLER: Once<fn()> = Once::new();
38
39/// Injects a handler to be executed before scheduling.
40pub fn inject_pre_schedule_handler(handler: fn()) {
41    PRE_SCHEDULE_HANDLER.call_once(|| handler);
42}
43
44/// Injects a handler to be executed after scheduling.
45pub fn inject_post_schedule_handler(handler: fn()) {
46    POST_SCHEDULE_HANDLER.call_once(|| handler);
47}*/
48
49/// A task that executes a function to the end.
50///
51/// Each task is associated with per-task data and an optional user space.
52/// If having a user space, the task can switch to the user space to
53/// execute user code. Multiple tasks can share a single user space.
54#[verus_verify]
55#[derive(Debug)]
56pub struct Task {
57    /*#[expect(clippy::type_complexity)]
58    func: ForceSync<Cell<Option<Box<dyn FnOnce() + Send>>>>,
59
60    data: Box<dyn Any + Send + Sync>,
61    local_data: ForceSync<Box<dyn Any + Send>>,
62
63    user_ctx: Option<Arc<UserContext>>,
64    ctx: SyncUnsafeCell<TaskContext>,
65    /// kernel stack, note that the top is SyscallFrame/TrapFrame
66    kstack: KernelStack,
67
68    /// If we have switched this task to a CPU.
69    ///
70    /// This is to enforce not context switching to an already running task.
71    /// See [`processor::switch_to_task`] for more details.
72    switched_to_cpu: AtomicBool,
73
74    schedule_info: TaskScheduleInfo,*/
75}
76/*
77impl Task {
78    /// Gets the current task.
79    ///
80    /// It returns `None` if the function is called in the bootstrap context.
81    pub fn current() -> Option<CurrentTask> {
82        let current_task = current_task()?;
83
84        // SAFETY: `current_task` is the current task.
85        Some(unsafe { CurrentTask::new(current_task) })
86    }
87
88    pub(super) fn ctx(&self) -> &SyncUnsafeCell<TaskContext> {
89        &self.ctx
90    }
91
92    /// Sets thread-local storage pointer.
93    pub fn set_tls_pointer(&self, tls: usize) {
94        let ctx_ptr = self.ctx.get();
95
96        // SAFETY: it's safe to set user tls pointer in kernel context.
97        unsafe { (*ctx_ptr).set_tls_pointer(tls) }
98    }
99
100    /// Gets thread-local storage pointer.
101    pub fn tls_pointer(&self) -> usize {
102        let ctx_ptr = self.ctx.get();
103
104        // SAFETY: it's safe to get user tls pointer in kernel context.
105        unsafe { (*ctx_ptr).tls_pointer() }
106    }
107
108    /// Yields execution so that another task may be scheduled.
109    ///
110    /// Note that this method cannot be simply named "yield" as the name is
111    /// a Rust keyword.
112    #[track_caller]
113    pub fn yield_now() {
114        scheduler::yield_now()
115    }
116
117    /// Kicks the task scheduler to run the task.
118    ///
119    /// BUG: This method highly depends on the current scheduling policy.
120    #[track_caller]
121    pub fn run(self: &Arc<Self>) {
122        scheduler::run_new_task(self.clone());
123    }
124
125    /// Returns the task data.
126    pub fn data(&self) -> &Box<dyn Any + Send + Sync> {
127        &self.data
128    }
129
130    /// Get the attached scheduling information.
131    pub fn schedule_info(&self) -> &TaskScheduleInfo {
132        &self.schedule_info
133    }
134
135    /// Returns the user context of this task, if it has.
136    pub fn user_ctx(&self) -> Option<&Arc<UserContext>> {
137        if self.user_ctx.is_some() {
138            Some(self.user_ctx.as_ref().unwrap())
139        } else {
140            None
141        }
142    }
143}
144
145/// Options to create or spawn a new task.
146pub struct TaskOptions {
147    func: Option<Box<dyn FnOnce() + Send>>,
148    data: Option<Box<dyn Any + Send + Sync>>,
149    local_data: Option<Box<dyn Any + Send>>,
150    user_ctx: Option<Arc<UserContext>>,
151}
152
153impl TaskOptions {
154    /// Creates a set of options for a task.
155    pub fn new<F>(func: F) -> Self
156    where
157        F: FnOnce() + Send + 'static,
158    {
159        Self {
160            func: Some(Box::new(func)),
161            data: None,
162            local_data: None,
163            user_ctx: None,
164        }
165    }
166
167    /// Sets the function that represents the entry point of the task.
168    pub fn func<F>(mut self, func: F) -> Self
169    where
170        F: Fn() + Send + 'static,
171    {
172        self.func = Some(Box::new(func));
173        self
174    }
175
176    /// Sets the data associated with the task.
177    pub fn data<T>(mut self, data: T) -> Self
178    where
179        T: Any + Send + Sync,
180    {
181        self.data = Some(Box::new(data));
182        self
183    }
184
185    /// Sets the local data associated with the task.
186    pub fn local_data<T>(mut self, data: T) -> Self
187    where
188        T: Any + Send,
189    {
190        self.local_data = Some(Box::new(data));
191        self
192    }
193
194    /// Sets the user context associated with the task.
195    pub fn user_ctx(mut self, user_ctx: Option<Arc<UserContext>>) -> Self {
196        self.user_ctx = user_ctx;
197        self
198    }
199
200    /// Builds a new task without running it immediately.
201    pub fn build(self) -> Result<Task> {
202        /// all task will entering this function
203        /// this function is mean to executing the task_fn in Task
204        extern "C" fn kernel_task_entry() -> ! {
205            // SAFETY: The new task is switched on a CPU for the first time, `after_switching_to`
206            // hasn't been called yet.
207            unsafe { processor::after_switching_to() };
208
209            let current_task = Task::current()
210                .expect("no current task, it should have current task in kernel task entry");
211
212            // SAFETY: The `func` field will only be accessed by the current task in the task
213            // context, so the data won't be accessed concurrently.
214            let task_func = unsafe { current_task.func.get() };
215            let task_func = task_func
216                .take()
217                .expect("task function is `None` when trying to run");
218            task_func();
219
220            // Manually drop all the on-stack variables to prevent memory leakage!
221            // This is needed because `scheduler::exit_current()` will never return.
222            //
223            // However, `current_task` _borrows_ the current task without holding
224            // an extra reference count. So we do nothing here.
225
226            scheduler::exit_current();
227        }
228
229        let kstack = KernelStack::new_with_guard_page()?;
230
231        let mut ctx = SyncUnsafeCell::new(TaskContext::default());
232        if let Some(user_ctx) = self.user_ctx.as_ref() {
233            ctx.get_mut().set_tls_pointer(user_ctx.tls_pointer());
234        };
235        ctx.get_mut()
236            .set_instruction_pointer(kernel_task_entry as usize);
237        // We should reserve space for the return address in the stack, otherwise
238        // we will write across the page boundary due to the implementation of
239        // the context switch.
240        //
241        // According to the System V AMD64 ABI, the stack pointer should be aligned
242        // to at least 16 bytes. And a larger alignment is needed if larger arguments
243        // are passed to the function. The `kernel_task_entry` function does not
244        // have any arguments, so we only need to align the stack pointer to 16 bytes.
245        ctx.get_mut().set_stack_pointer(kstack.end_vaddr() - 16);
246
247        let new_task = Task {
248            func: ForceSync::new(Cell::new(self.func)),
249            data: self.data.unwrap_or_else(|| Box::new(())),
250            local_data: ForceSync::new(self.local_data.unwrap_or_else(|| Box::new(()))),
251            user_ctx: self.user_ctx,
252            ctx,
253            kstack,
254            schedule_info: TaskScheduleInfo {
255                cpu: AtomicCpuId::default(),
256            },
257            switched_to_cpu: AtomicBool::new(false),
258        };
259
260        Ok(new_task)
261    }
262
263    /// Builds a new task and runs it immediately.
264    #[track_caller]
265    pub fn spawn(self) -> Result<Arc<Task>> {
266        let task = Arc::new(self.build()?);
267        task.run();
268        Ok(task)
269    }
270}
271
272/// The current task.
273///
274/// This type is not `Send`, so it cannot outlive the current task.
275///
276/// This type is also not `Sync`, so it can provide access to the local data of the current task.
277#[derive(Debug)]
278pub struct CurrentTask(NonNull<Task>);
279
280// The intern `NonNull<Task>` contained by `CurrentTask` implies that `CurrentTask` is `!Send` and
281// `!Sync`. But it is still good to do this explicitly because these properties are key for
282// soundness.
283impl !Send for CurrentTask {}
284impl !Sync for CurrentTask {}
285
286impl CurrentTask {
287    /// # Safety
288    ///
289    /// The caller must ensure that `task` is the current task.
290    unsafe fn new(task: NonNull<Task>) -> Self {
291        Self(task)
292    }
293
294    /// Returns the local data of the current task.
295    ///
296    /// Note that the local data is only accessible in the task context. Although there is a
297    /// current task in the non-task context (e.g. IRQ handlers), access to the local data is
298    /// forbidden as it may cause soundness problems.
299    ///
300    /// # Panics
301    ///
302    /// This method will panic if called in a non-task context.
303    pub fn local_data(&self) -> &(dyn Any + Send) {
304        assert!(!in_interrupt_context());
305
306        let local_data = &self.local_data;
307
308        // SAFETY: The `local_data` field will only be accessed by the current task in the task
309        // context, so the data won't be accessed concurrently.
310        &**unsafe { local_data.get() }
311    }
312
313    /// Returns a cloned `Arc<Task>`.
314    pub fn cloned(&self) -> Arc<Task> {
315        let ptr = self.0.as_ptr();
316
317        // SAFETY: The current task is always a valid task and it is always contained in an `Arc`.
318        unsafe { Arc::increment_strong_count(ptr) };
319
320        // SAFETY: We've increased the reference count in the current `Arc<Task>` above.
321        unsafe { Arc::from_raw(ptr) }
322    }
323}
324
325impl Deref for CurrentTask {
326    type Target = Task;
327
328    fn deref(&self) -> &Self::Target {
329        // SAFETY: The current task is always a valid task.
330        unsafe { self.0.as_ref() }
331    }
332}
333
334impl AsRef<Task> for CurrentTask {
335    fn as_ref(&self) -> &Task {
336        self
337    }
338}
339
340impl Borrow<Task> for CurrentTask {
341    fn borrow(&self) -> &Task {
342        self
343    }
344}
345
346/// Trait for manipulating the task context.
347pub trait TaskContextApi {
348    /// Sets instruction pointer
349    fn set_instruction_pointer(&mut self, ip: usize);
350
351    /// Gets instruction pointer
352    fn instruction_pointer(&self) -> usize;
353
354    /// Sets stack pointer
355    fn set_stack_pointer(&mut self, sp: usize);
356
357    /// Gets stack pointer
358    fn stack_pointer(&self) -> usize;
359}
360
361#[cfg(ktest)]
362mod test {
363    use crate::prelude::*;
364
365    #[ktest]
366    fn create_task() {
367        #[expect(clippy::eq_op)]
368        let task = || {
369            assert_eq!(1, 1);
370        };
371        let task = Arc::new(
372            crate::task::TaskOptions::new(task)
373                .data(())
374                .build()
375                .unwrap(),
376        );
377        task.run();
378    }
379
380    #[ktest]
381    fn spawn_task() {
382        #[expect(clippy::eq_op)]
383        let task = || {
384            assert_eq!(1, 1);
385        };
386        let _ = crate::task::TaskOptions::new(task).data(()).spawn();
387    }
388}
389*/