use alloc::sync::Arc;
use cubecl_common::{device::ServiceId, device_handle::DeviceHandle};
use cubecl_environment::{stream::StreamId, sync::Mutex};
use cubecl_zspace::{Shape, Strides};
use crate::{
memory_management::{
ManagedMemoryBinding, ManagedMemoryHandle, ManagedMemoryId, WeakMemoryBinding,
},
server::{CopyDescriptor, Server, ServerError, TensorMapBinding},
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Overlap {
Disjoint,
Same,
Partial,
}
pub struct Handle {
pub memory: ManagedMemoryHandle,
pub service: ServiceId,
pub offset_start: Option<u64>,
pub offset_end: Option<u64>,
pub stream: StreamId,
pub(crate) size: u64,
pub peer: Option<PeerMemory>,
}
#[derive(Clone)]
pub struct PeerMemory {
inner: Arc<PeerMemoryInner>,
}
struct PeerMemoryInner {
pin: ManagedMemoryBinding,
address: u64,
owner: DeviceHandle<dyn Server>,
failure: Mutex<Option<ServerError>>,
}
impl PeerMemory {
pub(crate) fn new(
pin: ManagedMemoryBinding,
address: u64,
owner: DeviceHandle<dyn Server>,
) -> Self {
Self {
inner: Arc::new(PeerMemoryInner {
pin,
address,
owner,
failure: Mutex::new(None),
}),
}
}
pub fn address(&self) -> u64 {
self.inner.address
}
pub fn owner(&self) -> &DeviceHandle<dyn Server> {
&self.inner.owner
}
pub fn failure(&self) -> Option<ServerError> {
self.inner.failure.lock().clone()
}
pub fn fail(&self, error: ServerError) {
*self.inner.failure.lock() = Some(error);
}
pub fn repair(&self) -> bool {
self.inner.failure.lock().take().is_some()
}
fn names(&self, binding: &BufferBinding) -> bool {
self.inner.pin.id() == binding.memory.id()
}
}
impl core::fmt::Debug for PeerMemory {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PeerMemory")
.field("address", &format_args!("{:#x}", self.inner.address))
.finish()
}
}
impl core::fmt::Debug for Handle {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Handle")
.field("id", &self.memory)
.field("service", &self.service)
.field("offset_start", &self.offset_start)
.field("offset_end", &self.offset_end)
.field("stream", &self.stream)
.field("size", &self.size)
.field("peer", &self.peer)
.finish()
}
}
impl Clone for Handle {
fn clone(&self) -> Self {
Self {
memory: self.memory.clone(),
service: self.service,
offset_start: self.offset_start,
offset_end: self.offset_end,
stream: self.stream,
size: self.size,
peer: self.peer.clone(),
}
}
}
impl Handle {
pub fn from_memory(
id: ManagedMemoryHandle,
service: ServiceId,
stream: StreamId,
size: u64,
) -> Self {
Self {
memory: id,
service,
offset_start: None,
offset_end: None,
stream,
size,
peer: None,
}
}
pub fn new(service: ServiceId, stream: StreamId, size: u64) -> Self {
Self {
memory: ManagedMemoryHandle::new(),
service,
offset_start: None,
offset_end: None,
stream,
size,
peer: None,
}
}
pub fn can_mut(&self) -> bool {
self.memory.can_mut()
}
pub fn binding(self) -> BufferBinding {
BufferBinding {
memory: self.memory.binding(),
service: self.service,
offset_start: self.offset_start,
offset_end: self.offset_end,
stream: self.stream,
size: self.size,
peer: self.peer,
}
}
pub fn offset_start(mut self, offset: u64) -> Self {
if let Some(val) = &mut self.offset_start {
*val += offset;
} else {
self.offset_start = Some(offset);
}
self
}
pub fn offset_end(mut self, offset: u64) -> Self {
if let Some(val) = &mut self.offset_end {
*val += offset;
} else {
self.offset_end = Some(offset);
}
self
}
pub fn copy_descriptor(
self,
shape: Shape,
strides: Strides,
elem_size: usize,
) -> CopyDescriptor {
CopyDescriptor {
shape,
strides,
elem_size,
handle: self.binding(),
}
}
pub fn overlap(&self, other: &Self) -> Overlap {
let bytes = |handle: &Self| {
handle.offset_start.unwrap_or(0)..handle.size - handle.offset_end.unwrap_or(0)
};
let mine = bytes(self);
let theirs = bytes(other);
if self.memory.descriptor().id != other.memory.descriptor().id
|| mine.end <= theirs.start
|| theirs.end <= mine.start
{
Overlap::Disjoint
} else if mine == theirs {
Overlap::Same
} else {
Overlap::Partial
}
}
pub fn size_in_used(&self) -> u64 {
self.size - self.offset_start.unwrap_or(0) - self.offset_end.unwrap_or(0)
}
pub fn size(&self) -> u64 {
self.size
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Clone, Debug)]
pub enum KernelResource {
Buffer(BufferBinding),
TensorMap(TensorMapBinding),
Peer(PeerBinding),
}
impl KernelResource {
pub fn local_binding(&self) -> Option<&BufferBinding> {
match self {
KernelResource::Buffer(binding) => Some(binding),
KernelResource::TensorMap(map) => Some(&map.binding),
KernelResource::Peer(_) => None,
}
}
}
#[derive(Clone, Debug)]
pub struct PeerBinding {
binding: BufferBinding,
address: u64,
}
impl PeerBinding {
pub fn new(binding: BufferBinding) -> Option<Self> {
let peer = binding.peer.as_ref()?;
if !peer.names(&binding) {
return None;
}
let address = peer.address() + binding.offset_start.unwrap_or(0);
Some(Self { binding, address })
}
pub fn binding(&self) -> &BufferBinding {
&self.binding
}
pub fn memory(&self) -> &PeerMemory {
self.binding
.peer
.as_ref()
.expect("a peer binding is built from shared memory")
}
pub fn into_binding(self) -> BufferBinding {
self.binding
}
pub fn address(&self) -> u64 {
self.address
}
pub fn size(&self) -> u64 {
self.binding.size_in_used()
}
}
#[derive(Clone, Debug)]
pub struct BufferBinding {
pub memory: ManagedMemoryBinding,
pub service: ServiceId,
pub offset_start: Option<u64>,
pub offset_end: Option<u64>,
pub stream: StreamId,
pub size: u64,
pub peer: Option<PeerMemory>,
}
#[derive(Clone, Debug)]
pub struct WeakBufferBinding {
memory: WeakMemoryBinding,
service: ServiceId,
offset_start: Option<u64>,
offset_end: Option<u64>,
stream: StreamId,
size: u64,
}
impl WeakBufferBinding {
pub fn upgrade(&self) -> Option<BufferBinding> {
Some(BufferBinding {
memory: self.memory.upgrade()?,
service: self.service,
offset_start: self.offset_start,
offset_end: self.offset_end,
stream: self.stream,
size: self.size,
peer: None,
})
}
}
impl BufferBinding {
pub fn downgrade(&self) -> WeakBufferBinding {
WeakBufferBinding {
memory: self.memory.downgrade(),
service: self.service,
offset_start: self.offset_start,
offset_end: self.offset_end,
stream: self.stream,
size: self.size,
}
}
pub fn size_in_used(&self) -> u64 {
self.size - self.offset_start.unwrap_or(0) - self.offset_end.unwrap_or(0)
}
pub fn range(&self) -> core::ops::Range<u64> {
self.offset_start.unwrap_or(0)..self.size - self.offset_end.unwrap_or(0)
}
pub fn claim_key(&self) -> (ManagedMemoryId, u64, u64) {
let range = self.range();
(self.memory.id(), range.start, range.end)
}
pub fn size(&self) -> u64 {
self.size
}
}