Skip to main content

cudf/core/memory/
installation.rs

1//! Installation identity, native state records, and install outcomes.
2
3use std::sync::Arc;
4
5use super::{failure::DeviceMemoryFailureInfo, options::DeviceMemoryManagerOptions};
6use crate::core::error::{self, Result};
7
8/// Whether cudf has already installed a process-local device memory manager
9/// for a CUDA device, and whether that manager was created with a failure
10/// callback.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct DeviceMemoryManagerFailureCallbackState {
13    pub exists: bool,
14    pub failure_callback_installed: bool,
15    pub current_resource_installed: bool,
16    pub failure_callback_panicked: bool,
17}
18
19/// Stable identity of the process-lifetime callback and memory partition that
20/// owns a device memory manager installation.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct DeviceMemoryManagerInstallationIdentity {
23    /// Opaque fingerprint identifying the process-lifetime callback owner.
24    pub owner_fingerprint: [u8; 16],
25    /// Callback contract revision included in identity comparisons for installation reuse.
26    pub callback_version: u32,
27    /// Opaque fingerprint identifying the immutable memory partition declaration.
28    pub partition_fingerprint: [u8; 16],
29}
30
31impl DeviceMemoryManagerInstallationIdentity {
32    /// Constructs a caller-declared installation identity without acquiring resources.
33    ///
34    /// Explicit installation compares the identity with the existing process-lifetime
35    /// manager before reuse; construction does not validate the fingerprints.
36    pub const fn new(owner_fingerprint: [u8; 16], callback_version: u32, partition_fingerprint: [u8; 16]) -> Self {
37        Self { owner_fingerprint, callback_version, partition_fingerprint }
38    }
39}
40
41/// Process-lifetime allocator configuration for one device.
42///
43/// `owner` requires `on_failure`. Identified reuse requires the same owner
44/// and physical-base options; the first callback remains installed. Without
45/// an owner, callback installation retains the original callback on reuse.
46#[derive(Clone, Default)]
47pub struct InstallSpec {
48    pub options: DeviceMemoryManagerOptions,
49    pub owner: Option<DeviceMemoryManagerInstallationIdentity>,
50    /// Runs on the allocating thread. It must not re-enter the allocator,
51    /// block on application locks, or unwind across the native boundary.
52    pub on_failure: Option<Arc<dyn Fn(DeviceMemoryFailureInfo) + Send + Sync + 'static>>,
53}
54
55pub(super) fn installation_identity_to_sys(
56    identity: DeviceMemoryManagerInstallationIdentity,
57) -> cudf_sys::cudf_memory_manager_installation_identity_t {
58    cudf_sys::cudf_memory_manager_installation_identity_t {
59        owner_fingerprint: identity.owner_fingerprint,
60        callback_version: identity.callback_version,
61        reserved_padding: 0,
62        partition_fingerprint: identity.partition_fingerprint,
63    }
64}
65
66fn installation_identity_from_sys(
67    identity: cudf_sys::cudf_memory_manager_installation_identity_t,
68) -> DeviceMemoryManagerInstallationIdentity {
69    DeviceMemoryManagerInstallationIdentity::new(
70        identity.owner_fingerprint,
71        identity.callback_version,
72        identity.partition_fingerprint,
73    )
74}
75
76/// Authoritative state of the process-lifetime manager/callback installation.
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78#[non_exhaustive]
79pub enum DeviceMemoryManagerInstallationStatus {
80    /// No process-local memory manager exists for the device.
81    Uninstalled,
82    /// A manager exists, but its device-current resource installation is incomplete or was replaced.
83    Incomplete,
84    /// An identified manager and its failure callback are installed as the current device resource.
85    Owned,
86    /// A manager exists without the identity or failure callback required for owned reuse.
87    Unidentified,
88    /// The retained failure callback panicked and the manager is permanently unusable.
89    CallbackPanicked,
90}
91
92/// Whether a later owner may install or reuse the observed manager.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94#[non_exhaustive]
95pub enum DeviceMemoryManagerInstallationRetry {
96    /// No manager exists and a new installation can be attempted.
97    Installable,
98    /// The current-resource switch can be retried with the retained manager and callback binding.
99    Retryable,
100    /// The installed manager can be reused with matching options, identity, and callback binding.
101    Reusable,
102    /// The observed installation cannot be repaired or replaced through another install call.
103    PermanentlyUnusable,
104}
105
106/// Closed native reason for an installation disposition.
107#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108#[non_exhaustive]
109pub enum DeviceMemoryManagerInstallationCause {
110    /// No additional installation failure or incompatibility was recorded.
111    None,
112    /// The native device-current resource switch failed.
113    CurrentResourceInstallFailed,
114    /// Another resource replaced the manager after its current-resource installation.
115    CurrentResourceReplaced,
116    /// The process-lifetime failure callback panicked.
117    FailureCallbackPanicked,
118    /// The existing manager has no required failure callback.
119    FailureCallbackMissing,
120    /// The existing manager has no identified callback owner and memory partition.
121    IdentityMissing,
122    /// Requested physical-base or allocation-limit settings conflict with the installed manager.
123    OptionsMismatch,
124    /// Requested owner or memory-partition identity differs from the installed identity.
125    IdentityMismatch,
126    /// Requested callback function or argument differs from the retained process-lifetime binding.
127    CallbackBindingMismatch,
128}
129
130/// Atomic inspection of a device's native manager and Rust callback owner.
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub struct DeviceMemoryManagerInstallationState {
133    /// Process-visible CUDA ordinal inspected by this installation transaction.
134    pub device_ordinal: usize,
135    /// Authoritative native installation and callback-health classification.
136    pub status: DeviceMemoryManagerInstallationStatus,
137    /// Whether a subsequent explicit installation can create, retry, or reuse the manager.
138    pub retry: DeviceMemoryManagerInstallationRetry,
139    /// Reason for the recorded disposition or the rejected installation request.
140    pub cause: DeviceMemoryManagerInstallationCause,
141    /// Immutable settings of the existing manager, or `None` when no manager exists.
142    pub effective_options: Option<DeviceMemoryManagerOptions>,
143    /// Retained callback-owner and partition identity, or `None` for an unidentified installation.
144    pub identity: Option<DeviceMemoryManagerInstallationIdentity>,
145    /// Native creation generation, or `None` when no generation was recorded.
146    pub installation_generation: Option<u64>,
147    /// Whether the manager retains a process-lifetime allocation-failure callback.
148    pub failure_callback_installed: bool,
149    /// Whether the manager is currently installed as the device-current resource.
150    pub current_resource_installed: bool,
151    /// Whether the retained callback has recorded a panic.
152    pub failure_callback_panicked: bool,
153}
154
155/// Whether an explicit install created or reused the process manager.
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum DeviceMemoryManagerInstallationOutcomeKind {
158    Installed,
159    Reused,
160}
161
162/// Result of one successful explicit installation transaction.
163#[derive(Debug, Clone, Copy, PartialEq, Eq)]
164pub struct DeviceMemoryManagerInstallationOutcome {
165    pub kind: DeviceMemoryManagerInstallationOutcomeKind,
166    pub state: DeviceMemoryManagerInstallationState,
167}
168
169/// Failure from an explicit install together with the exact native
170/// record returned by that same locked transaction, when one was available.
171#[derive(Debug)]
172pub struct DeviceMemoryManagerInstallationError {
173    pub(super) error: error::Error,
174    pub(super) state: Option<Box<DeviceMemoryManagerInstallationState>>,
175}
176
177impl From<error::Error> for DeviceMemoryManagerInstallationError {
178    fn from(error: error::Error) -> Self {
179        Self { error, state: None }
180    }
181}
182
183impl DeviceMemoryManagerInstallationError {
184    pub(super) fn with_state(error: error::Error, state: Option<DeviceMemoryManagerInstallationState>) -> Self {
185        Self { error, state: state.map(Box::new) }
186    }
187
188    /// The ordinary cudf error for callers that do not need recovery state.
189    pub fn error(&self) -> &error::Error {
190        &self.error
191    }
192
193    /// The authoritative record returned by the rejected native transaction.
194    ///
195    /// This is `None` when Rust callback-ownership checks or native validation
196    /// reject before the install transaction reaches its registry, or when
197    /// the native record itself violates its ABI contract. A preliminary
198    /// diagnostic inspection does not supply transactional installation state.
199    pub fn installation_state(&self) -> Option<DeviceMemoryManagerInstallationState> {
200        self.state.as_deref().copied()
201    }
202
203    /// Discard the optional installation state and recover the ordinary error.
204    pub fn into_error(self) -> error::Error {
205        self.error
206    }
207}
208
209impl std::fmt::Display for DeviceMemoryManagerInstallationError {
210    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
211        self.error.fmt(formatter)
212    }
213}
214
215impl std::error::Error for DeviceMemoryManagerInstallationError {
216    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
217        Some(&self.error)
218    }
219}
220
221pub(super) fn installation_state_from_native_record(
222    record: cudf_sys::cudf_memory_manager_installation_record_t,
223) -> Result<DeviceMemoryManagerInstallationState> {
224    if record.struct_size != core::mem::size_of::<cudf_sys::cudf_memory_manager_installation_record_t>() {
225        return Err(error::Error::new_with(
226            error::ErrorCode::ForeignInvariantViolation,
227            "memory::device_memory_manager::installation_state_for_device",
228            "native installation record returned an incompatible struct size",
229        )
230        .with_detail("struct_size", record.struct_size.to_string()));
231    }
232    let status = match record.status {
233        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_UNINSTALLED => DeviceMemoryManagerInstallationStatus::Uninstalled,
234        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_INCOMPLETE => DeviceMemoryManagerInstallationStatus::Incomplete,
235        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_OWNED => DeviceMemoryManagerInstallationStatus::Owned,
236        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_UNIDENTIFIED => DeviceMemoryManagerInstallationStatus::Unidentified,
237        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CALLBACK_PANICKED => {
238            DeviceMemoryManagerInstallationStatus::CallbackPanicked
239        },
240        value => {
241            return Err(error::Error::new_with(
242                error::ErrorCode::ForeignInvariantViolation,
243                "memory::device_memory_manager::installation_state_for_device",
244                "native installation record returned an unknown status",
245            )
246            .with_detail("status", value.to_string()));
247        },
248    };
249    let retry = match record.retry {
250        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_INSTALLABLE => DeviceMemoryManagerInstallationRetry::Installable,
251        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_RETRYABLE => DeviceMemoryManagerInstallationRetry::Retryable,
252        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_REUSABLE => DeviceMemoryManagerInstallationRetry::Reusable,
253        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_PERMANENTLY_UNUSABLE => {
254            DeviceMemoryManagerInstallationRetry::PermanentlyUnusable
255        },
256        value => {
257            return Err(error::Error::new_with(
258                error::ErrorCode::ForeignInvariantViolation,
259                "memory::device_memory_manager::installation_state_for_device",
260                "native installation record returned an unknown retry classification",
261            )
262            .with_detail("retry", value.to_string()));
263        },
264    };
265    let cause = match record.cause {
266        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_NONE => DeviceMemoryManagerInstallationCause::None,
267        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_CURRENT_RESOURCE_INSTALL_FAILED => {
268            DeviceMemoryManagerInstallationCause::CurrentResourceInstallFailed
269        },
270        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_CURRENT_RESOURCE_REPLACED => {
271            DeviceMemoryManagerInstallationCause::CurrentResourceReplaced
272        },
273        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_FAILURE_CALLBACK_PANICKED => {
274            DeviceMemoryManagerInstallationCause::FailureCallbackPanicked
275        },
276        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_FAILURE_CALLBACK_MISSING => {
277            DeviceMemoryManagerInstallationCause::FailureCallbackMissing
278        },
279        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_IDENTITY_MISSING => {
280            DeviceMemoryManagerInstallationCause::IdentityMissing
281        },
282        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_OPTIONS_MISMATCH => {
283            DeviceMemoryManagerInstallationCause::OptionsMismatch
284        },
285        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_IDENTITY_MISMATCH => {
286            DeviceMemoryManagerInstallationCause::IdentityMismatch
287        },
288        cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_CAUSE_CALLBACK_BINDING_MISMATCH => {
289            DeviceMemoryManagerInstallationCause::CallbackBindingMismatch
290        },
291        value => {
292            return Err(error::Error::new_with(
293                error::ErrorCode::ForeignInvariantViolation,
294                "memory::device_memory_manager::installation_state_for_device",
295                "native installation record returned an unknown cause",
296            )
297            .with_detail("cause", value.to_string()));
298        },
299    };
300    let exists = record.flags & cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_FLAG_EXISTS != 0;
301    let has_identity = record.flags & cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_FLAG_HAS_IDENTITY != 0;
302    let device_ordinal = usize::try_from(record.device_ordinal).map_err(|_| {
303        error::Error::new_with(
304            error::ErrorCode::ForeignInvariantViolation,
305            "memory::device_memory_manager::installation_state_for_device",
306            "native installation record returned a negative device ordinal",
307        )
308    })?;
309    Ok(DeviceMemoryManagerInstallationState {
310        device_ordinal,
311        status,
312        retry,
313        cause,
314        effective_options: exists
315            .then(|| DeviceMemoryManagerOptions::from_sys(record.effective_options))
316            .transpose()?,
317        identity: has_identity.then(|| installation_identity_from_sys(record.identity)),
318        installation_generation: (record.installation_generation != 0).then_some(record.installation_generation),
319        failure_callback_installed: record.flags
320            & cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_FLAG_FAILURE_CALLBACK_INSTALLED
321            != 0,
322        current_resource_installed: record.flags
323            & cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_FLAG_CURRENT_RESOURCE_INSTALLED
324            != 0,
325        failure_callback_panicked: record.flags & cudf_sys::CUDF_MEMORY_MANAGER_INSTALLATION_FLAG_CALLBACK_PANICKED
326            != 0,
327    })
328}