capsules_system/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 kernel::capabilities::ProcessManagementCapability;
18use kernel::deferred_call::DeferredCall;
19use kernel::platform::chip::Chip;
20use kernel::process::ProcessId;
21use kernel::process::StoppedExecutingReason;
22use kernel::scheduler::{Scheduler, SchedulingDecision};
23use kernel::utilities::cells::OptionalCell;
24use kernel::Kernel;
25
26/// Priority scheduler based on the order of processes in the `PROCESSES` array.
27pub struct PrioritySched<CAP: ProcessManagementCapability> {
28 kernel: &'static Kernel,
29 running: OptionalCell<(usize, ProcessId)>,
30 cap: CAP,
31}
32
33impl<CAP: ProcessManagementCapability> PrioritySched<CAP> {
34 pub const fn new(kernel: &'static Kernel, cap: CAP) -> Self {
35 Self {
36 kernel,
37 running: OptionalCell::empty(),
38 cap,
39 }
40 }
41}
42
43impl<C: Chip, CAP: ProcessManagementCapability> Scheduler<C> for PrioritySched<CAP> {
44 fn next(&self) -> SchedulingDecision {
45 // Iterates in-order through the process array, always running the
46 // first process it finds that is ready to run. This enforces the
47 // priorities of all processes.
48 let next = self
49 .kernel
50 .process_iter_capability(&self.cap)
51 .enumerate()
52 .find(|(_i, proc)| proc.ready())
53 .map(|(i, proc)| (i, proc.processid()));
54 self.running.insert(next);
55
56 next.map_or(SchedulingDecision::TrySleep, |next| {
57 SchedulingDecision::RunProcess((next.1, None))
58 })
59 }
60
61 fn continue_process(&self, _: ProcessId, chip: &C) -> bool {
62 // In addition to checking for interrupts, also checks if any higher
63 // priority processes have become ready. This check is necessary because
64 // a system call by this process could make another process ready, if
65 // this app is communicating via IPC with a higher priority app.
66 !(chip.has_pending_interrupts()
67 || DeferredCall::has_tasks()
68 || self
69 .kernel
70 .process_iter_capability(&self.cap)
71 .enumerate()
72 .find(|(_i, proc)| proc.ready())
73 .is_some_and(|(i, _ready_proc)| {
74 self.running.map_or(false, |running| i < running.0)
75 }))
76 }
77
78 fn result(&self, _: StoppedExecutingReason, _: Option<u32>) {
79 self.running.clear()
80 }
81}