kernel/scheduler/priority.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
// Licensed under the Apache License, Version 2.0 or the MIT License.
// SPDX-License-Identifier: Apache-2.0 OR MIT
// Copyright Tock Contributors 2022.
//! Fixed Priority Scheduler for Tock
//!
//! This scheduler assigns priority to processes based on their order in the
//! `PROCESSES` array, and runs the highest priority process available at any
//! point in time. Kernel tasks (bottom half interrupt handling / deferred call
//! handling) always take priority over userspace processes.
//!
//! Notably, there is no need to enforce timeslices, as it is impossible for a
//! process running to not be the highest priority process at any point while it
//! is running. The only way for a process to longer be the highest priority is
//! for an interrupt to occur, which will cause the process to stop running.
use crate::deferred_call::DeferredCall;
use crate::kernel::Kernel;
use crate::platform::chip::Chip;
use crate::process::ProcessId;
use crate::process::StoppedExecutingReason;
use crate::scheduler::{Scheduler, SchedulingDecision};
use crate::utilities::cells::OptionalCell;
/// Priority scheduler based on the order of processes in the `PROCESSES` array.
pub struct PrioritySched {
kernel: &'static Kernel,
running: OptionalCell<ProcessId>,
}
impl PrioritySched {
pub const fn new(kernel: &'static Kernel) -> Self {
Self {
kernel,
running: OptionalCell::empty(),
}
}
}
impl<C: Chip> Scheduler<C> for PrioritySched {
fn next(&self) -> SchedulingDecision {
// Iterates in-order through the process array, always running the
// first process it finds that is ready to run. This enforces the
// priorities of all processes.
let next = self
.kernel
.get_process_iter()
.find(|&proc| proc.ready())
.map(|proc| proc.processid());
self.running.insert(next);
next.map_or(SchedulingDecision::TrySleep, |next| {
SchedulingDecision::RunProcess((next, None))
})
}
unsafe fn continue_process(&self, _: ProcessId, chip: &C) -> bool {
// In addition to checking for interrupts, also checks if any higher
// priority processes have become ready. This check is necessary because
// a system call by this process could make another process ready, if
// this app is communicating via IPC with a higher priority app.
!(chip.has_pending_interrupts()
|| DeferredCall::has_tasks()
|| self
.kernel
.get_process_iter()
.find(|proc| proc.ready())
.is_some_and(|ready_proc| {
self.running.map_or(false, |running| {
ready_proc.processid().index < running.index
})
}))
}
fn result(&self, _: StoppedExecutingReason, _: Option<u32>) {
self.running.clear()
}
}