kernel/scheduler/priority.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Fixed Priority Scheduler for Tock
6//!
7//! This scheduler assigns priority to processes based on their order in the
8//! `PROCESSES` array, and runs the highest priority process available at any
9//! point in time. Kernel tasks (bottom half interrupt handling / deferred call
10//! handling) always take priority over userspace processes.
11//!
12//! Notably, there is no need to enforce timeslices, as it is impossible for a
13//! process running to not be the highest priority process at any point while it
14//! is running. The only way for a process to longer be the highest priority is
15//! for an interrupt to occur, which will cause the process to stop running.
16
17use crate::deferred_call::DeferredCall;
18use crate::kernel::Kernel;
19use crate::platform::chip::Chip;
20use crate::process::ProcessId;
21use crate::process::StoppedExecutingReason;
22use crate::scheduler::{Scheduler, SchedulingDecision};
23use crate::utilities::cells::OptionalCell;
24
25/// Priority scheduler based on the order of processes in the `PROCESSES` array.
26pub struct PrioritySched {
27 kernel: &'static Kernel,
28 running: OptionalCell<ProcessId>,
29}
30
31impl PrioritySched {
32 pub const fn new(kernel: &'static Kernel) -> Self {
33 Self {
34 kernel,
35 running: OptionalCell::empty(),
36 }
37 }
38}
39
40impl<C: Chip> Scheduler<C> for PrioritySched {
41 fn next(&self) -> SchedulingDecision {
42 // Iterates in-order through the process array, always running the
43 // first process it finds that is ready to run. This enforces the
44 // priorities of all processes.
45 let next = self
46 .kernel
47 .get_process_iter()
48 .find(|&proc| proc.ready())
49 .map(|proc| proc.processid());
50 self.running.insert(next);
51
52 next.map_or(SchedulingDecision::TrySleep, |next| {
53 SchedulingDecision::RunProcess((next, None))
54 })
55 }
56
57 unsafe fn continue_process(&self, _: ProcessId, chip: &C) -> bool {
58 // In addition to checking for interrupts, also checks if any higher
59 // priority processes have become ready. This check is necessary because
60 // a system call by this process could make another process ready, if
61 // this app is communicating via IPC with a higher priority app.
62 !(chip.has_pending_interrupts()
63 || DeferredCall::has_tasks()
64 || self
65 .kernel
66 .get_process_iter()
67 .find(|proc| proc.ready())
68 .is_some_and(|ready_proc| {
69 self.running.map_or(false, |running| {
70 ready_proc.processid().index < running.index
71 })
72 }))
73 }
74
75 fn result(&self, _: StoppedExecutingReason, _: Option<u32>) {
76 self.running.clear()
77 }
78}