use std::{sync, sync::Arc};
use cudf::runtime::{
DeviceFaultObservation, DeviceHealthSnapshot, DeviceHealthStatus, device_health_snapshot, observe_device_faults,
};
use query_planning::error::{QueryError, QueryErrorCode, Result};
use super::{
DeviceFacts, DeviceLedger, DeviceMemoryFailureReport, DeviceMemoryFailureTracker,
registry::EngineDeviceHealthIdentity,
};
use crate::runtime::{ServiceStreamPool, gpu::lanes::DeviceLanePermits};
#[derive(Debug, Clone)]
pub(crate) struct DeviceContext {
inner: Arc<DeviceContextInner>,
}
#[derive(Debug)]
struct DeviceContextInner {
device: cudf::DeviceId,
stream_pool: ServiceStreamPool,
_mr_manager: cudf::DeviceMemoryManager,
memory_manager_installation: cudf::DeviceMemoryManagerInstallationState,
engine_health_identity: EngineDeviceHealthIdentity,
memory_failure_tracker: DeviceMemoryFailureTracker,
ledger: Arc<DeviceLedger>,
facts: DeviceFacts,
lane_permits: Arc<DeviceLanePermits>,
_fault_observation: DeviceFaultObservation,
}
impl DeviceContext {
#[expect(
clippy::too_many_arguments,
reason = "service construction inputs are distinct admission and ownership values"
)]
pub(crate) fn new(
device: cudf::DeviceId,
stream_pool: ServiceStreamPool,
mr_manager: cudf::DeviceMemoryManager,
memory_manager_installation: cudf::DeviceMemoryManagerInstallationState,
engine_health_identity: EngineDeviceHealthIdentity,
memory_failure_tracker: DeviceMemoryFailureTracker,
ledger: Arc<DeviceLedger>,
facts: DeviceFacts,
max_active_attempts: usize,
) -> Result<Self> {
if device.ordinal() != facts.device {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device context facts must describe the context device",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("facts_device_ordinal", facts.device));
}
if stream_pool.device() != device {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device context stream pool must match the context device",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("pool_device_ordinal", stream_pool.device().ordinal()));
}
if mr_manager.device() != device {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device context memory manager must match the context device",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("memory_manager_device_ordinal", mr_manager.device().ordinal()));
}
if memory_manager_installation.device_ordinal != device.ordinal() {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device memory manager installation record must match the context device",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("installation_device_ordinal", memory_manager_installation.device_ordinal));
}
if memory_manager_installation.installation_generation
!= Some(engine_health_identity.memory_manager_installation_generation)
{
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"engine health identity must bind the installed memory-manager generation",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("reason", "engine_health_installation_generation_mismatch")
.with_fact(
"engine_memory_manager_installation_generation",
engine_health_identity.memory_manager_installation_generation,
)
.with_fact(
"memory_manager_installation_generation",
format!("{:?}", memory_manager_installation.installation_generation),
));
}
if memory_failure_tracker.device_ordinal() != device.ordinal() {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device context memory failure tracker must match the context device",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("memory_failure_tracker_device_ordinal", memory_failure_tracker.device_ordinal()));
}
if ledger.device_ordinal() != device.ordinal() {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device context memory ledger must match the context device",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("device_ledger_ordinal", ledger.device_ordinal()));
}
if max_active_attempts == 0 {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device active-attempt limit must be greater than zero",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("field", "max_active_attempts")
.with_fact("reason", "zero"));
}
if max_active_attempts != ledger.profile().max_active_attempts {
return Err(QueryError::new(
QueryErrorCode::InvalidPlan,
"query_engine.gpu.device_context",
"device active-attempt limit must match the ledger profile",
)
.with_fact("device_ordinal", device.ordinal())
.with_fact("field", "max_active_attempts")
.with_fact("reason", "active_limit_mismatches_ledger_profile")
.with_fact("max_active_attempts", max_active_attempts)
.with_fact("ledger_max_active_attempts", ledger.profile().max_active_attempts));
}
let lane_permits =
sync::Arc::new(DeviceLanePermits::new(device.ordinal(), ledger.profile().max_active_attempts));
let fault_ledger = Arc::downgrade(&ledger);
let fault_observation = observe_device_faults(device, move |health| {
if let Some(ledger) = fault_ledger.upgrade()
&& let Err(error) = close_admission_for_fault(&ledger, health)
{
eprintln!("[query-engine] warning: failed to close admission after a device fault: {error}");
}
});
let context = Self {
inner: Arc::new(DeviceContextInner {
device,
stream_pool,
_mr_manager: mr_manager,
memory_manager_installation,
engine_health_identity,
memory_failure_tracker,
ledger,
facts,
lane_permits,
_fault_observation: fault_observation,
}),
};
context.synchronize_native_admission_health()?;
Ok(context)
}
pub fn device(&self) -> cudf::DeviceId {
self.inner.device
}
pub(crate) fn stream_pool(&self) -> &ServiceStreamPool {
&self.inner.stream_pool
}
pub(crate) fn memory_manager_installation(&self) -> cudf::DeviceMemoryManagerInstallationState {
self.inner.memory_manager_installation
}
pub(crate) fn engine_health_identity(&self) -> EngineDeviceHealthIdentity {
self.inner.engine_health_identity
}
pub(crate) fn memory_failure_report(&self) -> DeviceMemoryFailureReport {
self.inner.memory_failure_tracker.report()
}
pub(crate) fn ledger(&self) -> &Arc<DeviceLedger> {
&self.inner.ledger
}
pub(crate) fn lane_permits(&self) -> &Arc<DeviceLanePermits> {
&self.inner.lane_permits
}
pub fn facts(&self) -> &DeviceFacts {
&self.inner.facts
}
pub(crate) fn capacity_bytes(&self) -> u64 {
self.inner.ledger.managed_capacity_bytes()
}
pub(crate) fn synchronize_native_admission_health(&self) -> Result<bool> {
close_admission_for_fault(&self.inner.ledger, device_health_snapshot(self.inner.device))
}
pub(crate) fn native_fault_probe(&self) -> impl Fn() -> Option<String> + Send + Sync + 'static {
let device = self.inner.device;
move || persistent_fault_reason(device_health_snapshot(device))
}
}
fn close_admission_for_fault(ledger: &DeviceLedger, health: DeviceHealthSnapshot) -> Result<bool> {
persistent_fault_reason(health).map_or(Ok(false), |reason| ledger.close_admission_if_open(reason))
}
fn persistent_fault_reason(health: DeviceHealthSnapshot) -> Option<String> {
(health.status() == DeviceHealthStatus::Unhealthy).then(|| {
format!(
"persistent CUDA fault on device {} (fault_count={}, first_fault={:?})",
health.device().ordinal(),
health.fault_count(),
health.first_fault()
)
})
}