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*/