pub(crate) mod atomic_mode;
mod kernel_stack;
mod preempt;
mod processor;
pub mod scheduler;
use core::{any::Any, cell::UnsafeCell};
use kernel_stack::KernelStack;
pub(crate) use preempt::cpu_local::reset_preempt_info;
use processor::current_task;
pub use self::{
preempt::{disable_preempt, DisabledPreemptGuard},
scheduler::info::{AtomicCpuId, TaskScheduleInfo},
};
pub(crate) use crate::arch::task::{context_switch, TaskContext};
use crate::{prelude::*, user::UserSpace};
pub struct Task {
func: Box<dyn Fn() + Send + Sync>,
data: Box<dyn Any + Send + Sync>,
user_space: Option<Arc<UserSpace>>,
ctx: UnsafeCell<TaskContext>,
#[allow(dead_code)]
kstack: KernelStack,
schedule_info: TaskScheduleInfo,
}
unsafe impl Sync for Task {}
impl Task {
pub fn current() -> Option<Arc<Task>> {
current_task()
}
pub(super) fn ctx(&self) -> &UnsafeCell<TaskContext> {
&self.ctx
}
pub fn set_tls_pointer(&self, tls: usize) {
let ctx_ptr = self.ctx.get();
unsafe { (*ctx_ptr).set_tls_pointer(tls) }
}
pub fn tls_pointer(&self) -> usize {
let ctx_ptr = self.ctx.get();
unsafe { (*ctx_ptr).tls_pointer() }
}
pub fn yield_now() {
scheduler::yield_now()
}
pub fn run(self: &Arc<Self>) {
scheduler::run_new_task(self.clone());
}
pub fn data(&self) -> &Box<dyn Any + Send + Sync> {
&self.data
}
pub fn schedule_info(&self) -> &TaskScheduleInfo {
&self.schedule_info
}
pub fn user_space(&self) -> Option<&Arc<UserSpace>> {
if self.user_space.is_some() {
Some(self.user_space.as_ref().unwrap())
} else {
None
}
}
fn exit(self: Arc<Self>) -> ! {
drop(self);
scheduler::exit_current();
unreachable!()
}
}
pub struct TaskOptions {
func: Option<Box<dyn Fn() + Send + Sync>>,
data: Option<Box<dyn Any + Send + Sync>>,
user_space: Option<Arc<UserSpace>>,
}
impl TaskOptions {
pub fn new<F>(func: F) -> Self
where
F: Fn() + Send + Sync + 'static,
{
Self {
func: Some(Box::new(func)),
data: None,
user_space: None,
}
}
pub fn func<F>(mut self, func: F) -> Self
where
F: Fn() + Send + Sync + 'static,
{
self.func = Some(Box::new(func));
self
}
pub fn data<T>(mut self, data: T) -> Self
where
T: Any + Send + Sync,
{
self.data = Some(Box::new(data));
self
}
pub fn user_space(mut self, user_space: Option<Arc<UserSpace>>) -> Self {
self.user_space = user_space;
self
}
pub fn build(self) -> Result<Task> {
extern "C" fn kernel_task_entry() {
let current_task = current_task()
.expect("no current task, it should have current task in kernel task entry");
current_task.func.call(());
current_task.exit();
}
let kstack = KernelStack::new_with_guard_page()?;
let mut ctx = UnsafeCell::new(TaskContext::default());
if let Some(user_space) = self.user_space.as_ref() {
ctx.get_mut().set_tls_pointer(user_space.tls_pointer());
};
ctx.get_mut()
.set_instruction_pointer(kernel_task_entry as usize);
ctx.get_mut()
.set_stack_pointer(crate::mm::paddr_to_vaddr(kstack.end_paddr() - 16));
let new_task = Task {
func: self.func.unwrap(),
data: self.data.unwrap(),
user_space: self.user_space,
ctx,
kstack,
schedule_info: TaskScheduleInfo {
cpu: AtomicCpuId::default(),
},
};
Ok(new_task)
}
pub fn spawn(self) -> Result<Arc<Task>> {
let task = Arc::new(self.build()?);
task.run();
Ok(task)
}
}
pub trait TaskContextApi {
fn set_instruction_pointer(&mut self, ip: usize);
fn instruction_pointer(&self) -> usize;
fn set_stack_pointer(&mut self, sp: usize);
fn stack_pointer(&self) -> usize;
}
#[cfg(ktest)]
mod test {
use crate::prelude::*;
#[ktest]
fn create_task() {
#[allow(clippy::eq_op)]
let task = || {
assert_eq!(1, 1);
};
let task = Arc::new(
crate::task::TaskOptions::new(task)
.data(())
.build()
.unwrap(),
);
task.run();
}
#[ktest]
fn spawn_task() {
#[allow(clippy::eq_op)]
let task = || {
assert_eq!(1, 1);
};
let _ = crate::task::TaskOptions::new(task).data(()).spawn();
}
}