use crate::error::{self, Result}; use crate::ffi; pub struct CudaStream { raw: ffi::CudaStream, } impl CudaStream { pub fn new() -> Result { let mut raw = std::ptr::null_mut(); error::check(unsafe { ffi::cudaStreamCreate(&mut raw) })?; Ok(Self { raw }) } pub fn synchronize(&self) -> Result<()> { error::check(unsafe { ffi::cudaStreamSynchronize(self.raw) }) } pub fn as_raw(&self) -> ffi::CudaStream { self.raw } } impl Drop for CudaStream { fn drop(&mut self) { if !self.raw.is_null() { unsafe { ffi::cudaStreamDestroy(self.raw) }; } } } // Can move across threads, but not shared without synchronization unsafe impl Send for CudaStream {} // --- Thread-local launch stream ------------------------------------------- // // Every kernel wrapper in xserv-kernels launches on `current_stream_raw()`, // which defaults to the legacy null stream (the historical behavior). CUDA // graph capture requires work to be issued on an explicit stream, so capture // code installs its stream here for the duration of the captured region via // `push_stream` / `StreamGuard`. use std::cell::Cell; thread_local! { static CURRENT_STREAM: Cell = const { Cell::new(std::ptr::null_mut()) }; } /// The stream kernel launches on this thread should use (null = legacy default). pub fn current_stream_raw() -> ffi::CudaStream { CURRENT_STREAM.with(|c| c.get()) } /// RAII guard that installs a launch stream for the current thread and /// restores the previous one on drop. pub struct StreamGuard { prev: ffi::CudaStream, } pub fn push_stream(stream: &CudaStream) -> StreamGuard { let prev = CURRENT_STREAM.with(|c| c.replace(stream.as_raw())); StreamGuard { prev } } impl Drop for StreamGuard { fn drop(&mut self) { CURRENT_STREAM.with(|c| c.set(self.prev)); } }