use std::fmt;
use std::marker::PhantomData;
use std::ops::{Deref, DerefMut};
use std::ptr::NonNull;
use std::slice;
use std::sync::Arc;
use crate::error::DriverError;
use crate::simt::context::CudaContext;
use crate::simt::device_buffer::DeviceCopy;
pub struct PinnedHostBuffer<T: DeviceCopy> {
ptr: NonNull<T>,
len: usize,
num_bytes: usize,
ctx: Arc<CudaContext>,
_marker: PhantomData<T>,
}
unsafe impl<T: DeviceCopy + Send> Send for PinnedHostBuffer<T> {}
unsafe impl<T: DeviceCopy + Sync> Sync for PinnedHostBuffer<T> {}
impl<T: DeviceCopy> PinnedHostBuffer<T> {
pub fn zeroed(ctx: &Arc<CudaContext>, len: usize) -> Result<Self, DriverError> {
let mut buffer = Self::allocate(ctx, len)?;
if buffer.num_bytes != 0 {
unsafe {
std::ptr::write_bytes(buffer.as_mut_ptr().cast::<u8>(), 0, buffer.num_bytes);
}
}
Ok(buffer)
}
pub fn from_slice(ctx: &Arc<CudaContext>, data: &[T]) -> Result<Self, DriverError> {
let buffer = Self::allocate(ctx, data.len())?;
if !data.is_empty() {
unsafe {
std::ptr::copy_nonoverlapping(data.as_ptr(), buffer.ptr.as_ptr(), data.len());
}
}
Ok(buffer)
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn num_bytes(&self) -> usize {
self.num_bytes
}
#[inline]
pub fn context(&self) -> &Arc<CudaContext> {
&self.ctx
}
#[inline]
pub fn as_ptr(&self) -> *const T {
self.ptr.as_ptr()
}
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T {
self.ptr.as_ptr()
}
#[inline]
pub fn as_slice(&self) -> &[T] {
unsafe { slice::from_raw_parts(self.ptr.as_ptr(), self.len) }
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe { slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) }
}
fn allocate(ctx: &Arc<CudaContext>, len: usize) -> Result<Self, DriverError> {
let num_bytes = allocation_size::<T>(len)?;
let ptr = if num_bytes == 0 {
NonNull::dangling()
} else {
ctx.bind_to_thread()?;
let ptr = unsafe { crate::simt::memory::malloc_host(num_bytes)? };
NonNull::new(ptr.cast::<T>()).ok_or(DriverError(
cuda_bindings::cudaError_enum_CUDA_ERROR_INVALID_VALUE,
))?
};
Ok(Self {
ptr,
len,
num_bytes,
ctx: ctx.clone(),
_marker: PhantomData,
})
}
}
impl<T: DeviceCopy> Drop for PinnedHostBuffer<T> {
fn drop(&mut self) {
if self.num_bytes != 0 {
self.ctx.record_err(self.ctx.bind_to_thread());
self.ctx
.record_err(unsafe { crate::simt::memory::free_host(self.ptr.as_ptr().cast()) });
}
}
}
impl<T: DeviceCopy> fmt::Debug for PinnedHostBuffer<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("PinnedHostBuffer")
.field("ptr", &self.ptr)
.field("len", &self.len)
.field("num_bytes", &self.num_bytes)
.field("ctx", &self.ctx)
.finish()
}
}
impl<T: DeviceCopy> AsRef<[T]> for PinnedHostBuffer<T> {
fn as_ref(&self) -> &[T] {
self.as_slice()
}
}
impl<T: DeviceCopy> AsMut<[T]> for PinnedHostBuffer<T> {
fn as_mut(&mut self) -> &mut [T] {
self.as_mut_slice()
}
}
impl<T: DeviceCopy> Deref for PinnedHostBuffer<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
self.as_slice()
}
}
impl<T: DeviceCopy> DerefMut for PinnedHostBuffer<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut_slice()
}
}
fn allocation_size<T>(len: usize) -> Result<usize, DriverError> {
len.checked_mul(std::mem::size_of::<T>()).ok_or(DriverError(
cuda_bindings::cudaError_enum_CUDA_ERROR_INVALID_VALUE,
))
}