Skip to main content

windows_eco/
process.rs

1use core::fmt;
2
3use bitflags::bitflags;
4use windows::Win32::System::Threading::{
5    GetCurrentProcess, GetProcessInformation, PROCESS_POWER_THROTTLING_CURRENT_VERSION,
6    PROCESS_POWER_THROTTLING_EXECUTION_SPEED, PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION,
7    SetProcessInformation,
8};
9use winver::WindowsVersion;
10
11use crate::{PROCESS_POWER_THROTTLING, WIN11_22H2};
12
13bitflags! {
14    /// Process Power Throttling Control Mask flags
15    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
16    pub struct PowerThrottlingControlMask: u32 {
17        /// Controls execution speed throttling
18        const EXECUTION_SPEED = PROCESS_POWER_THROTTLING_EXECUTION_SPEED;
19        /// Controls ignore timer resolution throttling
20        const IGNORE_TIMER_RESOLUTION = PROCESS_POWER_THROTTLING_IGNORE_TIMER_RESOLUTION;
21    }
22}
23
24bitflags! {
25    /// Process Power Throttling State Mask flags
26    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
27    pub struct PowerThrottlingStateMask: u32 {
28        /// Execution speed is being throttled
29        const EXECUTION_SPEED = 0x1;
30        /// Timer resolution requests are being ignored
31        const IGNORE_TIMER_RESOLUTION = 0x4;
32    }
33}
34
35/// Represents the power throttling state of a process.
36#[repr(C)]
37#[derive(Debug, Clone, Copy)]
38pub struct ProcessPowerThrottlingState {
39    /// The version of this structure.
40    ///
41    /// Must be set to `PROCESS_POWER_THROTTLING_CURRENT_VERSION`
42    version: u32,
43    /// See [PowerThrottlingControlMask]
44    control_mask: u32,
45    /// See [PowerThrottlingStateMask]
46    state_mask: u32,
47}
48
49const _: () = assert!(core::mem::size_of::<ProcessPowerThrottlingState>() == 12);
50
51impl Default for ProcessPowerThrottlingState {
52    fn default() -> Self {
53        Self {
54            version: PROCESS_POWER_THROTTLING_CURRENT_VERSION,
55            control_mask: 0,
56            state_mask: 0,
57        }
58    }
59}
60
61macro_rules! check_win11_22h2 {
62    () => {{
63        let ver = WindowsVersion::from_ntdll_dll()?;
64        if ver.build < WIN11_22H2 {
65            return Err(crate::Error::NotAvailable(ver.build));
66        }
67    }};
68}
69
70/// Retrieves the current power throttling state of the calling process.
71pub(crate) fn get_power_throttling_state(
72    version: u32,
73) -> Result<ProcessPowerThrottlingState, crate::Error> {
74    check_win11_22h2!();
75
76    let mut process_power_throttling = ProcessPowerThrottlingState {
77        version,
78        ..Default::default()
79    };
80    let process_information_size = core::mem::size_of::<ProcessPowerThrottlingState>() as u32;
81
82    unsafe {
83        GetProcessInformation(
84            GetCurrentProcess(),
85            PROCESS_POWER_THROTTLING,
86            &mut process_power_throttling as *mut _ as *mut std::ffi::c_void,
87            process_information_size,
88        )?;
89    }
90
91    Ok(process_power_throttling)
92}
93
94/// Sets the current power throttling state of the calling process.
95pub(crate) fn set_power_throttling_state(
96    version: u32,
97    state_mask: u32,
98    control_mask: u32,
99) -> Result<(), crate::Error> {
100    check_win11_22h2!();
101
102    let mut process_power_throttling = ProcessPowerThrottlingState {
103        version,
104        state_mask,
105        control_mask,
106        ..Default::default()
107    };
108    let process_information_size = core::mem::size_of::<ProcessPowerThrottlingState>() as u32;
109
110    unsafe {
111        SetProcessInformation(
112            GetCurrentProcess(),
113            PROCESS_POWER_THROTTLING,
114            &mut process_power_throttling as *mut _ as *mut std::ffi::c_void,
115            process_information_size,
116        )?;
117    }
118
119    Ok(())
120}
121
122impl ProcessPowerThrottlingState {
123    /// Creates a new [ProcessPowerThrottlingState] with specified control and state masks
124    pub fn new(
125        control_mask: PowerThrottlingControlMask,
126        state_mask: PowerThrottlingStateMask,
127    ) -> Self {
128        Self {
129            control_mask: control_mask.bits(),
130            state_mask: state_mask.bits(),
131            ..Default::default()
132        }
133    }
134
135    /// Create [ProcessPowerThrottlingState] from current process
136    pub fn from_windows() -> Result<Self, crate::Error> {
137        get_power_throttling_state(PROCESS_POWER_THROTTLING_CURRENT_VERSION)
138    }
139
140    /// Applies this power throttling state to the current process
141    pub fn apply(&self) -> Result<(), crate::Error> {
142        set_power_throttling_state(self.version, self.state_mask, self.control_mask)
143    }
144
145    /// Creates a ProcessPowerThrottlingState that enables execution speed throttling
146    pub fn enable_execution_speed_throttling(&mut self) {
147        self.control_mask |= PowerThrottlingControlMask::EXECUTION_SPEED.bits();
148        self.state_mask |= PowerThrottlingStateMask::EXECUTION_SPEED.bits();
149    }
150
151    /// Creates a ProcessPowerThrottlingState that disables execution speed throttling
152    pub fn disable_execution_speed_throttling(&mut self) {
153        self.control_mask |= PowerThrottlingControlMask::EXECUTION_SPEED.bits();
154        self.state_mask |= PowerThrottlingStateMask::empty().bits();
155    }
156
157    /// Creates a ProcessPowerThrottlingState that enables timer resolution throttling
158    pub fn enable_timer_resolution_throttling(&mut self) {
159        self.control_mask |= PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION.bits();
160        self.state_mask |= PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION.bits();
161    }
162
163    /// Creates a ProcessPowerThrottlingState that disables timer resolution throttling
164    pub fn disable_timer_resolution_throttling(&mut self) {
165        self.control_mask |= PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION.bits();
166        self.state_mask |= PowerThrottlingStateMask::empty().bits();
167    }
168
169    /// Creates a ProcessPowerThrottlingState that enables all available throttling
170    pub fn enable_all_throttling(&mut self) {
171        self.control_mask |= PowerThrottlingControlMask::all().bits();
172        self.state_mask |= PowerThrottlingStateMask::all().bits();
173    }
174
175    /// Creates a ProcessPowerThrottlingState that disables all throttling
176    pub fn disable_all_throttling(&mut self) {
177        self.control_mask |= PowerThrottlingControlMask::all().bits();
178        self.state_mask |= PowerThrottlingStateMask::empty().bits();
179    }
180
181    /// Get control mask as bitflags
182    pub fn control_flags(&self) -> PowerThrottlingControlMask {
183        PowerThrottlingControlMask::from_bits_truncate(self.control_mask)
184    }
185
186    /// Get state mask as bitflags
187    pub fn state_flags(&self) -> PowerThrottlingStateMask {
188        PowerThrottlingStateMask::from_bits_truncate(self.state_mask)
189    }
190
191    /// Check if execution speed throttling is enabled
192    pub fn is_execution_speed_throttled(&self) -> bool {
193        self.state_flags()
194            .contains(PowerThrottlingStateMask::EXECUTION_SPEED)
195    }
196
197    /// Check if timer resolution throttling is enabled
198    pub fn is_ignore_timer_resolution_throttled(&self) -> bool {
199        self.state_flags()
200            .contains(PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION)
201    }
202
203    /// Check if execution speed control is enabled
204    pub fn is_execution_speed_controlled(&self) -> bool {
205        self.control_flags()
206            .contains(PowerThrottlingControlMask::EXECUTION_SPEED)
207    }
208
209    /// Check if timer resolution control is enabled
210    pub fn is_timer_resolution_controlled(&self) -> bool {
211        self.control_flags()
212            .contains(PowerThrottlingControlMask::IGNORE_TIMER_RESOLUTION)
213    }
214
215    /// Retrieve the underlying version of power throttling API
216    pub fn version(&self) -> u32 {
217        self.version
218    }
219
220    /// Sets the underlying version of power throttling API
221    pub fn set_version(&mut self, version: u32) {
222        self.version = version;
223    }
224}
225
226impl fmt::Display for ProcessPowerThrottlingState {
227    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
228        let state_flags = self.state_flags();
229        let control_flags = self.control_flags();
230
231        if state_flags.is_empty() {
232            write!(f, "No Throttling Active")
233        } else {
234            let mut descriptions = Vec::new();
235
236            if state_flags.contains(PowerThrottlingStateMask::EXECUTION_SPEED) {
237                descriptions.push("Execution Speed Throttled");
238            }
239
240            if state_flags.contains(PowerThrottlingStateMask::IGNORE_TIMER_RESOLUTION) {
241                descriptions.push("Timer Resolution Throttled");
242            }
243
244            write!(f, "{}", descriptions.join(", "))?;
245
246            // Show controlled flags if different from state
247            if !control_flags.is_empty()
248                && control_flags
249                    != PowerThrottlingControlMask::from_bits_truncate(state_flags.bits())
250            {
251                write!(f, " (Controlled: {})", control_flags)?;
252            }
253
254            Ok(())
255        }
256    }
257}
258
259impl fmt::Display for PowerThrottlingControlMask {
260    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261        if self.is_empty() {
262            write!(f, "None")
263        } else {
264            let mut flags = Vec::new();
265            if self.contains(Self::EXECUTION_SPEED) {
266                flags.push("ExecutionSpeed");
267            }
268            if self.contains(Self::IGNORE_TIMER_RESOLUTION) {
269                flags.push("TimerResolution");
270            }
271            write!(f, "{}", flags.join("|"))
272        }
273    }
274}
275
276impl fmt::Display for PowerThrottlingStateMask {
277    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278        if self.is_empty() {
279            write!(f, "None")
280        } else {
281            let mut flags = Vec::new();
282            if self.contains(Self::EXECUTION_SPEED) {
283                flags.push("ExecutionSpeed");
284            }
285            if self.contains(Self::IGNORE_TIMER_RESOLUTION) {
286                flags.push("TimerResolution");
287            }
288            write!(f, "{}", flags.join("|"))
289        }
290    }
291}