use std::fmt;
use std::sync::{Arc, Mutex, PoisonError};
use cudarc::driver::{CudaContext, DriverError};
use crate::engine::partitioned::PLACEMENT_LOG_TARGET as LOG_TARGET;
#[derive(Debug)]
pub(crate) enum ContextError {
LibraryMissing,
Driver(DriverError),
}
impl fmt::Display for ContextError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ContextError::LibraryMissing => write!(f, "libcuda could not be loaded"),
ContextError::Driver(error) => write!(f, "CUDA driver error: {error}"),
}
}
}
pub(crate) fn libcuda_present() -> bool {
unsafe { cudarc::driver::sys::is_culib_present() }
}
pub(crate) fn libnvrtc_present() -> bool {
unsafe { cudarc::nvrtc::sys::is_culib_present() }
}
#[cfg(feature = "mpi")]
pub(crate) fn libnccl_present() -> bool {
unsafe { cudarc::nccl::sys::is_culib_present() }
}
pub(crate) fn context(ordinal: u32) -> Result<Arc<CudaContext>, ContextError> {
static CONTEXTS: Mutex<Vec<(u32, Arc<CudaContext>)>> = Mutex::new(Vec::new());
if !libcuda_present() {
return Err(ContextError::LibraryMissing);
}
let mut cache = CONTEXTS.lock().unwrap_or_else(PoisonError::into_inner);
if let Some((_, cuda_context)) = cache.iter().find(|(cached, _)| *cached == ordinal) {
return Ok(cuda_context.clone());
}
let cuda_context = CudaContext::new(ordinal as usize).map_err(ContextError::Driver)?;
cache.push((ordinal, cuda_context.clone()));
Ok(cuda_context)
}
pub(crate) fn bind_device_context(device: u32) {
match context(device) {
Ok(cuda_context) => {
if let Err(error) = cuda_context.bind_to_thread() {
log::warn!(target: LOG_TARGET, "failed to bind device {device} to a partition thread: {error}");
}
}
Err(error) => {
log::warn!(target: LOG_TARGET, "device {device} for a partition thread: {error}")
}
}
}