cubecl_common/device/handle/
mod.rs1mod base;
2
3pub use base::*;
4
5use crate::device::{DeviceId, DeviceService, ServerUtilitiesHandle, ServiceId};
6use core::any::Any;
7
8#[cfg(feature = "std")]
9#[allow(dead_code)]
10mod channel;
11
12#[cfg(feature = "std")]
13mod placement;
14
15#[cfg(feature = "std")]
16pub use placement::ProcessCpus;
17
18#[cfg(feature = "std")]
19mod exit;
20
21#[allow(dead_code)]
22mod mutex;
23
24#[cfg(feature = "std")]
25#[allow(dead_code)]
26mod reentrant;
27
28#[cfg(all(feature = "std", multi_threading))]
29type Inner = channel::ChannelDeviceHandle;
30#[cfg(all(feature = "std", not(multi_threading)))]
32type Inner = reentrant::ReentrantMutexDeviceHandle;
33#[cfg(all(not(feature = "std"), not(multi_threading)))]
34type Inner = mutex::MutexDeviceHandle;
35
36pub struct DeviceHandle<S: ?Sized, I: DeviceHandleSpec = Inner> {
43 handle: I,
44 service: ServiceId,
45 cast: fn(&mut dyn Any) -> &mut S,
46}
47
48impl<S: ?Sized, I: DeviceHandleSpec> Clone for DeviceHandle<S, I> {
49 fn clone(&self) -> Self {
50 Self {
51 handle: self.handle.clone(),
52 service: self.service,
53 cast: self.cast,
54 }
55 }
56}
57
58fn downcast<S: 'static>(state: &mut dyn Any) -> &mut S {
60 state
61 .downcast_mut::<S>()
62 .expect("State type mismatch in the device registry")
63}
64
65#[allow(missing_docs)]
66impl<S: DeviceService, I: DeviceHandleSpec> DeviceHandle<S, I> {
67 pub fn insert(device_id: DeviceId, service: S) -> Result<Self, ServiceCreationError> {
68 Ok(Self {
69 handle: I::insert::<S>(device_id, service)?,
70 service: ServiceId::of::<S>(device_id),
71 cast: downcast::<S>,
72 })
73 }
74
75 pub fn new(device_id: DeviceId) -> Self {
76 Self {
77 handle: I::new::<S>(device_id),
78 service: ServiceId::of::<S>(device_id),
79 cast: downcast::<S>,
80 }
81 }
82
83 pub fn close(device_id: DeviceId) -> Result<(), ServiceCloseError> {
97 I::close::<S>(device_id)
98 }
99
100 #[cfg(multi_threading)]
109 pub fn close_at_exit(device_id: DeviceId) {
110 exit::close_at_exit(device_id, core::any::TypeId::of::<S>(), Self::close);
111 }
112}
113
114#[allow(missing_docs)]
115impl<S: ?Sized + 'static, I: DeviceHandleSpec> DeviceHandle<S, I> {
116 pub const fn is_blocking() -> bool {
117 I::BLOCKING
118 }
119
120 pub fn seen_as<T: ?Sized>(self, cast: fn(&mut dyn Any) -> &mut T) -> DeviceHandle<T, I> {
123 DeviceHandle {
124 handle: self.handle,
125 service: self.service,
126 cast,
127 }
128 }
129
130 pub fn device_id(&self) -> DeviceId {
131 self.handle.device_id()
132 }
133
134 pub fn service_id(&self) -> ServiceId {
137 self.service
138 }
139
140 pub fn utilities(&self) -> ServerUtilitiesHandle {
141 self.handle.utilities()
142 }
143
144 pub fn submit_blocking<'a, R: Send, T: FnOnce(&mut S) -> R + Send + 'a>(
145 &self,
146 task: T,
147 ) -> Result<R, CallError> {
148 let cast = self.cast;
149 self.handle.submit_blocking(move |state| task(cast(state)))
150 }
151
152 pub fn submit<T: FnOnce(&mut S) + Send + 'static>(&self, task: T) {
153 let cast = self.cast;
154 self.handle.submit(move |state| task(cast(state)))
155 }
156
157 pub fn spawn<R: Send + 'static, T: FnOnce(&mut S) -> R + Send + 'static>(
159 &self,
160 task: T,
161 ) -> SpawnedCall<R> {
162 let cast = self.cast;
163 self.handle.spawn(move |state| task(cast(state)))
164 }
165
166 pub fn flush_queue(&self) {
167 self.handle.flush_queue();
168 }
169
170 pub fn exclusive<R: Send, T: FnOnce() -> R + Send>(&self, task: T) -> Result<R, CallError> {
171 self.handle.exclusive(task)
172 }
173
174 pub fn shutdown(device_id: DeviceId) {
193 I::shutdown(device_id)
194 }
195}
196
197#[cfg(test)]
201struct ShutdownGuard {
202 device_id: DeviceId,
203 shutdown: fn(DeviceId),
204}
205
206#[cfg(test)]
207impl Drop for ShutdownGuard {
208 fn drop(&mut self) {
209 (self.shutdown)(self.device_id);
210 }
211}
212
213#[cfg(test)]
219fn next_test_device_id() -> DeviceId {
220 use core::sync::atomic::{AtomicU16, Ordering};
221
222 static NEXT: AtomicU16 = AtomicU16::new(0);
223
224 DeviceId {
225 type_id: 0,
226 index_id: NEXT.fetch_add(1, Ordering::Relaxed),
227 }
228}
229
230#[cfg(test)]
242pub(crate) struct DeviceFixture<H> {
243 handle: H,
244 _guard: ShutdownGuard,
245 device_id: DeviceId,
246}
247
248#[cfg(test)]
249impl<H> DeviceFixture<H> {
250 pub(crate) fn new(build: fn(DeviceId) -> H, shutdown: fn(DeviceId)) -> Self {
251 let device_id = next_test_device_id();
252
253 Self {
254 handle: build(device_id),
255 _guard: ShutdownGuard {
256 device_id,
257 shutdown,
258 },
259 device_id,
260 }
261 }
262
263 pub(crate) fn device_id(&self) -> DeviceId {
264 self.device_id
265 }
266}
267
268#[cfg(test)]
269impl<H> core::ops::Deref for DeviceFixture<H> {
270 type Target = H;
271
272 fn deref(&self) -> &Self::Target {
273 &self.handle
274 }
275}
276
277#[cfg(test)]
278mod tests_channel {
279 type DeviceHandle<S> = super::DeviceHandle<S, channel::ChannelDeviceHandle>;
280
281 include!("./tests.rs");
282 include!("./tests_recursive.rs");
283}
284
285#[cfg(test)]
286mod tests_mutex {
287 type DeviceHandle<S> = super::DeviceHandle<S, mutex::MutexDeviceHandle>;
288
289 include!("./tests.rs");
290}
291
292#[cfg(test)]
293mod tests_reentrant {
294 type DeviceHandle<S> = super::DeviceHandle<S, reentrant::ReentrantMutexDeviceHandle>;
295
296 include!("./tests.rs");
297 include!("./tests_recursive.rs");
298}