xlog_cuda/
arrow_device.rs1use std::ffi::c_void;
2use std::sync::Arc;
3
4use arrow::ffi::{FFI_ArrowArray, FFI_ArrowSchema};
5
6use crate::memory::{CudaBuffer, TrackedCudaSlice};
7
8pub const ARROW_DEVICE_CUDA: i32 = 2;
9
10pub(crate) struct ArrowCudaAllocation {
11 _buffer: Arc<CudaBuffer>,
12 _extra: Vec<TrackedCudaSlice<u8>>,
13}
14
15impl ArrowCudaAllocation {
16 pub(crate) fn new(buffer: Arc<CudaBuffer>, extra: Vec<TrackedCudaSlice<u8>>) -> Self {
17 Self {
18 _buffer: buffer,
19 _extra: extra,
20 }
21 }
22}
23
24unsafe impl Send for ArrowCudaAllocation {}
27unsafe impl Sync for ArrowCudaAllocation {}
28
29#[repr(C)]
30pub struct ArrowDeviceArray {
31 pub device_type: i32,
32 pub device_id: i32,
33 pub array: *mut FFI_ArrowArray,
34 pub schema: *mut FFI_ArrowSchema,
35 pub release: Option<unsafe extern "C" fn(*mut ArrowDeviceArray)>,
36 pub private_data: *mut c_void,
37}
38
39unsafe extern "C" fn release_arrow_device_array(ptr: *mut ArrowDeviceArray) {
40 if ptr.is_null() {
41 return;
42 }
43 let dev = &mut *ptr;
44 if !dev.array.is_null() {
45 unsafe {
47 drop(Box::from_raw(dev.array));
48 }
49 }
50 if !dev.schema.is_null() {
51 unsafe {
53 drop(Box::from_raw(dev.schema));
54 }
55 }
56 if !dev.private_data.is_null() {
57 unsafe {
59 drop(Box::from_raw(
60 dev.private_data.cast::<ArrowCudaAllocation>(),
61 ));
62 }
63 }
64 dev.release = None;
65}
66
67pub struct ArrowDeviceArrayOwned {
68 ptr: *mut ArrowDeviceArray,
69}
70
71unsafe impl Send for ArrowDeviceArrayOwned {}
75unsafe impl Sync for ArrowDeviceArrayOwned {}
76
77impl ArrowDeviceArrayOwned {
78 pub fn as_ptr(&self) -> *mut ArrowDeviceArray {
79 self.ptr
80 }
81
82 pub fn into_raw(self) -> *mut ArrowDeviceArray {
83 let ptr = self.ptr;
84 std::mem::forget(self);
85 ptr
86 }
87
88 pub unsafe fn from_raw(ptr: *mut ArrowDeviceArray) -> Self {
98 Self { ptr }
99 }
100}
101
102impl Drop for ArrowDeviceArrayOwned {
103 fn drop(&mut self) {
104 unsafe {
106 if !self.ptr.is_null() {
107 if let Some(release) = (*self.ptr).release {
108 release(self.ptr);
109 }
110 }
111 }
112 }
113}
114
115impl ArrowDeviceArray {
116 #[expect(
117 clippy::new_ret_no_self,
118 reason = "the Arrow C device ABI constructor returns its paired FFI schema and array handles"
119 )]
120 pub unsafe fn new(
128 device_type: i32,
129 device_id: i32,
130 array: *mut FFI_ArrowArray,
131 schema: *mut FFI_ArrowSchema,
132 ) -> ArrowDeviceArrayOwned {
133 let dev = ArrowDeviceArray {
134 device_type,
135 device_id,
136 array,
137 schema,
138 release: Some(release_arrow_device_array),
139 private_data: std::ptr::null_mut(),
140 };
141 ArrowDeviceArrayOwned {
142 ptr: Box::into_raw(Box::new(dev)),
143 }
144 }
145}
146
147pub struct ArrowDeviceImport {
152 _data: arrow::array::ArrayData,
153 _outer: Option<ArrowDeviceArrayOwned>,
155}
156
157impl ArrowDeviceImport {
158 pub fn new(data: arrow::array::ArrayData) -> Self {
159 Self {
160 _data: data,
161 _outer: None,
162 }
163 }
164
165 #[cfg(any(test, feature = "arrow-device-import"))]
166 pub(crate) fn from_ffi(data: arrow::array::ArrayData, outer: ArrowDeviceArrayOwned) -> Self {
167 Self {
168 _data: data,
169 _outer: Some(outer),
170 }
171 }
172
173 #[cfg(feature = "arrow-device-import")]
174 pub(crate) fn data(&self) -> &arrow::array::ArrayData {
175 &self._data
176 }
177}
178
179impl ArrowDeviceArrayOwned {
180 pub unsafe fn into_ffi_parts(self) -> (i32, i32, Self, FFI_ArrowArray, FFI_ArrowSchema) {
186 let dev = &mut *self.ptr;
187 let device_type = dev.device_type;
188 let device_id = dev.device_id;
189
190 let array = FFI_ArrowArray::from_raw(dev.array);
194 let schema = FFI_ArrowSchema::from_raw(dev.schema);
195 (device_type, device_id, self, array, schema)
196 }
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use std::sync::atomic::{AtomicUsize, Ordering};
203
204 unsafe extern "C" fn release_outer(ptr: *mut ArrowDeviceArray) {
205 let owner = unsafe { Box::from_raw(ptr) };
206 drop(unsafe { Box::from_raw(owner.array) });
209 drop(unsafe { Box::from_raw(owner.schema) });
210 let drops = unsafe { Box::from_raw(owner.private_data.cast::<Arc<AtomicUsize>>()) };
211 drops.fetch_add(1, Ordering::SeqCst);
212 }
213
214 #[test]
215 fn ffi_import_keeps_outer_producer_alive_until_last_import_owner() {
216 let drops = Arc::new(AtomicUsize::new(0));
217 let data = arrow::array::ArrayData::new_empty(&arrow::datatypes::DataType::Int32);
218 let (array, schema) = arrow::ffi::to_ffi(&data).unwrap();
219 let raw = Box::into_raw(Box::new(ArrowDeviceArray {
220 device_type: ARROW_DEVICE_CUDA,
221 device_id: 0,
222 array: Box::into_raw(Box::new(array)),
223 schema: Box::into_raw(Box::new(schema)),
224 release: Some(release_outer),
225 private_data: Box::into_raw(Box::new(Arc::clone(&drops))).cast(),
226 }));
227 let device = unsafe { ArrowDeviceArrayOwned::from_raw(raw) };
230 let (_, _, outer, array, schema) = unsafe { device.into_ffi_parts() };
231 assert_eq!(drops.load(Ordering::SeqCst), 0);
232 let parsed = unsafe { arrow::ffi::from_ffi(array, &schema) }.unwrap();
233 drop(schema);
234 let imported = Arc::new(ArrowDeviceImport::from_ffi(parsed, outer));
235 let alias = Arc::clone(&imported);
236 drop(imported);
237 assert_eq!(drops.load(Ordering::SeqCst), 0);
238 drop(alias);
239 assert_eq!(drops.load(Ordering::SeqCst), 1);
240 }
241}