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}