1use arrayvec::ArrayString;
2use bincode::de::Decoder;
3use bincode::enc::Encoder;
4use bincode::error::{DecodeError, EncodeError};
5use bincode::{Decode, Encode};
6use cu29_traits::CuResult;
7use hashbrown::HashMap;
8use object_pool::{Pool, ReusableOwned};
9use serde::de::{self, MapAccess, SeqAccess, Visitor};
10use serde::{Deserialize, Deserializer, Serialize, Serializer};
11use smallvec::SmallVec;
12use std::alloc::{Layout, alloc, dealloc};
13use std::cell::Cell;
14#[cfg(feature = "remote-debug")]
15use std::cell::RefCell;
16use std::cell::UnsafeCell;
17use std::fmt::Debug;
18use std::fs::OpenOptions;
19use std::marker::PhantomData;
20use std::mem::{align_of, size_of};
21use std::ops::{Deref, DerefMut};
22use std::path::{Path, PathBuf};
23use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
24use std::sync::{Arc, Mutex, MutexGuard, OnceLock};
25
26use memmap2::{MmapMut, MmapOptions};
27use tempfile::NamedTempFile;
28
29type PoolID = ArrayString<64>;
30
31pub trait PoolMonitor: Send + Sync {
33 fn id(&self) -> PoolID;
35
36 fn space_left(&self) -> usize;
38
39 fn total_size(&self) -> usize;
41
42 fn buffer_size(&self) -> usize;
44}
45
46static POOL_REGISTRY: OnceLock<Mutex<HashMap<String, Arc<dyn PoolMonitor>>>> = OnceLock::new();
47const MAX_POOLS: usize = 16;
48
49fn lock_unpoison<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
50 match mutex.lock() {
51 Ok(guard) => guard,
52 Err(poison) => poison.into_inner(),
53 }
54}
55
56fn register_pool(pool: Arc<dyn PoolMonitor>) {
58 POOL_REGISTRY
59 .get_or_init(|| Mutex::new(HashMap::new()))
60 .lock()
61 .unwrap_or_else(|poison| poison.into_inner())
62 .insert(pool.id().to_string(), pool);
63}
64
65type PoolStats = (PoolID, usize, usize, usize);
66
67pub fn pools_statistics() -> SmallVec<[PoolStats; MAX_POOLS]> {
70 let registry_lock = match POOL_REGISTRY.get() {
72 Some(lock) => lock_unpoison(lock),
73 None => return SmallVec::new(), };
75 let mut result = SmallVec::with_capacity(MAX_POOLS);
76 for pool in registry_lock.values() {
77 result.push((
78 pool.id(),
79 pool.space_left(),
80 pool.total_size(),
81 pool.buffer_size(),
82 ));
83 }
84 result
85}
86
87pub trait ElementType: Default + Sized + Copy + Debug + Unpin + Send + Sync {
89 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError>;
90 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError>;
91}
92
93impl<T> ElementType for T
95where
96 T: Default + Sized + Copy + Debug + Unpin + Send + Sync,
97 T: Encode,
98 T: Decode<()>,
99{
100 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
101 self.encode(encoder)
102 }
103
104 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
105 Self::decode(decoder)
106 }
107}
108
109pub trait ArrayLike: Deref<Target = [Self::Element]> + DerefMut + Debug + Sync + Send {
110 type Element: ElementType;
111}
112
113thread_local! {
114 static SHARED_HANDLE_SERIALIZATION_ENABLED: Cell<bool> = const { Cell::new(false) };
115}
116
117pub struct SharedHandleSerializationGuard {
118 previous: bool,
119}
120
121impl Drop for SharedHandleSerializationGuard {
122 fn drop(&mut self) {
123 SHARED_HANDLE_SERIALIZATION_ENABLED.with(|enabled| enabled.set(self.previous));
124 }
125}
126
127pub fn enable_shared_handle_serialization() -> SharedHandleSerializationGuard {
128 let previous = SHARED_HANDLE_SERIALIZATION_ENABLED.with(|enabled| {
129 let previous = enabled.get();
130 enabled.set(true);
131 previous
132 });
133 SharedHandleSerializationGuard { previous }
134}
135
136fn shared_handle_serialization_enabled() -> bool {
137 SHARED_HANDLE_SERIALIZATION_ENABLED.with(Cell::get)
138}
139
140#[cfg(feature = "remote-debug")]
141#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
142#[serde(rename_all = "snake_case")]
143pub enum DebugHandleEncoding {
144 RawLittleEndian,
145 Cbor,
146}
147
148#[cfg(feature = "remote-debug")]
149#[derive(Clone, Debug)]
150pub(crate) struct CollectedDebugHandleAttachment {
151 pub id: u32,
152 pub encoding: DebugHandleEncoding,
153 pub element_type: Option<CuSharedMemoryElementType>,
154 pub len_elements: Option<usize>,
155 pub data: Vec<u8>,
156}
157
158#[cfg(feature = "remote-debug")]
159#[derive(Serialize)]
160struct DebugHandleDescriptor {
161 #[serde(rename = "__cu_handle__")]
162 marker: bool,
163 attachment_id: Option<u32>,
164 encoding: DebugHandleEncoding,
165 element_type: Option<CuSharedMemoryElementType>,
166 len_elements: Option<usize>,
167 byte_len: Option<usize>,
168}
169
170#[cfg(feature = "remote-debug")]
171#[derive(Default)]
172struct DebugHandleSerializationState {
173 include_contents: bool,
174 attachments: Vec<CollectedDebugHandleAttachment>,
175}
176
177#[cfg(feature = "remote-debug")]
178thread_local! {
179 static DEBUG_HANDLE_SERIALIZATION_STATE: RefCell<Option<DebugHandleSerializationState>> =
180 const { RefCell::new(None) };
181}
182
183#[cfg(feature = "remote-debug")]
189pub(crate) fn collect_debug_handle_attachments<R>(
190 f: impl FnOnce() -> R,
191) -> (R, Vec<CollectedDebugHandleAttachment>) {
192 let previous = DEBUG_HANDLE_SERIALIZATION_STATE
193 .with(|state| state.replace(Some(DebugHandleSerializationState::default())));
194 let result = f();
195 let collected = DEBUG_HANDLE_SERIALIZATION_STATE.with(|state| {
196 let current = state.replace(previous);
197 current
198 .map(|current| current.attachments)
199 .unwrap_or_default()
200 });
201 (result, collected)
202}
203
204#[cfg(feature = "remote-debug")]
205pub(crate) fn with_debug_handle_contents<R>(include_contents: bool, f: impl FnOnce() -> R) -> R {
206 let previous = DEBUG_HANDLE_SERIALIZATION_STATE.with(|state| {
207 let mut state = state.borrow_mut();
208 state.as_mut().map(|state| {
209 let previous = state.include_contents;
210 state.include_contents = include_contents;
211 previous
212 })
213 });
214 let result = f();
215 if let Some(previous) = previous {
216 DEBUG_HANDLE_SERIALIZATION_STATE.with(|state| {
217 if let Some(state) = state.borrow_mut().as_mut() {
218 state.include_contents = previous;
219 }
220 });
221 }
222 result
223}
224
225#[cfg(feature = "remote-debug")]
226fn primitive_slice_bytes<U: ElementType + 'static>(
227 values: &[U],
228 element_type: CuSharedMemoryElementType,
229) -> Vec<u8> {
230 macro_rules! cast_slice {
231 ($ty:ty) => {{
232 unsafe { core::slice::from_raw_parts(values.as_ptr().cast::<$ty>(), values.len()) }
235 }};
236 }
237
238 match element_type {
239 CuSharedMemoryElementType::U8 => cast_slice!(u8).to_vec(),
240 CuSharedMemoryElementType::I8 => cast_slice!(i8).iter().map(|value| *value as u8).collect(),
241 CuSharedMemoryElementType::U16 => cast_slice!(u16)
242 .iter()
243 .flat_map(|value| value.to_le_bytes())
244 .collect(),
245 CuSharedMemoryElementType::U32 => cast_slice!(u32)
246 .iter()
247 .flat_map(|value| value.to_le_bytes())
248 .collect(),
249 CuSharedMemoryElementType::U64 => cast_slice!(u64)
250 .iter()
251 .flat_map(|value| value.to_le_bytes())
252 .collect(),
253 CuSharedMemoryElementType::I16 => cast_slice!(i16)
254 .iter()
255 .flat_map(|value| value.to_le_bytes())
256 .collect(),
257 CuSharedMemoryElementType::I32 => cast_slice!(i32)
258 .iter()
259 .flat_map(|value| value.to_le_bytes())
260 .collect(),
261 CuSharedMemoryElementType::I64 => cast_slice!(i64)
262 .iter()
263 .flat_map(|value| value.to_le_bytes())
264 .collect(),
265 CuSharedMemoryElementType::F32 => cast_slice!(f32)
266 .iter()
267 .flat_map(|value| value.to_le_bytes())
268 .collect(),
269 CuSharedMemoryElementType::F64 => cast_slice!(f64)
270 .iter()
271 .flat_map(|value| value.to_le_bytes())
272 .collect(),
273 }
274}
275
276#[cfg(feature = "remote-debug")]
277fn debug_handle_descriptor<U>(values: &[U]) -> Result<Option<DebugHandleDescriptor>, String>
278where
279 U: ElementType + Serialize + 'static,
280{
281 DEBUG_HANDLE_SERIALIZATION_STATE.with(|state| {
282 let mut state = state.borrow_mut();
283 let Some(state) = state.as_mut() else {
284 return Ok(None);
285 };
286
287 let element_type = CuSharedMemoryElementType::of::<U>();
288 let encoding = if element_type.is_some() {
289 DebugHandleEncoding::RawLittleEndian
290 } else {
291 DebugHandleEncoding::Cbor
292 };
293 let mut byte_len = None;
294 let attachment_id = if state.include_contents {
295 let data = match element_type {
296 Some(element_type) => primitive_slice_bytes(values, element_type),
297 None => minicbor_serde::to_vec(values)
298 .map_err(|err| format!("failed to encode debug handle contents: {err}"))?,
299 };
300 byte_len = Some(data.len());
301 let id = state.attachments.len() as u32;
302 state.attachments.push(CollectedDebugHandleAttachment {
303 id,
304 encoding,
305 element_type,
306 len_elements: Some(values.len()),
307 data,
308 });
309 Some(id)
310 } else {
311 None
312 };
313
314 Ok(Some(DebugHandleDescriptor {
315 marker: true,
316 attachment_id,
317 encoding,
318 element_type,
319 len_elements: Some(values.len()),
320 byte_len,
321 }))
322 })
323}
324
325#[cfg(feature = "remote-debug")]
326fn debug_handle_value_descriptor<T>(value: &T) -> Result<Option<DebugHandleDescriptor>, String>
327where
328 T: Serialize,
329{
330 DEBUG_HANDLE_SERIALIZATION_STATE.with(|state| {
331 let mut state = state.borrow_mut();
332 let Some(state) = state.as_mut() else {
333 return Ok(None);
334 };
335
336 let mut byte_len = None;
337 let attachment_id = if state.include_contents {
338 let data = minicbor_serde::to_vec(value)
339 .map_err(|err| format!("failed to encode debug handle contents: {err}"))?;
340 byte_len = Some(data.len());
341 let id = state.attachments.len() as u32;
342 state.attachments.push(CollectedDebugHandleAttachment {
343 id,
344 encoding: DebugHandleEncoding::Cbor,
345 element_type: None,
346 len_elements: None,
347 data,
348 });
349 Some(id)
350 } else {
351 None
352 };
353
354 Ok(Some(DebugHandleDescriptor {
355 marker: true,
356 attachment_id,
357 encoding: DebugHandleEncoding::Cbor,
358 element_type: None,
359 len_elements: None,
360 byte_len,
361 }))
362 })
363}
364
365#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
366#[serde(rename_all = "snake_case")]
367pub enum CuSharedMemoryElementType {
368 U8,
369 U16,
370 U32,
371 U64,
372 I8,
373 I16,
374 I32,
375 I64,
376 F32,
377 F64,
378}
379
380impl CuSharedMemoryElementType {
381 pub fn of<E: ElementType + 'static>() -> Option<Self> {
382 let type_id = core::any::TypeId::of::<E>();
383 if type_id == core::any::TypeId::of::<u8>() {
384 Some(Self::U8)
385 } else if type_id == core::any::TypeId::of::<u16>() {
386 Some(Self::U16)
387 } else if type_id == core::any::TypeId::of::<u32>() {
388 Some(Self::U32)
389 } else if type_id == core::any::TypeId::of::<u64>() {
390 Some(Self::U64)
391 } else if type_id == core::any::TypeId::of::<i8>() {
392 Some(Self::I8)
393 } else if type_id == core::any::TypeId::of::<i16>() {
394 Some(Self::I16)
395 } else if type_id == core::any::TypeId::of::<i32>() {
396 Some(Self::I32)
397 } else if type_id == core::any::TypeId::of::<i64>() {
398 Some(Self::I64)
399 } else if type_id == core::any::TypeId::of::<f32>() {
400 Some(Self::F32)
401 } else if type_id == core::any::TypeId::of::<f64>() {
402 Some(Self::F64)
403 } else {
404 None
405 }
406 }
407}
408
409#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
410pub struct CuSharedMemoryHandleDescriptor {
411 #[serde(rename = "__cu_shm_handle__")]
412 pub marker: bool,
413 pub path: String,
414 pub offset_bytes: usize,
415 pub len_elements: usize,
416 pub element_type: CuSharedMemoryElementType,
417}
418
419impl CuSharedMemoryHandleDescriptor {
420 fn new(
421 path: String,
422 offset_bytes: usize,
423 len_elements: usize,
424 element_type: CuSharedMemoryElementType,
425 ) -> Self {
426 Self {
427 marker: true,
428 path,
429 offset_bytes,
430 len_elements,
431 element_type,
432 }
433 }
434}
435
436struct CuSharedMemoryRegion {
437 path: PathBuf,
438 mmap: UnsafeCell<MmapMut>,
439 _backing_file: Option<NamedTempFile>,
440}
441
442impl CuSharedMemoryRegion {
443 fn create(byte_len: usize) -> CuResult<Arc<Self>> {
444 let file = NamedTempFile::new()
445 .map_err(|e| cu29_traits::CuError::new_with_cause("create shared memory file", e))?;
446 file.as_file()
447 .set_len(byte_len as u64)
448 .map_err(|e| cu29_traits::CuError::new_with_cause("size shared memory file", e))?;
449 let mmap = unsafe {
450 MmapOptions::new()
451 .len(byte_len)
452 .map_mut(file.as_file())
453 .map_err(|e| cu29_traits::CuError::new_with_cause("map shared memory file", e))?
454 };
455 let region = Arc::new(Self {
456 path: file.path().to_path_buf(),
457 mmap: UnsafeCell::new(mmap),
458 _backing_file: Some(file),
459 });
460 cache_shared_region(region.clone());
461 Ok(region)
462 }
463
464 fn open(path: &Path) -> CuResult<Arc<Self>> {
465 if let Some(region) = cached_shared_region(path) {
466 return Ok(region);
467 }
468
469 let file = OpenOptions::new()
470 .read(true)
471 .write(true)
472 .open(path)
473 .map_err(|e| cu29_traits::CuError::new_with_cause("open shared memory file", e))?;
474 let len = file
475 .metadata()
476 .map_err(|e| cu29_traits::CuError::new_with_cause("stat shared memory file", e))?
477 .len() as usize;
478 let mmap = unsafe {
479 MmapOptions::new()
480 .len(len)
481 .map_mut(&file)
482 .map_err(|e| cu29_traits::CuError::new_with_cause("map shared memory file", e))?
483 };
484 let region = Arc::new(Self {
485 path: path.to_path_buf(),
486 mmap: UnsafeCell::new(mmap),
487 _backing_file: None,
488 });
489 cache_shared_region(region.clone());
490 Ok(region)
491 }
492}
493
494impl Debug for CuSharedMemoryRegion {
495 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496 f.debug_struct("CuSharedMemoryRegion")
497 .field("path", &self.path)
498 .finish_non_exhaustive()
499 }
500}
501
502unsafe impl Send for CuSharedMemoryRegion {}
507unsafe impl Sync for CuSharedMemoryRegion {}
510
511fn shared_region_cache() -> &'static Mutex<HashMap<PathBuf, std::sync::Weak<CuSharedMemoryRegion>>>
512{
513 static CACHE: OnceLock<Mutex<HashMap<PathBuf, std::sync::Weak<CuSharedMemoryRegion>>>> =
514 OnceLock::new();
515 CACHE.get_or_init(|| Mutex::new(HashMap::new()))
516}
517
518fn cache_shared_region(region: Arc<CuSharedMemoryRegion>) {
519 lock_unpoison(shared_region_cache()).insert(region.path.clone(), Arc::downgrade(®ion));
520}
521
522fn cached_shared_region(path: &Path) -> Option<Arc<CuSharedMemoryRegion>> {
523 lock_unpoison(shared_region_cache())
524 .get(path)
525 .and_then(std::sync::Weak::upgrade)
526}
527
528fn shared_slot_stride<E: ElementType>(len_elements: usize) -> usize {
529 let raw_bytes = len_elements
530 .checked_mul(size_of::<E>())
531 .expect("shared memory slot size overflow");
532 let alignment = align_of::<E>().max(1);
533 raw_bytes.div_ceil(alignment) * alignment
534}
535
536#[derive(Debug)]
537pub struct CuSharedMemoryBuffer<E: ElementType> {
538 region: Arc<CuSharedMemoryRegion>,
539 offset_bytes: usize,
540 len_elements: usize,
541 _marker: PhantomData<E>,
542}
543
544impl<E: ElementType + 'static> CuSharedMemoryBuffer<E> {
545 fn from_region(
546 region: Arc<CuSharedMemoryRegion>,
547 offset_bytes: usize,
548 len_elements: usize,
549 ) -> Self {
550 Self {
551 region,
552 offset_bytes,
553 len_elements,
554 _marker: PhantomData,
555 }
556 }
557
558 pub fn from_vec_detached(data: Vec<E>) -> CuResult<Self> {
559 let len_elements = data.len();
560 let slot_stride = shared_slot_stride::<E>(len_elements.max(1));
561 let region = CuSharedMemoryRegion::create(slot_stride)?;
562 let mut buffer = Self::from_region(region, 0, len_elements);
563 if !data.is_empty() {
564 buffer.copy_from_slice(&data);
565 }
566 Ok(buffer)
567 }
568
569 pub fn from_descriptor(descriptor: &CuSharedMemoryHandleDescriptor) -> CuResult<Self> {
570 let expected = CuSharedMemoryElementType::of::<E>()
571 .ok_or_else(|| cu29_traits::CuError::from("unsupported shared memory element type"))?;
572 if descriptor.element_type != expected {
573 return Err(cu29_traits::CuError::from(
574 "shared memory descriptor element type mismatch",
575 ));
576 }
577 let region = CuSharedMemoryRegion::open(Path::new(&descriptor.path))?;
578 Ok(Self::from_region(
579 region,
580 descriptor.offset_bytes,
581 descriptor.len_elements,
582 ))
583 }
584
585 pub fn descriptor(&self) -> Option<CuSharedMemoryHandleDescriptor>
586 where
587 E: 'static,
588 {
589 CuSharedMemoryElementType::of::<E>().map(|element_type| {
590 CuSharedMemoryHandleDescriptor::new(
591 self.region.path.display().to_string(),
592 self.offset_bytes,
593 self.len_elements,
594 element_type,
595 )
596 })
597 }
598}
599
600impl<E: ElementType> Deref for CuSharedMemoryBuffer<E> {
601 type Target = [E];
602
603 fn deref(&self) -> &Self::Target {
604 let ptr = unsafe { (*self.region.mmap.get()).as_ptr().add(self.offset_bytes) as *const E };
605 unsafe { std::slice::from_raw_parts(ptr, self.len_elements) }
606 }
607}
608
609impl<E: ElementType> DerefMut for CuSharedMemoryBuffer<E> {
610 fn deref_mut(&mut self) -> &mut Self::Target {
611 let ptr = unsafe {
612 (*self.region.mmap.get())
613 .as_mut_ptr()
614 .add(self.offset_bytes) as *mut E
615 };
616 unsafe { std::slice::from_raw_parts_mut(ptr, self.len_elements) }
617 }
618}
619
620impl<E: ElementType> ArrayLike for CuSharedMemoryBuffer<E> {
621 type Element = E;
622}
623
624impl<E: ElementType> Encode for CuSharedMemoryBuffer<E> {
625 fn encode<Enc: Encoder>(&self, encoder: &mut Enc) -> Result<(), EncodeError> {
626 let len = self.len_elements as u64;
627 Encode::encode(&len, encoder)?;
628 for value in self.deref() {
629 value.encode(encoder)?;
630 }
631 Ok(())
632 }
633}
634
635impl<E: ElementType + 'static> Decode<()> for CuSharedMemoryBuffer<E> {
636 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
637 let len = <u64 as Decode<()>>::decode(decoder)? as usize;
638 let mut vec = Vec::with_capacity(len);
639 for _ in 0..len {
640 vec.push(E::decode(decoder)?);
641 }
642 Self::from_vec_detached(vec).map_err(|e| DecodeError::OtherString(e.to_string()))
643 }
644}
645
646pub enum CuHandleInner<T: Debug + Send + Sync> {
652 Pooled(ReusableOwned<Box<T>>),
653 Detached(Box<T>), }
655
656impl<T> Debug for CuHandleInner<T>
657where
658 T: Debug + Send + Sync,
659{
660 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
661 match self {
662 CuHandleInner::Pooled(r) => {
663 write!(f, "Pooled: {:?}", r.deref().deref())
664 }
665 CuHandleInner::Detached(r) => write!(f, "Detached: {r:?}"),
666 }
667 }
668}
669
670impl<T> CuHandleInner<T>
671where
672 T: Debug + Send + Sync,
673{
674 fn inner_ref(&self) -> &T {
675 match self {
676 CuHandleInner::Pooled(pooled) => pooled.deref().as_ref(),
677 CuHandleInner::Detached(detached) => detached.deref(),
678 }
679 }
680
681 fn inner_mut(&mut self) -> &mut T {
682 match self {
683 CuHandleInner::Pooled(pooled) => pooled.deref_mut().as_mut(),
684 CuHandleInner::Detached(detached) => detached.deref_mut(),
685 }
686 }
687}
688
689impl<T> AsRef<T> for CuHandleInner<T>
690where
691 T: Debug + Send + Sync,
692{
693 fn as_ref(&self) -> &T {
694 self.inner_ref()
695 }
696}
697
698impl<T> AsMut<T> for CuHandleInner<T>
699where
700 T: Debug + Send + Sync,
701{
702 fn as_mut(&mut self) -> &mut T {
703 self.inner_mut()
704 }
705}
706
707impl<T: ArrayLike> Deref for CuHandleInner<T> {
708 type Target = [T::Element];
709
710 fn deref(&self) -> &Self::Target {
711 self.inner_ref().deref()
712 }
713}
714
715impl<T: ArrayLike> DerefMut for CuHandleInner<T> {
716 fn deref_mut(&mut self) -> &mut Self::Target {
717 self.inner_mut().deref_mut()
718 }
719}
720
721pub use crate::config::HandleContent;
725
726#[derive(Debug)]
733struct CuHandleCell<T: Debug + Send + Sync> {
734 touched: AtomicBool,
735 mode: AtomicU8,
736 inner: Mutex<CuHandleInner<T>>,
737}
738
739#[derive(Debug)]
743pub struct CuHandle<T: Debug + Send + Sync>(Arc<CuHandleCell<T>>);
744
745impl<T: Debug + Send + Sync> Clone for CuHandle<T> {
746 fn clone(&self) -> Self {
747 Self(self.0.clone())
748 }
749}
750
751impl<T: Debug + Send + Sync> Deref for CuHandle<T> {
752 type Target = Mutex<CuHandleInner<T>>;
753
754 fn deref(&self) -> &Self::Target {
755 &self.0.inner
756 }
757}
758
759impl<T: Debug + Send + Sync> CuHandle<T> {
760 fn from_inner(inner: CuHandleInner<T>, mode: HandleContent) -> Self {
762 CuHandle(Arc::new(CuHandleCell {
763 touched: AtomicBool::new(false),
764 mode: AtomicU8::new(mode as u8),
765 inner: Mutex::new(inner),
766 }))
767 }
768
769 pub fn new_detached(inner: T) -> Self {
771 Self::new_detached_box(Box::new(inner))
772 }
773
774 pub fn new_detached_box(inner: Box<T>) -> Self {
776 Self::from_inner(CuHandleInner::Detached(inner), HandleContent::default())
777 }
778
779 pub fn new_detached_with_mode(inner: T, mode: HandleContent) -> Self {
784 Self::from_inner(CuHandleInner::Detached(Box::new(inner)), mode)
785 }
786
787 pub fn with_inner<R>(&self, f: impl FnOnce(&CuHandleInner<T>) -> R) -> R {
789 let lock = lock_unpoison(&self.0.inner);
790 f(&*lock)
791 }
792
793 pub fn with_inner_mut<R>(&self, f: impl FnOnce(&mut CuHandleInner<T>) -> R) -> R {
795 let mut lock = lock_unpoison(&self.0.inner);
796 f(&mut *lock)
797 }
798
799 pub fn serialize_value<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
806 where
807 T: Serialize,
808 S: Serializer,
809 {
810 let inner = lock_unpoison(&self.0.inner);
811 let value = inner.inner_ref();
812
813 #[cfg(feature = "remote-debug")]
814 if let Some(descriptor) =
815 debug_handle_value_descriptor(value).map_err(<S::Error as serde::ser::Error>::custom)?
816 {
817 return descriptor.serialize(serializer);
818 }
819
820 value.serialize(serializer)
821 }
822
823 pub fn strong_count(&self) -> usize {
825 Arc::strong_count(&self.0)
826 }
827
828 pub fn is_unique(&self) -> bool {
830 self.strong_count() == 1
831 }
832
833 pub fn storage_id(&self) -> usize {
839 Arc::as_ptr(&self.0) as usize
840 }
841
842 pub fn mark_touched(&self) {
848 self.0.touched.store(true, Ordering::Relaxed);
849 }
850
851 pub fn was_touched(&self) -> bool {
854 self.0.touched.load(Ordering::Relaxed)
855 }
856
857 pub fn logging_mode(&self) -> HandleContent {
859 HandleContent::from_u8(self.0.mode.load(Ordering::Relaxed))
860 }
861
862 pub fn with_touched_inner<R>(&self, f: impl FnOnce(&CuHandleInner<T>) -> R) -> R {
865 self.mark_touched();
866 self.with_inner(f)
867 }
868
869 pub fn payload_should_log(&self) -> bool {
877 match self.logging_mode() {
878 HandleContent::All => true,
879 HandleContent::None => false,
880 HandleContent::TouchedOnly => self.was_touched(),
881 }
882 }
883
884 pub fn apply_handle_content_policy(&self, mode: HandleContent) {
888 self.0.mode.store(mode as u8, Ordering::Relaxed);
889 }
890}
891
892pub trait HandleContentAware {}
905
906impl<T: Debug + Send + Sync> HandleContentAware for CuHandle<T> {}
907
908pub trait PayloadDefaultLoggingPolicy {
916 fn payload_should_log(&self) -> bool {
917 true
918 }
919}
920
921impl<T: ?Sized> PayloadDefaultLoggingPolicy for T {}
922
923pub trait PayloadDefaultHandlePolicyApply {
928 fn apply_handle_content_policy(&self, _mode: HandleContent) {}
929}
930
931impl<T: ?Sized> PayloadDefaultHandlePolicyApply for T {}
932
933impl<U> Serialize for CuHandle<Vec<U>>
934where
935 U: ElementType + Serialize + 'static,
936{
937 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
938 let inner = lock_unpoison(&self.0.inner);
939 let values = inner.inner_ref();
940
941 #[cfg(feature = "remote-debug")]
942 if let Some(descriptor) = debug_handle_descriptor(values.as_slice())
943 .map_err(<S::Error as serde::ser::Error>::custom)?
944 {
945 return descriptor.serialize(serializer);
946 }
947
948 values.serialize(serializer)
949 }
950}
951
952impl<'de, U> Deserialize<'de> for CuHandle<Vec<U>>
953where
954 U: ElementType + Deserialize<'de> + 'static,
955{
956 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
957 Vec::<U>::deserialize(deserializer).map(CuHandle::new_detached)
958 }
959}
960
961impl<U> Serialize for CuHandle<CuSharedMemoryBuffer<U>>
962where
963 U: ElementType + Serialize + 'static,
964{
965 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
966 let inner = lock_unpoison(&self.0.inner);
967 let buffer = inner.inner_ref();
968
969 #[cfg(feature = "remote-debug")]
970 if let Some(descriptor) = debug_handle_descriptor(buffer.deref())
971 .map_err(<S::Error as serde::ser::Error>::custom)?
972 {
973 return descriptor.serialize(serializer);
974 }
975
976 if shared_handle_serialization_enabled()
977 && let Some(descriptor) = buffer.descriptor()
978 {
979 return descriptor.serialize(serializer);
980 }
981
982 buffer.deref().serialize(serializer)
983 }
984}
985
986impl<'de, U> Deserialize<'de> for CuHandle<CuSharedMemoryBuffer<U>>
987where
988 U: ElementType + Deserialize<'de> + 'static,
989{
990 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
991 enum Repr<U> {
992 Descriptor(CuSharedMemoryHandleDescriptor),
993 Data(Vec<U>),
994 }
995
996 impl<'de, U> Deserialize<'de> for Repr<U>
997 where
998 U: ElementType + Deserialize<'de>,
999 {
1000 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1001 struct ReprVisitor<U>(PhantomData<U>);
1002
1003 impl<'de, U> Visitor<'de> for ReprVisitor<U>
1004 where
1005 U: ElementType + Deserialize<'de>,
1006 {
1007 type Value = Repr<U>;
1008
1009 fn expecting(
1010 &self,
1011 formatter: &mut std::fmt::Formatter<'_>,
1012 ) -> std::fmt::Result {
1013 formatter
1014 .write_str("a shared-memory handle descriptor or an element sequence")
1015 }
1016
1017 fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
1018 let data =
1019 Vec::<U>::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
1020 Ok(Repr::Data(data))
1021 }
1022
1023 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
1024 let descriptor = CuSharedMemoryHandleDescriptor::deserialize(
1025 de::value::MapAccessDeserializer::new(map),
1026 )?;
1027 Ok(Repr::Descriptor(descriptor))
1028 }
1029 }
1030
1031 deserializer.deserialize_any(ReprVisitor(PhantomData))
1032 }
1033 }
1034
1035 match Repr::<U>::deserialize(deserializer)? {
1036 Repr::Descriptor(descriptor) => CuSharedMemoryBuffer::from_descriptor(&descriptor)
1037 .map(CuHandle::new_detached)
1038 .map_err(de::Error::custom),
1039 Repr::Data(data) => CuSharedMemoryBuffer::from_vec_detached(data)
1040 .map(CuHandle::new_detached)
1041 .map_err(de::Error::custom),
1042 }
1043 }
1044}
1045
1046impl<T: ArrayLike + Encode> Encode for CuHandle<T>
1047where
1048 <T as ArrayLike>::Element: 'static,
1049{
1050 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
1051 let inner = lock_unpoison(&self.0.inner);
1052 crate::monitoring::record_payload_handle_bytes(
1053 inner.inner_ref().len() * size_of::<T::Element>(),
1054 );
1055 inner.inner_ref().encode(encoder)
1056 }
1057}
1058
1059impl<T: Debug + Send + Sync> Default for CuHandle<T> {
1060 fn default() -> Self {
1061 panic!("Cannot create a default CuHandle")
1062 }
1063}
1064
1065impl<U: ElementType + Decode<()> + 'static> Decode<()> for CuHandle<Vec<U>> {
1066 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
1067 let vec: Vec<U> = Vec::decode(decoder)?;
1068 Ok(CuHandle::new_detached(vec))
1069 }
1070}
1071
1072impl<U: ElementType + Decode<()> + 'static> Decode<()> for CuHandle<CuSharedMemoryBuffer<U>> {
1073 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
1074 let buffer = CuSharedMemoryBuffer::<U>::decode(decoder)?;
1075 Ok(CuHandle::new_detached(buffer))
1076 }
1077}
1078
1079pub trait CuPool<T: ArrayLike>: PoolMonitor {
1082 fn acquire(&self) -> Option<CuHandle<T>>;
1084
1085 fn copy_from<O>(&self, from: &mut CuHandle<O>) -> CuHandle<T>
1087 where
1088 O: ArrayLike<Element = T::Element>;
1089}
1090
1091pub trait DeviceCuPool<T: ArrayLike>: CuPool<T> {
1093 fn copy_to_host_pool<O>(
1096 &self,
1097 from_device_handle: &CuHandle<T>,
1098 to_host_handle: &mut CuHandle<O>,
1099 ) -> CuResult<()>
1100 where
1101 O: ArrayLike<Element = T::Element>;
1102}
1103
1104pub struct CuHostMemoryPool<T> {
1106 id: PoolID,
1109 pool: Arc<Pool<Box<T>>>,
1110 size: usize,
1111 buffer_size: usize,
1112}
1113
1114impl<T: ArrayLike + 'static> CuHostMemoryPool<T> {
1115 pub fn new<F>(id: &str, size: usize, buffer_initializer: F) -> CuResult<Arc<Self>>
1116 where
1117 F: Fn() -> T,
1118 {
1119 let pool = Arc::new(Pool::new(size, move || Box::new(buffer_initializer())));
1120 let buffer_size = pool.try_pull().unwrap().len() * size_of::<T::Element>();
1121
1122 let og = Self {
1123 id: PoolID::from(id).map_err(|_| "Failed to create PoolID")?,
1124 pool,
1125 size,
1126 buffer_size,
1127 };
1128 let og = Arc::new(og);
1129 register_pool(og.clone());
1130 Ok(og)
1131 }
1132}
1133
1134impl<T: ArrayLike> PoolMonitor for CuHostMemoryPool<T> {
1135 fn id(&self) -> PoolID {
1136 self.id
1137 }
1138
1139 fn space_left(&self) -> usize {
1140 self.pool.len()
1141 }
1142
1143 fn total_size(&self) -> usize {
1144 self.size
1145 }
1146
1147 fn buffer_size(&self) -> usize {
1148 self.buffer_size
1149 }
1150}
1151
1152impl<T: ArrayLike> CuPool<T> for CuHostMemoryPool<T> {
1153 fn acquire(&self) -> Option<CuHandle<T>> {
1154 let owned_object = self.pool.try_pull_owned(); owned_object.map(|reusable| {
1157 CuHandle::from_inner(CuHandleInner::Pooled(reusable), HandleContent::default())
1158 })
1159 }
1160
1161 fn copy_from<O: ArrayLike<Element = T::Element>>(&self, from: &mut CuHandle<O>) -> CuHandle<T> {
1162 let to_handle = self.acquire().expect("No available buffers in the pool");
1163 {
1164 let from_lock = lock_unpoison(&from.0.inner);
1165 let mut to_lock = lock_unpoison(&to_handle.0.inner);
1166 to_lock.inner_mut().copy_from_slice(from_lock.inner_ref());
1167 }
1168 to_handle
1169 }
1170}
1171
1172pub struct CuSharedMemoryPool<E: ElementType> {
1175 id: PoolID,
1176 pool: Arc<Pool<Box<CuSharedMemoryBuffer<E>>>>,
1177 size: usize,
1178 buffer_size: usize,
1179}
1180
1181impl<E: ElementType + 'static> CuSharedMemoryPool<E> {
1182 pub fn new(id: &str, size: usize, elements_per_buffer: usize) -> CuResult<Arc<Self>> {
1183 let slot_stride = shared_slot_stride::<E>(elements_per_buffer.max(1));
1184 let region = CuSharedMemoryRegion::create(
1185 slot_stride
1186 .checked_mul(size)
1187 .ok_or_else(|| cu29_traits::CuError::from("shared memory pool size overflow"))?,
1188 )?;
1189 let next_slot = Arc::new(std::sync::atomic::AtomicUsize::new(0));
1190 let initializer_region = region.clone();
1191 let initializer_next_slot = next_slot.clone();
1192 let pool = Arc::new(Pool::new(size, move || {
1193 let slot = initializer_next_slot.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1194 assert!(slot < size, "shared memory pool slot index overflow");
1195 Box::new(CuSharedMemoryBuffer::from_region(
1196 initializer_region.clone(),
1197 slot * slot_stride,
1198 elements_per_buffer,
1199 ))
1200 }));
1201
1202 let pool = Arc::new(Self {
1203 id: PoolID::from(id).map_err(|_| "Failed to create PoolID")?,
1204 pool,
1205 size,
1206 buffer_size: elements_per_buffer * size_of::<E>(),
1207 });
1208 register_pool(pool.clone());
1209 Ok(pool)
1210 }
1211}
1212
1213impl<E: ElementType> PoolMonitor for CuSharedMemoryPool<E> {
1214 fn id(&self) -> PoolID {
1215 self.id
1216 }
1217
1218 fn space_left(&self) -> usize {
1219 self.pool.len()
1220 }
1221
1222 fn total_size(&self) -> usize {
1223 self.size
1224 }
1225
1226 fn buffer_size(&self) -> usize {
1227 self.buffer_size
1228 }
1229}
1230
1231impl<E: ElementType> CuPool<CuSharedMemoryBuffer<E>> for CuSharedMemoryPool<E> {
1232 fn acquire(&self) -> Option<CuHandle<CuSharedMemoryBuffer<E>>> {
1233 self.pool.try_pull_owned().map(|reusable| {
1234 CuHandle::from_inner(CuHandleInner::Pooled(reusable), HandleContent::default())
1235 })
1236 }
1237
1238 fn copy_from<O>(&self, from: &mut CuHandle<O>) -> CuHandle<CuSharedMemoryBuffer<E>>
1239 where
1240 O: ArrayLike<Element = E>,
1241 {
1242 let to_handle = self.acquire().expect("No available buffers in the pool");
1243 {
1244 let from_lock = lock_unpoison(&from.0.inner);
1245 let mut to_lock = lock_unpoison(&to_handle.0.inner);
1246 to_lock.inner_mut().copy_from_slice(from_lock.inner_ref());
1247 }
1248 to_handle
1249 }
1250}
1251
1252impl<E: ElementType + 'static> ArrayLike for Vec<E> {
1253 type Element = E;
1254}
1255
1256#[cfg(all(feature = "cuda", not(target_os = "macos")))]
1257pub mod cuda {
1258 use super::*;
1259 use cu29_traits::CuError;
1260 use cudarc::driver::{
1261 CudaContext, CudaSlice, CudaStream, DeviceRepr, HostSlice, SyncOnDrop, ValidAsZeroBits,
1262 };
1263 use std::sync::Arc;
1264
1265 #[derive(Debug)]
1266 pub struct CudaSliceWrapper<E>(CudaSlice<E>);
1267
1268 impl<E> Deref for CudaSliceWrapper<E>
1269 where
1270 E: ElementType,
1271 {
1272 type Target = [E];
1273
1274 fn deref(&self) -> &Self::Target {
1275 panic!("You need to copy data to host memory pool before accessing it.");
1277 }
1278 }
1279
1280 impl<E> DerefMut for CudaSliceWrapper<E>
1281 where
1282 E: ElementType,
1283 {
1284 fn deref_mut(&mut self) -> &mut Self::Target {
1285 panic!("You need to copy data to host memory pool before accessing it.");
1286 }
1287 }
1288
1289 impl<E: ElementType> ArrayLike for CudaSliceWrapper<E> {
1290 type Element = E;
1291 }
1292
1293 impl<E> CudaSliceWrapper<E> {
1294 pub fn len(&self) -> usize {
1296 self.0.len()
1297 }
1298
1299 pub fn is_empty(&self) -> bool {
1301 self.0.is_empty()
1302 }
1303
1304 pub fn num_bytes(&self) -> usize {
1306 self.0.num_bytes()
1307 }
1308
1309 pub fn as_cuda_slice(&self) -> &CudaSlice<E> {
1310 &self.0
1311 }
1312
1313 pub fn as_cuda_slice_mut(&mut self) -> &mut CudaSlice<E> {
1314 &mut self.0
1315 }
1316 }
1317
1318 pub struct HostSliceWrapper<'a, T: ArrayLike> {
1320 inner: &'a T,
1321 }
1322
1323 impl<T: ArrayLike> HostSlice<T::Element> for HostSliceWrapper<'_, T> {
1324 fn len(&self) -> usize {
1325 self.inner.len()
1326 }
1327
1328 unsafe fn stream_synced_slice<'b>(
1330 &'b self,
1331 stream: &'b CudaStream,
1332 ) -> (&'b [T::Element], SyncOnDrop<'b>) {
1333 (self.inner.deref(), SyncOnDrop::sync_stream(stream))
1334 }
1335
1336 unsafe fn stream_synced_mut_slice<'b>(
1338 &'b mut self,
1339 _stream: &'b CudaStream,
1340 ) -> (&'b mut [T::Element], SyncOnDrop<'b>) {
1341 panic!("Cannot get mutable reference from immutable wrapper")
1342 }
1343 }
1344
1345 pub struct HostSliceMutWrapper<'a, T: ArrayLike> {
1347 inner: &'a mut T,
1348 }
1349
1350 impl<T: ArrayLike> HostSlice<T::Element> for HostSliceMutWrapper<'_, T> {
1351 fn len(&self) -> usize {
1352 self.inner.len()
1353 }
1354
1355 unsafe fn stream_synced_slice<'b>(
1357 &'b self,
1358 stream: &'b CudaStream,
1359 ) -> (&'b [T::Element], SyncOnDrop<'b>) {
1360 (self.inner.deref(), SyncOnDrop::sync_stream(stream))
1361 }
1362
1363 unsafe fn stream_synced_mut_slice<'b>(
1365 &'b mut self,
1366 stream: &'b CudaStream,
1367 ) -> (&'b mut [T::Element], SyncOnDrop<'b>) {
1368 (self.inner.deref_mut(), SyncOnDrop::sync_stream(stream))
1369 }
1370 }
1371
1372 impl<E: ElementType + ValidAsZeroBits + DeviceRepr> CuCudaPool<E> {
1374 fn get_host_slice_wrapper<O: ArrayLike<Element = E>>(
1376 handle_inner: &CuHandleInner<O>,
1377 ) -> HostSliceWrapper<'_, O> {
1378 HostSliceWrapper {
1379 inner: handle_inner.inner_ref(),
1380 }
1381 }
1382
1383 fn get_host_slice_mut_wrapper<O: ArrayLike<Element = E>>(
1385 handle_inner: &mut CuHandleInner<O>,
1386 ) -> HostSliceMutWrapper<'_, O> {
1387 HostSliceMutWrapper {
1388 inner: handle_inner.inner_mut(),
1389 }
1390 }
1391 }
1392 pub struct CuCudaPool<E>
1394 where
1395 E: ElementType + ValidAsZeroBits + DeviceRepr + Unpin,
1396 {
1397 id: PoolID,
1398 stream: Arc<CudaStream>,
1399 pool: Arc<Pool<Box<CudaSliceWrapper<E>>>>,
1400 nb_buffers: usize,
1401 nb_element_per_buffer: usize,
1402 }
1403
1404 impl<E: ElementType + ValidAsZeroBits + DeviceRepr + 'static> CuCudaPool<E> {
1405 pub fn new(
1406 id: &str,
1407 ctx: Arc<CudaContext>,
1408 nb_buffers: usize,
1409 nb_element_per_buffer: usize,
1410 ) -> CuResult<Arc<Self>> {
1411 let stream = ctx.default_stream();
1412 let pool = (0..nb_buffers)
1413 .map(|_| {
1414 stream
1415 .alloc_zeros(nb_element_per_buffer)
1416 .map(CudaSliceWrapper)
1417 .map(Box::new)
1418 .map_err(|_| "Failed to allocate device memory")
1419 })
1420 .collect::<Result<Vec<_>, _>>()?;
1421
1422 let pool = Arc::new(Self {
1423 id: PoolID::from(id).map_err(|_| "Failed to create PoolID")?,
1424 stream,
1425 pool: Arc::new(Pool::from_vec(pool)),
1426 nb_buffers,
1427 nb_element_per_buffer,
1428 });
1429 register_pool(pool.clone());
1430 Ok(pool)
1431 }
1432 }
1433
1434 impl<E> PoolMonitor for CuCudaPool<E>
1435 where
1436 E: DeviceRepr + ElementType + ValidAsZeroBits,
1437 {
1438 fn id(&self) -> PoolID {
1439 self.id
1440 }
1441
1442 fn space_left(&self) -> usize {
1443 self.pool.len()
1444 }
1445
1446 fn total_size(&self) -> usize {
1447 self.nb_buffers
1448 }
1449
1450 fn buffer_size(&self) -> usize {
1451 self.nb_element_per_buffer * size_of::<E>()
1452 }
1453 }
1454
1455 impl<E> CuPool<CudaSliceWrapper<E>> for CuCudaPool<E>
1456 where
1457 E: DeviceRepr + ElementType + ValidAsZeroBits,
1458 {
1459 fn acquire(&self) -> Option<CuHandle<CudaSliceWrapper<E>>> {
1460 self.pool
1461 .try_pull_owned()
1462 .map(|x| CuHandle::from_inner(CuHandleInner::Pooled(x), HandleContent::default()))
1463 }
1464
1465 fn copy_from<O>(&self, from_handle: &mut CuHandle<O>) -> CuHandle<CudaSliceWrapper<E>>
1466 where
1467 O: ArrayLike<Element = E>,
1468 {
1469 let to_handle = self.acquire().expect("No available buffers in the pool");
1470
1471 {
1472 let from_lock = lock_unpoison(&from_handle.0.inner);
1473 let mut to_lock = lock_unpoison(&to_handle.0.inner);
1474
1475 match &mut *to_lock {
1476 CuHandleInner::Detached(to) => {
1477 let wrapper = Self::get_host_slice_wrapper(&*from_lock);
1478 self.stream
1479 .memcpy_htod(&wrapper, to.deref_mut().as_cuda_slice_mut())
1480 .expect("Failed to copy data to device");
1481 }
1482 CuHandleInner::Pooled(to) => {
1483 let wrapper = Self::get_host_slice_wrapper(&*from_lock);
1484 self.stream
1485 .memcpy_htod(&wrapper, to.deref_mut().as_mut().as_cuda_slice_mut())
1486 .expect("Failed to copy data to device");
1487 }
1488 }
1489 } to_handle }
1492 }
1493
1494 impl<E> DeviceCuPool<CudaSliceWrapper<E>> for CuCudaPool<E>
1495 where
1496 E: ElementType + ValidAsZeroBits + DeviceRepr,
1497 {
1498 fn copy_to_host_pool<O>(
1500 &self,
1501 device_handle: &CuHandle<CudaSliceWrapper<E>>,
1502 host_handle: &mut CuHandle<O>,
1503 ) -> Result<(), CuError>
1504 where
1505 O: ArrayLike<Element = E>,
1506 {
1507 let device_lock = device_handle.lock().map_err(|e| {
1508 CuError::from("Device handle mutex poisoned").add_cause(&e.to_string())
1509 })?;
1510 let mut host_lock = host_handle.lock().map_err(|e| {
1511 CuError::from("Host handle mutex poisoned").add_cause(&e.to_string())
1512 })?;
1513 let src = match &*device_lock {
1514 CuHandleInner::Pooled(source) => source.deref().as_ref().as_cuda_slice(),
1515 CuHandleInner::Detached(source) => source.deref().as_cuda_slice(),
1516 };
1517 let mut wrapper = Self::get_host_slice_mut_wrapper(&mut *host_lock);
1518 self.stream.memcpy_dtoh(src, &mut wrapper).map_err(|e| {
1519 CuError::from("Failed to copy data from device to host").add_cause(&e.to_string())
1520 })?;
1521 Ok(())
1522 }
1523 }
1524}
1525
1526#[derive(Debug)]
1527pub struct AlignedBuffer<E: ElementType> {
1529 ptr: *mut E,
1530 size: usize,
1531 layout: Layout,
1532}
1533
1534impl<E: ElementType> AlignedBuffer<E> {
1535 pub fn new(num_elements: usize, alignment: usize) -> Self {
1536 assert!(
1537 num_elements > 0 && size_of::<E>() > 0,
1538 "AlignedBuffer requires a non-zero element count and non-zero-sized element type"
1539 );
1540 let alignment = alignment.max(align_of::<E>());
1541 let alloc_size = num_elements
1542 .checked_mul(size_of::<E>())
1543 .expect("AlignedBuffer allocation size overflow");
1544 let layout = Layout::from_size_align(alloc_size, alignment).unwrap();
1545 let ptr = unsafe { alloc(layout) as *mut E };
1547 if ptr.is_null() {
1548 panic!("Failed to allocate memory");
1549 }
1550 unsafe {
1552 for i in 0..num_elements {
1553 std::ptr::write(ptr.add(i), E::default());
1554 }
1555 }
1556 Self {
1557 ptr,
1558 size: num_elements,
1559 layout,
1560 }
1561 }
1562}
1563
1564impl<E: ElementType> Deref for AlignedBuffer<E> {
1565 type Target = [E];
1566
1567 fn deref(&self) -> &Self::Target {
1568 unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
1570 }
1571}
1572
1573impl<E: ElementType> DerefMut for AlignedBuffer<E> {
1574 fn deref_mut(&mut self) -> &mut Self::Target {
1575 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) }
1577 }
1578}
1579
1580impl<E: ElementType> Drop for AlignedBuffer<E> {
1581 fn drop(&mut self) {
1582 unsafe { dealloc(self.ptr as *mut u8, self.layout) }
1584 }
1585}
1586
1587#[cfg(test)]
1588mod tests {
1589 use super::*;
1590
1591 #[test]
1592 fn test_handle_touched_flag_defaults_false() {
1593 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1594 assert!(!h.was_touched());
1595 assert_eq!(h.logging_mode(), HandleContent::All);
1596 }
1597
1598 #[test]
1599 fn test_handle_mark_touched_propagates_across_clones() {
1600 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1601 let clone = h.clone();
1602 assert!(!h.was_touched());
1603 assert!(!clone.was_touched());
1604
1605 clone.mark_touched();
1606 assert!(h.was_touched());
1608 assert!(clone.was_touched());
1609 }
1610
1611 #[test]
1612 fn test_handle_strong_count_tracks_clones() {
1613 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1614 assert_eq!(h.strong_count(), 1);
1615 assert!(h.is_unique());
1616
1617 let clone = h.clone();
1618 assert_eq!(h.strong_count(), 2);
1619 assert_eq!(clone.strong_count(), 2);
1620 assert!(!h.is_unique());
1621 assert!(!clone.is_unique());
1622
1623 drop(clone);
1624 assert_eq!(h.strong_count(), 1);
1625 assert!(h.is_unique());
1626 }
1627
1628 #[test]
1629 fn test_handle_storage_id_tracks_shared_allocation() {
1630 let first: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1]);
1631 let clone = first.clone();
1632 let second: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1]);
1633
1634 assert_eq!(first.storage_id(), clone.storage_id());
1635 assert_ne!(first.storage_id(), second.storage_id());
1636 }
1637
1638 #[test]
1639 fn test_with_touched_inner_marks_and_reads() {
1640 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![10, 20]);
1641 let first = h.with_touched_inner(|inner| inner.as_ref()[0]);
1642 assert_eq!(first, 10);
1643 assert!(h.was_touched());
1644 }
1645
1646 #[test]
1647 fn test_with_inner_does_not_mark_touched() {
1648 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![10, 20]);
1649 let _ = h.with_inner(|inner| inner.as_ref()[0]);
1650 assert!(
1651 !h.was_touched(),
1652 "with_inner must not flip the touched flag"
1653 );
1654 }
1655
1656 #[test]
1657 fn test_payload_should_log_mode_all() {
1658 let h: CuHandle<Vec<u8>> = CuHandle::new_detached_with_mode(vec![1], HandleContent::All);
1659 assert!(h.payload_should_log());
1660 h.mark_touched();
1661 assert!(h.payload_should_log());
1662 }
1663
1664 #[test]
1665 fn test_payload_should_log_mode_none() {
1666 let h: CuHandle<Vec<u8>> = CuHandle::new_detached_with_mode(vec![1], HandleContent::None);
1667 assert!(!h.payload_should_log());
1668 h.mark_touched();
1669 assert!(
1670 !h.payload_should_log(),
1671 "HandleContent::None must never log payload, even when touched"
1672 );
1673 }
1674
1675 #[test]
1676 fn test_payload_should_log_mode_touched_only() {
1677 let h: CuHandle<Vec<u8>> =
1678 CuHandle::new_detached_with_mode(vec![1], HandleContent::TouchedOnly);
1679 assert!(!h.payload_should_log(), "untouched + TouchedOnly => skip");
1680 h.mark_touched();
1681 assert!(h.payload_should_log(), "touched + TouchedOnly => log");
1682 }
1683
1684 #[test]
1685 fn test_default_policy_returns_true_for_non_handle_types() {
1686 use crate::pool::PayloadDefaultLoggingPolicy as _;
1687 let v: u64 = 42;
1690 assert!(v.payload_should_log());
1691 }
1692
1693 #[test]
1694 fn test_apply_handle_content_policy_overrides_mode() {
1695 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1698 assert_eq!(h.logging_mode(), HandleContent::All);
1699 assert!(h.payload_should_log());
1700
1701 h.apply_handle_content_policy(HandleContent::TouchedOnly);
1702 assert_eq!(h.logging_mode(), HandleContent::TouchedOnly);
1703 assert!(
1704 !h.payload_should_log(),
1705 "after switch to TouchedOnly an untouched handle must skip"
1706 );
1707
1708 h.mark_touched();
1709 assert!(h.payload_should_log(), "touched + TouchedOnly => log");
1710 }
1711
1712 #[test]
1713 fn test_default_apply_policy_is_noop_for_non_handle_types() {
1714 use crate::pool::PayloadDefaultHandlePolicyApply as _;
1715 let v: u64 = 42;
1718 v.apply_handle_content_policy(HandleContent::TouchedOnly);
1719 }
1721
1722 #[cfg(feature = "remote-debug")]
1723 #[test]
1724 fn debug_handle_contents_are_deferred_until_explicitly_included() {
1725 let handle = CuHandle::new_detached(vec![1.25_f32, -2.5_f32]);
1726
1727 let (deferred, attachments) = collect_debug_handle_attachments(|| {
1728 with_debug_handle_contents(false, || {
1729 minicbor_serde::to_vec(&handle).expect("serialize deferred handle")
1730 })
1731 });
1732 let deferred: serde_json::Value =
1733 minicbor_serde::from_slice(&deferred).expect("decode deferred descriptor");
1734 assert_eq!(
1735 deferred.get("__cu_handle__"),
1736 Some(&serde_json::json!(true))
1737 );
1738 assert_eq!(
1739 deferred.get("attachment_id"),
1740 Some(&serde_json::Value::Null)
1741 );
1742 assert!(attachments.is_empty());
1743
1744 let (included, attachments) = collect_debug_handle_attachments(|| {
1745 with_debug_handle_contents(true, || {
1746 minicbor_serde::to_vec(&handle).expect("serialize included handle")
1747 })
1748 });
1749 let included: serde_json::Value =
1750 minicbor_serde::from_slice(&included).expect("decode included descriptor");
1751 assert_eq!(included.get("attachment_id"), Some(&serde_json::json!(0)));
1752 assert_eq!(attachments.len(), 1);
1753 assert_eq!(
1754 attachments[0].encoding,
1755 DebugHandleEncoding::RawLittleEndian
1756 );
1757 assert_eq!(
1758 attachments[0].element_type,
1759 Some(CuSharedMemoryElementType::F32)
1760 );
1761 assert_eq!(
1762 attachments[0].data,
1763 [1.25_f32.to_le_bytes(), (-2.5_f32).to_le_bytes()].concat()
1764 );
1765 }
1766
1767 #[test]
1768 fn test_pool() {
1769 use std::cell::RefCell;
1770 let objs = RefCell::new(vec![vec![1], vec![2], vec![3]]);
1771 let holding = objs.borrow().clone();
1772 let objs_as_slices = holding.iter().map(|x| x.as_slice()).collect::<Vec<_>>();
1773 let pool = CuHostMemoryPool::new("mytestcudapool", 3, || objs.borrow_mut().pop().unwrap())
1774 .unwrap();
1775
1776 let obj1 = pool.acquire().unwrap();
1777 {
1778 let obj2 = pool.acquire().unwrap();
1779 assert!(objs_as_slices.contains(&obj1.lock().unwrap().deref().deref()));
1780 assert!(objs_as_slices.contains(&obj2.lock().unwrap().deref().deref()));
1781 assert_eq!(pool.space_left(), 1);
1782 }
1783 assert_eq!(pool.space_left(), 2);
1784
1785 let obj3 = pool.acquire().unwrap();
1786 assert!(objs_as_slices.contains(&obj3.lock().unwrap().deref().deref()));
1787
1788 assert_eq!(pool.space_left(), 1);
1789
1790 let _obj4 = pool.acquire().unwrap();
1791 assert_eq!(pool.space_left(), 0);
1792
1793 let obj5 = pool.acquire();
1794 assert!(obj5.is_none());
1795 }
1796
1797 #[cfg(all(feature = "cuda", has_nvidia_gpu))]
1798 #[test]
1799 fn test_cuda_pool() {
1800 use crate::pool::cuda::CuCudaPool;
1801 use cudarc::driver::CudaContext;
1802 let ctx = CudaContext::new(0).unwrap();
1803 let pool = CuCudaPool::<f32>::new("mytestcudapool", ctx, 3, 1).unwrap();
1804
1805 let _obj1 = pool.acquire().unwrap();
1806
1807 {
1808 let _obj2 = pool.acquire().unwrap();
1809 assert_eq!(pool.space_left(), 1);
1810 }
1811 assert_eq!(pool.space_left(), 2);
1812
1813 let _obj3 = pool.acquire().unwrap();
1814
1815 assert_eq!(pool.space_left(), 1);
1816
1817 let _obj4 = pool.acquire().unwrap();
1818 assert_eq!(pool.space_left(), 0);
1819
1820 let obj5 = pool.acquire();
1821 assert!(obj5.is_none());
1822 }
1823
1824 #[cfg(all(feature = "cuda", has_nvidia_gpu))]
1825 #[test]
1826 fn test_copy_roundtrip() {
1827 use crate::pool::cuda::CuCudaPool;
1828 use cudarc::driver::CudaContext;
1829 let ctx = CudaContext::new(0).unwrap();
1830 let host_pool = CuHostMemoryPool::new("mytesthostpool", 3, || vec![0.0; 1]).unwrap();
1831 let cuda_pool = CuCudaPool::<f32>::new("mytestcudapool", ctx, 3, 1).unwrap();
1832
1833 let cuda_handle = {
1834 let mut initial_handle = host_pool.acquire().unwrap();
1835 {
1836 let mut inner_initial_handle = initial_handle.lock().unwrap();
1837 if let CuHandleInner::Pooled(ref mut pooled) = *inner_initial_handle {
1838 pooled[0] = 42.0;
1839 } else {
1840 panic!();
1841 }
1842 }
1843
1844 cuda_pool.copy_from(&mut initial_handle)
1846 };
1847
1848 let mut final_handle = host_pool.acquire().unwrap();
1850 cuda_pool
1851 .copy_to_host_pool(&cuda_handle, &mut final_handle)
1852 .unwrap();
1853
1854 let value = final_handle.lock().unwrap().deref().deref()[0];
1855 assert_eq!(value, 42.0);
1856 }
1857}