Skip to main content

onnx_runtime_memory_api/
context_pin.rs

1//! Keeping a provider/context alive across an outstanding deferred release.
2//!
3//! A deferred release is a promise that someone will free device memory later.
4//! Whoever performs that free needs the provider/context it was allocated
5//! against to still exist: a stream-ordered free retiring after its CUDA
6//! context has been torn down unmaps handles the teardown already released,
7//! which is undefined behaviour at the driver level rather than a Rust error.
8//!
9//! The host side of a plugin boundary cannot depend on the governor, so the
10//! pin is expressed here as two object-safe traits. The governor implements
11//! them over its provider-context records; a mechanism holds
12//! [`ProviderContextPin`] values and knows nothing about how the pin is
13//! counted.
14//!
15//! The direction of the guarantee matters. A pin does not ask a context to
16//! stay alive; it makes teardown observe the outstanding work. Acquiring a pin
17//! against a context that is already retiring must fail rather than succeed,
18//! because a release that cannot be pinned is a release that must not be
19//! queued.
20
21use core::fmt::Debug;
22
23use crate::binding::ProviderContextIdentity;
24
25/// Why a provider/context pin could not be acquired.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ProviderContextPinError {
28    /// The context is retiring, lost, or terminated, so no new work may be
29    /// attached to it. The caller must not queue the release.
30    ContextUnavailable(ProviderContextIdentity),
31    /// The implementation's outstanding-work counter cannot represent another
32    /// pin. Treated as a refusal rather than a wrap.
33    PinCountOverflow(ProviderContextIdentity),
34}
35
36impl core::fmt::Display for ProviderContextPinError {
37    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38        match self {
39            Self::ContextUnavailable(identity) => write!(
40                formatter,
41                "provider context {identity:?} is no longer accepting work"
42            ),
43            Self::PinCountOverflow(identity) => write!(
44                formatter,
45                "provider context {identity:?} has too many outstanding pins"
46            ),
47        }
48    }
49}
50
51impl std::error::Error for ProviderContextPinError {}
52
53/// A live claim on a provider/context.
54///
55/// Dropping the value releases the claim, so implementations carry the
56/// bookkeeping in `Drop` rather than in an explicit method. Nothing about the
57/// pin is inspectable beyond the identity it belongs to: a holder must not be
58/// able to extend, transfer, or interrogate the context through it.
59pub trait ProviderContextPin: Send + Sync + Debug {
60    /// Which context this pin holds.
61    fn context(&self) -> ProviderContextIdentity;
62}
63
64/// Something that can hand out [`ProviderContextPin`]s for one context.
65pub trait ProviderContextPinSource: Send + Sync + Debug {
66    /// Which context this source pins.
67    fn context(&self) -> ProviderContextIdentity;
68
69    /// Claim the context until the returned pin is dropped.
70    ///
71    /// Fails when the context is no longer accepting work. Callers must treat
72    /// a failure as a refusal to queue the deferred release, not as a reason
73    /// to proceed unpinned.
74    fn pin(&self) -> Result<Box<dyn ProviderContextPin>, ProviderContextPinError>;
75}