1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
//! Windows-specific helpers

use crate::{
    cpu::cpuset::CpuSet,
    errors::{self, RawHwlocError},
    ffi::int,
    topology::Topology,
};
#[allow(unused)]
#[cfg(test)]
use similar_asserts::assert_eq;
use std::{ffi::c_uint, iter::FusedIterator, num::NonZeroUsize};

/// # Windows-specific helpers
///
/// These functions query Windows processor groups. These groups partition the
/// operating system into virtual sets of up to 64 neighbor PUs. Threads and
/// processes may only be bound inside a single group. Although Windows
/// processor groups may be exposed in the hwloc hierarchy as hwloc Groups,
/// they are also often merged into existing hwloc objects such as NUMA nodes
/// or Packages. This API provides explicit information about Windows processor
/// groups so that applications know whether binding to a large set of PUs may
/// fail because it spans over multiple Windows processor groups.
//
// --- Implementation details ---
//
// Upstream docs: https://hwloc.readthedocs.io/en/v2.9/group__hwlocality__windows.html
impl Topology {
    /// Number of Windows processor groups
    ///
    /// # Errors
    ///
    /// One reason why this function can fail is if the topology does not match
    /// the current system (e.g. loaded from another machine through XML).
    #[allow(clippy::missing_errors_doc)]
    #[doc(alias = "hwloc_windows_get_nr_processor_groups")]
    pub fn num_processor_groups(&self) -> Result<NonZeroUsize, RawHwlocError> {
        let count =
            // SAFETY: - Topology is trusted to contain a valid ptr (type invariant)
            //         - hwloc ops are trusted not to modify *const parameters
            //         - Per documentation, flags must be zero
            errors::call_hwloc_int_normal("hwloc_windows_get_nr_processor_groups", || unsafe {
                hwlocality_sys::hwloc_windows_get_nr_processor_groups(self.as_ptr(), 0)
            })?;
        let count = NonZeroUsize::new(int::expect_usize(count))
            .expect("Unexpected 0 processor group count");
        Ok(count)
    }

    /// Enumerate the cpusets of Windows processor groups
    ///
    /// # Errors
    ///
    /// One reason why this function can fail is if the topology does not match
    /// the current system (e.g. loaded from another machine through XML).
    #[allow(clippy::missing_errors_doc)]
    #[doc(alias = "hwloc_windows_get_processor_group_cpuset")]
    pub fn processor_groups(
        &self,
    ) -> Result<
        impl DoubleEndedIterator<Item = Result<CpuSet, RawHwlocError>>
            + Clone
            + ExactSizeIterator
            + FusedIterator
            + '_,
        RawHwlocError,
    > {
        Ok(
            (0..usize::from(self.num_processor_groups()?)).map(|pg_index| {
                let mut set = CpuSet::new();
                let pg_index = c_uint::try_from(pg_index)
                    .expect("Can't fail, pg_index upper bound comes from hwloc");
                errors::call_hwloc_int_normal(
                    "hwloc_windows_get_processor_group_cpuset",
                    // SAFETY: - Topology is trusted to contain a valid ptr
                    //           (type invariant)
                    //         - Bitmap is trusted to contain a valid ptr
                    //           (type invariant)
                    //         - hwloc ops are trusted not to modify *const
                    //           parameters
                    //         - hwloc ops are trusted to keep *mut parameters
                    //           in a valid state unless stated otherwise
                    //         - pg_index is in bounds by construction
                    //         - Per documentation, flags must be zero
                    || unsafe {
                        hwlocality_sys::hwloc_windows_get_processor_group_cpuset(
                            self.as_ptr(),
                            pg_index,
                            set.as_mut_ptr(),
                            0,
                        )
                    },
                )?;
                Ok(set)
            }),
        )
    }
}