nvml_wrapper/high_level/event_loop.rs
1/*!
2A convenient abstraction for working with events.
3
4Simply register the devices you wish to receive events for and then compose
5a handler for the events. Event handling looks like this (details removed):
6
7```no_run
8# extern crate nvml_wrapper as nvml;
9#
10# #[cfg(target_os = "linux")]
11# fn main() {
12# example::actual_main().unwrap();
13# }
14#
15# #[cfg(target_os = "windows")]
16# fn main() {}
17#
18# #[cfg(target_os = "linux")]
19# mod example {
20# use nvml::Nvml;
21# use nvml::error::{NvmlError, NvmlErrorWithSource};
22# use nvml::high_level::EventLoopProvider;
23# use nvml::high_level::Event::*;
24#
25# pub fn actual_main() -> Result<(), NvmlErrorWithSource> {
26# let nvml = Nvml::init()?;
27# let device = nvml.device_by_index(0)?;
28# let mut event_loop = nvml.create_event_loop(vec![&device])?;
29#
30event_loop.run_forever(|event, state| match event {
31 // If there were no errors, extract the event
32 Ok(event) => match event {
33 ClockChange(device) => { /* ... */ },
34 PowerStateChange(device) => { /* ... */ },
35 _ => { /* ... */ }
36 },
37
38 // If there was an error, handle it
39 Err(error) => match error {
40 // If the error is `Unknown`, continue looping and hope for the best
41 NvmlError::Unknown => {},
42 // The other errors that can occur are almost guaranteed to mean that
43 // further looping will never be successful (`GpuLost` and
44 // `Uninitialized`), so we stop looping
45 _ => state.interrupt()
46 }
47});
48
49# Ok(())
50# }
51# }
52```
53
54The full, fleshed-out example can be viewed in the examples directory
55(`event_loop.rs`). Run it as follows:
56
57```bash
58cargo run --example event_loop
59```
60
61The functionality in this module is only available on Linux platforms; NVML does
62not support events on any other platform.
63*/
64
65use crate::bitmasks::event::EventTypes;
66use crate::enums::event::XidError;
67use crate::error::{NvmlError, NvmlErrorWithSource};
68use crate::struct_wrappers::event::EventData;
69use crate::Device;
70use crate::EventSet;
71use crate::Nvml;
72#[cfg(feature = "serde")]
73use serde_derive::{Deserialize, Serialize};
74
75// TODO: Tests
76
77/**
78Represents the event types that an `EventLoop` can gather for you.
79
80These are analagous to the constants in `bitmasks::event`.
81
82Checking to see if the `Device` within an `Event` is the same physical device as
83another `Device` that you have on hand can be accomplished via `Device.uuid()`.
84*/
85#[derive(Debug)]
86pub enum Event<'nvml> {
87 ClockChange(Device<'nvml>),
88 CriticalXidError(Device<'nvml>, XidError),
89 DoubleBitEccError(Device<'nvml>),
90 PowerStateChange(Device<'nvml>),
91 SingleBitEccError(Device<'nvml>),
92 /// Returned if none of the other `Events` are contained in the `EventData`
93 /// the `EventLoop` processes.
94 Unknown,
95}
96
97impl<'nvml> From<EventData<'nvml>> for Event<'nvml> {
98 fn from(struct_: EventData<'nvml>) -> Self {
99 if struct_.event_type.contains(EventTypes::CLOCK_CHANGE) {
100 Event::ClockChange(struct_.device)
101 } else if struct_.event_type.contains(EventTypes::CRITICAL_XID_ERROR) {
102 // We can unwrap here because we know `event_data` will be `Some`
103 // since the error is `CRITICAL_XID_ERROR`
104 Event::CriticalXidError(struct_.device, struct_.event_data.unwrap())
105 } else if struct_
106 .event_type
107 .contains(EventTypes::DOUBLE_BIT_ECC_ERROR)
108 {
109 Event::DoubleBitEccError(struct_.device)
110 } else if struct_.event_type.contains(EventTypes::PSTATE_CHANGE) {
111 Event::PowerStateChange(struct_.device)
112 } else if struct_
113 .event_type
114 .contains(EventTypes::SINGLE_BIT_ECC_ERROR)
115 {
116 Event::SingleBitEccError(struct_.device)
117 } else {
118 Event::Unknown
119 }
120 }
121}
122
123/**
124Holds the `EventSet` utilized within an event loop.
125
126A usage example is available (`examples/event_loop.rs`). It can be run as
127follows:
128
129```bash
130cargo run --example event_loop
131```
132*/
133pub struct EventLoop<'nvml> {
134 set: EventSet<'nvml>,
135}
136
137impl<'nvml> EventLoop<'nvml> {
138 /**
139 Register another device that this `EventLoop` should receive events for.
140
141 This method takes ownership of this struct and then hands it back to you if
142 everything went well with the registration process.
143
144 # Errors
145
146 * `Uninitialized`, if the library has not been successfully initialized
147 * `GpuLost`, if a GPU has fallen off the bus or is otherwise inaccessible
148 * `Unknown`, on any unexpected error
149
150 # Platform Support
151
152 Only supports Linux.
153 */
154 pub fn register_device(
155 mut self,
156 device: &'nvml Device<'nvml>,
157 ) -> Result<Self, NvmlErrorWithSource> {
158 self.set = device.register_events(device.supported_event_types()?, self.set)?;
159
160 Ok(self)
161 }
162
163 /**
164 Handle events with the given callback until the loop is manually interrupted.
165
166 # Errors
167
168 The function itself does not return anything. You will be given errors to
169 handle within your closure if they occur; events are handed to you wrapped
170 in a `Result`.
171
172 The errors that you will need to handle are:
173
174 * `Uninitialized`, if the library has not been successfully initialized
175 * `GpuLost`, if a GPU has fallen off the bus or is otherwise inaccessible
176 * `Unknown`, on any unexpected error
177
178 # Examples
179
180 See the `event_loop` example in the `examples` directory at the root.
181
182 # Platform Support
183
184 Only supports Linux.
185 */
186 pub fn run_forever<F>(&mut self, mut callback: F)
187 where
188 F: FnMut(Result<Event<'nvml>, NvmlError>, &mut EventLoopState),
189 {
190 let mut state = EventLoopState { interrupted: false };
191
192 loop {
193 if state.interrupted {
194 break;
195 };
196
197 match self.set.wait(1) {
198 Ok(data) => {
199 callback(Ok(data.into()), &mut state);
200 }
201 Err(NvmlError::Timeout) => continue,
202 value => callback(value.map(|d| d.into()), &mut state),
203 };
204 }
205 }
206
207 /// Obtain a reference to the `EventSet` contained within this struct.
208 pub fn as_inner(&'nvml self) -> &'nvml EventSet<'nvml> {
209 &(self.set)
210 }
211
212 /// Obtain a mutable reference to the `EventSet` contained within this
213 /// struct.
214 pub fn as_mut_inner(&'nvml mut self) -> &'nvml mut EventSet<'nvml> {
215 &mut (self.set)
216 }
217
218 /// Consumes this `EventLoop` and yields the `EventSet` contained within.
219 pub fn into_inner(self) -> EventSet<'nvml> {
220 self.set
221 }
222}
223
224impl<'nvml> From<EventSet<'nvml>> for EventLoop<'nvml> {
225 fn from(set: EventSet<'nvml>) -> Self {
226 Self { set }
227 }
228}
229
230/// Keeps track of whether an `EventLoop` is interrupted or not.
231#[derive(Debug, Clone, Eq, PartialEq, Hash)]
232#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
233pub struct EventLoopState {
234 interrupted: bool,
235}
236
237impl EventLoopState {
238 /// Call this to mark the loop as interrupted.
239 pub fn interrupt(&mut self) {
240 self.interrupted = true;
241 }
242}
243
244/// Adds a method to obtain an `EventLoop` to the `Nvml` struct.
245///
246/// `use` it at your leisure.
247pub trait EventLoopProvider {
248 // Thanks to Thinkofname for lifetime help, again :)
249 fn create_event_loop<'nvml>(
250 &'nvml self,
251 devices: Vec<&'nvml Device<'nvml>>,
252 ) -> Result<EventLoop<'nvml>, NvmlErrorWithSource>;
253}
254
255impl EventLoopProvider for Nvml {
256 /**
257 Create an event loop that will register itself to recieve events for the given
258 `Device`s.
259
260 This function creates an event set and registers each devices' supported event
261 types for it. The returned `EventLoop` struct then has methods that you can
262 call to actually utilize it.
263
264 # Errors
265
266 * `Uninitialized`, if the library has not been successfully initialized
267 * `GpuLost`, if any of the given `Device`s have fallen off the bus or are
268 otherwise inaccessible
269 * `Unknown`, on any unexpected error
270
271 # Platform Support
272
273 Only supports Linux.
274 */
275 fn create_event_loop<'nvml>(
276 &'nvml self,
277 devices: Vec<&Device<'nvml>>,
278 ) -> Result<EventLoop<'nvml>, NvmlErrorWithSource> {
279 let mut set = self.create_event_set()?;
280
281 for d in devices {
282 set = d.register_events(d.supported_event_types()?, set)?;
283 }
284
285 Ok(EventLoop { set })
286 }
287}