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;
724
725#[derive(Debug)]
732struct CuHandleCell<T: Debug + Send + Sync> {
733 touched: AtomicBool,
734 mode: AtomicU8,
735 inner: Mutex<CuHandleInner<T>>,
736}
737
738#[derive(Debug)]
742#[cfg_attr(feature = "reflect", derive(bevy_reflect::Reflect))]
743#[cfg_attr(
744 feature = "reflect",
745 reflect(opaque, from_reflect = false, type_path = false)
746)]
747pub struct CuHandle<T: Debug + Send + Sync>(Arc<CuHandleCell<T>>);
748
749#[cfg(feature = "reflect")]
750impl<T: Debug + Send + Sync + 'static> bevy_reflect::TypePath for CuHandle<T> {
751 fn type_path() -> &'static str {
752 core::any::type_name::<Self>()
753 }
754 fn short_type_path() -> &'static str {
755 Self::type_path()
756 }
757}
758
759impl<T: Debug + Send + Sync + bincode::ValueDecode> bincode::ValueDecode for CuHandle<T> {
760 const DECODE: &'static bincode::ValueDecodeSpec = T::DECODE;
761}
762
763impl<T: Debug + Send + Sync> Clone for CuHandle<T> {
764 fn clone(&self) -> Self {
765 Self(self.0.clone())
766 }
767}
768
769impl<T: Debug + Send + Sync> Deref for CuHandle<T> {
770 type Target = Mutex<CuHandleInner<T>>;
771
772 fn deref(&self) -> &Self::Target {
773 &self.0.inner
774 }
775}
776
777impl<T: Debug + Send + Sync> CuHandle<T> {
778 fn from_inner(inner: CuHandleInner<T>, mode: HandleContent) -> Self {
780 CuHandle(Arc::new(CuHandleCell {
781 touched: AtomicBool::new(false),
782 mode: AtomicU8::new(mode as u8),
783 inner: Mutex::new(inner),
784 }))
785 }
786
787 pub fn new_detached(inner: T) -> Self {
789 Self::new_detached_box(Box::new(inner))
790 }
791
792 pub fn new_detached_box(inner: Box<T>) -> Self {
794 Self::from_inner(CuHandleInner::Detached(inner), HandleContent::default())
795 }
796
797 pub fn new_detached_with_mode(inner: T, mode: HandleContent) -> Self {
802 Self::from_inner(CuHandleInner::Detached(Box::new(inner)), mode)
803 }
804
805 pub fn with_inner<R>(&self, f: impl FnOnce(&CuHandleInner<T>) -> R) -> R {
807 let lock = lock_unpoison(&self.0.inner);
808 f(&*lock)
809 }
810
811 pub fn with_inner_mut<R>(&self, f: impl FnOnce(&mut CuHandleInner<T>) -> R) -> R {
813 let mut lock = lock_unpoison(&self.0.inner);
814 f(&mut *lock)
815 }
816
817 pub fn serialize_value<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
824 where
825 T: Serialize,
826 S: Serializer,
827 {
828 let inner = lock_unpoison(&self.0.inner);
829 let value = inner.inner_ref();
830
831 #[cfg(feature = "remote-debug")]
832 if let Some(descriptor) =
833 debug_handle_value_descriptor(value).map_err(<S::Error as serde::ser::Error>::custom)?
834 {
835 return descriptor.serialize(serializer);
836 }
837
838 value.serialize(serializer)
839 }
840
841 pub fn strong_count(&self) -> usize {
843 Arc::strong_count(&self.0)
844 }
845
846 pub fn is_unique(&self) -> bool {
848 self.strong_count() == 1
849 }
850
851 pub fn storage_id(&self) -> usize {
857 Arc::as_ptr(&self.0) as usize
858 }
859
860 pub fn mark_touched(&self) {
866 self.0.touched.store(true, Ordering::Relaxed);
867 }
868
869 pub fn was_touched(&self) -> bool {
872 self.0.touched.load(Ordering::Relaxed)
873 }
874
875 pub fn logging_mode(&self) -> HandleContent {
877 HandleContent::from_u8(self.0.mode.load(Ordering::Relaxed))
878 }
879
880 pub fn with_touched_inner<R>(&self, f: impl FnOnce(&CuHandleInner<T>) -> R) -> R {
883 self.mark_touched();
884 self.with_inner(f)
885 }
886
887 pub fn payload_should_log(&self) -> bool {
895 match self.logging_mode() {
896 HandleContent::All => true,
897 HandleContent::None => false,
898 HandleContent::TouchedOnly => self.was_touched(),
899 }
900 }
901
902 pub fn apply_handle_content_policy(&self, mode: HandleContent) {
906 self.0.mode.store(mode as u8, Ordering::Relaxed);
907 }
908}
909
910pub trait HandleContentAware {}
923
924impl<T: Debug + Send + Sync> HandleContentAware for CuHandle<T> {}
925
926pub trait PayloadDefaultLoggingPolicy {
934 fn payload_should_log(&self) -> bool {
935 true
936 }
937}
938
939impl<T: ?Sized> PayloadDefaultLoggingPolicy for T {}
940
941pub trait PayloadDefaultHandlePolicyApply {
946 fn apply_handle_content_policy(&self, _mode: HandleContent) {}
947}
948
949impl<T: ?Sized> PayloadDefaultHandlePolicyApply for T {}
950
951impl<U> Serialize for CuHandle<Vec<U>>
952where
953 U: ElementType + Serialize + 'static,
954{
955 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
956 let inner = lock_unpoison(&self.0.inner);
957 let values = inner.inner_ref();
958
959 #[cfg(feature = "remote-debug")]
960 if let Some(descriptor) = debug_handle_descriptor(values.as_slice())
961 .map_err(<S::Error as serde::ser::Error>::custom)?
962 {
963 return descriptor.serialize(serializer);
964 }
965
966 values.serialize(serializer)
967 }
968}
969
970impl<'de, U> Deserialize<'de> for CuHandle<Vec<U>>
971where
972 U: ElementType + Deserialize<'de> + 'static,
973{
974 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
975 Vec::<U>::deserialize(deserializer).map(CuHandle::new_detached)
976 }
977}
978
979impl<U> Serialize for CuHandle<CuSharedMemoryBuffer<U>>
980where
981 U: ElementType + Serialize + 'static,
982{
983 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
984 let inner = lock_unpoison(&self.0.inner);
985 let buffer = inner.inner_ref();
986
987 #[cfg(feature = "remote-debug")]
988 if let Some(descriptor) = debug_handle_descriptor(buffer.deref())
989 .map_err(<S::Error as serde::ser::Error>::custom)?
990 {
991 return descriptor.serialize(serializer);
992 }
993
994 if shared_handle_serialization_enabled()
995 && let Some(descriptor) = buffer.descriptor()
996 {
997 return descriptor.serialize(serializer);
998 }
999
1000 buffer.deref().serialize(serializer)
1001 }
1002}
1003
1004impl<'de, U> Deserialize<'de> for CuHandle<CuSharedMemoryBuffer<U>>
1005where
1006 U: ElementType + Deserialize<'de> + 'static,
1007{
1008 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1009 enum Repr<U> {
1010 Descriptor(CuSharedMemoryHandleDescriptor),
1011 Data(Vec<U>),
1012 }
1013
1014 impl<'de, U> Deserialize<'de> for Repr<U>
1015 where
1016 U: ElementType + Deserialize<'de>,
1017 {
1018 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1019 struct ReprVisitor<U>(PhantomData<U>);
1020
1021 impl<'de, U> Visitor<'de> for ReprVisitor<U>
1022 where
1023 U: ElementType + Deserialize<'de>,
1024 {
1025 type Value = Repr<U>;
1026
1027 fn expecting(
1028 &self,
1029 formatter: &mut std::fmt::Formatter<'_>,
1030 ) -> std::fmt::Result {
1031 formatter
1032 .write_str("a shared-memory handle descriptor or an element sequence")
1033 }
1034
1035 fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
1036 let data =
1037 Vec::<U>::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
1038 Ok(Repr::Data(data))
1039 }
1040
1041 fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Self::Value, A::Error> {
1042 let descriptor = CuSharedMemoryHandleDescriptor::deserialize(
1043 de::value::MapAccessDeserializer::new(map),
1044 )?;
1045 Ok(Repr::Descriptor(descriptor))
1046 }
1047 }
1048
1049 deserializer.deserialize_any(ReprVisitor(PhantomData))
1050 }
1051 }
1052
1053 match Repr::<U>::deserialize(deserializer)? {
1054 Repr::Descriptor(descriptor) => CuSharedMemoryBuffer::from_descriptor(&descriptor)
1055 .map(CuHandle::new_detached)
1056 .map_err(de::Error::custom),
1057 Repr::Data(data) => CuSharedMemoryBuffer::from_vec_detached(data)
1058 .map(CuHandle::new_detached)
1059 .map_err(de::Error::custom),
1060 }
1061 }
1062}
1063
1064impl<T: ArrayLike + Encode> Encode for CuHandle<T>
1065where
1066 <T as ArrayLike>::Element: 'static,
1067{
1068 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
1069 let inner = lock_unpoison(&self.0.inner);
1070 crate::monitoring::record_payload_handle_bytes(
1071 inner.inner_ref().len() * size_of::<T::Element>(),
1072 );
1073 inner.inner_ref().encode(encoder)
1074 }
1075}
1076
1077impl<T: Debug + Send + Sync> Default for CuHandle<T> {
1078 fn default() -> Self {
1079 panic!("Cannot create a default CuHandle")
1080 }
1081}
1082
1083impl<U: ElementType + Decode<()> + 'static> Decode<()> for CuHandle<Vec<U>> {
1084 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
1085 let vec: Vec<U> = Vec::decode(decoder)?;
1086 Ok(CuHandle::new_detached(vec))
1087 }
1088}
1089
1090impl<U: ElementType + Decode<()> + 'static> Decode<()> for CuHandle<CuSharedMemoryBuffer<U>> {
1091 fn decode<D: Decoder<Context = ()>>(decoder: &mut D) -> Result<Self, DecodeError> {
1092 let buffer = CuSharedMemoryBuffer::<U>::decode(decoder)?;
1093 Ok(CuHandle::new_detached(buffer))
1094 }
1095}
1096
1097pub trait CuPool<T: ArrayLike>: PoolMonitor {
1100 fn acquire(&self) -> Option<CuHandle<T>>;
1102
1103 fn copy_from<O>(&self, from: &mut CuHandle<O>) -> CuHandle<T>
1105 where
1106 O: ArrayLike<Element = T::Element>;
1107}
1108
1109pub trait DeviceCuPool<T: ArrayLike>: CuPool<T> {
1111 fn copy_to_host_pool<O>(
1114 &self,
1115 from_device_handle: &CuHandle<T>,
1116 to_host_handle: &mut CuHandle<O>,
1117 ) -> CuResult<()>
1118 where
1119 O: ArrayLike<Element = T::Element>;
1120}
1121
1122pub struct CuHostMemoryPool<T> {
1124 id: PoolID,
1127 pool: Arc<Pool<Box<T>>>,
1128 size: usize,
1129 buffer_size: usize,
1130}
1131
1132impl<T: ArrayLike + 'static> CuHostMemoryPool<T> {
1133 pub fn new<F>(id: &str, size: usize, buffer_initializer: F) -> CuResult<Arc<Self>>
1134 where
1135 F: Fn() -> T,
1136 {
1137 let pool = Arc::new(Pool::new(size, move || Box::new(buffer_initializer())));
1138 let buffer_size = pool.try_pull().unwrap().len() * size_of::<T::Element>();
1139
1140 let og = Self {
1141 id: PoolID::from(id).map_err(|_| "Failed to create PoolID")?,
1142 pool,
1143 size,
1144 buffer_size,
1145 };
1146 let og = Arc::new(og);
1147 register_pool(og.clone());
1148 Ok(og)
1149 }
1150}
1151
1152impl<T: ArrayLike> PoolMonitor for CuHostMemoryPool<T> {
1153 fn id(&self) -> PoolID {
1154 self.id
1155 }
1156
1157 fn space_left(&self) -> usize {
1158 self.pool.len()
1159 }
1160
1161 fn total_size(&self) -> usize {
1162 self.size
1163 }
1164
1165 fn buffer_size(&self) -> usize {
1166 self.buffer_size
1167 }
1168}
1169
1170impl<T: ArrayLike> CuPool<T> for CuHostMemoryPool<T> {
1171 fn acquire(&self) -> Option<CuHandle<T>> {
1172 let owned_object = self.pool.try_pull_owned(); owned_object.map(|reusable| {
1175 CuHandle::from_inner(CuHandleInner::Pooled(reusable), HandleContent::default())
1176 })
1177 }
1178
1179 fn copy_from<O: ArrayLike<Element = T::Element>>(&self, from: &mut CuHandle<O>) -> CuHandle<T> {
1180 let to_handle = self.acquire().expect("No available buffers in the pool");
1181 {
1182 let from_lock = lock_unpoison(&from.0.inner);
1183 let mut to_lock = lock_unpoison(&to_handle.0.inner);
1184 to_lock.inner_mut().copy_from_slice(from_lock.inner_ref());
1185 }
1186 to_handle
1187 }
1188}
1189
1190pub struct CuSharedMemoryPool<E: ElementType> {
1193 id: PoolID,
1194 pool: Arc<Pool<Box<CuSharedMemoryBuffer<E>>>>,
1195 size: usize,
1196 buffer_size: usize,
1197}
1198
1199impl<E: ElementType + 'static> CuSharedMemoryPool<E> {
1200 pub fn new(id: &str, size: usize, elements_per_buffer: usize) -> CuResult<Arc<Self>> {
1201 let slot_stride = shared_slot_stride::<E>(elements_per_buffer.max(1));
1202 let region = CuSharedMemoryRegion::create(
1203 slot_stride
1204 .checked_mul(size)
1205 .ok_or_else(|| cu29_traits::CuError::from("shared memory pool size overflow"))?,
1206 )?;
1207 let next_slot = Arc::new(std::sync::atomic::AtomicUsize::new(0));
1208 let initializer_region = region.clone();
1209 let initializer_next_slot = next_slot.clone();
1210 let pool = Arc::new(Pool::new(size, move || {
1211 let slot = initializer_next_slot.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
1212 assert!(slot < size, "shared memory pool slot index overflow");
1213 Box::new(CuSharedMemoryBuffer::from_region(
1214 initializer_region.clone(),
1215 slot * slot_stride,
1216 elements_per_buffer,
1217 ))
1218 }));
1219
1220 let pool = Arc::new(Self {
1221 id: PoolID::from(id).map_err(|_| "Failed to create PoolID")?,
1222 pool,
1223 size,
1224 buffer_size: elements_per_buffer * size_of::<E>(),
1225 });
1226 register_pool(pool.clone());
1227 Ok(pool)
1228 }
1229}
1230
1231impl<E: ElementType> PoolMonitor for CuSharedMemoryPool<E> {
1232 fn id(&self) -> PoolID {
1233 self.id
1234 }
1235
1236 fn space_left(&self) -> usize {
1237 self.pool.len()
1238 }
1239
1240 fn total_size(&self) -> usize {
1241 self.size
1242 }
1243
1244 fn buffer_size(&self) -> usize {
1245 self.buffer_size
1246 }
1247}
1248
1249impl<E: ElementType> CuPool<CuSharedMemoryBuffer<E>> for CuSharedMemoryPool<E> {
1250 fn acquire(&self) -> Option<CuHandle<CuSharedMemoryBuffer<E>>> {
1251 self.pool.try_pull_owned().map(|reusable| {
1252 CuHandle::from_inner(CuHandleInner::Pooled(reusable), HandleContent::default())
1253 })
1254 }
1255
1256 fn copy_from<O>(&self, from: &mut CuHandle<O>) -> CuHandle<CuSharedMemoryBuffer<E>>
1257 where
1258 O: ArrayLike<Element = E>,
1259 {
1260 let to_handle = self.acquire().expect("No available buffers in the pool");
1261 {
1262 let from_lock = lock_unpoison(&from.0.inner);
1263 let mut to_lock = lock_unpoison(&to_handle.0.inner);
1264 to_lock.inner_mut().copy_from_slice(from_lock.inner_ref());
1265 }
1266 to_handle
1267 }
1268}
1269
1270impl<E: ElementType + 'static> ArrayLike for Vec<E> {
1271 type Element = E;
1272}
1273
1274#[cfg(all(feature = "cuda", not(target_os = "macos")))]
1275pub mod cuda {
1276 use super::*;
1277 use cu29_traits::CuError;
1278 use cudarc::driver::{
1279 CudaContext, CudaSlice, CudaStream, DeviceRepr, HostSlice, SyncOnDrop, ValidAsZeroBits,
1280 };
1281 use std::sync::Arc;
1282
1283 #[derive(Debug)]
1284 pub struct CudaSliceWrapper<E>(CudaSlice<E>);
1285
1286 impl<E> Deref for CudaSliceWrapper<E>
1287 where
1288 E: ElementType,
1289 {
1290 type Target = [E];
1291
1292 fn deref(&self) -> &Self::Target {
1293 panic!("You need to copy data to host memory pool before accessing it.");
1295 }
1296 }
1297
1298 impl<E> DerefMut for CudaSliceWrapper<E>
1299 where
1300 E: ElementType,
1301 {
1302 fn deref_mut(&mut self) -> &mut Self::Target {
1303 panic!("You need to copy data to host memory pool before accessing it.");
1304 }
1305 }
1306
1307 impl<E: ElementType> ArrayLike for CudaSliceWrapper<E> {
1308 type Element = E;
1309 }
1310
1311 impl<E> CudaSliceWrapper<E> {
1312 pub fn len(&self) -> usize {
1314 self.0.len()
1315 }
1316
1317 pub fn is_empty(&self) -> bool {
1319 self.0.is_empty()
1320 }
1321
1322 pub fn num_bytes(&self) -> usize {
1324 self.0.num_bytes()
1325 }
1326
1327 pub fn as_cuda_slice(&self) -> &CudaSlice<E> {
1328 &self.0
1329 }
1330
1331 pub fn as_cuda_slice_mut(&mut self) -> &mut CudaSlice<E> {
1332 &mut self.0
1333 }
1334 }
1335
1336 pub struct HostSliceWrapper<'a, T: ArrayLike> {
1338 inner: &'a T,
1339 }
1340
1341 impl<T: ArrayLike> HostSlice<T::Element> for HostSliceWrapper<'_, T> {
1342 fn len(&self) -> usize {
1343 self.inner.len()
1344 }
1345
1346 unsafe fn stream_synced_slice<'b>(
1348 &'b self,
1349 stream: &'b CudaStream,
1350 ) -> (&'b [T::Element], SyncOnDrop<'b>) {
1351 (self.inner.deref(), SyncOnDrop::sync_stream(stream))
1352 }
1353
1354 unsafe fn stream_synced_mut_slice<'b>(
1356 &'b mut self,
1357 _stream: &'b CudaStream,
1358 ) -> (&'b mut [T::Element], SyncOnDrop<'b>) {
1359 panic!("Cannot get mutable reference from immutable wrapper")
1360 }
1361 }
1362
1363 pub struct HostSliceMutWrapper<'a, T: ArrayLike> {
1365 inner: &'a mut T,
1366 }
1367
1368 impl<T: ArrayLike> HostSlice<T::Element> for HostSliceMutWrapper<'_, T> {
1369 fn len(&self) -> usize {
1370 self.inner.len()
1371 }
1372
1373 unsafe fn stream_synced_slice<'b>(
1375 &'b self,
1376 stream: &'b CudaStream,
1377 ) -> (&'b [T::Element], SyncOnDrop<'b>) {
1378 (self.inner.deref(), SyncOnDrop::sync_stream(stream))
1379 }
1380
1381 unsafe fn stream_synced_mut_slice<'b>(
1383 &'b mut self,
1384 stream: &'b CudaStream,
1385 ) -> (&'b mut [T::Element], SyncOnDrop<'b>) {
1386 (self.inner.deref_mut(), SyncOnDrop::sync_stream(stream))
1387 }
1388 }
1389
1390 impl<E: ElementType + ValidAsZeroBits + DeviceRepr> CuCudaPool<E> {
1392 fn get_host_slice_wrapper<O: ArrayLike<Element = E>>(
1394 handle_inner: &CuHandleInner<O>,
1395 ) -> HostSliceWrapper<'_, O> {
1396 HostSliceWrapper {
1397 inner: handle_inner.inner_ref(),
1398 }
1399 }
1400
1401 fn get_host_slice_mut_wrapper<O: ArrayLike<Element = E>>(
1403 handle_inner: &mut CuHandleInner<O>,
1404 ) -> HostSliceMutWrapper<'_, O> {
1405 HostSliceMutWrapper {
1406 inner: handle_inner.inner_mut(),
1407 }
1408 }
1409 }
1410 pub struct CuCudaPool<E>
1412 where
1413 E: ElementType + ValidAsZeroBits + DeviceRepr + Unpin,
1414 {
1415 id: PoolID,
1416 stream: Arc<CudaStream>,
1417 pool: Arc<Pool<Box<CudaSliceWrapper<E>>>>,
1418 nb_buffers: usize,
1419 nb_element_per_buffer: usize,
1420 }
1421
1422 impl<E: ElementType + ValidAsZeroBits + DeviceRepr + 'static> CuCudaPool<E> {
1423 pub fn new(
1424 id: &str,
1425 ctx: Arc<CudaContext>,
1426 nb_buffers: usize,
1427 nb_element_per_buffer: usize,
1428 ) -> CuResult<Arc<Self>> {
1429 let stream = ctx.default_stream();
1430 let pool = (0..nb_buffers)
1431 .map(|_| {
1432 stream
1433 .alloc_zeros(nb_element_per_buffer)
1434 .map(CudaSliceWrapper)
1435 .map(Box::new)
1436 .map_err(|_| "Failed to allocate device memory")
1437 })
1438 .collect::<Result<Vec<_>, _>>()?;
1439
1440 let pool = Arc::new(Self {
1441 id: PoolID::from(id).map_err(|_| "Failed to create PoolID")?,
1442 stream,
1443 pool: Arc::new(Pool::from_vec(pool)),
1444 nb_buffers,
1445 nb_element_per_buffer,
1446 });
1447 register_pool(pool.clone());
1448 Ok(pool)
1449 }
1450 }
1451
1452 impl<E> PoolMonitor for CuCudaPool<E>
1453 where
1454 E: DeviceRepr + ElementType + ValidAsZeroBits,
1455 {
1456 fn id(&self) -> PoolID {
1457 self.id
1458 }
1459
1460 fn space_left(&self) -> usize {
1461 self.pool.len()
1462 }
1463
1464 fn total_size(&self) -> usize {
1465 self.nb_buffers
1466 }
1467
1468 fn buffer_size(&self) -> usize {
1469 self.nb_element_per_buffer * size_of::<E>()
1470 }
1471 }
1472
1473 impl<E> CuPool<CudaSliceWrapper<E>> for CuCudaPool<E>
1474 where
1475 E: DeviceRepr + ElementType + ValidAsZeroBits,
1476 {
1477 fn acquire(&self) -> Option<CuHandle<CudaSliceWrapper<E>>> {
1478 self.pool
1479 .try_pull_owned()
1480 .map(|x| CuHandle::from_inner(CuHandleInner::Pooled(x), HandleContent::default()))
1481 }
1482
1483 fn copy_from<O>(&self, from_handle: &mut CuHandle<O>) -> CuHandle<CudaSliceWrapper<E>>
1484 where
1485 O: ArrayLike<Element = E>,
1486 {
1487 let to_handle = self.acquire().expect("No available buffers in the pool");
1488
1489 {
1490 let from_lock = lock_unpoison(&from_handle.0.inner);
1491 let mut to_lock = lock_unpoison(&to_handle.0.inner);
1492
1493 match &mut *to_lock {
1494 CuHandleInner::Detached(to) => {
1495 let wrapper = Self::get_host_slice_wrapper(&*from_lock);
1496 self.stream
1497 .memcpy_htod(&wrapper, to.deref_mut().as_cuda_slice_mut())
1498 .expect("Failed to copy data to device");
1499 }
1500 CuHandleInner::Pooled(to) => {
1501 let wrapper = Self::get_host_slice_wrapper(&*from_lock);
1502 self.stream
1503 .memcpy_htod(&wrapper, to.deref_mut().as_mut().as_cuda_slice_mut())
1504 .expect("Failed to copy data to device");
1505 }
1506 }
1507 } to_handle }
1510 }
1511
1512 impl<E> DeviceCuPool<CudaSliceWrapper<E>> for CuCudaPool<E>
1513 where
1514 E: ElementType + ValidAsZeroBits + DeviceRepr,
1515 {
1516 fn copy_to_host_pool<O>(
1518 &self,
1519 device_handle: &CuHandle<CudaSliceWrapper<E>>,
1520 host_handle: &mut CuHandle<O>,
1521 ) -> Result<(), CuError>
1522 where
1523 O: ArrayLike<Element = E>,
1524 {
1525 let device_lock = device_handle.lock().map_err(|e| {
1526 CuError::from("Device handle mutex poisoned").add_cause(&e.to_string())
1527 })?;
1528 let mut host_lock = host_handle.lock().map_err(|e| {
1529 CuError::from("Host handle mutex poisoned").add_cause(&e.to_string())
1530 })?;
1531 let src = match &*device_lock {
1532 CuHandleInner::Pooled(source) => source.deref().as_ref().as_cuda_slice(),
1533 CuHandleInner::Detached(source) => source.deref().as_cuda_slice(),
1534 };
1535 let mut wrapper = Self::get_host_slice_mut_wrapper(&mut *host_lock);
1536 self.stream.memcpy_dtoh(src, &mut wrapper).map_err(|e| {
1537 CuError::from("Failed to copy data from device to host").add_cause(&e.to_string())
1538 })?;
1539 Ok(())
1540 }
1541 }
1542}
1543
1544#[derive(Debug)]
1545pub struct AlignedBuffer<E: ElementType> {
1547 ptr: *mut E,
1548 size: usize,
1549 layout: Layout,
1550}
1551
1552impl<E: ElementType> AlignedBuffer<E> {
1553 pub fn new(num_elements: usize, alignment: usize) -> Self {
1554 assert!(
1555 num_elements > 0 && size_of::<E>() > 0,
1556 "AlignedBuffer requires a non-zero element count and non-zero-sized element type"
1557 );
1558 let alignment = alignment.max(align_of::<E>());
1559 let alloc_size = num_elements
1560 .checked_mul(size_of::<E>())
1561 .expect("AlignedBuffer allocation size overflow");
1562 let layout = Layout::from_size_align(alloc_size, alignment).unwrap();
1563 let ptr = unsafe { alloc(layout) as *mut E };
1565 if ptr.is_null() {
1566 panic!("Failed to allocate memory");
1567 }
1568 unsafe {
1570 for i in 0..num_elements {
1571 std::ptr::write(ptr.add(i), E::default());
1572 }
1573 }
1574 Self {
1575 ptr,
1576 size: num_elements,
1577 layout,
1578 }
1579 }
1580}
1581
1582impl<E: ElementType> Deref for AlignedBuffer<E> {
1583 type Target = [E];
1584
1585 fn deref(&self) -> &Self::Target {
1586 unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
1588 }
1589}
1590
1591impl<E: ElementType> DerefMut for AlignedBuffer<E> {
1592 fn deref_mut(&mut self) -> &mut Self::Target {
1593 unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) }
1595 }
1596}
1597
1598impl<E: ElementType> Drop for AlignedBuffer<E> {
1599 fn drop(&mut self) {
1600 unsafe { dealloc(self.ptr as *mut u8, self.layout) }
1602 }
1603}
1604
1605#[cfg(test)]
1606mod tests {
1607 use super::*;
1608
1609 #[cfg(feature = "self-describing-logs")]
1610 #[test]
1611 fn test_catalog_decodes_native_handle_buffer() {
1612 use cu29_value::Value;
1613 use cu29_value::decode::{ValueDecodeDescription, ValueDecodeLimits};
1614
1615 let handle = CuHandle::new_detached(vec![1u16, 300, 65535]);
1616 let config = bincode::config::standard();
1617 let bytes = bincode::encode_to_vec(&handle, config).unwrap();
1618 let description = ValueDecodeDescription::from_type::<CuHandle<Vec<u16>>>().unwrap();
1619 let (decoded, used) = description
1620 .decode(&bytes, config, ValueDecodeLimits::default())
1621 .unwrap();
1622 assert_eq!(used, bytes.len());
1623 assert_eq!(
1624 decoded,
1625 Value::Seq(vec![Value::U16(1), Value::U16(300), Value::U16(65535)])
1626 );
1627 assert!(!handle.was_touched());
1628 }
1629
1630 #[test]
1631 fn test_handle_touched_flag_defaults_false() {
1632 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1633 assert!(!h.was_touched());
1634 assert_eq!(h.logging_mode(), HandleContent::All);
1635 }
1636
1637 #[test]
1638 fn test_handle_mark_touched_propagates_across_clones() {
1639 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1640 let clone = h.clone();
1641 assert!(!h.was_touched());
1642 assert!(!clone.was_touched());
1643
1644 clone.mark_touched();
1645 assert!(h.was_touched());
1647 assert!(clone.was_touched());
1648 }
1649
1650 #[test]
1651 fn test_handle_strong_count_tracks_clones() {
1652 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1653 assert_eq!(h.strong_count(), 1);
1654 assert!(h.is_unique());
1655
1656 let clone = h.clone();
1657 assert_eq!(h.strong_count(), 2);
1658 assert_eq!(clone.strong_count(), 2);
1659 assert!(!h.is_unique());
1660 assert!(!clone.is_unique());
1661
1662 drop(clone);
1663 assert_eq!(h.strong_count(), 1);
1664 assert!(h.is_unique());
1665 }
1666
1667 #[test]
1668 fn test_handle_storage_id_tracks_shared_allocation() {
1669 let first: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1]);
1670 let clone = first.clone();
1671 let second: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1]);
1672
1673 assert_eq!(first.storage_id(), clone.storage_id());
1674 assert_ne!(first.storage_id(), second.storage_id());
1675 }
1676
1677 #[test]
1678 fn test_with_touched_inner_marks_and_reads() {
1679 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![10, 20]);
1680 let first = h.with_touched_inner(|inner| inner.as_ref()[0]);
1681 assert_eq!(first, 10);
1682 assert!(h.was_touched());
1683 }
1684
1685 #[test]
1686 fn test_with_inner_does_not_mark_touched() {
1687 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![10, 20]);
1688 let _ = h.with_inner(|inner| inner.as_ref()[0]);
1689 assert!(
1690 !h.was_touched(),
1691 "with_inner must not flip the touched flag"
1692 );
1693 }
1694
1695 #[test]
1696 fn test_payload_should_log_mode_all() {
1697 let h: CuHandle<Vec<u8>> = CuHandle::new_detached_with_mode(vec![1], HandleContent::All);
1698 assert!(h.payload_should_log());
1699 h.mark_touched();
1700 assert!(h.payload_should_log());
1701 }
1702
1703 #[test]
1704 fn test_payload_should_log_mode_none() {
1705 let h: CuHandle<Vec<u8>> = CuHandle::new_detached_with_mode(vec![1], HandleContent::None);
1706 assert!(!h.payload_should_log());
1707 h.mark_touched();
1708 assert!(
1709 !h.payload_should_log(),
1710 "HandleContent::None must never log payload, even when touched"
1711 );
1712 }
1713
1714 #[test]
1715 fn test_payload_should_log_mode_touched_only() {
1716 let h: CuHandle<Vec<u8>> =
1717 CuHandle::new_detached_with_mode(vec![1], HandleContent::TouchedOnly);
1718 assert!(!h.payload_should_log(), "untouched + TouchedOnly => skip");
1719 h.mark_touched();
1720 assert!(h.payload_should_log(), "touched + TouchedOnly => log");
1721 }
1722
1723 #[test]
1724 fn test_default_policy_returns_true_for_non_handle_types() {
1725 use crate::pool::PayloadDefaultLoggingPolicy as _;
1726 let v: u64 = 42;
1729 assert!(v.payload_should_log());
1730 }
1731
1732 #[test]
1733 fn test_apply_handle_content_policy_overrides_mode() {
1734 let h: CuHandle<Vec<u8>> = CuHandle::new_detached(vec![1, 2, 3]);
1737 assert_eq!(h.logging_mode(), HandleContent::All);
1738 assert!(h.payload_should_log());
1739
1740 h.apply_handle_content_policy(HandleContent::TouchedOnly);
1741 assert_eq!(h.logging_mode(), HandleContent::TouchedOnly);
1742 assert!(
1743 !h.payload_should_log(),
1744 "after switch to TouchedOnly an untouched handle must skip"
1745 );
1746
1747 h.mark_touched();
1748 assert!(h.payload_should_log(), "touched + TouchedOnly => log");
1749 }
1750
1751 #[test]
1752 fn test_default_apply_policy_is_noop_for_non_handle_types() {
1753 use crate::pool::PayloadDefaultHandlePolicyApply as _;
1754 let v: u64 = 42;
1757 v.apply_handle_content_policy(HandleContent::TouchedOnly);
1758 }
1760
1761 #[cfg(feature = "remote-debug")]
1762 #[test]
1763 fn debug_handle_contents_are_deferred_until_explicitly_included() {
1764 let handle = CuHandle::new_detached(vec![1.25_f32, -2.5_f32]);
1765
1766 let (deferred, attachments) = collect_debug_handle_attachments(|| {
1767 with_debug_handle_contents(false, || {
1768 minicbor_serde::to_vec(&handle).expect("serialize deferred handle")
1769 })
1770 });
1771 let deferred: serde_json::Value =
1772 minicbor_serde::from_slice(&deferred).expect("decode deferred descriptor");
1773 assert_eq!(
1774 deferred.get("__cu_handle__"),
1775 Some(&serde_json::json!(true))
1776 );
1777 assert_eq!(
1778 deferred.get("attachment_id"),
1779 Some(&serde_json::Value::Null)
1780 );
1781 assert!(attachments.is_empty());
1782
1783 let (included, attachments) = collect_debug_handle_attachments(|| {
1784 with_debug_handle_contents(true, || {
1785 minicbor_serde::to_vec(&handle).expect("serialize included handle")
1786 })
1787 });
1788 let included: serde_json::Value =
1789 minicbor_serde::from_slice(&included).expect("decode included descriptor");
1790 assert_eq!(included.get("attachment_id"), Some(&serde_json::json!(0)));
1791 assert_eq!(attachments.len(), 1);
1792 assert_eq!(
1793 attachments[0].encoding,
1794 DebugHandleEncoding::RawLittleEndian
1795 );
1796 assert_eq!(
1797 attachments[0].element_type,
1798 Some(CuSharedMemoryElementType::F32)
1799 );
1800 assert_eq!(
1801 attachments[0].data,
1802 [1.25_f32.to_le_bytes(), (-2.5_f32).to_le_bytes()].concat()
1803 );
1804 }
1805
1806 #[test]
1807 fn test_pool() {
1808 use std::cell::RefCell;
1809 let objs = RefCell::new(vec![vec![1], vec![2], vec![3]]);
1810 let holding = objs.borrow().clone();
1811 let objs_as_slices = holding.iter().map(|x| x.as_slice()).collect::<Vec<_>>();
1812 let pool = CuHostMemoryPool::new("mytestcudapool", 3, || objs.borrow_mut().pop().unwrap())
1813 .unwrap();
1814
1815 let obj1 = pool.acquire().unwrap();
1816 {
1817 let obj2 = pool.acquire().unwrap();
1818 assert!(objs_as_slices.contains(&obj1.lock().unwrap().deref().deref()));
1819 assert!(objs_as_slices.contains(&obj2.lock().unwrap().deref().deref()));
1820 assert_eq!(pool.space_left(), 1);
1821 }
1822 assert_eq!(pool.space_left(), 2);
1823
1824 let obj3 = pool.acquire().unwrap();
1825 assert!(objs_as_slices.contains(&obj3.lock().unwrap().deref().deref()));
1826
1827 assert_eq!(pool.space_left(), 1);
1828
1829 let _obj4 = pool.acquire().unwrap();
1830 assert_eq!(pool.space_left(), 0);
1831
1832 let obj5 = pool.acquire();
1833 assert!(obj5.is_none());
1834 }
1835
1836 #[cfg(all(feature = "cuda", has_nvidia_gpu))]
1837 #[test]
1838 fn test_cuda_pool() {
1839 use crate::pool::cuda::CuCudaPool;
1840 use cudarc::driver::CudaContext;
1841 let ctx = CudaContext::new(0).unwrap();
1842 let pool = CuCudaPool::<f32>::new("mytestcudapool", ctx, 3, 1).unwrap();
1843
1844 let _obj1 = pool.acquire().unwrap();
1845
1846 {
1847 let _obj2 = pool.acquire().unwrap();
1848 assert_eq!(pool.space_left(), 1);
1849 }
1850 assert_eq!(pool.space_left(), 2);
1851
1852 let _obj3 = pool.acquire().unwrap();
1853
1854 assert_eq!(pool.space_left(), 1);
1855
1856 let _obj4 = pool.acquire().unwrap();
1857 assert_eq!(pool.space_left(), 0);
1858
1859 let obj5 = pool.acquire();
1860 assert!(obj5.is_none());
1861 }
1862
1863 #[cfg(all(feature = "cuda", has_nvidia_gpu))]
1864 #[test]
1865 fn test_copy_roundtrip() {
1866 use crate::pool::cuda::CuCudaPool;
1867 use cudarc::driver::CudaContext;
1868 let ctx = CudaContext::new(0).unwrap();
1869 let host_pool = CuHostMemoryPool::new("mytesthostpool", 3, || vec![0.0; 1]).unwrap();
1870 let cuda_pool = CuCudaPool::<f32>::new("mytestcudapool", ctx, 3, 1).unwrap();
1871
1872 let cuda_handle = {
1873 let mut initial_handle = host_pool.acquire().unwrap();
1874 {
1875 let mut inner_initial_handle = initial_handle.lock().unwrap();
1876 if let CuHandleInner::Pooled(ref mut pooled) = *inner_initial_handle {
1877 pooled[0] = 42.0;
1878 } else {
1879 panic!();
1880 }
1881 }
1882
1883 cuda_pool.copy_from(&mut initial_handle)
1885 };
1886
1887 let mut final_handle = host_pool.acquire().unwrap();
1889 cuda_pool
1890 .copy_to_host_pool(&cuda_handle, &mut final_handle)
1891 .unwrap();
1892
1893 let value = final_handle.lock().unwrap().deref().deref()[0];
1894 assert_eq!(value, 42.0);
1895 }
1896}