Skip to main content

zerodds_rt_linux/
affinity.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 ZeroDDS Contributors
3
4//! CPU-Affinity-API. Plattform-Routing nach `target_os`.
5//!
6//! Auf Linux delegiert dieses Modul an die internen `syscalls::pin_to_cpus`
7//! / `syscalls::get_cpus`. Auf anderen Targets gibt jede
8//! Funktion `Unsupported` zurueck.
9
10use std::io;
11
12/// Pinnt den **aufrufenden Thread** auf die angegebenen CPU-Indizes.
13///
14/// Reihenfolge im Slice ist irrelevant — der Kernel verwendet eine
15/// Bit-Mask. Doppelte Indizes werden silently dedupliziert.
16///
17/// # Errors
18/// * `InvalidInput` wenn `cpus.is_empty()` oder ein Index `>=
19///   CPU_SETSIZE` ist (Linux: 1024).
20/// * Kernel-Errno aus `sched_setaffinity(2)` — typisch `EINVAL`,
21///   wenn keine der CPUs online ist.
22/// * `Unsupported` auf Nicht-Linux-Targets.
23///
24/// # Beispiel
25/// ```no_run
26/// use zerodds_rt_linux::pin_current_thread_to_cpus;
27/// pin_current_thread_to_cpus(&[2, 3]).unwrap();
28/// ```
29pub fn pin_current_thread_to_cpus(cpus: &[usize]) -> io::Result<()> {
30    #[cfg(target_os = "linux")]
31    {
32        crate::syscalls::pin_to_cpus(cpus)
33    }
34    #[cfg(not(target_os = "linux"))]
35    {
36        let _ = cpus;
37        Err(io::Error::new(
38            io::ErrorKind::Unsupported,
39            "pin_current_thread_to_cpus requires Linux",
40        ))
41    }
42}
43
44/// Liefert die CPUs, auf denen der aufrufende Thread laufen darf.
45///
46/// # Errors
47/// Kernel-Errno aus `sched_getaffinity(2)`.
48/// `Unsupported` auf Nicht-Linux-Targets.
49pub fn current_thread_cpus() -> io::Result<std::vec::Vec<usize>> {
50    #[cfg(target_os = "linux")]
51    {
52        crate::syscalls::get_cpus()
53    }
54    #[cfg(not(target_os = "linux"))]
55    {
56        Err(io::Error::new(
57            io::ErrorKind::Unsupported,
58            "current_thread_cpus() requires Linux",
59        ))
60    }
61}
62
63#[cfg(test)]
64#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
65mod tests {
66    use super::*;
67
68    #[test]
69    #[cfg(not(target_os = "linux"))]
70    fn pin_returns_unsupported_off_linux() {
71        let err = pin_current_thread_to_cpus(&[0]).unwrap_err();
72        assert_eq!(err.kind(), io::ErrorKind::Unsupported);
73    }
74
75    #[test]
76    #[cfg(not(target_os = "linux"))]
77    fn cpus_returns_unsupported_off_linux() {
78        let err = current_thread_cpus().unwrap_err();
79        assert_eq!(err.kind(), io::ErrorKind::Unsupported);
80    }
81
82    #[test]
83    #[cfg(target_os = "linux")]
84    fn linux_round_trip_pin_then_read() {
85        let allowed = current_thread_cpus().expect("getaffinity");
86        let target = allowed[0];
87        pin_current_thread_to_cpus(&[target]).expect("setaffinity");
88        let after = current_thread_cpus().expect("getaffinity post");
89        assert_eq!(after, std::vec![target]);
90        // Restore.
91        pin_current_thread_to_cpus(&allowed).expect("restore");
92    }
93
94    #[test]
95    #[cfg(target_os = "linux")]
96    fn linux_empty_input_invalid() {
97        let err = pin_current_thread_to_cpus(&[]).unwrap_err();
98        assert_eq!(err.kind(), io::ErrorKind::InvalidInput);
99    }
100}