diff --git a/ostd/src/task/mod.rs b/ostd/src/task/mod.rs index ae3a9984d..5a850b0c1 100644 --- a/ostd/src/task/mod.rs +++ b/ostd/src/task/mod.rs @@ -27,7 +27,7 @@ pub use self::{ scheduler::info::{AtomicCpuId, TaskScheduleInfo}, }; pub(crate) use crate::arch::task::{context_switch, TaskContext}; -use crate::{prelude::*, user::UserSpace}; +use crate::{prelude::*, trap::in_interrupt_context, user::UserSpace}; /// A task that executes a function to the end. /// @@ -38,7 +38,10 @@ use crate::{prelude::*, user::UserSpace}; pub struct Task { #[allow(clippy::type_complexity)] func: ForceSync>>>, + data: Box, + local_data: ForceSync>, + user_space: Option>, ctx: SyncUnsafeCell, /// kernel stack, note that the top is SyscallFrame/TrapFrame @@ -138,6 +141,7 @@ impl Task { pub struct TaskOptions { func: Option>, data: Option>, + local_data: Option>, user_space: Option>, } @@ -150,6 +154,7 @@ impl TaskOptions { Self { func: Some(Box::new(func)), data: None, + local_data: None, user_space: None, } } @@ -172,6 +177,15 @@ impl TaskOptions { self } + /// Sets the local data associated with the task. + pub fn local_data(mut self, data: T) -> Self + where + T: Any + Send, + { + self.local_data = Some(Box::new(data)); + self + } + /// Sets the user space associated with the task. pub fn user_space(mut self, user_space: Option>) -> Self { self.user_space = user_space; @@ -228,7 +242,8 @@ impl TaskOptions { let new_task = Task { func: ForceSync::new(Cell::new(self.func)), - data: self.data.unwrap(), + data: self.data.unwrap_or_else(|| Box::new(())), + local_data: ForceSync::new(self.local_data.unwrap_or_else(|| Box::new(()))), user_space: self.user_space, ctx, kstack, @@ -252,12 +267,16 @@ impl TaskOptions { /// The current task. /// /// This type is not `Send`, so it cannot outlive the current task. +/// +/// This type is also not `Sync`, so it can provide access to the local data of the current task. #[derive(Debug)] pub struct CurrentTask(NonNull); -// The intern `NonNull` contained by `CurrentTask` implies that `CurrentTask` is `!Send`. -// But it is still good to do this explicitly because this property is key for soundness. +// The intern `NonNull` contained by `CurrentTask` implies that `CurrentTask` is `!Send` and +// `!Sync`. But it is still good to do this explicitly because these properties are key for +// soundness. impl !Send for CurrentTask {} +impl !Sync for CurrentTask {} impl CurrentTask { /// # Safety @@ -267,6 +286,25 @@ impl CurrentTask { Self(task) } + /// Returns the local data of the current task. + /// + /// Note that the local data is only accessible in the task context. Although there is a + /// current task in the non-task context (e.g. IRQ handlers), access to the local data is + /// forbidden as it may cause soundness problems. + /// + /// # Panics + /// + /// This method will panic if called in a non-task context. + pub fn local_data(&self) -> &(dyn Any + Send) { + assert!(!in_interrupt_context()); + + let local_data = &self.local_data; + + // SAFETY: The `local_data` field will only be accessed by the current task in the task + // context, so the data won't be accessed concurrently. + &**unsafe { local_data.get() } + } + /// Returns a cloned `Arc`. pub fn cloned(&self) -> Arc { let ptr = self.0.as_ptr();