use std::{
fmt, mem,
os::raw::c_void,
ptr::{self, NonNull},
sync::Arc,
};
use crate::{
core::{
error::{self, Result},
native_teardown::drop_device_owned_handle,
types::CudfType,
},
ops::validation::{self, FiniteValidationProof, Int64RangeValidationProof},
runtime::{DeviceId, ExecutionBindingId, ExecutionPlacement, Runtime, execution_domain::ExecutionDomain},
};
pub struct DeviceBuffer {
handle: NonNull<cudf_sys::cudf_device_buffer_handle_state>,
domain: Arc<ExecutionDomain>,
}
pub struct ValidatedFloat32DeviceBuffer<'a> {
buffer: &'a DeviceBuffer,
proof: FiniteValidationProof,
}
impl ValidatedFloat32DeviceBuffer<'_> {
pub fn len(&self) -> usize {
self.proof.validated_values()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn as_ptr(&self) -> Result<*const c_void> {
self.buffer.as_ptr()
}
pub fn binding_id(&self) -> ExecutionBindingId {
self.proof.binding_id()
}
pub fn placement(&self) -> ExecutionPlacement {
self.proof.placement()
}
}
impl fmt::Debug for ValidatedFloat32DeviceBuffer<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ValidatedFloat32DeviceBuffer")
.field("len", &self.len())
.field("binding_id", &self.binding_id())
.finish_non_exhaustive()
}
}
pub struct ValidatedInt64DeviceBufferRange<'a> {
buffer: &'a DeviceBuffer,
proof: Int64RangeValidationProof,
}
impl ValidatedInt64DeviceBufferRange<'_> {
pub fn len(&self) -> usize {
self.proof.validated_values()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn min_inclusive(&self) -> i64 {
self.proof.min_inclusive()
}
pub fn max_exclusive(&self) -> i64 {
self.proof.max_exclusive()
}
pub fn as_ptr(&self) -> Result<*const c_void> {
self.buffer.as_ptr()
}
pub fn binding_id(&self) -> ExecutionBindingId {
self.proof.binding_id()
}
pub fn placement(&self) -> ExecutionPlacement {
self.proof.placement()
}
}
impl fmt::Debug for ValidatedInt64DeviceBufferRange<'_> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("ValidatedInt64DeviceBufferRange")
.field("len", &self.len())
.field("min_inclusive", &self.min_inclusive())
.field("max_exclusive", &self.max_exclusive())
.field("binding_id", &self.binding_id())
.finish_non_exhaustive()
}
}
unsafe impl Send for DeviceBuffer {}
unsafe impl Sync for DeviceBuffer {}
impl DeviceBuffer {
fn create_on_current_device(size_bytes: usize, domain: Arc<ExecutionDomain>) -> Result<Self> {
let mut handle = ptr::null_mut();
let exec_options = domain.enter_execution()?;
error::check_status_in(
unsafe { cudf_sys::cudf_device_buffer_create_ex(size_bytes, exec_options.options(), &mut handle) },
"device_buffer::create_on_current_device",
)?;
Ok(Self {
handle: error::require_non_null(handle, "device_buffer::create_on_current_device", "device buffer")?,
domain,
})
}
pub(crate) fn new_in_runtime(runtime: &Runtime, size_bytes: usize) -> Result<Self> {
let domain = Arc::clone(runtime.execution_domain());
Self::create_on_current_device(size_bytes, domain)
}
pub(crate) fn from_host_data_in_runtime<T: CudfType>(runtime: &Runtime, data: &[T]) -> Result<Self> {
let domain = Arc::clone(runtime.execution_domain());
let byte_len = mem::size_of_val(data);
let buf = Self::create_on_current_device(byte_len, domain)?;
if byte_len > 0 {
let exec_options = buf.domain.enter_execution()?;
error::check_status_in(
unsafe {
cudf_sys::cudf_device_buffer_copy_from_host_ex(
buf.handle.as_ptr(),
data.as_ptr() as *const c_void,
byte_len,
exec_options.options(),
)
},
"device_buffer::from_host_data_in_runtime",
)?;
}
Ok(buf)
}
pub(crate) fn copy_from_host_range_async(&mut self, dst_offset: usize, data: &[u8]) -> Result<()> {
if data.is_empty() {
return Ok(());
}
let exec_options = self.domain.enter_execution()?;
error::check_status_in(
unsafe {
cudf_sys::cudf_device_buffer_copy_from_host_range_async_ex(
self.handle.as_ptr(),
dst_offset,
data.as_ptr() as *const c_void,
data.len(),
exec_options.options(),
)
},
"device_buffer::copy_from_host_range_async",
)
}
pub fn size_bytes(&self) -> Result<usize> {
let mut out: usize = 0;
error::check_status_in(
unsafe { cudf_sys::cudf_device_buffer_size(self.handle.as_ptr(), &mut out) },
"device_buffer::size_bytes",
)?;
Ok(out)
}
pub fn is_empty(&self) -> Result<bool> {
Ok(self.size_bytes()? == 0)
}
pub fn as_ptr(&self) -> Result<*const c_void> {
let mut out: *mut c_void = ptr::null_mut();
error::check_status_in(
unsafe { cudf_sys::cudf_device_buffer_data(self.handle.as_ptr(), &mut out) },
"device_buffer::as_ptr",
)?;
Ok(out.cast_const())
}
pub fn as_mut_ptr(&mut self) -> Result<*mut c_void> {
let mut out: *mut c_void = ptr::null_mut();
error::check_status_in(
unsafe { cudf_sys::cudf_device_buffer_data(self.handle.as_ptr(), &mut out) },
"device_buffer::as_mut_ptr",
)?;
Ok(out)
}
pub fn device(&self) -> DeviceId {
self.domain.device_id()
}
pub fn placement(&self) -> ExecutionPlacement {
self.domain.placement()
}
pub fn binding_id(&self) -> ExecutionBindingId {
self.domain.binding_id()
}
pub fn validate_finite_float32(&self) -> Result<ValidatedFloat32DeviceBuffer<'_>> {
let proof = validation::validate_finite_float32_buffer(self)
.map_err(|error| error.reanchor("device_buffer::validate_finite_float32"))?;
Ok(ValidatedFloat32DeviceBuffer { buffer: self, proof })
}
pub fn validate_finite_float64(&self) -> Result<()> {
validation::validate_finite_float64_buffer(self)
.map(|_| ())
.map_err(|error| error.reanchor("device_buffer::validate_finite_float64"))
}
pub fn validate_int64_range(
&self,
min_inclusive: i64,
max_exclusive: i64,
) -> Result<ValidatedInt64DeviceBufferRange<'_>> {
let proof = validation::validate_int64_buffer_range(self, min_inclusive, max_exclusive)
.map_err(|error| error.reanchor("device_buffer::validate_int64_range"))?;
Ok(ValidatedInt64DeviceBufferRange { buffer: self, proof })
}
pub fn validate_int64_range_or_sentinel(
&self,
min_inclusive: i64,
max_exclusive: i64,
sentinel: i64,
) -> Result<()> {
validation::validate_int64_buffer_range_or_sentinel(self, min_inclusive, max_exclusive, sentinel)
.map_err(|error| error.reanchor("device_buffer::validate_int64_range_or_sentinel"))
}
pub fn add_scalar_to_float32(&mut self, scalar: f32) -> Result<()> {
validation::add_scalar_to_float32_buffer(self, scalar)
}
pub fn fill_int64_where_float32_infinite(&mut self, paired_values: &Self, fill_value: i64) -> Result<()> {
validation::fill_int64_where_float32_infinite(self, paired_values, fill_value)
.map_err(|error| error.reanchor("device_buffer::fill_int64_where_float32_infinite"))
}
pub fn replace_int64_value_and_set_paired_float32_infinite(
&mut self,
paired_values: &mut Self,
from: i64,
to: i64,
) -> Result<()> {
validation::replace_int64_value_and_set_paired_float32_infinite(self, paired_values, from, to)
}
pub fn validate_finite_float32_unless_int64_equals(&self, paired_values: &Self, excluded_value: i64) -> Result<()> {
validation::validate_finite_float32_unless_int64_equals(self, paired_values, excluded_value)
.map_err(|error| error.reanchor("device_buffer::validate_finite_float32_unless_int64_equals"))
}
pub(crate) fn into_raw_parts(self) -> (cudf_sys::cudf_device_buffer_handle, Arc<ExecutionDomain>) {
let this = mem::ManuallyDrop::new(self);
let Self { handle: _, domain: _ } = &*this;
let domain = unsafe { ptr::read(&this.domain) };
(this.handle.as_ptr(), domain)
}
pub(crate) fn device_snapshot(&self) -> i32 {
self.domain.device_snapshot()
}
pub(crate) fn execution_domain(&self) -> &Arc<ExecutionDomain> {
&self.domain
}
pub(crate) unsafe fn from_raw_parts(
handle: cudf_sys::cudf_device_buffer_handle,
domain: Arc<ExecutionDomain>,
) -> Result<Self> {
Ok(Self { handle: error::require_non_null(handle, "device_buffer::from_raw_parts", "device buffer")?, domain })
}
}
impl Drop for DeviceBuffer {
fn drop(&mut self) {
drop_device_owned_handle(self.handle.as_ptr(), self.domain.device_id(), "DeviceBuffer::drop", |owned| unsafe {
cudf_sys::cudf_device_buffer_destroy(owned)
});
}
}