use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct StreamId(pub u32);
impl StreamId {
pub const DEFAULT: StreamId = StreamId(0);
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct AllocTag(pub &'static str);
impl AllocTag {
pub const UNTAGGED: AllocTag = AllocTag("untagged");
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Generation(pub u64);
static GENERATION_COUNTER: AtomicU64 = AtomicU64::new(1);
impl Generation {
pub fn next() -> Generation {
Generation(GENERATION_COUNTER.fetch_add(1, Ordering::Relaxed))
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Access {
Read,
Write,
ReadWrite,
}
impl Access {
pub fn reads(self) -> bool {
matches!(self, Access::Read | Access::ReadWrite)
}
pub fn writes(self) -> bool {
matches!(self, Access::Write | Access::ReadWrite)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BlockId {
pub ptr: u64,
pub generation: Generation,
pub alloc_stream: StreamId,
pub device_ordinal: u32,
}
impl BlockId {
pub fn from_block(block: &DeviceBlock) -> Self {
Self {
ptr: block.ptr,
generation: block.generation,
alloc_stream: block.alloc_stream,
device_ordinal: block.device_ordinal,
}
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum BlockState {
Live,
Retired,
Quarantined,
Freed,
}
#[derive(Debug)]
pub struct DeviceBlock {
pub ptr: u64,
pub device_ordinal: u32,
pub alloc_stream: StreamId,
pub bytes: usize,
pub align: usize,
pub tag: AllocTag,
pub generation: Generation,
pub state: BlockState,
}
#[derive(Debug)]
pub enum ResourceError {
OutOfBudget { requested: usize, remaining: usize },
Driver(String),
StreamMisuse(String),
UseAfterFree { generation: Generation },
OutOfBounds { generation: Generation },
}
impl fmt::Display for ResourceError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::OutOfBudget {
requested,
remaining,
} => write!(
f,
"out of budget: requested {} bytes, remaining {} bytes",
requested, remaining
),
Self::Driver(msg) => write!(f, "CUDA driver error: {}", msg),
Self::StreamMisuse(msg) => write!(f, "stream-ordered contract violated: {}", msg),
Self::UseAfterFree { generation } => {
write!(f, "use-after-free on generation {:?}", generation)
}
Self::OutOfBounds { generation } => {
write!(f, "out-of-bounds write on generation {:?}", generation)
}
}
}
}
impl std::error::Error for ResourceError {}
pub type ResourceResult<T> = std::result::Result<T, ResourceError>;
pub trait DeviceMemoryResource: Send + Sync {
fn allocate(
&self,
bytes: usize,
stream: StreamId,
tag: AllocTag,
) -> ResourceResult<DeviceBlock>;
fn deallocate(&self, block: DeviceBlock) -> ResourceResult<()>;
fn device_ordinal(&self) -> u32;
fn bytes_outstanding(&self) -> usize;
fn reap_pending(&self) -> ResourceResult<()> {
Ok(())
}
fn record_block_use(&self, block: &DeviceBlock, use_stream: StreamId) -> ResourceResult<()> {
let _ = (block, use_stream);
Err(ResourceError::StreamMisuse(
"record_block_use unsupported by this resource (the active backend \
does not track cross-stream uses; route allocations through a \
stream-ordered backend such as AsyncCudaResource, or take \
responsibility for cross-stream synchronization explicitly)"
.to_string(),
))
}
fn supports_block_use_tracking(&self) -> bool {
false
}
fn prepare_block_use(
&self,
block: BlockId,
use_stream: StreamId,
access: Access,
) -> ResourceResult<()> {
let _ = (block, use_stream, access);
Err(ResourceError::StreamMisuse(
"prepare_block_use unsupported by this resource (the active backend \
does not track cross-stream uses; route allocations through \
AsyncCudaResource or take responsibility for cross-stream \
synchronization explicitly)"
.to_string(),
))
}
fn finish_block_use(
&self,
block: BlockId,
use_stream: StreamId,
access: Access,
) -> ResourceResult<()> {
let _ = (block, use_stream, access);
Err(ResourceError::StreamMisuse(
"finish_block_use unsupported by this resource (the active backend \
does not track cross-stream uses; route allocations through \
AsyncCudaResource or take responsibility for cross-stream \
synchronization explicitly)"
.to_string(),
))
}
}