Skip to main content

cubecl_server/device_events/
base.rs

1use cubecl_common::profile::Duration;
2
3use crate::driver::DriverError;
4
5/// The device event calls an [`EventFence`](super::EventFence) and an
6/// [`EventProfiler`](super::EventProfiler) are built from.
7///
8/// The C-family device APIs agree on an event model: a timestamp is recorded
9/// into a queue, the device stamps it as the queue reaches it, and the span
10/// between two of them is read back afterwards. One implementation per backend
11/// spells those calls; everything above them — a fence's lifetime, a profiling
12/// window, the anchor that places one on the host clock — is written once.
13///
14/// This is deliberately only what those two types need. Memory, launches and
15/// module loading have their own seams; widening this trait to cover them would
16/// make it the device API rather than the device's *event* API, and the name
17/// would stop being true.
18pub trait EventApi: Send + Sync + 'static {
19    /// A timestamp recorded on the device.
20    ///
21    /// A handle into the driver rather than thread-affine state: it is
22    /// recorded on the server's thread and read back on whichever thread waits
23    /// for it, so the backend asserts `Send + Sync` on its own newtype, where
24    /// the justification is specific enough to check.
25    type Event: Send + Sync;
26
27    /// The queue an event is recorded into.
28    ///
29    /// No `Send` bound: a stream is held by the profiler, which lives inside a
30    /// server the backend already asserts is `Send` as a whole. Only the event
31    /// crosses threads on its own.
32    type Stream: Copy;
33
34    /// The backend as a reader of a log line sees it.
35    const BACKEND: &'static str;
36
37    /// Create an event with timing enabled.
38    ///
39    /// Timing is the point: the flag a pure synchronization event would take
40    /// to disable it is exactly the timestamp a profiler reads back.
41    ///
42    /// # Errors
43    ///
44    /// [`DriverError`] when the driver refuses the event, which is when the
45    /// device is already in trouble or out of resources.
46    fn event_create() -> Result<Self::Event, DriverError>;
47
48    /// Release an event. Called from the event's own `Drop`.
49    ///
50    /// # Errors
51    ///
52    /// [`DriverError`], which the caller can only log: a destructor has
53    /// nowhere to report.
54    fn event_destroy(event: &mut Self::Event) -> Result<(), DriverError>;
55
56    /// Enqueue the timestamp on `stream`, returning once it is queued.
57    ///
58    /// # Errors
59    ///
60    /// [`DriverError`] when the stream will not take the event — a stream
61    /// recording a graph is the case callers screen for in advance.
62    fn event_record(event: &Self::Event, stream: Self::Stream) -> Result<(), DriverError>;
63
64    /// Block the calling thread until the device has reached `event`.
65    ///
66    /// # Errors
67    ///
68    /// [`DriverError`], which is the fault the wait revealed on the stream the
69    /// event was recorded into.
70    fn event_wait(event: &Self::Event) -> Result<(), DriverError>;
71
72    /// Device time from `start` to `end`. Both must have been reached.
73    ///
74    /// Implementations clamp a negative reading to zero rather than trusting
75    /// it: the device clock is monotonic, but a negative duration would panic
76    /// the conversion, and a profiler is not worth a panic.
77    ///
78    /// # Errors
79    ///
80    /// [`DriverError`], including the driver's own "not ready" when either
81    /// event has not been reached.
82    fn event_elapsed(start: &Self::Event, end: &Self::Event) -> Result<Duration, DriverError>;
83
84    /// Make `stream` wait for `event` on the device, without blocking the host.
85    ///
86    /// # Errors
87    ///
88    /// [`DriverError`] when the driver refuses the dependency.
89    fn stream_wait_event(stream: Self::Stream, event: &Self::Event) -> Result<(), DriverError>;
90
91    /// Block the host until every operation enqueued on `stream` has completed.
92    ///
93    /// # Errors
94    ///
95    /// [`DriverError`] when the stream's work failed.
96    fn stream_synchronize(stream: Self::Stream) -> Result<(), DriverError>;
97
98    /// Create a stream that does not synchronize with the legacy default one.
99    ///
100    /// # Errors
101    ///
102    /// [`DriverError`] when the driver will not create the stream.
103    fn stream_create_non_blocking() -> Result<Self::Stream, DriverError>;
104
105    /// Release a stream created by [`stream_create_non_blocking`](Self::stream_create_non_blocking).
106    ///
107    /// # Errors
108    ///
109    /// [`DriverError`], which the caller can only log: this runs from a `Drop`.
110    fn stream_destroy(stream: Self::Stream) -> Result<(), DriverError>;
111}