async_cuda_core/ffi/ptr.rs
1/// Represents a device-local pointer. Pointers qualify as device-local if they refer to memory that
2/// lives on the device, and not on the host.
3///
4/// # Safety
5///
6/// ## Null
7///
8/// Creating a null pointer is always unsafe, because any CUDA operations on null pointers can cause
9/// undefined behavior.
10///
11/// Use the `unsafe` function `Ptr::null` to create a null pointer in cases where usage is safe.
12pub struct DevicePtr(*mut std::ffi::c_void);
13
14impl DevicePtr {
15 /// Create from raw pointer.
16 #[inline]
17 pub fn from_raw(raw: *mut std::ffi::c_void) -> Self {
18 if !raw.is_null() {
19 DevicePtr(raw)
20 } else {
21 panic!("unexpected null pointer");
22 }
23 }
24
25 /// Create null pointer.
26 ///
27 /// # Safety
28 ///
29 /// This is unsafe because operating on a `null` pointer in CUDA code can cause crashes. In some
30 /// cases it is allowed though, for example, a `null` pointer can designate the default stream
31 /// in stream-related operations.
32 #[inline]
33 pub unsafe fn null() -> Self {
34 DevicePtr(std::ptr::null_mut())
35 }
36
37 /// Whether or not the device pointer is a null pointer.
38 #[inline]
39 pub fn is_null(&self) -> bool {
40 self.0.is_null()
41 }
42
43 /// Get the readonly pointer value.
44 #[inline(always)]
45 pub fn as_ptr(&self) -> *const std::ffi::c_void {
46 let DevicePtr(ptr) = *self;
47 ptr as *const std::ffi::c_void
48 }
49
50 /// Get the mutable pointer value.
51 #[inline(always)]
52 pub fn as_mut_ptr(&mut self) -> *mut std::ffi::c_void {
53 let DevicePtr(ptr) = *self;
54 ptr
55 }
56
57 /// Take the pointer from this wrapper and replace it with a null pointer.
58 ///
59 /// # Safety
60 ///
61 /// This operation is unsafe because it creates a null pointer.
62 ///
63 /// # Usage
64 ///
65 /// This function can be used inside [`Drop`] if it known that the pointer object will not be
66 /// used for the remainder of the function scope, and the object is to be dropped.
67 ///
68 /// # Example
69 ///
70 /// ```ignore
71 /// # use async_cuda_core::ffi::DevicePtr;
72 /// pub struct Object {
73 /// internal: DevicePtr,
74 /// }
75 ///
76 /// impl Drop for Object {
77 ///
78 /// fn drop(&mut self) {
79 /// // SAFETY: This is safe because `self` and `self.internal`
80 /// // are not used beyond this unsafe block.
81 /// let ptr = unsafe {
82 /// self.internal.take_raw();
83 /// };
84 /// // Propertly deallocate the pointer here and do *NOT* use
85 /// // use `self` for anything!
86 /// }
87 ///
88 /// }
89 /// ```
90 #[inline]
91 pub unsafe fn take(&mut self) -> DevicePtr {
92 DevicePtr(std::mem::replace(&mut self.0, std::ptr::null_mut()))
93 }
94}
95
96impl From<*mut std::ffi::c_void> for DevicePtr {
97 fn from(value: *mut std::ffi::c_void) -> Self {
98 DevicePtr(value)
99 }
100}
101
102impl std::fmt::Display for DevicePtr {
103 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
104 write!(f, "{:?}", self.0)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_it_holds_on() {
114 let fake = 0xffffffff as *mut std::ffi::c_void;
115 let ptr = DevicePtr::from_raw(fake);
116 assert_eq!(ptr.as_ptr(), 0xffffffff as *const std::ffi::c_void);
117 }
118
119 #[test]
120 #[should_panic]
121 fn test_it_panics_when_null() {
122 let _ = DevicePtr::from_raw(std::ptr::null_mut());
123 }
124
125 #[test]
126 fn test_null() {
127 let ptr = unsafe { DevicePtr::null() };
128 assert!(ptr.is_null());
129 assert_eq!(ptr.as_ptr(), std::ptr::null_mut());
130 }
131
132 #[test]
133 fn test_take_raw() {
134 let fake = 0xffffffff as *mut std::ffi::c_void;
135 let mut ptr = DevicePtr::from_raw(fake);
136 assert_eq!(
137 unsafe { ptr.take().as_ptr() },
138 0xffffffff as *const std::ffi::c_void,
139 );
140 }
141}