compio_pool/cpu.rs
1//! Core enumeration and thread pinning — the helpers you need to stand up your
2//! own thread-per-core runtime around a [`Pool`](crate::Pool).
3
4pub use core_affinity::CoreId;
5
6/// The cores this process may run on, sorted by id, with duplicates removed.
7///
8/// This is the affinity mask, not the machine: under `taskset` or a cgroup
9/// cpuset only the permitted cores are returned, and that is the right set to
10/// put workers on.
11pub fn cores() -> Vec<CoreId> {
12 let mut cores = core_affinity::get_core_ids().unwrap_or_default();
13 cores.sort_by_key(|c| c.id);
14 cores.dedup_by_key(|c| c.id);
15 cores
16}
17
18/// Pin the calling thread to `core`. Returns whether the kernel accepted it.
19///
20/// Call it inside each thread's closure before you build that thread's runtime,
21/// so the ring and everything allocated after it belong to that core.
22pub fn pin_current(core: CoreId) -> bool {
23 core_affinity::set_for_current(core)
24}
25
26/// The `/proc/irq/<n>/smp_affinity` mask that names exactly `cpu`:
27/// comma-separated 32-bit hex words, most significant first.
28///
29/// `scripts/tune-nic.sh` writes the same format, so a worker's log line and the
30/// IRQ it should be fed by can be compared by eye.
31///
32/// ```
33/// assert_eq!(compio_pool::cpu::affinity_mask(3), "00000008");
34/// assert_eq!(compio_pool::cpu::affinity_mask(35), "00000008,00000000");
35/// ```
36pub fn affinity_mask(cpu: usize) -> String {
37 let words = cpu / 32 + 1;
38 let mut out = String::with_capacity(words * 9);
39 for w in (0..words).rev() {
40 let bits: u32 = if w == cpu / 32 { 1 << (cpu % 32) } else { 0 };
41 if !out.is_empty() {
42 out.push(',');
43 }
44 out.push_str(&format!("{bits:08x}"));
45 }
46 out
47}