use crate::error::*;
use crate::memory::malloc::{cuda_free_locked, cuda_malloc_locked};
use crate::memory::DeviceCopy;
use std::mem;
use std::ops;
use std::ptr;
use std::slice;
#[derive(Debug)]
pub struct LockedBuffer<T: DeviceCopy> {
buf: *mut T,
capacity: usize,
}
unsafe impl<T: Send + DeviceCopy> Send for LockedBuffer<T> {}
unsafe impl<T: Sync + DeviceCopy> Sync for LockedBuffer<T> {}
impl<T: DeviceCopy + Clone> LockedBuffer<T> {
pub fn new(value: &T, size: usize) -> CudaResult<Self> {
unsafe {
let mut uninit = LockedBuffer::uninitialized(size)?;
for x in 0..size {
*uninit.get_unchecked_mut(x) = *value;
}
Ok(uninit)
}
}
pub fn from_slice(slice: &[T]) -> CudaResult<Self> {
unsafe {
let mut uninit = LockedBuffer::uninitialized(slice.len())?;
for (i, x) in slice.iter().enumerate() {
*uninit.get_unchecked_mut(i) = *x;
}
Ok(uninit)
}
}
}
impl<T: DeviceCopy> LockedBuffer<T> {
pub unsafe fn uninitialized(size: usize) -> CudaResult<Self> {
let ptr: *mut T = if size > 0 && mem::size_of::<T>() > 0 {
cuda_malloc_locked(size)?
} else {
ptr::NonNull::dangling().as_ptr()
};
Ok(LockedBuffer {
buf: ptr as *mut T,
capacity: size,
})
}
pub fn as_slice(&self) -> &[T] {
self
}
pub fn as_mut_slice(&mut self) -> &mut [T] {
self
}
pub unsafe fn from_raw_parts(ptr: *mut T, size: usize) -> LockedBuffer<T> {
LockedBuffer {
buf: ptr,
capacity: size,
}
}
pub fn drop(mut buf: LockedBuffer<T>) -> DropResult<LockedBuffer<T>> {
if buf.buf.is_null() {
return Ok(());
}
if buf.capacity > 0 && mem::size_of::<T>() > 0 {
let capacity = buf.capacity;
let ptr = mem::replace(&mut buf.buf, ptr::null_mut());
unsafe {
match cuda_free_locked(ptr) {
Ok(()) => {
mem::forget(buf);
Ok(())
}
Err(e) => Err((e, LockedBuffer::from_raw_parts(ptr, capacity))),
}
}
} else {
Ok(())
}
}
}
impl<T: DeviceCopy> AsRef<[T]> for LockedBuffer<T> {
fn as_ref(&self) -> &[T] {
self
}
}
impl<T: DeviceCopy> AsMut<[T]> for LockedBuffer<T> {
fn as_mut(&mut self) -> &mut [T] {
self
}
}
impl<T: DeviceCopy> ops::Deref for LockedBuffer<T> {
type Target = [T];
fn deref(&self) -> &[T] {
unsafe {
let p = self.buf;
slice::from_raw_parts(p, self.capacity)
}
}
}
impl<T: DeviceCopy> ops::DerefMut for LockedBuffer<T> {
fn deref_mut(&mut self) -> &mut [T] {
unsafe {
let ptr = self.buf;
slice::from_raw_parts_mut(ptr, self.capacity)
}
}
}
impl<T: DeviceCopy> Drop for LockedBuffer<T> {
fn drop(&mut self) {
if self.buf.is_null() {
return;
}
if self.capacity > 0 && mem::size_of::<T>() > 0 {
unsafe {
let _ = cuda_free_locked(self.buf);
}
}
self.capacity = 0;
}
}
#[cfg(test)]
mod test {
use super::*;
use std::mem;
#[derive(Clone, Copy, Debug)]
struct ZeroSizedType;
unsafe impl DeviceCopy for ZeroSizedType {}
#[test]
fn test_new() {
let _context = crate::quick_init().unwrap();
let val = 0u64;
let mut buffer = LockedBuffer::new(&val, 5).unwrap();
buffer[0] = 1;
}
#[test]
fn test_from_slice() {
let _context = crate::quick_init().unwrap();
let values = [0u64; 10];
let mut buffer = LockedBuffer::from_slice(&values).unwrap();
for i in buffer[0..3].iter_mut() {
*i = 10;
}
}
#[test]
fn from_raw_parts() {
let _context = crate::quick_init().unwrap();
let mut buffer = LockedBuffer::new(&0u64, 5).unwrap();
buffer[2] = 1;
let ptr = buffer.as_mut_ptr();
let len = buffer.len();
mem::forget(buffer);
let buffer = unsafe { LockedBuffer::from_raw_parts(ptr, len) };
assert_eq!(&[0u64, 0, 1, 0, 0], buffer.as_slice());
drop(buffer);
}
#[test]
fn zero_length_buffer() {
let _context = crate::quick_init().unwrap();
let buffer = LockedBuffer::new(&0u64, 0).unwrap();
drop(buffer);
}
#[test]
fn zero_size_type() {
let _context = crate::quick_init().unwrap();
let buffer = LockedBuffer::new(&ZeroSizedType, 10).unwrap();
drop(buffer);
}
#[test]
fn overflows_usize() {
let _context = crate::quick_init().unwrap();
let err = LockedBuffer::new(&0u64, ::std::usize::MAX - 1).unwrap_err();
assert_eq!(CudaError::InvalidMemoryAllocation, err);
}
#[test]
fn test_allocate_correct_size() {
let _context = crate::quick_init().unwrap();
let allocation_size = 1;
unsafe {
let _buffer = LockedBuffer::<u64>::uninitialized(allocation_size).unwrap();
}
}
}