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}