diff --git a/ostd/src/task/mod.rs b/ostd/src/task/mod.rs index ecbe57be9..bffba7964 100644 --- a/ostd/src/task/mod.rs +++ b/ostd/src/task/mod.rs @@ -7,12 +7,17 @@ mod kernel_stack; mod preempt; mod processor; pub mod scheduler; +mod utils; -use core::{any::Any, cell::SyncUnsafeCell}; +use core::{ + any::Any, + cell::{Cell, SyncUnsafeCell}, +}; use kernel_stack::KernelStack; pub(crate) use preempt::cpu_local::reset_preempt_info; use processor::current_task; +use utils::ForceSync; pub use self::{ preempt::{disable_preempt, DisabledPreemptGuard}, @@ -28,7 +33,8 @@ use crate::{prelude::*, user::UserSpace}; /// execute user code. Multiple tasks can share a single user space. #[derive(Debug)] pub struct Task { - func: SyncUnsafeCell>>, + #[allow(clippy::type_complexity)] + func: ForceSync>>>, data: Box, user_space: Option>, ctx: SyncUnsafeCell, @@ -104,7 +110,7 @@ impl Task { /// Options to create or spawn a new task. pub struct TaskOptions { - func: Option>, + func: Option>, data: Option>, user_space: Option>, } @@ -125,7 +131,7 @@ impl TaskOptions { /// Sets the function that represents the entry point of the task. pub fn func(mut self, func: F) -> Self where - F: Fn() + Send + Sync + 'static, + F: Fn() + Send + 'static, { self.func = Some(Box::new(func)); self @@ -153,9 +159,10 @@ impl TaskOptions { extern "C" fn kernel_task_entry() -> ! { let current_task = current_task() .expect("no current task, it should have current task in kernel task entry"); - // SAFETY: The scheduler will ensure that the task is only accessed - // by one CPU. - let task_func = unsafe { &mut *current_task.func.get() }; + + // SAFETY: The `func` field will only be accessed by the current task in the task + // context, so the data won't be accessed concurrently. + let task_func = unsafe { current_task.func.get() }; let task_func = task_func .take() .expect("task function is `None` when trying to run"); @@ -186,7 +193,7 @@ impl TaskOptions { ctx.get_mut().set_stack_pointer(kstack.end_vaddr() - 16); let new_task = Task { - func: SyncUnsafeCell::new(self.func), + func: ForceSync::new(Cell::new(self.func)), data: self.data.unwrap(), user_space: self.user_space, ctx, diff --git a/ostd/src/task/utils.rs b/ostd/src/task/utils.rs new file mode 100644 index 000000000..256aa2f18 --- /dev/null +++ b/ostd/src/task/utils.rs @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MPL-2.0 + +use alloc::fmt; + +/// Always [`Sync`], but unsafe to reference the data. +pub(super) struct ForceSync(T); + +impl fmt::Debug for ForceSync { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("ForceSync").finish_non_exhaustive() + } +} + +// SAFETY: The caller of the `ForceSync::get` method must ensure that the underlying data is not +// concurrently accessed if the underlying type is not `Sync`. +unsafe impl Sync for ForceSync {} + +impl ForceSync { + /// Creates an instance with `data` as the inner data. + pub(super) fn new(data: T) -> Self { + Self(data) + } + + /// Returns a reference to the inner data. + /// + /// # Safety + /// + /// If the data type is not [`Sync`], the caller must ensure that the data is not accessed + /// concurrently. + pub(super) unsafe fn get(&self) -> &T { + &self.0 + } +}