Skip to main content

DeviceBuffer

Struct DeviceBuffer 

Source
pub struct DeviceBuffer { /* private fields */ }
Expand description

An owning handle to a single device allocation.

§Ownership & lifetime

A DeviceBuffer is the sole owner of the allocation it names. It is produced only by ExecutionProvider::allocate and released only by ExecutionProvider::deallocate, which consumes it by value. The owning EP is both allocator and deallocator: the buffer records the DeviceId (hence which EP instance) that may free it, so a buffer must never be handed to a different EP. Ownership is unique — no two DeviceBuffers ever alias the same allocation.

§No Drop

DeviceBuffer deliberately does not implement Drop. Freeing device memory generally needs the EP’s context/stream (a CUDA context, an MLX queue, an allocator arena) that this bare handle does not carry, so a silent drop could not free correctly. Consequences:

  • Dropping a DeviceBuffer without passing it to deallocate leaks the allocation. It can never double-free, which is the memory-safety property we prioritize (plan §4.4).
  • The session layer owns the discipline of pairing every allocate with exactly one deallocate. Higher layers may wrap this handle in an RAII/Arc type that calls back into the EP; that policy lives above the EP contract, not here.

§Access

The base address is reachable only through DeviceBuffer::as_ptr (shared) and DeviceBuffer::as_mut_ptr (unique). Obtaining a pointer is safe; dereferencing it is unsafe and valid only on host-accessible devices (DeviceType::is_host_accessible) within the owning EP’s context.

§Thread-safety

See the Send/Sync impls below for the exact invariant.

Implementations§

Source§

impl DeviceBuffer

Source

pub unsafe fn from_raw_parts( ptr: *mut c_void, device: DeviceId, size: usize, align: usize, ) -> Self

Wrap a raw device allocation in an owning handle.

§Safety

The caller (the owning EP) must guarantee all of:

  • ptr is non-null and points to the start of an allocation of at least size bytes on device, aligned to at least align bytes.
  • The allocation was produced by device’s EP and will be freed exactly once, only by returning this handle to that EP’s deallocate (or via an equivalent raw free of the pointer obtained from DeviceBuffer::into_raw).
  • No other live DeviceBuffer aliases the same allocation.

align must be a power of two (checked in debug builds).

Source

pub unsafe fn from_borrowed_parts( ptr: *mut c_void, device: DeviceId, size: usize, align: usize, ) -> Self

Wrap foreign, borrowed memory in a non-owning DeviceBuffer.

Unlike DeviceBuffer::from_raw_parts, the returned handle does not own the allocation: it aliases memory owned by someone else (for example a memmap2::Mmap over an on-disk weight file). This lets an EP reference initializer bytes zero-copy instead of allocating + copying them into fresh RAM.

is_borrowed returns true, and the owning EP’s deallocate must treat it as a no-op free (the guard checks is_borrowed()). into_raw still yields the raw pointer, but the caller must not free it.

§Safety

The caller must guarantee all of:

  • ptr is non-null and points to the start of a readable region of at least size bytes on device, aligned to at least align bytes.
  • The memory is owned by another object (e.g. an mmap) that outlives this buffer and every use of it (read via as_ptr). Nothing else may free or unmap it while this handle or any alias derived from it lives.
  • The buffer is treated as read-only: it is never written through (as_mut_ptr must not be used to mutate borrowed memory) and is never passed to an EP’s deallocate expecting a free — deallocate skips the free for borrowed buffers.

align must be a power of two (checked in debug builds).

Source

pub unsafe fn from_borrowed_mut_parts( ptr: *mut c_void, device: DeviceId, size: usize, align: usize, ) -> Option<Self>

Wrap foreign memory in a non-owning, exclusively writable buffer handle.

This is intended for persistent external output bindings: the real owner retains the allocation while an executor temporarily writes through this alias.

§Safety

The caller must guarantee all of:

  • ptr names a non-null writable allocation of at least size bytes on device, aligned to at least align bytes.
  • The real owner outlives this handle and every operation using it.
  • No other writer accesses the allocation while this handle is live.
  • This handle is never used to free the allocation; deallocate treats it as borrowed.
Source

pub fn is_borrowed(&self) -> bool

Whether this handle merely borrows (aliases) foreign memory rather than owning it. A borrowed buffer must never be freed by deallocate.

Source

pub fn device(&self) -> DeviceId

The device this allocation lives on (and whose EP must free it).

Source

pub fn len(&self) -> usize

Allocation size in bytes.

Source

pub fn is_empty(&self) -> bool

Whether the allocation is zero-length.

Source

pub fn alignment(&self) -> usize

Alignment (bytes) the base pointer was allocated to.

Source

pub fn as_ptr(&self) -> *const c_void

Shared base pointer. Safe to obtain; dereferencing is unsafe and only sound on host-accessible devices within the owning EP’s context.

Source

pub fn as_mut_ptr(&mut self) -> *mut c_void

Unique mutable base pointer. Requires &mut self so the borrow checker forbids two writers sharing one buffer — this is what makes the Sync impl sound (a shared &DeviceBuffer can never hand out a writable pointer through safe code).

Source

pub fn into_raw(self) -> *mut c_void

Consume the handle, returning the raw pointer without freeing it. For an owned buffer the caller assumes the single-free obligation from DeviceBuffer::from_raw_parts. For a borrowed buffer (see DeviceBuffer::from_borrowed_parts) the pointer must not be freed; check is_borrowed first if the caller intends to free.

Trait Implementations§

Source§

impl Debug for DeviceBuffer

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Send for DeviceBuffer

Source§

impl Sync for DeviceBuffer

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.