axtask/api.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
//! Task APIs for multi-task configuration.
use alloc::{string::String, sync::Arc};
use kernel_guard::NoPreemptIrqSave;
pub(crate) use crate::run_queue::{current_run_queue, select_run_queue};
#[doc(cfg(feature = "multitask"))]
pub use crate::task::{CurrentTask, TaskId, TaskInner};
#[doc(cfg(feature = "multitask"))]
pub use crate::task_ext::{TaskExtMut, TaskExtRef};
#[doc(cfg(feature = "multitask"))]
pub use crate::wait_queue::WaitQueue;
/// The reference type of a task.
pub type AxTaskRef = Arc<AxTask>;
/// The wrapper type for [`cpumask::CpuMask`] with SMP configuration.
pub type AxCpuMask = cpumask::CpuMask<{ axconfig::SMP }>;
cfg_if::cfg_if! {
if #[cfg(feature = "sched_rr")] {
const MAX_TIME_SLICE: usize = 5;
pub(crate) type AxTask = scheduler::RRTask<TaskInner, MAX_TIME_SLICE>;
pub(crate) type Scheduler = scheduler::RRScheduler<TaskInner, MAX_TIME_SLICE>;
} else if #[cfg(feature = "sched_cfs")] {
pub(crate) type AxTask = scheduler::CFSTask<TaskInner>;
pub(crate) type Scheduler = scheduler::CFScheduler<TaskInner>;
} else {
// If no scheduler features are set, use FIFO as the default.
pub(crate) type AxTask = scheduler::FifoTask<TaskInner>;
pub(crate) type Scheduler = scheduler::FifoScheduler<TaskInner>;
}
}
#[cfg(feature = "preempt")]
struct KernelGuardIfImpl;
#[cfg(feature = "preempt")]
#[crate_interface::impl_interface]
impl kernel_guard::KernelGuardIf for KernelGuardIfImpl {
fn disable_preempt() {
if let Some(curr) = current_may_uninit() {
curr.disable_preempt();
}
}
fn enable_preempt() {
if let Some(curr) = current_may_uninit() {
curr.enable_preempt(true);
}
}
}
/// Gets the current task, or returns [`None`] if the current task is not
/// initialized.
pub fn current_may_uninit() -> Option<CurrentTask> {
CurrentTask::try_get()
}
/// Gets the current task.
///
/// # Panics
///
/// Panics if the current task is not initialized.
pub fn current() -> CurrentTask {
CurrentTask::get()
}
/// Initializes the task scheduler (for the primary CPU).
pub fn init_scheduler() {
info!("Initialize scheduling...");
crate::run_queue::init();
#[cfg(feature = "irq")]
crate::timers::init();
info!(" use {} scheduler.", Scheduler::scheduler_name());
}
/// Initializes the task scheduler for secondary CPUs.
pub fn init_scheduler_secondary() {
crate::run_queue::init_secondary();
#[cfg(feature = "irq")]
crate::timers::init();
}
/// Handles periodic timer ticks for the task manager.
///
/// For example, advance scheduler states, checks timed events, etc.
#[cfg(feature = "irq")]
#[doc(cfg(feature = "irq"))]
pub fn on_timer_tick() {
use kernel_guard::NoOp;
crate::timers::check_events();
// Since irq and preemption are both disabled here,
// we can get current run queue with the default `kernel_guard::NoOp`.
current_run_queue::<NoOp>().scheduler_timer_tick();
}
/// Adds the given task to the run queue, returns the task reference.
pub fn spawn_task(task: TaskInner) -> AxTaskRef {
let task_ref = task.into_arc();
select_run_queue::<NoPreemptIrqSave>(&task_ref).add_task(task_ref.clone());
task_ref
}
/// Spawns a new task with the given parameters.
///
/// Returns the task reference.
pub fn spawn_raw<F>(f: F, name: String, stack_size: usize) -> AxTaskRef
where
F: FnOnce() + Send + 'static,
{
spawn_task(TaskInner::new(f, name, stack_size))
}
/// Spawns a new task with the default parameters.
///
/// The default task name is an empty string. The default task stack size is
/// [`axconfig::TASK_STACK_SIZE`].
///
/// Returns the task reference.
pub fn spawn<F>(f: F) -> AxTaskRef
where
F: FnOnce() + Send + 'static,
{
spawn_raw(f, "".into(), axconfig::TASK_STACK_SIZE)
}
/// Set the priority for current task.
///
/// The range of the priority is dependent on the underlying scheduler. For
/// example, in the [CFS] scheduler, the priority is the nice value, ranging from
/// -20 to 19.
///
/// Returns `true` if the priority is set successfully.
///
/// [CFS]: https://en.wikipedia.org/wiki/Completely_Fair_Scheduler
pub fn set_priority(prio: isize) -> bool {
current_run_queue::<NoPreemptIrqSave>().set_current_priority(prio)
}
/// Set the affinity for the current task.
/// [`AxCpuMask`] is used to specify the CPU affinity.
/// Returns `true` if the affinity is set successfully.
///
/// TODO: support set the affinity for other tasks.
pub fn set_current_affinity(cpumask: AxCpuMask) -> bool {
if cpumask.is_empty() {
false
} else {
let curr = current().clone();
curr.set_cpumask(cpumask);
// After setting the affinity, we need to check if current cpu matches
// the affinity. If not, we need to migrate the task to the correct CPU.
#[cfg(feature = "smp")]
if !cpumask.get(axhal::cpu::this_cpu_id()) {
const MIGRATION_TASK_STACK_SIZE: usize = 4096;
// Spawn a new migration task for migrating.
let migration_task = TaskInner::new(
move || crate::run_queue::migrate_entry(curr),
"migration-task".into(),
MIGRATION_TASK_STACK_SIZE,
)
.into_arc();
// Migrate the current task to the correct CPU using the migration task.
current_run_queue::<NoPreemptIrqSave>().migrate_current(migration_task);
assert!(cpumask.get(axhal::cpu::this_cpu_id()), "Migration failed");
}
true
}
}
/// Current task gives up the CPU time voluntarily, and switches to another
/// ready task.
pub fn yield_now() {
current_run_queue::<NoPreemptIrqSave>().yield_current()
}
/// Current task is going to sleep for the given duration.
///
/// If the feature `irq` is not enabled, it uses busy-wait instead.
pub fn sleep(dur: core::time::Duration) {
sleep_until(axhal::time::wall_time() + dur);
}
/// Current task is going to sleep, it will be woken up at the given deadline.
///
/// If the feature `irq` is not enabled, it uses busy-wait instead.
pub fn sleep_until(deadline: axhal::time::TimeValue) {
#[cfg(feature = "irq")]
current_run_queue::<NoPreemptIrqSave>().sleep_until(deadline);
#[cfg(not(feature = "irq"))]
axhal::time::busy_wait_until(deadline);
}
/// Exits the current task.
pub fn exit(exit_code: i32) -> ! {
current_run_queue::<NoPreemptIrqSave>().exit_current(exit_code)
}
/// The idle task routine.
///
/// It runs an infinite loop that keeps calling [`yield_now()`].
pub fn run_idle() -> ! {
loop {
yield_now();
debug!("idle task: waiting for IRQs...");
#[cfg(feature = "irq")]
axhal::arch::wait_for_irqs();
}
}