Skip to main content

ostd/task/
atomic_mode.rs

1// SPDX-License-Identifier: MPL-2.0
2
3//! Atomic Mode
4//!
5//! Multitasking, while powerful, can sometimes lead to undesirable
6//! or catastrophic consequences if being misused.
7//! For instance, a user of OSTD might accidentally write an IRQ handler
8//! that relies on mutexes,
9//! which could attempt to sleep within an interrupt context---something that must be avoided.
10//! Another common mistake is
11//! acquiring a spinlock in a task context and then attempting to yield or sleep,
12//! which can easily lead to deadlocks.
13//!
14//! To mitigate the risks associated with improper multitasking,
15//! we introduce the concept of atomic mode.
16//! Kernel code is considered to be running in atomic mode
17//! if one of the following conditions is met:
18//!
19//! 1. Task preemption is disabled, such as when a spinlock is held.
20//! 2. Local IRQs are disabled, such as during interrupt context.
21//!
22//! While in atomic mode,
23//! any attempt to perform "sleep-like" actions will trigger a panic:
24//!
25//! 1. Switching to another task.
26//! 2. Switching to user space.
27//!
28//! This module provides API to detect such "sleep-like" actions.
29
30use core::sync::atomic::Ordering;
31
32/// Marks a function as one that might sleep.
33///
34/// This function will panic if it is executed in atomic mode.
35#[track_caller]
36pub fn might_sleep() {
37    let preempt_count = super::preempt::cpu_local::get_guard_count();
38    let is_local_irq_enabled = crate::arch::irq::is_local_enabled();
39    if (preempt_count != 0 || !is_local_irq_enabled)
40        && !crate::IN_BOOTSTRAP_CONTEXT.load(Ordering::Relaxed)
41    {
42        panic!(
43            "This function might break atomic mode (preempt_count = {}, is_local_irq_enabled = {})",
44            preempt_count, is_local_irq_enabled
45        );
46    }
47}
48
49/// A marker trait for guard types that enforce the atomic mode.
50///
51/// Key kernel primitives such as `SpinLock` and `Rcu` rely on
52/// [the atomic mode](crate::task::atomic_mode) for correctness or soundness.
53/// The existence of such a guard guarantees that the current task is executing
54/// in the atomic mode.
55///
56/// It requires [`core::fmt::Debug`] by default to make it easier to derive
57/// [`Debug`] for types with `&dyn InAtomicMode`.
58///
59/// # Safety
60///
61/// The implementer must ensure that the atomic mode is maintained while
62/// the guard type is alive.
63///
64/// A guard is only valid evidence about the task that created it: it proves
65/// nothing about any other task. Therefore, such guards must be `!Send` and
66/// `!Sync`, so that the guards and their references are confined to the
67/// creating task.
68pub unsafe trait InAtomicMode: core::fmt::Debug {}
69
70/// Abstracts any type from which one can obtain a reference to an atomic-mode guard.
71pub trait AsAtomicModeGuard {
72    /// Returns a guard for the atomic mode.
73    fn as_atomic_mode_guard(&self) -> &dyn InAtomicMode;
74}
75
76impl<G: InAtomicMode> AsAtomicModeGuard for G {
77    fn as_atomic_mode_guard(&self) -> &dyn InAtomicMode {
78        self
79    }
80}
81
82impl AsAtomicModeGuard for dyn InAtomicMode + '_ {
83    fn as_atomic_mode_guard(&self) -> &dyn InAtomicMode {
84        self
85    }
86}