Skip to main content

baracuda_driver/
event.rs

1//! CUDA events — lightweight synchronization objects you can record on
2//! a stream and later wait on, or use to measure elapsed device time.
3
4use std::sync::Arc;
5
6use baracuda_cuda_sys::types::CUevent_flags;
7use baracuda_cuda_sys::{driver, CUevent};
8
9use crate::context::Context;
10use crate::error::{check, Result};
11use crate::stream::Stream;
12
13/// A CUDA event.
14#[derive(Clone)]
15pub struct Event {
16    inner: Arc<EventInner>,
17}
18
19struct EventInner {
20    handle: CUevent,
21    context: Context,
22}
23
24// SAFETY: CUevent is documented safe for multi-thread use.
25unsafe impl Send for EventInner {}
26unsafe impl Sync for EventInner {}
27
28impl core::fmt::Debug for EventInner {
29    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
30        f.debug_struct("Event")
31            .field("handle", &self.handle)
32            .finish_non_exhaustive()
33    }
34}
35
36impl core::fmt::Debug for Event {
37    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38        self.inner.fmt(f)
39    }
40}
41
42impl Event {
43    /// Create a new event with default flags (timing enabled).
44    pub fn new(context: &Context) -> Result<Self> {
45        Self::with_flags(context, CUevent_flags::DEFAULT)
46    }
47
48    /// Create an event optimized for synchronization (no timing).
49    pub fn no_timing(context: &Context) -> Result<Self> {
50        Self::with_flags(context, CUevent_flags::DISABLE_TIMING)
51    }
52
53    /// Create an event with raw flags (see [`CUevent_flags`]).
54    pub fn with_flags(context: &Context, flags: u32) -> Result<Self> {
55        context.set_current()?;
56        let d = driver()?;
57        let cu = d.cu_event_create()?;
58        let mut event: CUevent = core::ptr::null_mut();
59        check(unsafe { cu(&mut event, flags) })?;
60        Ok(Self {
61            inner: Arc::new(EventInner {
62                handle: event,
63                context: context.clone(),
64            }),
65        })
66    }
67
68    /// Record this event on the given stream. The event "happens" when all
69    /// prior work on `stream` has completed.
70    pub fn record(&self, stream: &Stream) -> Result<()> {
71        let d = driver()?;
72        let cu = d.cu_event_record()?;
73        check(unsafe { cu(self.inner.handle, stream.as_raw()) })
74    }
75
76    /// As [`record`](Self::record) but with a raw CUDA event-record flags
77    /// bitmask. See `CU_EVENT_RECORD_*` in NVIDIA's headers.
78    pub fn record_with_flags(&self, stream: &Stream, flags: u32) -> Result<()> {
79        let d = driver()?;
80        let cu = d.cu_event_record_with_flags()?;
81        check(unsafe { cu(self.inner.handle, stream.as_raw(), flags) })
82    }
83
84    /// Block the calling host thread until this event has completed.
85    pub fn synchronize(&self) -> Result<()> {
86        let d = driver()?;
87        let cu = d.cu_event_synchronize()?;
88        check(unsafe { cu(self.inner.handle) })
89    }
90
91    /// `Ok(true)` if the event has completed.
92    pub fn is_complete(&self) -> Result<bool> {
93        use baracuda_cuda_sys::CUresult;
94        let d = driver()?;
95        let cu = d.cu_event_query()?;
96        match unsafe { cu(self.inner.handle) } {
97            CUresult::SUCCESS => Ok(true),
98            CUresult::ERROR_NOT_READY => Ok(false),
99            other => Err(crate::error::Error::Status { status: other }),
100        }
101    }
102
103    /// Elapsed milliseconds of device work between `start` (recorded first)
104    /// and `end` (recorded later). Both events must have been created with
105    /// timing enabled.
106    pub fn elapsed_time_ms(start: &Event, end: &Event) -> Result<f32> {
107        let d = driver()?;
108        let cu = d.cu_event_elapsed_time()?;
109        let mut ms: f32 = 0.0;
110        check(unsafe { cu(&mut ms, start.inner.handle, end.inner.handle) })?;
111        Ok(ms)
112    }
113
114    /// The [`Context`] this event lives in.
115    #[inline]
116    pub fn context(&self) -> &Context {
117        &self.inner.context
118    }
119
120    /// Raw `CUevent`. Use with care.
121    #[inline]
122    pub fn as_raw(&self) -> CUevent {
123        self.inner.handle
124    }
125}
126
127impl Drop for EventInner {
128    fn drop(&mut self) {
129        if let Ok(d) = driver() {
130            if let Ok(cu) = d.cu_event_destroy() {
131                let _ = unsafe { cu(self.handle) };
132            }
133        }
134    }
135}