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}